diff --git a/softlight/include/softlight/SL_SceneGraph.hpp b/softlight/include/softlight/SL_SceneGraph.hpp index 07062ca9..c7094d7c 100644 --- a/softlight/include/softlight/SL_SceneGraph.hpp +++ b/softlight/include/softlight/SL_SceneGraph.hpp @@ -442,18 +442,49 @@ class SL_SceneGraph */ size_t import(SL_SceneGraph& inGraph) noexcept; - - -#if 0 + /** + * @brief Place mesh data into the scene graph. + * + * @param m + * The mesh to be inserted. This mesh will not have any association with a + * mesh node unless explicitly specified. + * + * @param meshBounds + * The bounding box to use for the mesh. + * + * @return The index within the scene graph which can be used by a mesh + * node. + */ size_t insert_mesh(const SL_Mesh& m, const SL_BoundingBox& meshBounds) noexcept; + /** + * @brief Insert a mesh node and have it use currently existing mesh data. + * + * @param parentId + * The parent ID of *this node. + * + * @param name + * A name to provide this node. This should be unique. + * + * @param numSubMeshes + * The number of SL_Mesh objects used by the inserted node. + * + * @param subMeshIds + * The index of all sub-mesh (a.k.a. SL_Mesh) ids currently within the + * scene graph to be used by the inserted node. + * + * @param transform + * An initial transformation which will be concatenated with the parent + * node's transformation. + * + * @return The index within the scene graph of the new mesh node. + */ size_t insert_mesh_node( size_t parentId, const char* name, - const size_t numSubMeshes, + size_t numSubMeshes, const size_t* subMeshIds, const SL_Transform& transform) noexcept; -#endif }; diff --git a/softlight/include/softlight/SL_SpatialHierarchy.hpp b/softlight/include/softlight/SL_SpatialHierarchy.hpp index 56b5ccff..7daefc2f 100644 --- a/softlight/include/softlight/SL_SpatialHierarchy.hpp +++ b/softlight/include/softlight/SL_SpatialHierarchy.hpp @@ -94,7 +94,7 @@ class SL_SpatialHierarchy size_type parent(size_type nodeIndex) const; - bool reparent(size_type nodeIndex, size_type parentIndex); + bool reparent(size_type nodeIndex, size_type newParentId); bool duplicate(size_type nodeIndex); @@ -351,148 +351,124 @@ typename SL_SpatialHierarchy::size_type SL_Sp template -bool SL_SpatialHierarchy::reparent(size_type nodeIndex, size_type parentIndex) +bool SL_SpatialHierarchy::reparent(size_type nodeIndex, size_type newParentId) { // data validation & early exits - if (nodeIndex == ROOT_NODE_INDEX || nodeIndex >= mParents.size() || mParents[nodeIndex] == parentIndex) + if (nodeIndex == ROOT_NODE_INDEX || nodeIndex >= mParents.size() || mParents[nodeIndex] == newParentId) { return false; } - if (parentIndex >= mParents.size() && parentIndex != ROOT_NODE_INDEX) + if (newParentId >= mParents.size() && newParentId != ROOT_NODE_INDEX) { return false; } // Cannot make a node a parent of its ancestor. It's possible, but then // what would the new ancestor of the node then be? - if (parentIndex != ROOT_NODE_INDEX) + if (newParentId != ROOT_NODE_INDEX) { - if (is_descendant(parentIndex, nodeIndex)) + if (is_descendant(newParentId, nodeIndex)) { return false; } } - const size_type numChildren = total_children(nodeIndex); - const size_type numNewSiblings = total_children(parentIndex); - const size_type displacement = 1 + numChildren; - const size_type newParentOffset = 1 + parentIndex + numNewSiblings; + const size_t numChildren = total_children(nodeIndex); + const size_t displacement = 1 + numChildren; + const size_t numNewSiblings = total_children(newParentId); + const size_t newNodeIndex = 1 + newParentId + numNewSiblings; // Keep track of the range of elements which need to be updated. - size_type effectStart, effectEnd, numAffected; + const size_t effectStart = nodeIndex < newParentId ? nodeIndex : newNodeIndex; + const size_t effectEnd = nodeIndex < newParentId ? newNodeIndex : (nodeIndex+displacement); + const size_t numAffected = effectEnd - effectStart; - if (parentIndex == ROOT_NODE_INDEX) + // Determine if we're moving "up", closer to the root, or "down" away from + // the root + const bool movingUp = newParentId < nodeIndex && (newParentId != ROOT_NODE_INDEX); + size_t amountToMove; + + if (movingUp) { - effectStart = nodeIndex; - effectEnd = mParents.size(); + amountToMove = nodeIndex - newNodeIndex; } - else if (nodeIndex < parentIndex) + else { - // node is moving down in the hierarchy - effectStart = nodeIndex; - effectEnd = newParentOffset; + amountToMove = newNodeIndex - nodeIndex; } - else + + for (size_t i = effectStart; i < effectEnd; ++i) { - // node is moving up in the hierarchy, consider the nodes of all - // current siblings/children - if (parentIndex+numNewSiblings > nodeIndex+numChildren) + size_t& rParentId = mParents[i]; + size_t pId = rParentId; + size_t nId = i; + + // Update the requested node's index + if (nId == nodeIndex) { - effectStart = 1 + parentIndex; - effectEnd = newParentOffset; + if (newParentId != ROOT_NODE_INDEX) + { + pId = newParentId - (nodeIndex < newParentId ? displacement : 0); + } + else + { + pId = newParentId; + } } else { - effectStart = newParentOffset; - effectEnd = 1 + nodeIndex + numChildren; + // Determine if there's a node which even needs its parent ID updated. + if (pId == ROOT_NODE_INDEX || pId < effectStart) + { + continue; + } - // sibling nodes aren't considered with the current implementation - // we need to do this manually - size_type currentParent = mParents[nodeIndex]; - for (size_type i = nodeIndex; i < mParents.size(); ++i) + if (movingUp) { - if (mParents[i] < currentParent) + if (nId < nodeIndex) { - break; + pId += displacement; } - - if (mParents[i] == currentParent) + else { - mParents[i] += displacement; + pId -= amountToMove; + } + } + else + { + if (i > nodeIndex+numChildren) + { + pId -= displacement; + } + else + { + pId += amountToMove-displacement; } } } - } - numAffected = effectEnd - effectStart; - - size_type numRotations, newParentIndex, newNodeIndex; - if ((parentIndex < nodeIndex) && (parentIndex != ROOT_NODE_INDEX)) - { - numRotations = displacement; - newNodeIndex = newParentOffset; - newParentIndex = parentIndex; - } - else - { - numRotations = numAffected - displacement; // "size - numRotations" for left-rotate - newNodeIndex = newParentOffset - displacement; - newParentIndex = parentIndex - (parentIndex != ROOT_NODE_INDEX ? displacement : 0); + rParentId = pId; } - for (size_type i = effectStart; i < effectEnd; ++i) + if (nodeIndex > newParentId) { - // handle all the easy cases first - if (mParents[i] == ROOT_NODE_INDEX) - { - continue; - } - - if (mParents[i] < effectStart) + for (size_t i = effectEnd; i < mParents.size(); ++i) { - continue; - } - - if (parentIndex == ROOT_NODE_INDEX) - { - if (i >= nodeIndex && i < nodeIndex+displacement) - { - mParents[i] = newNodeIndex + (mParents[i]-nodeIndex); - } - else if (mParents[i] > nodeIndex) - { - mParents[i] -= displacement; - } - } - else if (parentIndex < nodeIndex) - { - if (i >= nodeIndex && i < nodeIndex+displacement) - { - mParents[i] -= displacement-numChildren; - } - else //if (mParents[i] >= nodeIndex) + if (mParents[i] < nodeIndex) { mParents[i] += displacement; } - } - else - { - if (i >= nodeIndex && i < nodeIndex+displacement) + else if (mParents[i] <= newParentId || mParents[i] == ROOT_NODE_INDEX) { - mParents[i] += numRotations; - } - else if (mParents[i] >= nodeIndex) - { - mParents[i] -= displacement; + break; } } } - mParents[nodeIndex] = newParentIndex; - - _rotate_right(mParents.begin() + effectStart, numAffected, numRotations); - _rotate_right(mNodes.begin() + effectStart, numAffected, numRotations); + const size_t numRotations = movingUp ? displacement : (numAffected-displacement); + _rotate_right(mParents.data() + effectStart, numAffected, numRotations); + _rotate_right(mNodes.data() + effectStart, numAffected, numRotations); return true; } @@ -523,7 +499,7 @@ bool SL_SpatialHierarchy::duplicate(size_type mParents[insertedOffset] = ROOT_NODE_INDEX; for (size_type i = insertedOffset+1; i < mNodes.size(); ++i) { - mParents[i] = (mParents[i]-nodeIndex) + insertedOffset + 1; + mParents[i] = (mParents[i]-nodeIndex) + insertedOffset; } return reparent(insertedOffset, mParents[nodeIndex]); diff --git a/softlight/src/SL_SceneGraph.cpp b/softlight/src/SL_SceneGraph.cpp index 2f7404c7..5ddafbcb 100644 --- a/softlight/src/SL_SceneGraph.cpp +++ b/softlight/src/SL_SceneGraph.cpp @@ -49,12 +49,6 @@ void rotate_right(RandomIter nums, size_t size, size_t numRotations) *nums = prev; } -template -void rotate_left(RandomIter nums, size_t size, size_t numRotations) -{ - rotate_right(nums, size, size-numRotations); -} - } // end anonymous namespace @@ -488,25 +482,27 @@ size_t SL_SceneGraph::delete_node(const size_t nodeIndex) noexcept -------------------------------------*/ bool SL_SceneGraph::reparent_node(const size_t nodeIndex, const size_t newParentId) noexcept { - if (newParentId == mNodeParentIds[nodeIndex]) + // data validation & early exits + if (nodeIndex == SL_SceneNodeProp::SCENE_NODE_ROOT_ID || nodeIndex >= mNodeParentIds.size() || mNodeParentIds[nodeIndex] == newParentId) { - // easy win - return true; + return false; } - if (nodeIndex == SL_SceneNodeProp::SCENE_NODE_ROOT_ID) + if (newParentId >= mNodeParentIds.size() && newParentId != SL_SceneNodeProp::SCENE_NODE_ROOT_ID) { return false; } - if (node_is_child(newParentId, nodeIndex)) + // Cannot make a node a parent of its ancestor. It's possible, but then + // what would the new ancestor of the node then be? + if (newParentId != SL_SceneNodeProp::SCENE_NODE_ROOT_ID) { - LS_LOG_MSG("Cannot make a node ", nodeIndex, " a parent of its ancestor ", newParentId, '.'); - return false; + if (node_is_child(newParentId, nodeIndex)) + { + return false; + } } - LS_DEBUG_ASSERT(nodeIndex < mNodes.size()); - const size_t numChildren = num_total_children(nodeIndex); const size_t displacement = 1 + numChildren; const size_t numNewSiblings = num_total_children(newParentId); @@ -514,35 +510,21 @@ bool SL_SceneGraph::reparent_node(const size_t nodeIndex, const size_t newParent // Keep track of the range of elements which need to be updated. const size_t effectStart = nodeIndex < newParentId ? nodeIndex : newNodeIndex; - const size_t effectEnd = nodeIndex < newParentId ? newNodeIndex : (nodeIndex+displacement); + size_t effectEnd = nodeIndex < newParentId ? newNodeIndex : (nodeIndex+displacement); const size_t numAffected = effectEnd - effectStart; // Determine if we're moving "up", closer to the root, or "down" away from // the root - const bool movingUp = newParentId < nodeIndex && (newParentId != SCENE_NODE_ROOT_ID); + const bool movingUp = newParentId < nodeIndex && (newParentId != SL_SceneNodeProp::SCENE_NODE_ROOT_ID); size_t amountToMove; if (movingUp) { amountToMove = nodeIndex - newNodeIndex; - - rotate_right(mNodeParentIds.data() + effectStart, numAffected, displacement); - rotate_right(mNodes.data() + effectStart, numAffected, displacement); - rotate_right(mBaseTransforms.data() + effectStart, numAffected, displacement); - rotate_right(mCurrentTransforms.data() + effectStart, numAffected, displacement); - rotate_right(mModelMatrices.data() + effectStart, numAffected, displacement); - rotate_right(mNodeNames.data() + effectStart, numAffected, displacement); } else { amountToMove = newNodeIndex - nodeIndex; - - rotate_left(mNodeParentIds.data() + effectStart, numAffected, displacement); - rotate_left(mNodes.data() + effectStart, numAffected, displacement); - rotate_left(mBaseTransforms.data() + effectStart, numAffected, displacement); - rotate_left(mCurrentTransforms.data() + effectStart, numAffected, displacement); - rotate_left(mModelMatrices.data() + effectStart, numAffected, displacement); - rotate_left(mNodeNames.data() + effectStart, numAffected, displacement); } for (size_t i = effectStart; i < effectEnd; ++i) @@ -551,14 +533,19 @@ bool SL_SceneGraph::reparent_node(const size_t nodeIndex, const size_t newParent size_t pId = rParentId; size_t nId = mNodes[i].nodeId; - mNodes[i].nodeId = i; mCurrentTransforms[i].set_dirty(); // Update the requested node's index if (nId == nodeIndex) { - pId = newParentId; - + if (newParentId != SL_SceneNodeProp::SCENE_NODE_ROOT_ID) + { + pId = newParentId - (nodeIndex < newParentId ? displacement : 0); + } + else + { + pId = newParentId; + } } else { @@ -581,7 +568,7 @@ bool SL_SceneGraph::reparent_node(const size_t nodeIndex, const size_t newParent } else { - if (i <= amountToMove-numChildren) + if (i > nodeIndex+numChildren) { pId -= displacement; } @@ -595,6 +582,37 @@ bool SL_SceneGraph::reparent_node(const size_t nodeIndex, const size_t newParent rParentId = pId; } + if (nodeIndex > newParentId) + { + while (effectEnd < mNodeParentIds.size()) + { + size_t i = effectEnd++; + + if (mNodeParentIds[i] < nodeIndex) + { + mNodeParentIds[i] += displacement; + } + else if (mNodeParentIds[i] <= newParentId || mNodeParentIds[i] == SL_SceneNodeProp::SCENE_NODE_ROOT_ID) + { + break; + } + } + } + + const size_t numRotations = movingUp ? displacement : (numAffected-displacement); + rotate_right(mNodeParentIds.data() + effectStart, numAffected, numRotations); + rotate_right(mNodes.data() + effectStart, numAffected, numRotations); + rotate_right(mBaseTransforms.data() + effectStart, numAffected, numRotations); + rotate_right(mCurrentTransforms.data() + effectStart, numAffected, numRotations); + rotate_right(mModelMatrices.data() + effectStart, numAffected, numRotations); + rotate_right(mNodeNames.data() + effectStart, numAffected, numRotations); + + // realign node IDs with their current index + for (size_t i = effectStart; i < effectEnd; ++i) + { + mNodes[i].nodeId = i; + } + // Animations need love too for (SL_Animation& anim : mAnimations) { @@ -625,8 +643,6 @@ bool SL_SceneGraph::reparent_node(const size_t nodeIndex, const size_t newParent } } - //LS_LOG_MSG("\tDone."); - LS_DEBUG_ASSERT(newNodeIndex <= mNodes.size()); return true; @@ -1041,7 +1057,6 @@ size_t SL_SceneGraph::import(SL_SceneGraph& inGraph) noexcept /*------------------------------------- * -------------------------------------*/ -#if 0 size_t SL_SceneGraph::insert_mesh(const SL_Mesh& m, const SL_BoundingBox& meshBounds) noexcept { LS_DEBUG_ASSERT(mMeshes.size() == mMeshBounds.size()); @@ -1060,7 +1075,7 @@ size_t SL_SceneGraph::insert_mesh(const SL_Mesh& m, const SL_BoundingBox& meshBo size_t SL_SceneGraph::insert_mesh_node( size_t parentId, const char* name, - const size_t numSubMeshes, + size_t numSubMeshes, const size_t* subMeshIds, const SL_Transform& transform) noexcept { @@ -1084,12 +1099,17 @@ size_t SL_SceneGraph::insert_mesh_node( node.dataId = mNodeMeshes.size(); node.type = SL_SceneNodeType::NODE_TYPE_MESH; + mNodeParentIds.push_back(parentId); mNodes.push_back(node); mNodeNames.emplace_back(name); mBaseTransforms.push_back(transform.transform()); mCurrentTransforms.push_back(transform); mModelMatrices.push_back(transform.transform()); + if (parentId != SCENE_NODE_ROOT_ID && mNodes.size() > 1) + { + reparent_node(mNodes.size()-1, parentId); + } + return mNodes.size()-1; } -#endif diff --git a/softlight/tests/sl_fullscreen_quad.cpp b/softlight/tests/sl_fullscreen_quad.cpp index 852407c3..fbadfb26 100644 --- a/softlight/tests/sl_fullscreen_quad.cpp +++ b/softlight/tests/sl_fullscreen_quad.cpp @@ -370,39 +370,24 @@ int load_quad_into_scene(SL_SceneGraph& graph) ibo.init(6, SL_DataType::VERTEX_DATA_INT, indices); vao.set_index_buffer(0); - graph.mNodes.emplace_back(SL_SceneNode{}); - SL_SceneNode& node = graph.mNodes.back(); - node.type = SL_SceneNodeType::NODE_TYPE_MESH; - node.dataId = 0; - node.nodeId = 0; - - graph.mMeshBounds.emplace_back(SL_BoundingBox()); - graph.mMeshBounds.back().compare_and_update(math::vec3{-1.f, -1.f, 0.f}); - graph.mMeshBounds.back().compare_and_update(math::vec3{1.f, 1.f, 0.f}); - SL_Texture& tex = context.texture(1); graph.mMaterials.emplace_back(SL_Material{}); SL_Material& mat = graph.mMaterials.back(); mat.pTextures[0] = &tex; - graph.mNodeParentIds.emplace_back(SCENE_NODE_ROOT_ID); - graph.mBaseTransforms.emplace_back(math::mat4{1.f}); - graph.mCurrentTransforms.emplace_back(SL_Transform{}); - graph.mCurrentTransforms.back().extract_transforms(graph.mBaseTransforms.back()); - graph.mModelMatrices.emplace_back(math::mat4{1.f}); - - graph.mMeshes.emplace_back(SL_Mesh()); - SL_Mesh& mesh = graph.mMeshes.back(); + SL_Mesh mesh; mesh.vaoId = vaoId; mesh.elementBegin = 0; mesh.elementEnd = 6; mesh.mode = SL_RenderMode::RENDER_MODE_INDEXED_TRIANGLES; mesh.materialId = 0; - graph.mNodeNames.emplace_back("FS_Quad"); - graph.mNumNodeMeshes.emplace_back(1); - graph.mNodeMeshes.emplace_back(utils::Pointer{new size_t[1]}); - graph.mNodeMeshes.back()[0] = 0; + SL_BoundingBox box; + box.min_point(math::vec3{-1.f, -1.f, 0.f}); + box.max_point(math::vec3{1.f, 1.f, 0.f}); + + size_t subMeshId = graph.insert_mesh(mesh, box); + graph.insert_mesh_node(SCENE_NODE_ROOT_ID, "FS_Quad", 1, &subMeshId, SL_Transform{}); return 0; } diff --git a/softlight/tests/sl_spatial_hierarchy_test.cpp b/softlight/tests/sl_spatial_hierarchy_test.cpp index 5d6c021b..9f147336 100644 --- a/softlight/tests/sl_spatial_hierarchy_test.cpp +++ b/softlight/tests/sl_spatial_hierarchy_test.cpp @@ -68,33 +68,43 @@ int main() std::cout << sceneGraph << '\n' << std::endl; */ + std::cout << "Reparent " << sceneGraph[1] << " -> " << sceneGraph[2] << ':' << std::endl; sceneGraph.reparent(1, 2); std::cout << sceneGraph << '\n' << std::endl; + std::cout << "Reparent " << sceneGraph[3] << " -> ROOT:" << std::endl; sceneGraph.reparent(3, SceneGraphType::ROOT_NODE_INDEX); std::cout << sceneGraph << '\n' << std::endl; + std::cout << "Reparent " << sceneGraph[4] << " -> " << sceneGraph[2] << ':' << std::endl; sceneGraph.reparent(4, 2); std::cout << sceneGraph << '\n' << std::endl; + std::cout << "Reparent " << sceneGraph[3] << " -> " << sceneGraph[5] << ':' << std::endl; sceneGraph.reparent(3, 5); std::cout << sceneGraph << '\n' << std::endl; + std::cout << "Reparent " << sceneGraph[1] << " -> " << sceneGraph[3] << ':' << std::endl; sceneGraph.reparent(1, 3); std::cout << sceneGraph << '\n' << std::endl; + std::cout << "Reparent " << sceneGraph[6] << " -> " << sceneGraph[2] << ':' << std::endl; sceneGraph.reparent(6, 2); std::cout << sceneGraph << '\n' << std::endl; + std::cout << "Duplicate " << sceneGraph[2] << ':' << std::endl; sceneGraph.duplicate(2); std::cout << sceneGraph << '\n' << std::endl; + std::cout << "Reparent " << sceneGraph[2] << " -> " << sceneGraph[0] << ':' << std::endl; sceneGraph.reparent(2, 0); std::cout << sceneGraph << '\n' << std::endl; + std::cout << "Reparent " << sceneGraph[0] << " -> " << sceneGraph[9] << ':' << std::endl; sceneGraph.reparent(0, 9); std::cout << sceneGraph << '\n' << std::endl; + std::cout << "Reparent " << sceneGraph[6] << " -> ROOT:" << std::endl; sceneGraph.reparent(6, SceneGraphType::ROOT_NODE_INDEX); std::cout << sceneGraph << '\n' << std::endl;