diff --git a/doc/artwork/scenedata-dod.svg b/doc/artwork/scenedata-dod.svg
new file mode 100644
index 0000000000..182e608aef
--- /dev/null
+++ b/doc/artwork/scenedata-dod.svg
@@ -0,0 +1,1610 @@
+
+
diff --git a/doc/artwork/scenedata-tree.svg b/doc/artwork/scenedata-tree.svg
new file mode 100644
index 0000000000..db891c0fa0
--- /dev/null
+++ b/doc/artwork/scenedata-tree.svg
@@ -0,0 +1,1290 @@
+
+
diff --git a/doc/changelog.dox b/doc/changelog.dox
index 00c587c04e..26ca1c203f 100644
--- a/doc/changelog.dox
+++ b/doc/changelog.dox
@@ -219,6 +219,9 @@ See also:
@ref Trade::PhongMaterialData::normalTextureScale() and
@ref Trade::PhongMaterialData::normalTextureSwizzle() to make new features
added for PBR materials recognizable also in classic Phong workflows.
+- A completely redesigned @ref Trade::SceneData class that stores data of
+ the whole scene in a data-oriented way, allowing for storing custom fields
+ as well. See [mosra/magnum#525](https://github.com/mosra/magnum/pull/525).
- New @ref Trade::SkinData class and @ref Trade::AbstractImporter::skin2D() /
@ref Trade::AbstractImporter::skin3D() family of APIs for skin import, as
well as support in @ref Trade::AnySceneImporter "AnySceneImporter"
@@ -629,6 +632,27 @@ See also:
directly but rather generated on-the-fly from attribute data, which makes
them less efficient than calling @ref Trade::MaterialData::hasAttribute()
etc.
+- @ref Trade::SceneData constructor taking a @ref std::vector of 2D and 3D
+ children is deprecated in favor of the new scene representation. Use
+ @ref Trade::SceneData::SceneData(SceneMappingType, UnsignedLong, Containers::Array&&, Containers::Array&&, const void*)
+ instead.
+- @cpp Trade::SceneData::children2D() @ce and
+ @cpp Trade::SceneData::children3D() @ce are deprecated in favor of the
+ new scene representation. Use @ref Trade::SceneData::childrenFor() with
+ @cpp -1 @ce passed as the @p object argument to get a list of top-level
+ objects.
+- @cpp Trade::ObjectData*D @ce, @cpp Trade::MeshObjectData*D @ce classes,
+ @cpp Trade::ObjectInstanceType*D @ce, @cpp Trade::ObjectFlag*D @ce,
+ @cpp Trade::ObjectFlags*D @ce enums and the corresponding
+ @cpp Trade::AbstractImporter::object*DCount() @ce,
+ @cpp Trade::AbstractImporter::object*DForName() @ce,
+ @cpp Trade::AbstractImporter::object*DName() @ce and
+ @cpp Trade::AbstractImporter::object*D() @ce accessor APIs are deprecated
+ in favor of the unified representation in @ref Trade::SceneData and the
+ @ref Trade::AbstractImporter::objectCount(),
+ @relativeref{Trade::AbstractImporter,objectForName()} and
+ @relativeref{Trade::AbstractImporter,objectName()} accessors that are
+ shared for 2D and 3D.
- @ref Trade::AbstractImporter::material() now returns
@ref Corrade::Containers::Optional instead of a @ref Corrade::Containers::Pointer,
as the new @ref Trade::MaterialData class isn't polymorphic anymore. If
@@ -802,6 +826,23 @@ See also:
@cpp Trade::AbstractMaterialData @ce aliases to, doesn't have a
@cpp virtual @ce destructor as subclasses with extra data members aren't a
desired use case anymore.
+- @ref Trade::SceneData constructor taking a @ref std::vector of 2D and 3D
+ children that got deprecated in favor of
+ @ref Trade::SceneData::SceneData(SceneMappingType, UnsignedLong, Containers::Array&&, Containers::Array&&, const void*)
+ no longer accepts a scene that has both 2D and 3D children.
+- The deprecated @cpp Trade::AbstractImporter::object*DCount() @ce,
+ @cpp Trade::AbstractImporter::object*DForName() @ce,
+ @cpp Trade::AbstractImporter::object*DName() @ce and
+ @cpp Trade::AbstractImporter::object*D() @ce accessors now behave different
+ for objects with multiple mesh assignments. This handling was originally
+ present in importer plugins themselves, but as the new
+ @ref Trade::SceneData representation supports multiple mesh/camera/...
+ assignments to a single object natively, the handling was moved to a single
+ place in the compatibility layer. Because the compatibility layer cannot
+ renumber object IDs, the newly added objects are not immediately following
+ the original ID but instead allocated at the end of the object ID range
+ reported by the importer. While the newly added objects have different IDs,
+ they retain the parent name like before.
- @ref Trade::AbstractImporter::doDefaultScene() is now @cpp const @ce ---
since the function is not expected to fail, no kind of complex lazy
population can be done anyway. This is now consistent with `do*Count()`
diff --git a/doc/snippets/MagnumTrade.cpp b/doc/snippets/MagnumTrade.cpp
index e4a1fb3b17..75ac34c709 100644
--- a/doc/snippets/MagnumTrade.cpp
+++ b/doc/snippets/MagnumTrade.cpp
@@ -24,8 +24,11 @@
*/
#include
+#include
#include
+#include
#include
+#include
#include
#include
@@ -47,12 +50,15 @@
#include "Magnum/Trade/LightData.h"
#include "Magnum/Trade/MaterialData.h"
#include "Magnum/Trade/MeshData.h"
-#include "Magnum/Trade/ObjectData2D.h"
-#include "Magnum/Trade/MeshObjectData3D.h"
#include "Magnum/Trade/PbrClearCoatMaterialData.h"
#include "Magnum/Trade/PbrSpecularGlossinessMaterialData.h"
#include "Magnum/Trade/PbrMetallicRoughnessMaterialData.h"
#include "Magnum/Trade/PhongMaterialData.h"
+#include "Magnum/Trade/SceneData.h"
+#include "Magnum/SceneGraph/Drawable.h"
+#include "Magnum/SceneGraph/Scene.h"
+#include "Magnum/SceneGraph/Object.h"
+#include "Magnum/SceneGraph/MatrixTransformation3D.h"
#ifdef MAGNUM_TARGET_GL
#include "Magnum/GL/Texture.h"
#include "Magnum/GL/TextureFormat.h"
@@ -66,12 +72,16 @@
#ifdef MAGNUM_BUILD_DEPRECATED
#define _MAGNUM_NO_DEPRECATED_MESHDATA /* So it doesn't yell here */
+#define _MAGNUM_NO_DEPRECATED_OBJECTDATA /* So it doesn't yell here */
#include "Magnum/Trade/MeshData2D.h"
#include "Magnum/Trade/MeshData3D.h"
+#include "Magnum/Trade/MeshObjectData3D.h"
+#include "Magnum/Trade/ObjectData2D.h"
#endif
#define DOXYGEN_ELLIPSIS(...) __VA_ARGS__
+#define DOXYGEN_IGNORE(...) __VA_ARGS__
using namespace Magnum;
using namespace Magnum::Math::Literals;
@@ -180,21 +190,6 @@ importer->openFile("scene.gltf"); // memory-maps all files
}
#endif
-{
-Containers::Pointer importer;
-Int materialIndex;
-/* [AbstractImporter-usage-cast] */
-Containers::Pointer data = importer->object3D(12);
-if(data && data->instanceType() == Trade::ObjectInstanceType3D::Mesh) {
- auto& mesh = static_cast(*data);
-
- materialIndex = mesh.material();
- // ...
-}
-/* [AbstractImporter-usage-cast] */
-static_cast(materialIndex);
-}
-
{
Containers::Pointer importer;
/* [AbstractImporter-setFileCallback] */
@@ -844,9 +839,9 @@ MeshTools::transformPointsInPlace(transformation, data.positions(0));
/* [MeshData2D-transform] */
CORRADE_IGNORE_DEPRECATED_POP
}
-#endif
{
+CORRADE_IGNORE_DEPRECATED_PUSH
Trade::ObjectData2D& baz();
Trade::ObjectData2D& data = baz();
/* [ObjectData2D-transformation] */
@@ -855,9 +850,9 @@ Matrix3 transformation =
Matrix3::scaling(data.scaling());
/* [ObjectData2D-transformation] */
static_cast(transformation);
+CORRADE_IGNORE_DEPRECATED_POP
}
-#ifdef MAGNUM_BUILD_DEPRECATED
{
CORRADE_IGNORE_DEPRECATED_PUSH
Trade::MeshData3D& bar();
@@ -871,9 +866,9 @@ MeshTools::transformVectorsInPlace(transformation, data.normals(0));
/* [MeshData3D-transform] */
CORRADE_IGNORE_DEPRECATED_POP
}
-#endif
{
+CORRADE_IGNORE_DEPRECATED_PUSH
Trade::ObjectData3D& fizz();
Trade::ObjectData3D& data = fizz();
/* [ObjectData3D-transformation] */
@@ -882,6 +877,228 @@ Matrix4 transformation =
Matrix4::scaling(data.scaling());
/* [ObjectData3D-transformation] */
static_cast(transformation);
+CORRADE_IGNORE_DEPRECATED_POP
+}
+#endif
+
+{
+/* [SceneFieldData-usage] */
+Containers::StridedArrayView1D transformationMapping = DOXYGEN_ELLIPSIS({});
+Containers::StridedArrayView1D transformations = DOXYGEN_ELLIPSIS({});
+
+Trade::SceneFieldData field{Trade::SceneField::Transformation,
+ transformationMapping, transformations};
+/* [SceneFieldData-usage] */
+}
+
+{
+/* [SceneFieldData-usage-offset-only] */
+struct Node {
+ UnsignedInt object;
+ Int parent;
+ Matrix4 transform;
+};
+
+/* Layout defined statically, 120 objects in total */
+constexpr Trade::SceneFieldData parents{Trade::SceneField::Parent, 120,
+ Trade::SceneMappingType::UnsignedInt, offsetof(Node, object), sizeof(Node),
+ Trade::SceneFieldType::Int, offsetof(Node, parent), sizeof(Node)};
+constexpr Trade::SceneFieldData transforms{Trade::SceneField::Transformation, 120,
+ Trade::SceneMappingType::UnsignedInt, offsetof(Node, object), sizeof(Node),
+ Trade::SceneFieldType::Matrix4x4, offsetof(Node, transform), sizeof(Node)};
+
+/* Actual data populated later */
+Containers::Array data{120*sizeof(Node)};
+DOXYGEN_ELLIPSIS()
+Trade::SceneData{Trade::SceneMappingType::UnsignedInt, 120, std::move(data),
+ {parents, transforms}};
+/* [SceneFieldData-usage-offset-only] */
+}
+
+{
+typedef SceneGraph::Scene Scene3D;
+typedef SceneGraph::Object Object3D;
+/* [SceneData-usage1] */
+Trade::SceneData data = DOXYGEN_ELLIPSIS(Trade::SceneData{{}, 0, nullptr, nullptr});
+if(!data.is3D() ||
+ !data.hasField(Trade::SceneField::Parent) ||
+ !data.hasField(Trade::SceneField::Mesh))
+ Fatal{} << "Oh noes!";
+
+Scene3D scene;
+Containers::Array objects{std::size_t(data.mappingBound())};
+/* [SceneData-usage1] */
+
+/* [SceneData-usage2] */
+auto parents = data.parentsAsArray();
+for(Containers::Pair& parent: parents)
+ objects[parent.first()] = new Object3D{};
+/* [SceneData-usage2] */
+
+/* [SceneData-usage3] */
+for(Containers::Pair& parent: parents)
+ objects[parent.first()]->setParent(
+ parent.second() == -1 ? &scene : objects[parent.second()]
+ );
+/* [SceneData-usage3] */
+
+/* [SceneData-usage4] */
+for(Containers::Pair& transformation:
+ data.transformations3DAsArray())
+{
+ if(Object3D* const object = objects[transformation.first()])
+ object->setTransformation(transformation.second());
+}
+/* [SceneData-usage4] */
+
+/* [SceneData-usage5] */
+class Drawable: public SceneGraph::Drawable3D {
+ public:
+ explicit Drawable(Object3D& object, UnsignedInt mesh, Int material, DOXYGEN_ELLIPSIS(int))DOXYGEN_IGNORE(: SceneGraph::Drawable3D{object} {
+ static_cast(mesh);
+ static_cast(material);
+ } int foo);
+
+ DOXYGEN_ELLIPSIS(void draw(const Matrix4&, SceneGraph::Camera3D&) override {})
+};
+
+for(const Containers::Pair>&
+ meshMaterial: data.meshesMaterialsAsArray())
+{
+ if(Object3D* const object = objects[meshMaterial.first()])
+ new Drawable{*object, meshMaterial.second().first(),
+ meshMaterial.second().second(), DOXYGEN_ELLIPSIS(0)};
+}
+/* [SceneData-usage5] */
+
+/* [SceneData-usage-advanced] */
+Containers::StridedArrayView1D transformationMapping =
+ data.mapping(Trade::SceneField::Transformation);
+Containers::StridedArrayView1D transformations =
+ data.field(Trade::SceneField::Transformation);
+for(std::size_t i = 0; i != transformationMapping.size(); ++i) {
+ if(Object3D* const object = objects[transformationMapping[i]])
+ object->setTransformation(transformations[i]);
+}
+/* [SceneData-usage-advanced] */
+}
+
+{
+Trade::SceneData data{{}, 0, nullptr, nullptr};
+/* [SceneData-per-object] */
+Containers::Pointer importer = DOXYGEN_ELLIPSIS({});
+
+for(const Containers::Pair& meshMaterial:
+ data.meshesMaterialsFor(importer->objectForName("Chair")))
+{
+ Debug{} << "Mesh:" << importer->meshName(meshMaterial.first());
+ if(meshMaterial.second() != -1)
+ Debug{} << "With a material:" << importer->materialName(meshMaterial.second());
+}
+/* [SceneData-per-object] */
+}
+
+{
+Trade::SceneData data{{}, 0, nullptr, nullptr};
+typedef SceneGraph::Object Object3D;
+Containers::Array objects;
+/* [SceneData-usage-mutable] */
+Containers::StridedArrayView1D transformationMapping =
+ data.mapping(Trade::SceneField::Transformation);
+Containers::StridedArrayView1D mutableTransformations =
+ data.mutableField(Trade::SceneField::Transformation);
+for(std::size_t i = 0; i != transformationMapping.size(); ++i) {
+ if(Object3D* const object = objects[transformationMapping[i]])
+ mutableTransformations[i] = object->transformation();
+}
+/* [SceneData-usage-mutable] */
+}
+
+{
+const std::size_t nodeCount{}, meshAssignmentCount{};
+/* [SceneData-populating] */
+struct Common {
+ UnsignedShort object;
+ Short parent;
+ Matrix4 transformation;
+};
+
+Containers::StridedArrayView1D common;
+Containers::ArrayView meshMaterialMapping;
+Containers::ArrayView meshes;
+Containers::ArrayView meshMaterials;
+Containers::Array data = Containers::ArrayTuple{
+ {nodeCount, common},
+ {meshAssignmentCount, meshMaterialMapping},
+ {meshAssignmentCount, meshes},
+ {meshAssignmentCount, meshMaterials}
+};
+
+// populate the views ...
+
+Trade::SceneData scene{
+ Trade::SceneMappingType::UnsignedShort, nodeCount,
+ std::move(data), {
+ Trade::SceneFieldData{Trade::SceneField::Parent,
+ common.slice(&Common::object), common.slice(&Common::parent)},
+ Trade::SceneFieldData{Trade::SceneField::Transformation,
+ common.slice(&Common::object), common.slice(&Common::transformation)},
+ Trade::SceneFieldData{Trade::SceneField::Mesh,
+ meshMaterialMapping, meshes},
+ Trade::SceneFieldData{Trade::SceneField::MeshMaterial,
+ meshMaterialMapping, meshMaterials}
+ }};
+/* [SceneData-populating] */
+}
+
+{
+std::size_t nodeCount{};
+/* [SceneData-populating-custom1] */
+DOXYGEN_ELLIPSIS()
+Containers::ArrayView cellMapping;
+Containers::ArrayView cellFrustums;
+Containers::StridedArrayView2D cellLights;
+Containers::Array data = Containers::ArrayTuple{
+ DOXYGEN_ELLIPSIS()
+ {32*24, cellMapping},
+ {32*24, cellFrustums},
+ {{32*24, 8}, cellLights},
+};
+
+for(std::size_t i = 0; i != cellMapping.size(); ++i) {
+ cellMapping[i] = nodeCount + i;
+ cellFrustums[i] = DOXYGEN_ELLIPSIS({});
+ for(std::size_t j = 0; j != cellLights[i].size(); ++j)
+ cellLights[i][j] = DOXYGEN_ELLIPSIS({});
+}
+/* [SceneData-populating-custom1] */
+
+/* [SceneData-populating-custom2] */
+constexpr Trade::SceneField CellFrustum = Trade::sceneFieldCustom(0x00);
+constexpr Trade::SceneField CellLights = Trade::sceneFieldCustom(0x01);
+
+Trade::SceneData scene{
+ Trade::SceneMappingType::UnsignedShort, nodeCount + cellMapping.size(),
+ std::move(data), {
+ DOXYGEN_ELLIPSIS()
+ Trade::SceneFieldData{CellFrustum, cellMapping, cellFrustums},
+ Trade::SceneFieldData{CellLights, cellMapping, cellLights},
+ }};
+/* [SceneData-populating-custom2] */
+}
+
+{
+constexpr Trade::SceneField CellFrustum = Trade::sceneFieldCustom(0);
+constexpr Trade::SceneField CellLights = Trade::sceneFieldCustom(1);
+Trade::SceneData scene{{}, 0, nullptr, nullptr};
+/* [SceneData-populating-custom-retrieve] */
+Containers::StridedArrayView1D cellFrustums =
+ scene.field(CellFrustum);
+Containers::StridedArrayView2D cellLights =
+ scene.field(CellLights);
+/* [SceneData-populating-custom-retrieve] */
+static_cast(cellFrustums);
+static_cast(cellLights);
}
}
diff --git a/doc/snippets/README.md b/doc/snippets/README.md
index d391a6da2b..64477e04d2 100644
--- a/doc/snippets/README.md
+++ b/doc/snippets/README.md
@@ -14,12 +14,13 @@ smaller file sizes:
The output printed by the application can be used to update the example output
in `doc/getting-started.dox`.
-### triangulate.svg
+### triangulate.svg, scenedata-tree.svg, scenedata-dod.svg
Created by Inkscape from `doc/artwork/triangulate.svg` by saving as Optimized
SVG and:
-- cleaning up the `