Browse Source

SceneTools: utility to reorder & cluster the parent field.

Useful for calculating hierarchic transformations.
pull/542/merge
Vladimír Vondruš 4 years ago
parent
commit
a802d6cd11
  1. 8
      doc/snippets/CMakeLists.txt
  2. 65
      doc/snippets/MagnumSceneTools.cpp
  3. 52
      src/Magnum/SceneTools/CMakeLists.txt
  4. 122
      src/Magnum/SceneTools/OrderClusterParents.cpp
  5. 77
      src/Magnum/SceneTools/OrderClusterParents.h
  6. 2
      src/Magnum/SceneTools/Test/CMakeLists.txt
  7. 324
      src/Magnum/SceneTools/Test/OrderClusterParentsTest.cpp

8
doc/snippets/CMakeLists.txt

@ -188,6 +188,14 @@ if(WITH_SCENEGRAPH)
endif() endif()
endif() endif()
if(WITH_SCENETOOLS)
add_library(snippets-MagnumSceneTools STATIC
MagnumSceneTools.cpp)
target_link_libraries(snippets-MagnumSceneTools PRIVATE MagnumSceneTools)
set_target_properties(snippets-MagnumSceneTools
PROPERTIES FOLDER "Magnum/doc/snippets")
endif()
if(WITH_VK) if(WITH_VK)
add_library(snippets-MagnumVk STATIC MagnumVk.cpp) add_library(snippets-MagnumVk STATIC MagnumVk.cpp)
target_link_libraries(snippets-MagnumVk PRIVATE MagnumVk) target_link_libraries(snippets-MagnumVk PRIVATE MagnumVk)

65
doc/snippets/MagnumSceneTools.cpp

@ -0,0 +1,65 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/Pair.h>
#include "Magnum/Math/Matrix4.h"
#include "Magnum/SceneTools/OrderClusterParents.h"
#include "Magnum/Trade/SceneData.h"
#define DOXYGEN_ELLIPSIS(...) __VA_ARGS__
using namespace Magnum;
int main() {
{
/* [orderClusterParents-transformations] */
Trade::SceneData scene = DOXYGEN_ELLIPSIS(Trade::SceneData{{}, 0, nullptr, {}});
/* Put all transformations into an array indexed by object ID. Objects
implicitly have an identity transformation, first element is reserved for
the global transformation. */
Containers::Array<Matrix4> transformations{std::size_t(scene.mappingBound() + 1)};
for(const Containers::Pair<UnsignedInt, Matrix4>& transformation:
scene.transformations3DAsArray())
{
transformations[transformation.first() + 1] = transformation.second();
}
/* Go through ordered parents and compose absolute transformations for all
nodes in the hierarchy, objects in the root use transformations[0]. The
function ensures that the parent transformation is already calculated when
referenced by child nodes. */
for(const Containers::Pair<UnsignedInt, Int>& parent:
SceneTools::orderClusterParents(scene))
{
transformations[parent.first() + 1] =
transformations[parent.second() + 1]*
transformations[parent.first() + 1];
}
/* [orderClusterParents-transformations] */
}
}

52
src/Magnum/SceneTools/CMakeLists.txt

@ -23,7 +23,15 @@
# DEALINGS IN THE SOFTWARE. # DEALINGS IN THE SOFTWARE.
# #
# Files shared between main library and unit test library
set(MagnumSceneTools_SRCS )
# Files compiled with different flags for main library and unit test library
set(MagnumSceneTools_GracefulAssert_SRCS
OrderClusterParents.cpp)
set(MagnumSceneTools_HEADERS set(MagnumSceneTools_HEADERS
OrderClusterParents.h
SceneTools.h SceneTools.h
visibility.h) visibility.h)
@ -32,8 +40,24 @@ set(MagnumSceneTools_PRIVATE_HEADERS
Implementation/combine.h Implementation/combine.h
Implementation/convertToSingleFunctionObjects.h) Implementation/convertToSingleFunctionObjects.h)
## Objects shared between main and test library
#add_library(MagnumSceneToolsObjects OBJECT
#${MagnumSceneTools_SRCS}
#${MagnumSceneTools_HEADERS}
#${MagnumSceneTools_PRIVATE_HEADERS})
#target_include_directories(MagnumSceneToolsObjects PUBLIC $<TARGET_PROPERTY:Magnum,INTERFACE_INCLUDE_DIRECTORIES>)
#if(NOT BUILD_STATIC)
#target_compile_definitions(MagnumSceneToolsObjects PRIVATE "MagnumSceneToolsObjects_EXPORTS")
#endif()
#if(NOT BUILD_STATIC OR BUILD_STATIC_PIC)
#set_target_properties(MagnumSceneToolsObjects PROPERTIES POSITION_INDEPENDENT_CODE ON)
#endif()
#set_target_properties(MagnumSceneToolsObjects PROPERTIES FOLDER "Magnum/SceneTools")
# Main SceneTools library # Main SceneTools library
add_library(MagnumSceneTools INTERFACE add_library(MagnumSceneTools ${SHARED_OR_STATIC}
#$<TARGET_OBJECTS:MagnumSceneToolsObjects>
${MagnumSceneTools_GracefulAssert_SRCS}
${MagnumSceneTools_HEADERS} ${MagnumSceneTools_HEADERS}
${MagnumSceneTools_PRIVATE_HEADERS}) ${MagnumSceneTools_PRIVATE_HEADERS})
set_target_properties(MagnumSceneTools PROPERTIES set_target_properties(MagnumSceneTools PROPERTIES
@ -44,9 +68,9 @@ if(NOT BUILD_STATIC)
elseif(BUILD_STATIC_PIC) elseif(BUILD_STATIC_PIC)
set_target_properties(MagnumSceneTools PROPERTIES POSITION_INDEPENDENT_CODE ON) set_target_properties(MagnumSceneTools PROPERTIES POSITION_INDEPENDENT_CODE ON)
endif() endif()
#target_link_libraries(MagnumSceneTools PUBLIC target_link_libraries(MagnumSceneTools PUBLIC
#Magnum Magnum
#MagnumTrade) MagnumTrade)
install(TARGETS MagnumSceneTools install(TARGETS MagnumSceneTools
RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR} RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR}
@ -70,18 +94,20 @@ endif()
if(BUILD_TESTS) if(BUILD_TESTS)
# Library with graceful assert for testing # Library with graceful assert for testing
add_library(MagnumSceneToolsTestLib INTERFACE ) add_library(MagnumSceneToolsTestLib ${SHARED_OR_STATIC}
#set_target_properties(MagnumSceneToolsTestLib PROPERTIES #$<TARGET_OBJECTS:MagnumSceneToolsObjects>
#DEBUG_POSTFIX "-d" ${MagnumSceneTools_GracefulAssert_SRCS})
#FOLDER "Magnum/SceneTools") set_target_properties(MagnumSceneToolsTestLib PROPERTIES
#target_compile_definitions(MagnumSceneToolsTestLib PRIVATE DEBUG_POSTFIX "-d"
#"CORRADE_GRACEFUL_ASSERT" "MagnumSceneTools_EXPORTS") FOLDER "Magnum/SceneTools")
target_compile_definitions(MagnumSceneToolsTestLib PRIVATE
"CORRADE_GRACEFUL_ASSERT" "MagnumSceneTools_EXPORTS")
if(BUILD_STATIC_PIC) if(BUILD_STATIC_PIC)
set_target_properties(MagnumSceneToolsTestLib PROPERTIES POSITION_INDEPENDENT_CODE ON) set_target_properties(MagnumSceneToolsTestLib PROPERTIES POSITION_INDEPENDENT_CODE ON)
endif() endif()
#target_link_libraries(MagnumSceneToolsTestLib PUBLIC target_link_libraries(MagnumSceneToolsTestLib PUBLIC
#Magnum Magnum
#MagnumTrade) MagnumTrade)
add_subdirectory(Test) add_subdirectory(Test)
endif() endif()

122
src/Magnum/SceneTools/OrderClusterParents.cpp

@ -0,0 +1,122 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include "OrderClusterParents.h"
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/GrowableArray.h>
#include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/Pair.h>
#include "Magnum/Trade/SceneData.h"
namespace Magnum { namespace SceneTools {
Containers::Array<Containers::Pair<UnsignedInt, Int>> orderClusterParents(const Trade::SceneData& scene) {
const Containers::Optional<UnsignedInt> parentFieldId = scene.findFieldId(Trade::SceneField::Parent);
CORRADE_ASSERT(parentFieldId,
"SceneTools::orderClusterParents(): the scene has no hierarchy", {});
Containers::Array<Containers::Pair<UnsignedInt, Int>> out{NoInit, scene.fieldSize(*parentFieldId)};
Containers::StridedArrayView1D<UnsignedInt> mapping{out, &out.data()->first(), out.size(), sizeof(decltype(out)::Type)};
Containers::StridedArrayView1D<Int> parentOffset{out, &out.data()->second(), out.size(), sizeof(decltype(out)::Type)};
orderClusterParentsInto(scene, mapping, parentOffset);
return out;
}
void orderClusterParentsInto(const Trade::SceneData& scene, const Containers::StridedArrayView1D<UnsignedInt>& mappingDestination, const Containers::StridedArrayView1D<Int>& parentDestination) {
#ifndef CORRADE_NO_ASSERT
const Containers::Optional<UnsignedInt> parentFieldId = scene.findFieldId(Trade::SceneField::Parent);
CORRADE_ASSERT(parentFieldId,
"SceneTools::orderClusterParentsInto(): the scene has no hierarchy", );
const std::size_t parentFieldSize = scene.fieldSize(*parentFieldId);
CORRADE_ASSERT(mappingDestination.size() == parentFieldSize,
"SceneTools::orderClusterParentsInto(): expected mapping destination view with" << parentFieldSize << "elements but got" << mappingDestination.size(), );
CORRADE_ASSERT(parentDestination.size() == scene.fieldSize(*parentFieldId),
"SceneTools::orderClusterParentsInto(): expected parent destination view with" << parentFieldSize << "elements but got" << parentDestination.size(), );
#endif
/* Convert the parent list to a child list to sort them toplogically */
const Containers::Array<Containers::Pair<UnsignedInt, Int>> parents = scene.parentsAsArray();
/* Children offset for each node including root. First calculate the count
of children for each ... */
Containers::Array<UnsignedInt> childrenOffsets{DirectInit, scene.mappingBound() + 2, 0u};
for(const Containers::Pair<UnsignedInt, Int>& parent: parents) {
CORRADE_INTERNAL_ASSERT(parent.first() < scene.mappingBound() && (parent.second() == -1 || UnsignedInt(parent.second()) < scene.mappingBound()));
++childrenOffsets[parent.second() + 1];
}
/* ... then convert the counts to a running offset. Now
`[childrenOffsets[i + 1], childrenOffsets[i + 2])` contains a range in
which the `children` array below contains a list of children for `i`. */
UnsignedInt offset = 0;
for(UnsignedInt& i: childrenOffsets) {
UnsignedInt nextOffset = offset + i;
i = offset;
offset = nextOffset;
}
CORRADE_INTERNAL_ASSERT(offset == parents.size());
/* Go through the parent list again, convert that to child ranges */
Containers::Array<UnsignedInt> children{NoInit, parents.size()};
{
Containers::Array<UnsignedInt> currentChildrenOffsets{DirectInit, scene.mappingBound() + 1, 0u};
for(const Containers::Pair<UnsignedInt, Int>& parent: parents) {
UnsignedInt& currentChildrenOffset = currentChildrenOffsets[parent.second() + 1];
children[childrenOffsets[parent.second() + 1] + currentChildrenOffset] = parent.first();
++currentChildrenOffset;
}
}
/* Go breadth-first (so we have nodes sharing the same parent next to each
other) and build a list of (id, parent id) where a parent is always
before its children */
std::size_t outputOffset = 0;
Containers::Array<Int> parentsToProcess;
arrayAppend(parentsToProcess, -1);
for(std::size_t i = 0; i != parentsToProcess.size(); ++i) {
const Int objectId = parentsToProcess[i];
for(std::size_t j = childrenOffsets[objectId + 1], jMax = childrenOffsets[objectId + 2]; j != jMax; ++j) {
/** @todo better diagnostic once we can use a BitArray to detect
which nodes are parented more than once (OTOH maybe that's
unnecessary extra work which isn't desired to be done here but
should be instead in a dedicated cycle/sparse checker utility?) */
CORRADE_ASSERT_OUTPUT(outputOffset < parents.size(),
"SceneTools::orderClusterParents(): hierarchy is cyclic", );
arrayAppend(parentsToProcess, children[j]);
mappingDestination[outputOffset] = children[j];
parentDestination[outputOffset] = objectId;
++outputOffset;
}
}
/** @todo better diagnostic once we can use a BitArray to detect which
nodes are unreachable from root (OTOH again maybe that's undesirable
extra work that doesn't belong here?) */
CORRADE_ASSERT(outputOffset == parents.size(),
"SceneTools::orderClusterParents(): hierarchy is sparse", );
}
}}

77
src/Magnum/SceneTools/OrderClusterParents.h

@ -0,0 +1,77 @@
#ifndef Magnum_SceneTools_OrderClusterParents_h
#define Magnum_SceneTools_OrderClusterParents_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
/** @file
* @brief Function @ref Magnum::SceneTools::orderClusterParents(), @ref Magnum::SceneTools::orderClusterParentsInto()
* @m_since_latest
*/
#include "Magnum/Magnum.h"
#include "Magnum/SceneTools/visibility.h"
#include "Magnum/Trade/Trade.h"
namespace Magnum { namespace SceneTools {
/**
@brief Calculate ordered and clustered parents
@m_since_latest
Extracts the @ref Trade::SceneField::Parent field from @p scene and converts it
to match the following rules:
- a parent object reference appears always before any of its children
- the array is clustered so children sharing the same parent are together
This form is useful primarily for calculating absolute object transformations,
for example:
@snippet MagnumSceneTools.cpp orderClusterParents-transformations
The operation is done in an @f$ \mathcal{O}(n) @f$ execution time and memory
complexity, with @f$ n @f$ being @ref Trade::SceneData::mappingBound(). The
@ref Trade::SceneField::Parent field is expected to be contained in the scene,
having no cycles (i.e., every node listed just once) and not being sparse
(i.e., every node listed in the field reachable from the root).
@see @ref Trade::SceneData::hasField()
*/
MAGNUM_SCENETOOLS_EXPORT Containers::Array<Containers::Pair<UnsignedInt, Int>> orderClusterParents(const Trade::SceneData& scene);
/**
@brief Calculate ordered and clustered parents into a pre-allocated view
@m_since_latest
Like @ref orderClusterParents(), but puts the result into
@p mappingDestination and @p parentDestination instead of allocating a new
array. Expect that both views have a size equal to size of the
@ref Trade::SceneField::Parent view in @p scene.
@see @ref Trade::SceneData::fieldSize(SceneField) const
*/
MAGNUM_SCENETOOLS_EXPORT void orderClusterParentsInto(const Trade::SceneData& scene, const Containers::StridedArrayView1D<UnsignedInt>& mappingDestination, const Containers::StridedArrayView1D<Int>& parentDestination);
}}
#endif

2
src/Magnum/SceneTools/Test/CMakeLists.txt

@ -25,8 +25,10 @@
corrade_add_test(SceneToolsCombineTest CombineTest.cpp LIBRARIES MagnumTrade) corrade_add_test(SceneToolsCombineTest CombineTest.cpp LIBRARIES MagnumTrade)
corrade_add_test(SceneToolsConvertToSingleFun___Test ConvertToSingleFunctionObjectsTest.cpp LIBRARIES MagnumTrade) corrade_add_test(SceneToolsConvertToSingleFun___Test ConvertToSingleFunctionObjectsTest.cpp LIBRARIES MagnumTrade)
corrade_add_test(SceneToolsOrderClusterParentsTest OrderClusterParentsTest.cpp LIBRARIES MagnumSceneToolsTestLib)
set_target_properties( set_target_properties(
SceneToolsCombineTest SceneToolsCombineTest
SceneToolsConvertToSingleFun___Test SceneToolsConvertToSingleFun___Test
SceneToolsOrderClusterParentsTest
PROPERTIES FOLDER "Magnum/SceneTools/Test") PROPERTIES FOLDER "Magnum/SceneTools/Test")

324
src/Magnum/SceneTools/Test/OrderClusterParentsTest.cpp

@ -0,0 +1,324 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <sstream>
#include <Corrade/Containers/Pair.h>
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/Container.h>
#include <Corrade/Utility/DebugStl.h>
#include "Magnum/SceneTools/OrderClusterParents.h"
#include "Magnum/Trade/SceneData.h"
namespace Magnum { namespace SceneTools { namespace Test { namespace {
struct OrderClusterParentsTest: TestSuite::Tester {
explicit OrderClusterParentsTest();
void test();
void noParentField();
void emptyParentField();
void intoNoParentField();
void intoEmptyParentField();
void intoWrongDestinationSize();
void sparse();
void cyclic();
void cyclicDeep();
void sparseAndCyclic();
};
OrderClusterParentsTest::OrderClusterParentsTest() {
addTests({&OrderClusterParentsTest::test,
&OrderClusterParentsTest::noParentField,
&OrderClusterParentsTest::emptyParentField,
&OrderClusterParentsTest::intoNoParentField,
&OrderClusterParentsTest::intoEmptyParentField,
&OrderClusterParentsTest::intoWrongDestinationSize,
&OrderClusterParentsTest::sparse,
&OrderClusterParentsTest::cyclic,
&OrderClusterParentsTest::cyclicDeep,
&OrderClusterParentsTest::sparseAndCyclic});
}
void OrderClusterParentsTest::test() {
struct Field {
/* To verify we don't have unnecessarily hardcoded 32-bit types */
UnsignedShort object;
Byte parent;
} data[]{
/* Backward parent reference */
{5, 1},
/* Forward parent reference */
{6, 9},
/* Root elements */
{3, -1},
{1, -1},
/* Deep hierarchy */
{9, 10},
{10, 3},
/* Multiple children */
{7, 3},
{157, 3},
{143, 6},
/* More root elements */
{2, -1}
/* Elements 0, 4, 8, 11-142, 144-156 deliberately not used */
};
Containers::StridedArrayView1D<Field> view = data;
Trade::SceneData scene{Trade::SceneMappingType::UnsignedShort, 158, {}, data, {
/* To verify it doesn't just pick the first field ever */
Trade::SceneFieldData{Trade::SceneField::Mesh, Trade::SceneMappingType::UnsignedShort, nullptr, Trade::SceneFieldType::UnsignedInt, nullptr},
Trade::SceneFieldData{Trade::SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)}
}};
CORRADE_COMPARE_AS(orderClusterParents(scene), (Containers::arrayView<Containers::Pair<UnsignedInt, Int>>({
/* Root objects first, in order as found */
{3, -1},
{1, -1},
{2, -1},
/* Then children of node 3, clustered together, in order as found */
{10, 3},
{7, 3},
{157, 3},
/* Then children of node 1 */
{5, 1},
/* Children of node 10 */
{9, 10},
/* Children of node 9 */
{6, 9},
/* Children of node 6 */
{143, 6},
})), TestSuite::Compare::Container);
}
void OrderClusterParentsTest::noParentField() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
Trade::SceneData scene{Trade::SceneMappingType::UnsignedByte, 0, nullptr, {}};
std::ostringstream out;
Error redirectError{&out};
orderClusterParents(scene);
CORRADE_COMPARE(out.str(),
"SceneTools::orderClusterParents(): the scene has no hierarchy\n");
}
void OrderClusterParentsTest::emptyParentField() {
Trade::SceneData scene{Trade::SceneMappingType::UnsignedInt, 0, nullptr, {
Trade::SceneFieldData{Trade::SceneField::Parent, Trade::SceneMappingType::UnsignedInt, nullptr, Trade::SceneFieldType::Int, nullptr}
}};
CORRADE_COMPARE_AS(orderClusterParents(scene),
(Containers::ArrayView<const Containers::Pair<UnsignedInt, Int>>{}),
TestSuite::Compare::Container);
}
void OrderClusterParentsTest::intoNoParentField() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
Trade::SceneData scene{Trade::SceneMappingType::UnsignedByte, 0, nullptr, {}};
std::ostringstream out;
Error redirectError{&out};
orderClusterParentsInto(scene, nullptr, nullptr);
CORRADE_COMPARE(out.str(),
"SceneTools::orderClusterParentsInto(): the scene has no hierarchy\n");
}
void OrderClusterParentsTest::intoEmptyParentField() {
Trade::SceneData scene{Trade::SceneMappingType::UnsignedInt, 0, nullptr, {
Trade::SceneFieldData{Trade::SceneField::Parent, Trade::SceneMappingType::UnsignedInt, nullptr, Trade::SceneFieldType::Int, nullptr}
}};
orderClusterParentsInto(scene, nullptr, nullptr);
CORRADE_VERIFY(true);
}
void OrderClusterParentsTest::intoWrongDestinationSize() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
struct Field {
UnsignedInt object;
Int parent;
} data[]{
{2, -1},
{3, 2},
{7, -1}
};
Containers::StridedArrayView1D<Field> view = data;
Trade::SceneData scene{Trade::SceneMappingType::UnsignedInt, 8, {}, data, {
Trade::SceneFieldData{Trade::SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)}
}};
UnsignedInt mappingCorrect[3];
UnsignedInt mapping[2];
Int parentOffsetCorrect[3];
Int parentOffset[2];
std::ostringstream out;
Error redirectError{&out};
orderClusterParentsInto(scene, mappingCorrect, parentOffset);
orderClusterParentsInto(scene, mapping, parentOffsetCorrect);
CORRADE_COMPARE(out.str(),
"SceneTools::orderClusterParentsInto(): expected parent destination view with 3 elements but got 2\n"
"SceneTools::orderClusterParentsInto(): expected mapping destination view with 3 elements but got 2\n");
}
void OrderClusterParentsTest::sparse() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
struct Field {
UnsignedInt object;
Int parent;
} data[]{
{2, -1},
{3, 2},
{7, -1},
/* Not reachable from root */
{15, 6},
{14, 6},
{11, 15},
};
Containers::StridedArrayView1D<Field> view = data;
Trade::SceneData scene{Trade::SceneMappingType::UnsignedInt, 16, {}, data, {
Trade::SceneFieldData{Trade::SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)}
}};
std::ostringstream out;
Error redirectError{&out};
orderClusterParents(scene);
CORRADE_COMPARE(out.str(),
"SceneTools::orderClusterParents(): hierarchy is sparse\n");
}
void OrderClusterParentsTest::cyclic() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
struct Field {
UnsignedInt object;
Int parent;
} data[]{
{2, -1},
{3, 2},
{7, -1},
/* Cycle of length 1, which will be treated as sparse hierarchy */
{13, 13}
};
Containers::StridedArrayView1D<Field> view = data;
Trade::SceneData scene{Trade::SceneMappingType::UnsignedInt, 16, {}, data, {
Trade::SceneFieldData{Trade::SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)}
}};
std::ostringstream out;
Error redirectError{&out};
orderClusterParents(scene);
CORRADE_COMPARE(out.str(),
"SceneTools::orderClusterParents(): hierarchy is sparse\n");
}
void OrderClusterParentsTest::cyclicDeep() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
struct Field {
UnsignedInt object;
Int parent;
} data[]{
{2, -1},
{3, 2},
{7, -1},
/* Cycle of length 3 */
{13, -1},
{5, 13},
{13, 3}
};
Containers::StridedArrayView1D<Field> view = data;
Trade::SceneData scene{Trade::SceneMappingType::UnsignedInt, 16, {}, data, {
Trade::SceneFieldData{Trade::SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)}
}};
std::ostringstream out;
Error redirectError{&out};
orderClusterParents(scene);
CORRADE_COMPARE(out.str(),
"SceneTools::orderClusterParents(): hierarchy is cyclic\n");
}
void OrderClusterParentsTest::sparseAndCyclic() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
struct Field {
UnsignedInt object;
Int parent;
} data[]{
{2, -1},
{3, 2},
{7, -1},
/* Cycle of length 3 */
{13, -1},
{5, 13},
{13, 3},
/* Not reachable from root */
{15, 6}
};
Containers::StridedArrayView1D<Field> view = data;
Trade::SceneData scene{Trade::SceneMappingType::UnsignedInt, 16, {}, data, {
Trade::SceneFieldData{Trade::SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)}
}};
std::ostringstream out;
Error redirectError{&out};
orderClusterParents(scene);
CORRADE_EXPECT_FAIL("The implementation needs to track already visited objects with a BitArray to detect this, it'd also provide a much better diagnostic.");
CORRADE_COMPARE(out.str(),
"SceneTools::orderClusterParents(): hierarchy is cyclic\n");
}
}}}}
CORRADE_TEST_MAIN(Magnum::SceneTools::Test::OrderClusterParentsTest)
Loading…
Cancel
Save