// HARFANG(R) Copyright (C) 2021 Emmanuel Julien, NWNC HARFANG. Released under GPL/LGPL/Commercial Licence, see licence.txt for details. #include "fabgen.h" #include "engine/assets_rw_interface.h" #include "engine/file_format.h" #include "engine/meta.h" #include "engine/render_pipeline.h" #include "engine/scene.h" #include "foundation/data_rw_interface.h" #include "foundation/file_rw_interface.h" #include "foundation/format.h" #include "foundation/log.h" #include "foundation/pack_float.h" #include "foundation/string.h" #include "json/json.hpp" #include #include namespace hg { Scene::Scene() : scene_ref(new SceneRef{this}) {} Scene::~Scene() { Clear(); scene_ref->scene = nullptr; } // void Scene::SetProbe(TextureRef irradiance, TextureRef radiance, TextureRef brdf) { environment.probe = {}; environment.probe.irradiance_map = irradiance; environment.probe.radiance_map = radiance; environment.brdf_map = brdf; } // static void _ResizeComponents(std::vector &cs) { ptrdiff_t i; for (i = cs.size() - 1; i >= 0; --i) if (cs[i] != invalid_gen_ref) break; cs.resize(i + 1); } // void Scene::Clear() { // physic world collisions.clear(); rigid_bodies.clear(); node_collisions.clear(); // instances node_instance.clear(); node_instance_view.clear(); // scene graph nodes.clear(); transforms.clear(); cameras.clear(); objects.clear(); lights.clear(); // animations anims.clear(); scene_anims.clear(); play_anims.clear(); // scripts scripts.clear(); scene_scripts.clear(); node_scripts.clear(); // current_camera = {}; transform_worlds.clear(); environment = {}; // key_values.clear(); } size_t Scene::GarbageCollectPass() { size_t removed_count = 0; #define GC_Components(LIST, IDX, ON_REMOVE) \ { \ std::vector is_refd(LIST.capacity(), false); \ \ for (const auto &n : nodes) { \ const auto ref = n.components[IDX]; \ if (LIST.is_valid(ref)) \ is_refd[ref.idx] = true; \ } \ \ for (size_t i = 0; i < is_refd.size(); ++i) \ if (!is_refd[i] && LIST.is_used(uint32_t(i))) { \ ON_REMOVE(LIST.get_ref(uint32_t(i))); \ ++removed_count; \ } \ } GC_Components(transforms, NCI_Transform, DestroyTransform); GC_Components(cameras, NCI_Camera, DestroyCamera); GC_Components(lights, NCI_Light, DestroyLight); GC_Components(objects, NCI_Object, DestroyObject); GC_Components(rigid_bodies, NCI_RigidBody, DestroyRigidBody); // cleanup collisions { std::vector is_refd(collisions.capacity(), false); for (auto i = std::begin(node_collisions); i != std::end(node_collisions);) { if (nodes.is_valid(i->first)) { auto &node_collisions = i->second; for (auto &ref : node_collisions) if (collisions.is_valid(ref)) is_refd[ref.idx] = true; else ref = InvalidComponentRef; ++i; } else { i = node_collisions.erase(i); } } for (size_t i = 0; i < is_refd.size(); ++i) if (!is_refd[i] && collisions.is_used(uint32_t(i))) { DestroyCollision(collisions.get_ref(uint32_t(i))); ++removed_count; } } // cleanup instances { std::vector is_refd(instances.capacity(), false); for (auto i = std::begin(node_instance); i != std::end(node_instance);) { if (nodes.is_valid(i->first) && instances.is_valid(i->second)) { is_refd[i->second.idx] = true; ++i; } else { i = node_instance.erase(i); } } for (size_t i = 0; i < is_refd.size(); ++i) if (!is_refd[i] && instances.is_used(uint32_t(i))) { DestroyInstance(instances.get_ref(uint32_t(i))); ++removed_count; } } // cleanup instance views { for (auto i = std::begin(node_instance_view); i != std::end(node_instance_view);) { bool is_node_instance_view_still_valid = true; const auto j = node_instance.find(i->first); if (j == std::end(node_instance)) { is_node_instance_view_still_valid = false; // node not found } else { if (!instances.is_valid(j->second)) is_node_instance_view_still_valid = false; // node references an invalid component } if (is_node_instance_view_still_valid) { ++i; } else { DestroyViewContent(i->second); i = node_instance_view.erase(i); } } } // cleanup scripts { std::vector is_refd(scripts.capacity(), false); for (auto i : scene_scripts) if (scripts.is_valid(i)) is_refd[i.idx] = true; for (auto i = std::begin(node_scripts); i != std::end(node_scripts);) { if (nodes.is_valid(i->first)) { for (auto j : i->second) if (scripts.is_valid(j)) is_refd[j.idx] = true; ++i; } else { i = node_scripts.erase(i); } } for (size_t i = 0; i < is_refd.size(); ++i) if (!is_refd[i] && scripts.is_used(uint32_t(i))) { DestroyScript(scripts.get_ref(uint32_t(i))); ++removed_count; } } // cleanup anims { removed_count += GarbageCollectAnims(); } return removed_count; } size_t Scene::GarbageCollect() { const auto t_start = time_now(); // n-pass to cleanup node/component size_t total_removed = 0, pass_count = 0; for (;; ++pass_count) { const size_t pass_removed = GarbageCollectPass(); if (pass_removed == 0) break; total_removed += pass_removed; } // stats const auto t_total = time_now() - t_start; return total_removed; } // ViewState Scene::ComputeCameraViewState(NodeRef ref, const Vec2 &aspect_ratio) const { if (const auto node_ = GetNode_(ref)) { const auto trs_ref = node_->components[NCI_Transform]; if (IsValidTransformRef(trs_ref)) { if (auto cam_ = GetComponent_(cameras, node_->components[NCI_Camera])) { const auto &world = transform_worlds[trs_ref.idx]; return cam_->ortho ? ComputeOrthographicViewState(world, cam_->size, cam_->zrange.znear, cam_->zrange.zfar, aspect_ratio) : ComputePerspectiveViewState(world, cam_->fov, cam_->zrange.znear, cam_->zrange.zfar, aspect_ratio, Vec2::Zero, cam_->center_offset); } else { warn("Invalid node camera"); } } else { warn("Invalid node transform"); } } else { warn("Invalid node"); } return {}; } ViewState Scene::ComputeCurrentCameraViewState(const Vec2 &aspect_ratio) const { return ComputeCameraViewState(current_camera, aspect_ratio); } void Scene::ComputeTransformWorldMatrix(uint32_t idx) { if (!transform_worlds_updated[idx]) { const auto &trs = transforms[idx]; auto world = TransformationMat4(trs.TRS.pos, trs.TRS.rot, trs.TRS.scl); const auto parent_ref = GetNodeComponentRef_(trs.parent); if (transforms.is_valid(parent_ref)) { ComputeTransformWorldMatrix(parent_ref.idx); world = transform_worlds[parent_ref.idx] * world; } transform_worlds_updated[idx] = true; transform_worlds[idx] = world; } } // void Scene::ReadyWorldMatrices() { transform_worlds.resize(transforms.capacity()); // EJ vector_list can have holes, so size() does not necessarily includes the highest index in use transform_worlds_updated.resize(transforms.capacity()); std::fill(std::begin(transform_worlds_updated), std::end(transform_worlds_updated), false); } void Scene::ComputeWorldMatrices() { for (auto i = transforms.first(); i != generational_vector_list::invalid_idx; i = transforms.next(i)) ComputeTransformWorldMatrix(i); } void Scene::StorePreviousWorldMatrices() { std::swap(transform_worlds, previous_transform_worlds); std::swap(transform_worlds_updated, previous_transform_worlds_updated); } void Scene::FixupPreviousWorldMatrices() { previous_transform_worlds.resize(transform_worlds.size()); previous_transform_worlds_updated.resize(transform_worlds_updated.size(), false); const size_t count = transform_worlds.size(); for (size_t i = 0; i < count; ++i) if (!previous_transform_worlds_updated[i]) previous_transform_worlds[i] == transform_worlds[i]; // ensure coherency of previous transform } // void Scene::Update(time_ns dt) { StorePreviousWorldMatrices(); ReadyWorldMatrices(); UpdatePlayingAnims(dt); ComputeWorldMatrices(); FixupPreviousWorldMatrices(); } // void Scene::GetModelDisplayLists(std::vector &out_opaque, std::vector &out_transparent, std::vector &out_opaque_skinned, std::vector &out_transparent_skinned, const PipelineResources &resources) const { out_opaque.clear(); out_opaque.reserve(nodes.size()); out_transparent.clear(); out_transparent.reserve(nodes.size()); out_opaque_skinned.clear(); out_transparent_skinned.clear(); for (const auto &node : nodes) { if (node.flags & (NF_Disabled | NF_InstanceDisabled)) continue; const ComponentRef trs_ref = node.components[NCI_Transform]; if (!transforms.is_valid(trs_ref)) continue; // [EJ12102020] FIXME this is not required for a skinned object const Object_ *obj_ = GetComponent_(objects, node.components[NCI_Object]); if (!obj_) continue; const uint16_t mdl_idx = resources.models.GetValidatedRefIndex(obj_->model); const Model &mdl = resources.models.Get_unsafe_(mdl_idx); const auto total_bone_count = obj_->bones.size(); const bool obj_has_valid_skin = total_bone_count > 0 && total_bone_count == mdl.bind_pose.size(); for (size_t i = 0; i < mdl.lists.size(); ++i) { const auto mat_idx = mdl.mats[i]; if (mat_idx < obj_->materials.size()) { // FIXME fall back to error material const auto mat = &obj_->materials[mat_idx]; const auto &bones_table = mdl.lists[i].bones_table; __ASSERT__(bones_table.size() <= max_skinned_model_matrix_count); const bool is_transparent = GetMaterialBlendMode(*mat) != BM_Opaque; if (!obj_has_valid_skin) { if (is_transparent) out_transparent.push_back({mat, trs_ref.idx, mdl_idx, uint16_t(i)}); // worlds vector entries map 1:1 to the transform_ vector_list else out_opaque.push_back({mat, trs_ref.idx, mdl_idx, uint16_t(i)}); // worlds vector entries map 1:1 to the transform_ vector_list } else { SkinnedModelDisplayList dl; dl.mat = mat; for (int j = 0; j < bones_table.size(); ++j) { auto bone_idx = bones_table[j]; __ASSERT__(bone_idx < total_bone_count); uint32_t mtx_idx = trs_ref.idx; // default to the node matrix in case a bone reference is invalid if (bone_idx < total_bone_count) { const NodeRef bone_ref = obj_->bones[bone_idx]; if (nodes.is_valid(bone_ref)) { const auto &bone_node_ = nodes[bone_ref.idx]; const ComponentRef bone_trs_ref = bone_node_.components[NCI_Transform]; if (transforms.is_valid(bone_trs_ref)) mtx_idx = bone_trs_ref.idx; // worlds vector entries map 1:1 to the transform_ vector_list } } else { bone_idx = 0; } dl.mtx_idxs[j] = mtx_idx; dl.bones_idxs[j] = bone_idx; } dl.bone_count = bones_table.size(); dl.mdl_idx = mdl_idx; dl.lst_idx = uint16_t(i); if (is_transparent) out_transparent_skinned.push_back(dl); else out_opaque_skinned.push_back(dl); } } } } } // std::vector Scene::GetLights() const { std::vector lights; lights.reserve(32); for (auto i = nodes.first(); i != generational_vector_list::invalid_idx; i = nodes.next(i)) { const auto &node = nodes[i]; if (IsValidLightRef(node.components[NCI_Light]) && IsValidTransformRef(node.components[NCI_Transform])) lights.push_back(GetNode(GetNodeRef(i))); } return lights; } // NodesChildren Scene::BuildNodesChildren() const { NodesChildren nodes_children; for (auto i = nodes.first(); i != generational_vector_list::invalid_idx; i = nodes.next(i)) if (const auto trs = GetComponent_(transforms, nodes[i].components[NCI_Transform])) if (IsValidNodeRef(trs->parent)) ++nodes_children.node_children[trs->parent].count; uint32_t total_children_count = 0; for (const auto &e : nodes_children.node_children) total_children_count += e.second.count; nodes_children.all_children.resize(total_children_count); uint32_t offset = 0; for (auto &e : nodes_children.node_children) { e.second.offset = offset; offset += e.second.count; e.second.count = 0; // will be set again by the coming last step } for (auto i = nodes.first(); i != generational_vector_list::invalid_idx; i = nodes.next(i)) if (const auto trs = GetComponent_(transforms, nodes[i].components[NCI_Transform])) if (IsValidNodeRef(trs->parent)) { auto &e = nodes_children.node_children[trs->parent]; nodes_children.all_children[size_t(e.offset) + e.count++] = nodes.get_ref(i); } return nodes_children; } std::vector NodesChildren::GetChildren(NodeRef node) const { const auto i = node_children.find(node); if (i == std::end(node_children)) return {}; const auto &e = i->second; std::vector children; children.reserve(e.count); for (uint32_t i = 0; i < e.count; ++i) children.push_back(all_children[size_t(e.offset) + i]); return children; } // Node Scene::CreateNode(std::string name) { return {scene_ref, nodes.add_ref({std::move(name)})}; } void Scene::DestroyNode(NodeRef ref) { nodes.remove_ref(ref); } // void Scene::EnableNode_(NodeRef ref, bool through_instance) { if (!nodes.is_valid(ref)) { warn("Invalid node reference"); return; } nodes[ref.idx].flags &= through_instance ? ~NF_InstanceDisabled : ~NF_Disabled; // enable instance content if (nodes[ref.idx].flags & (NF_Disabled | NF_InstanceDisabled)) // [EJ11262019] only if fully enabled return; const auto i = node_instance_view.find(ref); if (i != std::end(node_instance_view)) for (auto &instantiated_node_ref : i->second.nodes) EnableNode_(instantiated_node_ref, true); } void Scene::DisableNode_(NodeRef ref, bool through_instance) { if (!nodes.is_valid(ref)) { warn("Invalid node reference"); return; } nodes[ref.idx].flags |= through_instance ? NF_InstanceDisabled : NF_Disabled; // disable instance content const auto i = node_instance_view.find(ref); if (i != std::end(node_instance_view)) for (auto &instantiated_node_ref : i->second.nodes) DisableNode_(instantiated_node_ref, true); } void Scene::EnableNode(NodeRef ref) { EnableNode_(ref, false); } void Scene::DisableNode(NodeRef ref) { DisableNode_(ref, false); } // NodeRef Scene::IsInstantiatedBy(NodeRef ref) const { for (const auto &i : node_instance_view) for (const auto &r : i.second.nodes) if (r == ref) return i.first; return InvalidNodeRef; } // void Scene::ReserveNodes(const size_t count) { nodes.reserve(nodes.size() + count); } // Node Scene::GetNode(const std::string &name) const { for (auto i = nodes.first_ref(); i != InvalidNodeRef; i = nodes.next_ref(i)) if (nodes[i.idx].name == name) return {scene_ref, i}; return {scene_ref}; } // NodeRef Scene::GetNodeEx_(const std::vector &refs, const std::string &path) const { int mode = 0; size_t s = 0; for (; s < path.size(); ++s) if (path[s] == ':') { mode = 1; break; } else if (path[s] == '/') { mode = 2; break; } const auto name = left(path, s), remainder = slice(path, s + 1); for (const auto &ref : refs) if (nodes[ref.idx].name == name) { if (mode == 0) { return ref; // look no further } else if (mode == 1) { const auto &i = node_instance_view.find(ref); // look in instance if (i == std::end(node_instance_view)) return InvalidNodeRef; // not an instance const auto &scene_view = i->second; std::vector roots; for (const auto &i : scene_view.nodes) if (IsChildOf(i, ref)) roots.push_back(i); return GetNodeEx_(roots, remainder); } else if (mode == 2) { const auto child_refs = GetNodeChildRefs(ref); // look in children return GetNodeEx_(child_refs, remainder); } } return InvalidNodeRef; } Node Scene::GetNodeEx(const std::string &path) const { std::vector roots; // root nodes = no transform or no parent for (auto i = nodes.first_ref(); i != InvalidNodeRef; i = nodes.next_ref(i)) if (IsRoot(i)) roots.push_back(i); return GetNode(GetNodeEx_(roots, path)); } // std::vector Scene::GetNodes() const { std::vector nodes_; nodes_.reserve(nodes.size()); for (auto i = nodes.first_ref(); i != InvalidNodeRef; i = nodes.next_ref(i)) { const auto node_ = GetNode_(i); if (node_->flags & NF_Instantiated) continue; // do not return instantiated nodes nodes_.push_back({scene_ref, i}); } return nodes_; } std::vector Scene::GetAllNodes() const { std::vector nodes_; nodes_.reserve(nodes.size()); for (auto i = nodes.first_ref(); i != InvalidNodeRef; i = nodes.next_ref(i)) nodes_.push_back({scene_ref, i}); return nodes_; } std::vector Scene::GetNodesWithComponent(NodeComponentIdx idx) const { std::vector nodes_; for (auto i = nodes.first_ref(); i != InvalidNodeRef; i = nodes.next_ref(i)) { const auto node_ = &nodes[i.idx]; if (node_->flags & NF_Instantiated) continue; // do not return instantiated nodes const auto ref = node_->components[idx]; bool has_component{}; switch (idx) { case NCI_Transform: has_component = transforms.is_valid(ref); break; case NCI_Object: has_component = objects.is_valid(ref); break; case NCI_Camera: has_component = cameras.is_valid(ref); break; case NCI_Light: has_component = lights.is_valid(ref); break; } if (has_component) nodes_.push_back({scene_ref, i}); } return nodes_; } std::vector Scene::GetAllNodesWithComponent(NodeComponentIdx idx) const { std::vector nodes_; for (auto i = nodes.first_ref(); i != InvalidNodeRef; i = nodes.next_ref(i)) { const auto node_ = &nodes[i.idx]; const auto ref = node_->components[idx]; bool has_component{}; switch (idx) { case NCI_Transform: has_component = transforms.is_valid(ref); break; case NCI_Object: has_component = objects.is_valid(ref); break; case NCI_Camera: has_component = cameras.is_valid(ref); break; case NCI_Light: has_component = lights.is_valid(ref); break; } if (has_component) nodes_.push_back({scene_ref, i}); } return nodes_; } size_t Scene::GetNodeCount() const { size_t count = 0; for (auto i = nodes.first_ref(); i != InvalidNodeRef; i = nodes.next_ref(i)) { const auto node_ = GetNode_(i); if (node_->flags & NF_Instantiated) continue; // do not count instantiated nodes ++count; } return count; } size_t Scene::GetAllNodeCount() const { return nodes.size(); } // bool Scene::IsChildOf(NodeRef ref, NodeRef parent) const { const auto t_ref = GetNodeComponentRef_(ref); if (transforms.is_valid(t_ref)) if (transforms[t_ref.idx].parent != parent) return false; return true; } // std::vector Scene::GetNodeChildRefs(NodeRef ref) const { std::vector child_refs; child_refs.reserve(16); for (auto i = nodes.first(); i != generational_vector_list::invalid_idx; i = nodes.next(i)) if (const auto trs = GetComponent_(transforms, nodes[i].components[NCI_Transform])) if (trs->parent == ref) child_refs.push_back(GetNodeRef(i)); return child_refs; } std::vector Scene::GetNodeChildren(NodeRef ref) const { const auto child_refs = GetNodeChildRefs(ref); return NodeRefsToNodes(*this, child_refs); } // std::string Scene::GetNodeName(NodeRef ref) const { if (auto node_ = GetNode_(ref)) return node_->name; warn("Invalid node"); return {}; } void Scene::SetNodeName(NodeRef ref, const std::string &v) { if (auto node_ = GetNode_(ref)) node_->name = v; else warn("Invalid node"); } // uint32_t Scene::GetNodeFlags(NodeRef ref) const { if (auto node_ = GetNode_(ref)) return node_->flags; warn("Invalid node"); return 0; } void Scene::SetNodeFlags(NodeRef ref, uint32_t flags) { if (auto node_ = GetNode_(ref)) node_->flags = flags; else warn("Invalid node"); } // ComponentRef Scene::GetNodeTransformRef(NodeRef ref) const { return GetNodeComponentRef_(ref); } void Scene::SetNodeTransform(NodeRef ref, ComponentRef cref) { if (auto node_ = this->GetNode_(ref)) node_->components[NCI_Transform] = cref; else warn("Invalid node"); } // Mat4 Scene::GetNodeWorldMatrix(NodeRef ref) const { if (auto node_ = this->GetNode_(ref)) { const auto trs_ref = node_->components[NCI_Transform]; if (transforms.is_valid(trs_ref)) { if (trs_ref.idx < transform_worlds.size()) return transform_worlds[trs_ref.idx]; warn("Invalid node transform index"); } else { warn("Invalid node transform"); } } else { warn("Invalid node"); } return Mat4::Identity; } void Scene::SetNodeWorldMatrix(NodeRef ref, const Mat4 &world) { if (auto node_ = this->GetNode_(ref)) { const auto trs_ref = node_->components[NCI_Transform]; if (transforms.is_valid(trs_ref)) { if (trs_ref.idx < transform_worlds.size()) { transform_worlds[trs_ref.idx] = world; transform_worlds_updated[trs_ref.idx] = true; } else { warn("Invalid node transform index"); } } else { warn("Invalid node transform"); } } else { warn("Invalid node"); } } // Mat4 Scene::ComputeNodeWorldMatrix(NodeRef ref) const { if (auto node_ = this->GetNode_(ref)) { const auto trs_ref = node_->components[NCI_Transform]; if (transforms.is_valid(trs_ref)) { const auto &trs = transforms[trs_ref.idx]; auto mtx = TransformationMat4(trs.TRS.pos, trs.TRS.rot, trs.TRS.scl); if (trs.parent != InvalidNodeRef) mtx = ComputeNodeWorldMatrix(trs.parent) * mtx; return mtx; } else { warn("Invalid node transform"); } } else { warn("Invalid node"); } return Mat4::Identity; } // ComponentRef Scene::GetNodeCameraRef(NodeRef ref) const { return GetNodeComponentRef_(ref); } void Scene::SetNodeCamera(NodeRef ref, ComponentRef cref) { if (auto node_ = this->GetNode_(ref)) node_->components[NCI_Camera] = cref; else warn("Invalid node"); } // ComponentRef Scene::GetNodeObjectRef(NodeRef ref) const { return GetNodeComponentRef_(ref); } void Scene::SetNodeObject(NodeRef ref, ComponentRef cref) { if (auto node_ = this->GetNode_(ref)) node_->components[NCI_Object] = cref; else warn("Invalid node"); } // ComponentRef Scene::GetNodeLightRef(NodeRef ref) const { return GetNodeComponentRef_(ref); } void Scene::SetNodeLight(NodeRef ref, ComponentRef cref) { if (auto node_ = this->GetNode_(ref)) node_->components[NCI_Light] = cref; else warn("Invalid node"); } // ComponentRef Scene::GetNodeRigidBodyRef(NodeRef ref) const { return GetNodeComponentRef_(ref); } void Scene::SetNodeRigidBody(NodeRef ref, ComponentRef cref) { if (auto node_ = this->GetNode_(ref)) node_->components[NCI_RigidBody] = cref; else warn("Invalid node"); } ComponentRef Scene::GetNodeCollisionRef(NodeRef ref, size_t idx) const { auto i = nodes.is_valid(ref) ? node_collisions.find(ref) : std::end(node_collisions); return i != std::end(node_collisions) && idx < i->second.size() ? i->second[idx] : InvalidComponentRef; } Collision Scene::GetNodeCollision(NodeRef ref, size_t idx) const { const auto cref = GetNodeCollisionRef(ref, idx); return cref == InvalidComponentRef ? Collision{} : Collision{scene_ref, cref}; } void Scene::SetNodeCollision(NodeRef ref, size_t idx, const Collision &collision) { if (nodes.is_valid(ref)) { node_collisions[ref].resize(idx + 1); node_collisions[ref][idx] = collision.ref; } else { warn("Invalid node"); } } void Scene::RemoveNodeCollision(NodeRef ref, ComponentRef cref) { if (nodes.is_valid(ref)) { auto &collisions = node_collisions[ref]; for (size_t slot_idx = 0; slot_idx < collisions.size(); ++slot_idx) if (collisions[slot_idx] == cref) collisions[slot_idx] = invalid_gen_ref; _ResizeComponents(collisions); } else { warn("Invalid node"); } } void Scene::RemoveNodeCollision(NodeRef ref, size_t slot_idx) { if (nodes.is_valid(ref)) { auto &collisions = node_collisions[ref]; if (slot_idx < collisions.size()) if (collisions[slot_idx] != invalid_gen_ref) collisions[slot_idx] = invalid_gen_ref; _ResizeComponents(collisions); } else { warn("Invalid node"); } } // RigidBody Scene::CreateRigidBody() { return {scene_ref, rigid_bodies.add_ref({})}; } void Scene::DestroyRigidBody(ComponentRef ref) { rigid_bodies.remove_ref(ref); } void Scene::SetRigidBodyType(ComponentRef ref, RigidBodyType type) { if (auto rb = GetComponent_(rigid_bodies, ref)) rb->type = type; else warn("Invalid rigid body"); } RigidBodyType Scene::GetRigidBodyType(ComponentRef ref) const { if (auto rb = GetComponent_(rigid_bodies, ref)) return rb->type; warn("Invalid rigid body"); return RBT_Dynamic; } float Scene::GetRigidBodyLinearDamping(ComponentRef ref) const { if (auto rb = GetComponent_(rigid_bodies, ref)) return unpack_float(rb->linear_damping); warn("Invalid rigid body"); return 0.f; } void Scene::SetRigidBodyLinearDamping(ComponentRef ref, float damping) { if (auto rb = GetComponent_(rigid_bodies, ref)) rb->linear_damping = pack_float(damping); else warn("Invalid rigid body"); } float Scene::GetRigidBodyAngularDamping(ComponentRef ref) const { if (auto rb = GetComponent_(rigid_bodies, ref)) return unpack_float(rb->angular_damping); warn("Invalid rigid body"); return {}; } void Scene::SetRigidBodyAngularDamping(ComponentRef ref, float damping) { if (auto rb = GetComponent_(rigid_bodies, ref)) rb->angular_damping = pack_float(damping); else warn("Invalid rigid body"); } float Scene::GetRigidBodyRestitution(ComponentRef ref) const { if (const auto rb = GetComponent_(rigid_bodies, ref)) return unpack_float(rb->restitution); warn("Invalid rigid body"); return 0.f; } void Scene::SetRigidBodyRestitution(ComponentRef ref, float restitution) { if (auto rb = GetComponent_(rigid_bodies, ref)) rb->restitution = pack_float(restitution); else warn("Invalid rigid body"); } float Scene::GetRigidBodyFriction(ComponentRef ref) const { if (const auto rb = GetComponent_(rigid_bodies, ref)) return unpack_float(rb->friction); warn("Invalid rigid body"); return 0.5f; } void Scene::SetRigidBodyFriction(ComponentRef ref, float friction) { if (auto rb = GetComponent_(rigid_bodies, ref)) rb->friction = pack_float(friction); else warn("Invalid rigid body"); } float Scene::GetRigidBodyRollingFriction(ComponentRef ref) const { if (const auto rb = GetComponent_(rigid_bodies, ref)) return unpack_float(rb->rolling_friction); warn("Invalid rigid body"); return 0.f; } void Scene::SetRigidBodyRollingFriction(ComponentRef ref, float rolling_friction) { if (auto rb = GetComponent_(rigid_bodies, ref)) rb->rolling_friction = pack_float(rolling_friction); else warn("Invalid rigid body"); } // Collision Scene::CreateCollision() { return {scene_ref, collisions.add_ref({})}; } void Scene::DestroyCollision(ComponentRef ref) { collisions.remove_ref(ref); } void Scene::SetCollisionType(ComponentRef ref, CollisionType type) { if (auto col = GetComponent_(collisions, ref)) col->type = type; else warn("Invalid collision"); } CollisionType Scene::GetCollisionType(ComponentRef ref) const { if (const auto col = GetComponent_(collisions, ref)) return col->type; warn("Invalid collision"); return CT_Sphere; } void Scene::SetCollisionLocalTransform(ComponentRef ref, const Mat4 &m) { if (auto col = GetComponent_(collisions, ref)) Decompose(m, &col->trs.pos, &col->trs.rot, nullptr); // scale is the shape size, scaling collision shapes is NOT supported else warn("Invalid collision"); } Mat4 Scene::GetCollisionLocalTransform(ComponentRef ref) const { if (const auto col = GetComponent_(collisions, ref)) return TransformationMat4(col->trs.pos, col->trs.rot, Vec3::One); // scale is shape size, scaling collision shapes is NOT supported warn("Invalid collision"); return Mat4::Identity; } Vec3 Scene::GetCollisionPosition(ComponentRef ref) const { if (const auto col = GetComponent_(collisions, ref)) return col->trs.pos; warn("Invalid collision"); return {}; } void Scene::SetCollisionPosition(ComponentRef ref, const Vec3 &pos) { if (auto col = GetComponent_(collisions, ref)) col->trs.pos = pos; else warn("Invalid collision"); } Vec3 Scene::GetCollisionRotation(ComponentRef ref) const { if (const auto col = GetComponent_(collisions, ref)) return col->trs.rot; warn("Invalid collision"); return {}; } void Scene::SetCollisionRotation(ComponentRef ref, const Vec3 &rot) { if (auto col = GetComponent_(collisions, ref)) col->trs.rot = rot; else warn("Invalid collision"); } void Scene::SetCollisionMass(ComponentRef ref, float mass) { if (auto col = GetComponent_(collisions, ref)) col->mass = mass; else warn("Invalid collision"); } float Scene::GetCollisionMass(ComponentRef ref) const { if (const auto col = GetComponent_(collisions, ref)) return col->mass; warn("Invalid collision"); return 0.f; } void Scene::SetCollisionSize(ComponentRef ref, const Vec3 &size) { if (auto col = GetComponent_(collisions, ref)) col->trs.scl = size; else warn("Invalid collision"); } Vec3 Scene::GetCollisionSize(ComponentRef ref) const { if (const auto col = GetComponent_(collisions, ref)) return col->trs.scl; warn("Invalid collision"); return {-1.f, -1.f, -1.f}; } void Scene::SetCollisionRadius(ComponentRef ref, float radius) { if (auto col = GetComponent_(collisions, ref)) col->trs.scl.x = radius; else warn("Invalid collision"); } float Scene::GetCollisionRadius(ComponentRef ref) const { if (const auto col = GetComponent_(collisions, ref)) return col->trs.scl.x; warn("Invalid collision"); return -1.f; } void Scene::SetCollisionHeight(ComponentRef ref, float height) { if (auto col = GetComponent_(collisions, ref)) col->trs.scl.y = height; else warn("Invalid collision"); } float Scene::GetCollisionHeight(ComponentRef ref) const { if (const auto col = GetComponent_(collisions, ref)) return col->trs.scl.y; warn("Invalid collision"); return -1.f; } void Scene::SetCollisionResource(ComponentRef ref, const std::string &path) { if (auto col = GetComponent_(collisions, ref)) col->resource_path = path; else warn("Invalid collision"); } std::string Scene::GetCollisionResource(ComponentRef ref) { if (auto col = GetComponent_(collisions, ref)) return col->resource_path; warn("Invalid collision"); return {}; } // size_t Scene::GetNodeCollisionCount(NodeRef ref) const { auto i = nodes.is_valid(ref) ? node_collisions.find(ref) : std::end(node_collisions); return i != std::end(node_collisions) ? i->second.size() : 0; } // Collision Scene::CreateSphereCollision(float radius, float mass) { auto col = CreateCollision(); SetCollisionType(col.ref, CT_Sphere); SetCollisionRadius(col.ref, radius); SetCollisionMass(col.ref, mass); return col; } Collision Scene::CreateCubeCollision(float x, float y, float z, float mass) { auto col = CreateCollision(); SetCollisionType(col.ref, CT_Cube); SetCollisionSize(col.ref, {x, y, z}); SetCollisionMass(col.ref, mass); return col; } Collision Scene::CreateCapsuleCollision(float radius, float height, float mass) { auto col = CreateCollision(); SetCollisionType(col.ref, CT_Capsule); SetCollisionRadius(col.ref, radius); SetCollisionHeight(col.ref, height); SetCollisionMass(col.ref, mass); return col; } Collision Scene::CreateCylinderCollision(float radius, float height, float mass) { auto col = CreateCollision(); SetCollisionType(col.ref, CT_Cylinder); SetCollisionRadius(col.ref, radius); SetCollisionHeight(col.ref, height); SetCollisionMass(col.ref, mass); return col; } Collision Scene::CreateConeCollision(float radius, float height, float mass) { auto col = CreateCollision(); SetCollisionType(col.ref, CT_Cone); SetCollisionRadius(col.ref, radius); SetCollisionHeight(col.ref, height); SetCollisionMass(col.ref, mass); return col; } Collision Scene::CreateMeshCollision(const std::string &path, float mass) { auto col = CreateCollision(); SetCollisionType(col.ref, CT_Mesh); SetCollisionResource(col.ref, path); SetCollisionMass(col.ref, mass); return col; } Collision Scene::CreateMeshConvexCollision(const std::string &path, float mass) { auto col = CreateCollision(); SetCollisionType(col.ref, CT_MeshConvex); SetCollisionResource(col.ref, path); SetCollisionMass(col.ref, mass); return col; } // Instance Scene::CreateInstance() { return {scene_ref, instances.add_ref({})}; } void Scene::DestroyInstance(ComponentRef ref) { instances.remove_ref(ref); } void Scene::SetInstancePath(ComponentRef ref, const std::string &path) { if (instances.is_valid(ref)) instances[ref.idx].name = path; else warn("Invalid instance"); } std::string Scene::GetInstancePath(ComponentRef ref) const { if (instances.is_valid(ref)) return instances[ref.idx].name; warn("Invalid instance"); return {}; } void Scene::SetOnInstantiateAnim(ComponentRef ref, const std::string &anim) { if (instances.is_valid(ref)) instances[ref.idx].anim = anim; else warn("Invalid instance"); } void Scene::SetOnInstantiateAnimLoopMode(ComponentRef ref, AnimLoopMode loop_mode) { if (instances.is_valid(ref)) instances[ref.idx].loop_mode = loop_mode; else warn("Invalid instance"); } void Scene::ClearOnInstantiateAnim(ComponentRef ref) { if (instances.is_valid(ref)) instances[ref.idx].anim.clear(); else warn("Invalid instance"); } std::string Scene::GetOnInstantiateAnim(ComponentRef ref) { if (instances.is_valid(ref)) return instances[ref.idx].anim; warn("Invalid instance"); return {}; } AnimLoopMode Scene::GetOnInstantiateAnimLoopMode(ComponentRef ref) { if (instances.is_valid(ref)) return instances[ref.idx].loop_mode; warn("Invalid instance"); return {}; } ScenePlayAnimRef Scene::GetOnInstantiatePlayAnimRef(ComponentRef ref) { if (instances.is_valid(ref)) return instances[ref.idx].play_anim_ref; warn("Invalid instance"); return InvalidScenePlayAnimRef; } Instance Scene::GetNodeInstance(NodeRef ref) const { const auto cref = GetNodeInstanceRef(ref); if (cref != InvalidComponentRef) return {scene_ref, cref}; warn("Invalid node instance"); return {}; } ComponentRef Scene::GetNodeInstanceRef(NodeRef ref) const { const auto i = node_instance.find(ref); if (i != std::end(node_instance)) return i->second; return InvalidComponentRef; } void Scene::SetNodeInstance(NodeRef ref, ComponentRef cref) { if (cref == InvalidComponentRef) node_instance.erase(ref); else node_instance[ref] = cref; } void Scene::DestroyViewContent(const SceneView &view) { for (const auto &node : view.nodes) DestroyNode(node); for (const auto &anim : view.anims) DestroyAnim(anim); for (const auto &scene_anim : view.scene_anims) DestroySceneAnim(scene_anim); } void Scene::NodeDestroyInstance(NodeRef ref) { NodeStopOnInstantiateAnim(ref); const auto i = node_instance_view.find(ref); if (i != std::end(node_instance_view)) { DestroyViewContent(i->second); node_instance_view.erase(i); } else { warn("Invalid node instance view"); } } static bool LoadScene(const Reader &ir, const Handle &h, const char *name, Scene &scene, const Reader &deps_ir, const ReadProvider &deps_ip, PipelineResources &resources, const PipelineInfo &pipeline, LoadSceneContext &ctx, uint32_t flags); bool Scene::NodeSetupInstance( NodeRef ref, const Reader &ir, const ReadProvider &ip, PipelineResources &resources, const PipelineInfo &pipeline, uint32_t flags, int recursion_level) { if (recursion_level > 4) return true; const auto i = node_instance.find(ref); if (i == std::end(node_instance)) return false; const auto host_is_enabled = IsNodeEnabled(ref); if (instances.is_valid(i->second)) { LoadSceneContext ctx = {recursion_level}; { const auto &i_ = instances[i->second.idx]; if (!LoadScene(ir, ScopedReadHandle(ip, i_.name.c_str(), flags & LSSF_Silent), i_.name.c_str(), *this, ir, ip, resources, pipeline, ctx, flags)) return false; } auto &i_ = instances[i->second.idx]; // [EJ12102019] LoadScene might reallocate instances buffer so fetch i_ anew for (auto node : ctx.view.nodes) { auto &n = nodes[node.idx]; n.flags |= NF_Instantiated; // flag as instantiated if (!host_is_enabled) n.flags |= NF_InstanceDisabled; // flag as disabled through host if (auto trs = GetComponent_(transforms, n.components[NCI_Transform])) if (trs->parent == InvalidNodeRef) trs->parent = ref; // parent node to the instance node } for (auto &anim : ctx.view.anims) anims[anim.idx].flags |= AF_Instantiated; // flag as instantiated for (auto scene_anim : ctx.view.scene_anims) scene_anims[scene_anim.idx].flags |= SAF_Instantiated; // flag as instantiated node_instance_view[ref] = std::move(ctx.view); } return true; } ScenePlayAnimRef Scene::NodeStartOnInstantiateAnim(NodeRef ref) { NodeStopOnInstantiateAnim(ref); const auto i = node_instance.find(ref); if (i == std::end(node_instance)) return InvalidScenePlayAnimRef; // no instance on node if (!instances.is_valid(i->second)) return InvalidScenePlayAnimRef; // invalid instance ref auto &i_ = instances[i->second.idx]; if (i_.anim.empty()) return InvalidScenePlayAnimRef; // no anim to play on instantiation const auto i_view = node_instance_view.find(ref); if (i_view == std::end(node_instance_view)) return InvalidScenePlayAnimRef; // no instance view const auto anim_ref = i_view->second.GetSceneAnim(*this, i_.anim); return i_.play_anim_ref = PlayAnim(anim_ref, i_.loop_mode); } void Scene::NodeStopOnInstantiateAnim(NodeRef ref) { const auto i = node_instance.find(ref); if (i == std::end(node_instance)) return; // no instance on node if (!instances.is_valid(i->second)) return; // invalid instance ref auto &i_ = instances[i->second.idx]; StopAnim(i_.play_anim_ref); i_.play_anim_ref = InvalidScenePlayAnimRef; } bool Scene::NodeSetupInstanceFromFile(NodeRef ref, PipelineResources &resources, const PipelineInfo &pipeline, uint32_t flags, int recursion_level) { return NodeSetupInstance(ref, g_file_reader, g_file_read_provider, resources, pipeline, flags, recursion_level); } bool Scene::NodeSetupInstanceFromAssets(NodeRef ref, PipelineResources &resources, const PipelineInfo &pipeline, uint32_t flags, int recursion_level) { return NodeSetupInstance(ref, g_assets_reader, g_assets_read_provider, resources, pipeline, flags, recursion_level); } const SceneView &Scene::GetNodeInstanceSceneView(NodeRef ref) const { static SceneView dummy_view; const auto i = node_instance_view.find(ref); if (i == std::end(node_instance_view)) { warn(format("No instance scene view on node (%1:%2)").arg(ref.idx).arg(ref.gen).c_str()); return dummy_view; } return i->second; } Instance Scene::CreateInstance(const std::string &path) { auto instance = CreateInstance(); instance.SetPath(path); return instance; } void Scene::NodeMoveInstance(NodeRef from, NodeRef to) { if (!nodes.is_valid(from) || !nodes.is_valid(to)) return; NodeDestroyInstance(to); // drop current target instance scene view if any node_instance.erase(to); // drop current target instance component if any const bool tgt_disabled = nodes[to.idx].flags & NF_Disabled; const auto &i = node_instance_view.find(from); if (i != std::end(node_instance_view)) { for (auto n : i->second.nodes) { // re-parent instantiated nodes to the target node const auto trsf_ref = GetNodeComponentRef_(n); if (trsf_ref != InvalidComponentRef) if (transforms[trsf_ref.idx].parent == from) transforms[trsf_ref.idx].parent = to; // update disable flag tgt_disabled ? DisableNode_(n, true) : EnableNode_(n, true); } node_instance_view[to] = std::move(i->second); // transfer instance view } node_instance_view.erase(i); // drop from source if (node_instance.find(from) != std::end(node_instance)) node_instance[to] = node_instance[from]; node_instance.erase(from); // drop from source } // Node CreateCamera(Scene &scene, const Mat4 &mtx, float znear, float zfar, float fov) { Node node = scene.CreateNode(); node.SetName("Camera"); node.SetTransform(scene.CreateTransform(mtx)); node.SetCamera(scene.CreateCamera(znear, zfar, fov)); return node; } Node CreateOrthographicCamera(Scene &scene, const Mat4 &mtx, float znear, float zfar, float size) { Node node = scene.CreateNode(); node.SetName("Camera"); node.SetTransform(scene.CreateTransform(mtx)); node.SetCamera(scene.CreateOrthographicCamera(znear, zfar, size)); return node; } // Node CreatePointLight(Scene &scene, const Mat4 &mtx, float radius, const Color &diffuse, float diffuse_intensity, const Color &specular, float specular_intensity, float priority, LightShadowType shadow_type, float shadow_bias) { Node node = scene.CreateNode(); node.SetName("Point Light"); node.SetTransform(scene.CreateTransform(mtx)); node.SetLight(scene.CreatePointLight(radius, diffuse, diffuse_intensity, specular, specular_intensity, priority, shadow_type, shadow_bias)); return node; } Node CreateSpotLight(Scene &scene, const Mat4 &mtx, float radius, float inner_angle, float outer_angle, const Color &diffuse, float diffuse_intensity, const Color &specular, float specular_intensity, float priority, LightShadowType shadow_type, float shadow_bias) { Node node = scene.CreateNode(); node.SetName("Spot Light"); node.SetTransform(scene.CreateTransform(mtx)); node.SetLight( scene.CreateSpotLight(radius, inner_angle, outer_angle, diffuse, diffuse_intensity, specular, specular_intensity, priority, shadow_type, shadow_bias)); return node; } Node CreateLinearLight(Scene &scene, const Mat4 &mtx, const Color &diffuse, float diffuse_intensity, const Color &specular, float specular_intensity, float priority, LightShadowType shadow_type, float shadow_bias, const Vec4 &pssm_split) { Node node = scene.CreateNode(); node.SetName("Linear Light"); node.SetTransform(scene.CreateTransform(mtx)); node.SetLight(scene.CreateLinearLight(diffuse, diffuse_intensity, specular, specular_intensity, priority, shadow_type, shadow_bias, pssm_split)); return node; } Node CreateInstance(Scene &scene, const Mat4 &mtx, const std::string &name, const Reader &ir, const ReadProvider &ip, PipelineResources &resources, const PipelineInfo &pipeline, bool &success, uint32_t flags) { Node node = scene.CreateNode(); node.SetName(name); node.SetTransform(scene.CreateTransform(mtx)); node.SetInstance(scene.CreateInstance(name)); success = node.SetupInstance(ir, ip, resources, pipeline, flags); return node; } Node CreateInstanceFromFile( Scene &scene, const Mat4 &mtx, const std::string &name, PipelineResources &resources, const PipelineInfo &pipeline, bool &success, uint32_t flags) { return CreateInstance(scene, mtx, name, g_file_reader, g_file_read_provider, resources, pipeline, success, flags); } Node CreateInstanceFromAssets( Scene &scene, const Mat4 &mtx, const std::string &name, PipelineResources &resources, const PipelineInfo &pipeline, bool &success, uint32_t flags) { return CreateInstance(scene, mtx, name, g_assets_reader, g_assets_read_provider, resources, pipeline, success, flags); } // Script Scene::CreateScript() { return {scene_ref, scripts.add_ref({})}; } Script Scene::CreateScript(const std::string &path) { return {scene_ref, scripts.add_ref({path})}; } void Scene::DestroyScript(ComponentRef ref) { scripts.remove_ref(ref); } void Scene::SetScriptPath(ComponentRef ref, const std::string &path) { if (auto s = GetComponent_(scripts, ref)) s->path = path; else warn("Invalid script"); } std::string Scene::GetScriptPath(ComponentRef ref) const { if (const auto s = GetComponent_(scripts, ref)) return s->path; warn("Invalid script"); return {}; } // bool Scene::ScriptHasParam(ComponentRef ref, const std::string &name) const { if (const auto s = GetComponent_(scripts, ref)) return s->params.find(name) != std::end(s->params); warn("Invalid script"); return false; } bool Scene::SetScriptParam(ComponentRef ref, const std::string &name, ScriptParam param) { if (const auto s = GetComponent_(scripts, ref)) { s->params[name] = std::move(param); return true; } warn("Invalid script"); return false; } ScriptParam Scene::GetScriptParam(ComponentRef ref, const std::string &name) const { if (const auto s = GetComponent_(scripts, ref)) { const auto i = s->params.find(name); if (i != std::end(s->params)) return i->second; else warn(format("Invalid script parameter '%1'").arg(name).c_str()); } else { warn("Invalid script"); } return {SPT_Null}; } static std::map _empty_script_params; const std::map &Scene::GetScriptParams(ComponentRef ref) const { if (const auto s = GetComponent_(scripts, ref)) return s->params; warn("Invalid script"); return _empty_script_params; } // size_t Scene::GetNodeScriptCount(NodeRef ref) const { if (!nodes.is_valid(ref)) { warn("Invalid node"); return 0; } const auto i = node_scripts.find(ref); return i != std::end(node_scripts) ? i->second.size() : 0; } Script Scene::GetNodeScript(NodeRef ref, size_t idx) const { const auto cref = GetNodeScriptRef(ref, idx); return cref == InvalidComponentRef ? Script{} : Script{scene_ref, cref}; } ComponentRef Scene::GetNodeScriptRef(NodeRef ref, size_t idx) const { auto i = nodes.is_valid(ref) ? node_scripts.find(ref) : std::end(node_scripts); return i != std::end(node_scripts) && idx < i->second.size() ? i->second[idx] : InvalidComponentRef; } void Scene::SetNodeScript(NodeRef ref, size_t idx, const Script &script) { if (nodes.is_valid(ref)) { node_scripts[ref].resize(idx + 1); node_scripts[ref][idx] = script.ref; } else { warn("Invalid node"); } } void Scene::RemoveNodeScript(NodeRef ref, ComponentRef cref) { if (nodes.is_valid(ref)) { auto &scripts = node_scripts[ref]; for (size_t slot_idx = 0; slot_idx < scripts.size(); ++slot_idx) if (scripts[slot_idx] == cref) scripts[slot_idx] = invalid_gen_ref; _ResizeComponents(scripts); } else { warn("Invalid node"); } } void Scene::RemoveNodeScript(NodeRef ref, size_t slot_idx) { if (nodes.is_valid(ref)) { auto &scripts = node_scripts[ref]; if (slot_idx < scripts.size()) if (scripts[slot_idx] != invalid_gen_ref) scripts[slot_idx] = invalid_gen_ref; _ResizeComponents(scripts); } else { warn("Invalid node"); } } std::vector