Browse Source

Scene: Add the deserialization

Panagiotis Christopoulos Charitos 1 month ago
parent
commit
baea967396
54 changed files with 689 additions and 266 deletions
  1. 6 4
      AnKi/Editor/EditorUi.cpp
  2. 2 2
      AnKi/Scene/Components/BodyComponent.cpp
  3. 1 1
      AnKi/Scene/Components/BodyComponent.h
  4. 2 2
      AnKi/Scene/Components/CameraComponent.cpp
  5. 1 1
      AnKi/Scene/Components/CameraComponent.h
  6. 2 2
      AnKi/Scene/Components/DecalComponent.cpp
  7. 1 1
      AnKi/Scene/Components/DecalComponent.h
  8. 2 2
      AnKi/Scene/Components/FogDensityComponent.cpp
  9. 2 6
      AnKi/Scene/Components/FogDensityComponent.h
  10. 2 2
      AnKi/Scene/Components/GlobalIlluminationProbeComponent.cpp
  11. 1 1
      AnKi/Scene/Components/GlobalIlluminationProbeComponent.h
  12. 2 2
      AnKi/Scene/Components/JointComponent.cpp
  13. 1 1
      AnKi/Scene/Components/JointComponent.h
  14. 2 2
      AnKi/Scene/Components/LensFlareComponent.cpp
  15. 4 8
      AnKi/Scene/Components/LensFlareComponent.h
  16. 2 2
      AnKi/Scene/Components/LightComponent.cpp
  17. 1 1
      AnKi/Scene/Components/LightComponent.h
  18. 2 2
      AnKi/Scene/Components/MaterialComponent.cpp
  19. 1 1
      AnKi/Scene/Components/MaterialComponent.h
  20. 2 2
      AnKi/Scene/Components/MeshComponent.cpp
  21. 1 1
      AnKi/Scene/Components/MeshComponent.h
  22. 2 6
      AnKi/Scene/Components/MoveComponent.h
  23. 2 2
      AnKi/Scene/Components/ParticleEmitter2Component.cpp
  24. 1 1
      AnKi/Scene/Components/ParticleEmitter2Component.h
  25. 2 2
      AnKi/Scene/Components/PlayerControllerComponent.cpp
  26. 2 6
      AnKi/Scene/Components/PlayerControllerComponent.h
  27. 2 2
      AnKi/Scene/Components/ReflectionProbeComponent.cpp
  28. 3 7
      AnKi/Scene/Components/ReflectionProbeComponent.h
  29. 4 8
      AnKi/Scene/Components/SceneComponent.cpp
  30. 32 22
      AnKi/Scene/Components/SceneComponent.h
  31. 2 2
      AnKi/Scene/Components/ScriptComponent.cpp
  32. 1 1
      AnKi/Scene/Components/ScriptComponent.h
  33. 2 2
      AnKi/Scene/Components/SkinComponent.cpp
  34. 1 1
      AnKi/Scene/Components/SkinComponent.h
  35. 2 2
      AnKi/Scene/Components/SkyboxComponent.cpp
  36. 1 1
      AnKi/Scene/Components/SkyboxComponent.h
  37. 3 3
      AnKi/Scene/Components/TriggerComponent.cpp
  38. 1 1
      AnKi/Scene/Components/TriggerComponent.h
  39. 6 10
      AnKi/Scene/Components/UiComponent.h
  40. 2 0
      AnKi/Scene/DeveloperConsoleUiNode.cpp
  41. 15 0
      AnKi/Scene/EditorUiNode.cpp
  42. 1 12
      AnKi/Scene/EditorUiNode.h
  43. 189 20
      AnKi/Scene/SceneGraph.cpp
  44. 7 2
      AnKi/Scene/SceneGraph.h
  45. 48 14
      AnKi/Scene/SceneNode.cpp
  46. 52 7
      AnKi/Scene/SceneNode.h
  47. 48 2
      AnKi/Scene/SceneSerializer.cpp
  48. 65 7
      AnKi/Scene/SceneSerializer.h
  49. 29 33
      AnKi/Util/BitSet.h
  50. 77 0
      AnKi/Util/DynamicBitSet.h
  51. 1 1
      AnKi/Util/File.cpp
  52. 38 42
      AnKi/Util/File.h
  53. 6 1
      AnKi/Util/Forward.h
  54. 2 0
      Samples/PhysicsPlayground/FpsCharacterNode.cpp

+ 6 - 4
AnKi/Editor/EditorUi.cpp

@@ -690,12 +690,12 @@ void EditorUi::sceneNodePropertiesWindow()
 			ImGui::SetNextItemWidth(-1.0f);
 
 			I32 n = 0;
-			if(ImGui::BeginCombo(" ", kSceneComponentTypeName[state.m_selectedSceneComponentType]))
+			if(ImGui::BeginCombo(" ", kSceneComponentTypeInfos[state.m_selectedSceneComponentType].m_name))
 			{
-				for(const Char* name : kSceneComponentTypeName)
+				for(const SceneComponentTypeInfo& inf : kSceneComponentTypeInfos)
 				{
 					const Bool isSelected = (state.m_selectedSceneComponentType == n);
-					if(ImGui::Selectable(name, isSelected))
+					if(ImGui::Selectable(inf.m_name, isSelected))
 					{
 						state.m_selectedSceneComponentType = n;
 					}
@@ -744,7 +744,7 @@ void EditorUi::sceneNodePropertiesWindow()
 					// Header
 					{
 						String label;
-						label.sprintf(" %s %s (%u)", icon.cstr(), kSceneComponentTypeName[comp.getType()], comp.getUuid());
+						label.sprintf(" %s %s (%u)", icon.cstr(), kSceneComponentTypeInfos[comp.getType()].m_name, comp.getUuid());
 						ImGui::SeparatorText(label.cstr());
 
 						if(ImGui::Button(ICON_MDI_MINUS_BOX))
@@ -801,6 +801,8 @@ void EditorUi::sceneNodePropertiesWindow()
 				ImGui::EndChild();
 				ImGui::Text(" ");
 				++count;
+
+				return FunctorContinue::kContinue;
 			});
 		}
 	}

+ 2 - 2
AnKi/Scene/Components/BodyComponent.cpp

@@ -14,8 +14,8 @@
 
 namespace anki {
 
-BodyComponent::BodyComponent(SceneNode* node)
-	: SceneComponent(node, kClassType)
+BodyComponent::BodyComponent(SceneNode* node, U32 uuid)
+	: SceneComponent(node, kClassType, uuid)
 	, m_node(node)
 {
 	node->setIgnoreParentTransform(true);

+ 1 - 1
AnKi/Scene/Components/BodyComponent.h

@@ -31,7 +31,7 @@ class BodyComponent : public SceneComponent
 	ANKI_SCENE_COMPONENT(BodyComponent)
 
 public:
-	BodyComponent(SceneNode* node);
+	BodyComponent(SceneNode* node, U32 uuid);
 
 	~BodyComponent();
 

+ 2 - 2
AnKi/Scene/Components/CameraComponent.cpp

@@ -11,8 +11,8 @@
 
 namespace anki {
 
-CameraComponent::CameraComponent(SceneNode* node)
-	: SceneComponent(node, kClassType)
+CameraComponent::CameraComponent(SceneNode* node, U32 uuid)
+	: SceneComponent(node, kClassType, uuid)
 {
 	// Init main frustum
 	m_frustum.init(FrustumType::kPerspective);

+ 1 - 1
AnKi/Scene/Components/CameraComponent.h

@@ -19,7 +19,7 @@ class CameraComponent : public SceneComponent
 	ANKI_SCENE_COMPONENT(CameraComponent)
 
 public:
-	CameraComponent(SceneNode* node);
+	CameraComponent(SceneNode* node, U32 uuid);
 
 	~CameraComponent();
 

+ 2 - 2
AnKi/Scene/Components/DecalComponent.cpp

@@ -10,8 +10,8 @@
 
 namespace anki {
 
-DecalComponent::DecalComponent(SceneNode* node)
-	: SceneComponent(node, kClassType)
+DecalComponent::DecalComponent(SceneNode* node, U32 uuid)
+	: SceneComponent(node, kClassType, uuid)
 {
 	m_gpuSceneDecal.allocate();
 	setDiffuseImageFilename("EngineAssets/DefaultDecal.png");

+ 1 - 1
AnKi/Scene/Components/DecalComponent.h

@@ -18,7 +18,7 @@ class DecalComponent : public SceneComponent
 	ANKI_SCENE_COMPONENT(DecalComponent)
 
 public:
-	DecalComponent(SceneNode* node);
+	DecalComponent(SceneNode* node, U32 uuid);
 
 	~DecalComponent();
 

+ 2 - 2
AnKi/Scene/Components/FogDensityComponent.cpp

@@ -10,8 +10,8 @@
 
 namespace anki {
 
-FogDensityComponent::FogDensityComponent(SceneNode* node)
-	: SceneComponent(node, kClassType)
+FogDensityComponent::FogDensityComponent(SceneNode* node, U32 uuid)
+	: SceneComponent(node, kClassType, uuid)
 {
 	m_gpuSceneVolume.allocate();
 }

+ 2 - 6
AnKi/Scene/Components/FogDensityComponent.h

@@ -12,10 +12,6 @@
 
 namespace anki {
 
-/// @addtogroup scene
-/// @{
-
-/// @memberof FogDensityComponent
 enum class FogDensityComponentShape : U8
 {
 	kSphere,
@@ -23,7 +19,7 @@ enum class FogDensityComponentShape : U8
 	kCount
 };
 
-/// Fog density component. Controls the fog density.
+// Fog density component. Controls the fog density.
 class FogDensityComponent : public SceneComponent
 {
 	ANKI_SCENE_COMPONENT(FogDensityComponent)
@@ -31,7 +27,7 @@ class FogDensityComponent : public SceneComponent
 public:
 	static constexpr F32 kMinShapeSize = 1.0_cm;
 
-	FogDensityComponent(SceneNode* node);
+	FogDensityComponent(SceneNode* node, U32 uuid);
 
 	~FogDensityComponent();
 

+ 2 - 2
AnKi/Scene/Components/GlobalIlluminationProbeComponent.cpp

@@ -14,8 +14,8 @@
 
 namespace anki {
 
-GlobalIlluminationProbeComponent::GlobalIlluminationProbeComponent(SceneNode* node)
-	: SceneComponent(node, kClassType)
+GlobalIlluminationProbeComponent::GlobalIlluminationProbeComponent(SceneNode* node, U32 uuid)
+	: SceneComponent(node, kClassType, uuid)
 {
 	m_gpuSceneProbe.allocate();
 

+ 1 - 1
AnKi/Scene/Components/GlobalIlluminationProbeComponent.h

@@ -18,7 +18,7 @@ class GlobalIlluminationProbeComponent : public SceneComponent
 	ANKI_SCENE_COMPONENT(GlobalIlluminationProbeComponent)
 
 public:
-	GlobalIlluminationProbeComponent(SceneNode* node);
+	GlobalIlluminationProbeComponent(SceneNode* node, U32 uuid);
 
 	~GlobalIlluminationProbeComponent();
 

+ 2 - 2
AnKi/Scene/Components/JointComponent.cpp

@@ -9,8 +9,8 @@
 
 namespace anki {
 
-JointComponent::JointComponent(SceneNode* node)
-	: SceneComponent(node, kClassType)
+JointComponent::JointComponent(SceneNode* node, U32 uuid)
+	: SceneComponent(node, kClassType, uuid)
 	, m_node(node)
 {
 	node->setIgnoreParentTransform(true);

+ 1 - 1
AnKi/Scene/Components/JointComponent.h

@@ -28,7 +28,7 @@ class JointComponent : public SceneComponent
 	ANKI_SCENE_COMPONENT(JointComponent)
 
 public:
-	JointComponent(SceneNode* node);
+	JointComponent(SceneNode* node, U32 uuid);
 
 	~JointComponent();
 

+ 2 - 2
AnKi/Scene/Components/LensFlareComponent.cpp

@@ -10,8 +10,8 @@
 
 namespace anki {
 
-LensFlareComponent::LensFlareComponent(SceneNode* node)
-	: SceneComponent(node, kClassType)
+LensFlareComponent::LensFlareComponent(SceneNode* node, U32 uuid)
+	: SceneComponent(node, kClassType, uuid)
 {
 }
 

+ 4 - 8
AnKi/Scene/Components/LensFlareComponent.h

@@ -10,16 +10,13 @@
 
 namespace anki {
 
-/// @addtogroup scene
-/// @{
-
-/// Lens flare scene component.
+// Lens flare scene component.
 class LensFlareComponent final : public SceneComponent
 {
 	ANKI_SCENE_COMPONENT(LensFlareComponent)
 
 public:
-	LensFlareComponent(SceneNode* node);
+	LensFlareComponent(SceneNode* node, U32 uuid);
 
 	~LensFlareComponent();
 
@@ -78,9 +75,9 @@ public:
 private:
 	static constexpr F32 kAabbSize = 25.0_cm;
 
-	Vec4 m_colorMul = Vec4(1.0f); ///< Color multiplier.
+	Vec4 m_colorMul = Vec4(1.0f); // Color multiplier.
 
-	ImageResourcePtr m_image; ///< Array of textures.
+	ImageResourcePtr m_image; // Array of textures.
 
 	Vec2 m_firstFlareSize = Vec2(1.0f);
 	Vec2 m_otherFlareSize = Vec2(1.0f);
@@ -91,6 +88,5 @@ private:
 
 	void update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
-/// @}
 
 } // end namespace anki

+ 2 - 2
AnKi/Scene/Components/LightComponent.cpp

@@ -65,8 +65,8 @@ static void solarPosition(U32 year, U32 month, U32 day, F32 hourUtc, F32 latitud
 	}
 }
 
-LightComponent::LightComponent(SceneNode* node)
-	: SceneComponent(node, kClassType)
+LightComponent::LightComponent(SceneNode* node, U32 uuid)
+	: SceneComponent(node, kClassType, uuid)
 	, m_type(LightComponentType::kPoint)
 {
 	m_point.m_radius = 1.0f;

+ 1 - 1
AnKi/Scene/Components/LightComponent.h

@@ -33,7 +33,7 @@ class LightComponent : public SceneComponent
 	ANKI_SCENE_COMPONENT(LightComponent)
 
 public:
-	LightComponent(SceneNode* node);
+	LightComponent(SceneNode* node, U32 uuid);
 
 	~LightComponent();
 

+ 2 - 2
AnKi/Scene/Components/MaterialComponent.cpp

@@ -16,8 +16,8 @@
 
 namespace anki {
 
-MaterialComponent::MaterialComponent(SceneNode* node)
-	: SceneComponent(node, kClassType)
+MaterialComponent::MaterialComponent(SceneNode* node, U32 uuid)
+	: SceneComponent(node, kClassType, uuid)
 {
 	m_gpuSceneRenderable.allocate();
 }

+ 1 - 1
AnKi/Scene/Components/MaterialComponent.h

@@ -21,7 +21,7 @@ class MaterialComponent final : public SceneComponent
 	ANKI_SCENE_COMPONENT(MaterialComponent)
 
 public:
-	MaterialComponent(SceneNode* node);
+	MaterialComponent(SceneNode* node, U32 uuid);
 
 	~MaterialComponent();
 

+ 2 - 2
AnKi/Scene/Components/MeshComponent.cpp

@@ -10,8 +10,8 @@
 
 namespace anki {
 
-MeshComponent::MeshComponent(SceneNode* node)
-	: SceneComponent(node, kClassType)
+MeshComponent::MeshComponent(SceneNode* node, U32 uuid)
+	: SceneComponent(node, kClassType, uuid)
 {
 }
 

+ 1 - 1
AnKi/Scene/Components/MeshComponent.h

@@ -17,7 +17,7 @@ class MeshComponent final : public SceneComponent
 	ANKI_SCENE_COMPONENT(MeshComponent)
 
 public:
-	MeshComponent(SceneNode* node);
+	MeshComponent(SceneNode* node, U32 uuid);
 
 	~MeshComponent();
 

+ 2 - 6
AnKi/Scene/Components/MoveComponent.h

@@ -10,17 +10,14 @@
 
 namespace anki {
 
-/// @addtogroup scene
-/// @{
-
 /// A simple implicit component that updates the SceneNode's transform.
 class MoveComponent : public SceneComponent
 {
 	ANKI_SCENE_COMPONENT(MoveComponent)
 
 public:
-	MoveComponent(SceneNode* node)
-		: SceneComponent(node, kClassType)
+	MoveComponent(SceneNode* node, U32 uuid)
+		: SceneComponent(node, kClassType, uuid)
 	{
 		m_gpuSceneTransforms.allocate();
 	}
@@ -42,6 +39,5 @@ private:
 
 	void update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
-/// @}
 
 } // end namespace anki

+ 2 - 2
AnKi/Scene/Components/ParticleEmitter2Component.cpp

@@ -151,8 +151,8 @@ public:
 	}
 };
 
-ParticleEmitter2Component::ParticleEmitter2Component(SceneNode* node)
-	: SceneComponent(node, kClassType)
+ParticleEmitter2Component::ParticleEmitter2Component(SceneNode* node, U32 uuid)
+	: SceneComponent(node, kClassType, uuid)
 {
 	ParticleEmitterQuadGeometry::getSingleton().addUser();
 }

+ 1 - 1
AnKi/Scene/Components/ParticleEmitter2Component.h

@@ -32,7 +32,7 @@ class ParticleEmitter2Component : public SceneComponent
 	ANKI_SCENE_COMPONENT(ParticleEmitter2Component)
 
 public:
-	ParticleEmitter2Component(SceneNode* node);
+	ParticleEmitter2Component(SceneNode* node, U32 uuid);
 
 	~ParticleEmitter2Component();
 

+ 2 - 2
AnKi/Scene/Components/PlayerControllerComponent.cpp

@@ -10,8 +10,8 @@
 
 namespace anki {
 
-PlayerControllerComponent::PlayerControllerComponent(SceneNode* node)
-	: SceneComponent(node, kClassType)
+PlayerControllerComponent::PlayerControllerComponent(SceneNode* node, U32 uuid)
+	: SceneComponent(node, kClassType, uuid)
 {
 	PhysicsPlayerControllerInitInfo init;
 	init.m_initialPosition = node->getWorldTransform().getOrigin().xyz;

+ 2 - 6
AnKi/Scene/Components/PlayerControllerComponent.h

@@ -10,16 +10,13 @@
 
 namespace anki {
 
-/// @addtogroup scene
-/// @{
-
-/// Physics player controller component.
+// Physics player controller component.
 class PlayerControllerComponent : public SceneComponent
 {
 	ANKI_SCENE_COMPONENT(PlayerControllerComponent)
 
 public:
-	PlayerControllerComponent(SceneNode* node);
+	PlayerControllerComponent(SceneNode* node, U32 uuid);
 
 	void setVelocity(F32 forwardSpeed, F32 jumpSpeed, Vec3 forwardDir, Bool crouch)
 	{
@@ -42,6 +39,5 @@ private:
 
 	void update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
-/// @}
 
 } // end namespace anki

+ 2 - 2
AnKi/Scene/Components/ReflectionProbeComponent.cpp

@@ -12,8 +12,8 @@
 
 namespace anki {
 
-ReflectionProbeComponent::ReflectionProbeComponent(SceneNode* node)
-	: SceneComponent(node, kClassType)
+ReflectionProbeComponent::ReflectionProbeComponent(SceneNode* node, U32 uuid)
+	: SceneComponent(node, kClassType, uuid)
 {
 	m_worldPos = node->getWorldTransform().getOrigin().xyz;
 	m_gpuSceneProbe.allocate();

+ 3 - 7
AnKi/Scene/Components/ReflectionProbeComponent.h

@@ -12,18 +12,15 @@
 
 namespace anki {
 
-/// @addtogroup scene
-/// @{
-
 ANKI_CVAR2(NumericCVar<U32>, Render, ProbeReflections, Resolution, 128, 8, 2048, "The resolution of the reflection probe's reflection")
 
-/// Reflection probe component.
+// Reflection probe component.
 class ReflectionProbeComponent : public SceneComponent
 {
 	ANKI_SCENE_COMPONENT(ReflectionProbeComponent)
 
 public:
-	ReflectionProbeComponent(SceneNode* node);
+	ReflectionProbeComponent(SceneNode* node, U32 uuid);
 
 	~ReflectionProbeComponent();
 
@@ -49,7 +46,7 @@ public:
 		return m_worldPos;
 	}
 
-	/// The radius around the probe's center that can infuence the rendering of the env texture.
+	// The radius around the probe's center that can infuence the rendering of the env texture.
 	ANKI_INTERNAL F32 getRenderRadius() const;
 
 	ANKI_INTERNAL F32 getShadowsRenderRadius() const;
@@ -80,6 +77,5 @@ private:
 
 	Error serialize(SceneSerializer& serializer) override;
 };
-/// @}
 
 } // end namespace anki

+ 4 - 8
AnKi/Scene/Components/SceneComponent.cpp

@@ -8,16 +8,12 @@
 
 namespace anki {
 
-SceneComponent::SceneComponent([[maybe_unused]] SceneNode* node, SceneComponentType type)
-	: m_uuid(SceneGraph::getSingleton().getNewUuid())
+SceneComponent::SceneComponent([[maybe_unused]] SceneNode* node, SceneComponentType type, U32 uuid)
+	: m_uuid(uuid)
 	, m_type(U8(type))
 {
-}
-
-U32 SceneComponent::regenerateUuid()
-{
-	m_uuid = SceneGraph::getSingleton().getNewUuid();
-	return m_uuid;
+	ANKI_ASSERT(uuid);
+	ANKI_ASSERT(SceneComponentType(m_type) < SceneComponentType::kCount);
 }
 
 } // namespace anki

+ 32 - 22
AnKi/Scene/Components/SceneComponent.h

@@ -47,19 +47,24 @@ public: \
 \
 private:
 
-// Component names
-inline Array<const Char*, U32(SceneComponentType::kCount)> kSceneComponentTypeName = {
-#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, sceneNodeCanHaveMany, icon, serializable) ANKI_STRINGIZE(name)
-#define ANKI_SCENE_COMPONENT_SEPARATOR ,
-#include <AnKi/Scene/Components/SceneComponentClasses.def.h>
+class SceneComponentTypeInfo
+{
+public:
+	const Char* m_name;
+	F32 m_weight;
+	Bool m_sceneNodeCanHaveMany;
+	Bool m_serializable;
 };
 
-// Just a flag per component
-inline Array<Bool, U32(SceneComponentType::kCount)> kSceneComponentSceneNodeCanHaveMany = {
-#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, sceneNodeCanHaveMany, icon, serializable) sceneNodeCanHaveMany
+// Component names
+inline Array<SceneComponentTypeInfo, U32(SceneComponentType::kCount)> kSceneComponentTypeInfos = {{
+#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, sceneNodeCanHaveMany, icon, serializable) \
+	{ \
+		ANKI_STRINGIZE(name), weight, sceneNodeCanHaveMany, serializable \
+	}
 #define ANKI_SCENE_COMPONENT_SEPARATOR ,
 #include <AnKi/Scene/Components/SceneComponentClasses.def.h>
-};
+}};
 
 // Passed to SceneComponent::update.
 class SceneComponentUpdateInfo
@@ -100,7 +105,7 @@ private:
 class SceneComponent
 {
 public:
-	SceneComponent(SceneNode* node, SceneComponentType type);
+	SceneComponent(SceneNode* node, SceneComponentType type, U32 uuid);
 
 	virtual ~SceneComponent() = default;
 
@@ -151,7 +156,7 @@ public:
 
 	static constexpr F32 getUpdateOrderWeight(SceneComponentType type)
 	{
-		return m_updateOrderWeights[type];
+		return kSceneComponentTypeInfos[type].m_weight;
 	}
 
 	Bool updatedThisFrame() const
@@ -161,12 +166,22 @@ public:
 
 	virtual Error serialize([[maybe_unused]] SceneSerializer& serializer)
 	{
+		ANKI_ASSERT(!"Not supported");
 		return Error::kNone;
 	}
 
-protected:
-	U32 regenerateUuid();
+	void setSerialization(Bool enable)
+	{
+		ANKI_ASSERT(!enable || kSceneComponentTypeInfos[m_type].m_serializable);
+		m_serialize = enable;
+	}
+
+	Bool getSerialization() const
+	{
+		return m_serialize;
+	}
 
+protected:
 	// A convenience function for components to keep tabs on other components of a SceneNode
 	template<typename TComponent>
 	static void bookkeepComponent(SceneDynamicArray<TComponent*>& arr, SceneComponent* other, Bool added, Bool& firstDirty)
@@ -219,17 +234,12 @@ protected:
 	}
 
 private:
-	Timestamp m_timestamp = 1; ///< Indicates when an update happened
-	U32 m_uuid = 0;
+	Timestamp m_timestamp = 1; // Indicates when an update happened
+	U32 m_uuid : 31 = 0;
+	U32 m_serialize : 1 = kSceneComponentTypeInfos[m_type].m_serializable;
 
 	U32 m_arrayIdx : 24 = kMaxU32 >> 8u;
-	U32 m_type : 8 = 0; ///< Cache the type ID.
-
-	static constexpr Array<F32, U32(SceneComponentType::kCount)> m_updateOrderWeights = {
-#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, sceneNodeCanHaveMany, icon, serializable) weight
-#define ANKI_SCENE_COMPONENT_SEPARATOR ,
-#include <AnKi/Scene/Components/SceneComponentClasses.def.h>
-	};
+	U32 m_type : 8 = 0; // Cache the type ID.
 };
 
 } // end namespace anki

+ 2 - 2
AnKi/Scene/Components/ScriptComponent.cpp

@@ -12,8 +12,8 @@
 
 namespace anki {
 
-ScriptComponent::ScriptComponent(SceneNode* node)
-	: SceneComponent(node, kClassType)
+ScriptComponent::ScriptComponent(SceneNode* node, U32 uuid)
+	: SceneComponent(node, kClassType, uuid)
 {
 	ANKI_ASSERT(node);
 }

+ 1 - 1
AnKi/Scene/Components/ScriptComponent.h

@@ -17,7 +17,7 @@ class ScriptComponent : public SceneComponent
 	ANKI_SCENE_COMPONENT(ScriptComponent)
 
 public:
-	ScriptComponent(SceneNode* node);
+	ScriptComponent(SceneNode* node, U32 uuid);
 
 	~ScriptComponent();
 

+ 2 - 2
AnKi/Scene/Components/SkinComponent.cpp

@@ -13,8 +13,8 @@
 
 namespace anki {
 
-SkinComponent::SkinComponent(SceneNode* node)
-	: SceneComponent(node, kClassType)
+SkinComponent::SkinComponent(SceneNode* node, U32 uuid)
+	: SceneComponent(node, kClassType, uuid)
 {
 }
 

+ 1 - 1
AnKi/Scene/Components/SkinComponent.h

@@ -43,7 +43,7 @@ class SkinComponent : public SceneComponent
 public:
 	static constexpr U32 kMaxAnimationTracks = 4;
 
-	SkinComponent(SceneNode* node);
+	SkinComponent(SceneNode* node, U32 uuid);
 
 	~SkinComponent();
 

+ 2 - 2
AnKi/Scene/Components/SkyboxComponent.cpp

@@ -11,8 +11,8 @@
 
 namespace anki {
 
-SkyboxComponent::SkyboxComponent(SceneNode* node)
-	: SceneComponent(node, kClassType)
+SkyboxComponent::SkyboxComponent(SceneNode* node, U32 uuid)
+	: SceneComponent(node, kClassType, uuid)
 {
 }
 

+ 1 - 1
AnKi/Scene/Components/SkyboxComponent.h

@@ -27,7 +27,7 @@ class SkyboxComponent : public SceneComponent
 	ANKI_SCENE_COMPONENT(SkyboxComponent)
 
 public:
-	SkyboxComponent(SceneNode* node);
+	SkyboxComponent(SceneNode* node, U32 uuid);
 
 	~SkyboxComponent();
 

+ 3 - 3
AnKi/Scene/Components/TriggerComponent.cpp

@@ -12,7 +12,7 @@
 
 namespace anki {
 
-/// The callbacks execute before the TriggerComponent::update
+// The callbacks execute before the TriggerComponent::update
 class TriggerComponent::MyPhysicsTriggerCallbacks final : public PhysicsTriggerCallbacks
 {
 public:
@@ -59,8 +59,8 @@ public:
 
 TriggerComponent::MyPhysicsTriggerCallbacks TriggerComponent::m_callbacks;
 
-TriggerComponent::TriggerComponent(SceneNode* node)
-	: SceneComponent(node, kClassType)
+TriggerComponent::TriggerComponent(SceneNode* node, U32 uuid)
+	: SceneComponent(node, kClassType, uuid)
 {
 }
 

+ 1 - 1
AnKi/Scene/Components/TriggerComponent.h

@@ -23,7 +23,7 @@ class TriggerComponent : public SceneComponent
 	ANKI_SCENE_COMPONENT(TriggerComponent)
 
 public:
-	TriggerComponent(SceneNode* node);
+	TriggerComponent(SceneNode* node, U32 uuid);
 
 	~TriggerComponent();
 

+ 6 - 10
AnKi/Scene/Components/UiComponent.h

@@ -10,19 +10,16 @@
 
 namespace anki {
 
-/// @addtogroup scene
-/// @{
+using UiComponentDrawCallback = void (*)(UiCanvas& canvas, void* userData);
 
-using UiQueueElementDrawCallback = void (*)(UiCanvas& canvas, void* userData);
-
-/// UI scene component.
+// UI scene component.
 class UiComponent : public SceneComponent
 {
 	ANKI_SCENE_COMPONENT(UiComponent)
 
 public:
-	UiComponent(SceneNode* node)
-		: SceneComponent(node, kClassType)
+	UiComponent(SceneNode* node, U32 uuid)
+		: SceneComponent(node, kClassType, uuid)
 	{
 	}
 
@@ -30,7 +27,7 @@ public:
 	{
 	}
 
-	void init(UiQueueElementDrawCallback callback, void* userData)
+	void init(UiComponentDrawCallback callback, void* userData)
 	{
 		ANKI_ASSERT(callback != nullptr);
 		ANKI_ASSERT(userData != nullptr);
@@ -57,7 +54,7 @@ public:
 	}
 
 private:
-	UiQueueElementDrawCallback m_drawCallback = nullptr;
+	UiComponentDrawCallback m_drawCallback = nullptr;
 	void* m_userData = nullptr;
 	Bool m_enabled = true;
 
@@ -66,6 +63,5 @@ private:
 		updated = false;
 	}
 };
-/// @}
 
 } // end namespace anki

+ 2 - 0
AnKi/Scene/DeveloperConsoleUiNode.cpp

@@ -19,6 +19,8 @@ DeveloperConsoleUiNode::DeveloperConsoleUiNode(CString name)
 		},
 		this);
 	uic->setEnabled(false);
+
+	setSerialization(false);
 }
 
 DeveloperConsoleUiNode::~DeveloperConsoleUiNode()

+ 15 - 0
AnKi/Scene/EditorUiNode.cpp

@@ -9,6 +9,21 @@
 
 namespace anki {
 
+EditorUiNode::EditorUiNode(CString name)
+	: SceneNode(name)
+{
+	UiComponent* uic = newComponent<UiComponent>();
+	uic->init(
+		[](UiCanvas& canvas, void* ud) {
+			static_cast<EditorUiNode*>(ud)->m_editorUi.draw(canvas);
+		},
+		this);
+
+	uic->setEnabled(g_cvarCoreShowEditor);
+
+	setSerialization(false);
+}
+
 void EditorUiNode::frameUpdate([[maybe_unused]] Second prevUpdateTime, [[maybe_unused]] Second crntTime)
 {
 	getFirstComponentOfType<UiComponent>().setEnabled(g_cvarCoreShowEditor);

+ 1 - 12
AnKi/Scene/EditorUiNode.h

@@ -16,18 +16,7 @@ class EditorUiNode : public SceneNode
 public:
 	EditorUi m_editorUi;
 
-	EditorUiNode(CString name)
-		: SceneNode(name)
-	{
-		UiComponent* uic = newComponent<UiComponent>();
-		uic->init(
-			[](UiCanvas& canvas, void* ud) {
-				static_cast<EditorUiNode*>(ud)->m_editorUi.draw(canvas);
-			},
-			this);
-
-		uic->setEnabled(g_cvarCoreShowEditor);
-	}
+	EditorUiNode(CString name);
 
 private:
 	void frameUpdate([[maybe_unused]] Second prevUpdateTime, [[maybe_unused]] Second crntTime) override;

+ 189 - 20
AnKi/Scene/SceneGraph.cpp

@@ -117,6 +117,7 @@ Error SceneGraph::init(AllocAlignedCallback allocCallback, void* allocCallbackDa
 
 	// Init the default main camera
 	m_defaultMainCam = newSceneNode<SceneNode>("_MainCamera");
+	m_defaultMainCam->setSerialization(false);
 	CameraComponent* camc = m_defaultMainCam->newComponent<CameraComponent>();
 	camc->setPerspective(0.1f, 1000.0f, toRad(60.0f), (1080.0f / 1920.0f) * toRad(60.0f));
 	m_mainCam = m_defaultMainCam;
@@ -198,7 +199,7 @@ void SceneGraph::update(Second prevUpdateTime, Second crntTime)
 
 		// Add to list
 		m_nodes.pushBack(node);
-		++m_nodesCount;
+		++m_nodeCount;
 	}
 
 	// Re-index renamed nodes
@@ -356,8 +357,8 @@ void SceneGraph::update(Second prevUpdateTime, Second crntTime)
 		{
 			// Remove from the graph
 			m_nodes.erase(node);
-			ANKI_ASSERT(m_nodesCount > 0);
-			--m_nodesCount;
+			ANKI_ASSERT(m_nodeCount > 0);
+			--m_nodeCount;
 
 			if(m_mainCam != m_defaultMainCam && m_mainCam == node)
 			{
@@ -446,6 +447,8 @@ void SceneGraph::updateNode(U32 tid, SceneNode& node, UpdateSceneNodesCtx& ctx)
 				ctx.m_perThread[tid].m_skyboxComponent = &skyc;
 			}
 		}
+
+		return FunctorContinue::kContinue;
 	});
 
 	// Frame update
@@ -561,10 +564,25 @@ void SceneGraph::sceneNodeChangedName(SceneNode& node, CString oldName)
 	m_nodesRenamed.emplaceBack(std::pair(&node, oldName));
 }
 
+void SceneGraph::countSerializableNodes(SceneNode& root, U32& serializableNodeCount)
+{
+	if(root.getSerialization())
+	{
+		++serializableNodeCount;
+
+		root.visitChildrenMaxDepth(0, [&](SceneNode& child) {
+			countSerializableNodes(child, serializableNodeCount);
+			return FunctorContinue::kContinue;
+		});
+	}
+}
+
 Error SceneGraph::saveToTextFile(CString filename)
 {
 	ANKI_TRACE_FUNCTION();
 
+	const U64 begin = HighRezTimer::getCurrentTimeUs();
+
 	ANKI_LOGI("Saving scene: %s", filename.cstr());
 
 	File file;
@@ -572,40 +590,51 @@ Error SceneGraph::saveToTextFile(CString filename)
 
 	TextSceneSerializer serializer(&file, true);
 
+	// Header
+	ANKI_CHECK(file.writeText("ANKISCEN\n"));
 	U32 version = kSceneBinaryVersion;
 	ANKI_SERIALIZE(version, 1);
 
-#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, sceneNodeCanHaveMany, icon, serializable) \
-	if(serializable) \
-	{ \
-		U32 name##Count = m_componentArrays.get##name##s().getSize(); \
-		ANKI_SERIALIZE(name##Count, 1); \
-		for(SceneComponent & comp : m_componentArrays.get##name##s()) \
-		{ \
-			U32 uuid = comp.getUuid(); \
-			ANKI_SERIALIZE(uuid, 1); \
-			ANKI_CHECK(comp.serialize(serializer)); \
-		} \
-	}
-#include <AnKi/Scene/Components/SceneComponentClasses.def.h>
+	// Count the serializable nodes
+	U32 serializableNodeCount = 0;
+	visitNodes([&](SceneNode& node) {
+		if(node.getParent() == nullptr)
+		{
+			// Only root nodes, rest will be visited later
+			countSerializableNodes(node, serializableNodeCount);
+		}
+
+		return FunctorContinue::kContinue;
+	});
 
 	// Scene nodes
-	Error err = Error::kNone;
+	SceneNode::SerializeCommonArgs serializationArgs;
+	ANKI_ASSERT(serializableNodeCount <= m_nodeCount);
+	U32 nodeCount = serializableNodeCount;
+	ANKI_SERIALIZE(nodeCount, 1);
 
+	Error err = Error::kNone;
 	auto serializeNode = [&](SceneNode& node) -> Error {
 		SceneString className = (node.getSceneNodeRegistryRecord()) ? node.getSceneNodeRegistryRecord()->m_name : "SceneNode";
 		ANKI_SERIALIZE(className, 1);
 
-		ANKI_CHECK(node.serializeCommon(serializer));
+		U32 uuid = node.getUuid();
+		ANKI_SERIALIZE(uuid, 1);
+		SceneString name = node.m_name;
+		ANKI_SERIALIZE(name, 1);
+
+		ANKI_CHECK(node.serializeCommon(serializer, serializationArgs));
 		ANKI_CHECK(node.serialize(serializer));
 
+		--nodeCount;
+
 		return Error::kNone;
 	};
 
 	visitNodes([&](SceneNode& node) {
-		if(node.getParent() != nullptr)
+		if(node.getParent() != nullptr || !node.getSerialization())
 		{
-			// Skip non-root nodes, they will be visited later
+			// Skip non-root nodes (they will be visited later) and non-serializable nodes
 			return FunctorContinue::kContinue;
 		}
 
@@ -631,6 +660,146 @@ Error SceneGraph::saveToTextFile(CString filename)
 		return err;
 	}
 
+	ANKI_ASSERT(nodeCount == 0);
+
+	// Components
+	auto serializeComponent = [&](auto& comp) -> Error {
+		if(!serializationArgs.m_write.m_serializableComponentMask[comp.getType()].getBit(comp.getArrayIndex()))
+		{
+			return Error::kNone;
+		}
+
+		ANKI_ASSERT(comp.getSerialization());
+
+		U32 uuid = comp.getUuid();
+		ANKI_SERIALIZE(uuid, 1);
+
+		ANKI_CHECK(comp.serialize(serializer));
+
+		--serializationArgs.m_write.m_componentsToBeSerializedCount[comp.getType()];
+
+		return Error::kNone;
+	};
+
+#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, sceneNodeCanHaveMany, icon, serializable) \
+	if(serializable) \
+	{ \
+		U32 name##Count = serializationArgs.m_write.m_componentsToBeSerializedCount[SceneComponentType::k##name]; \
+		ANKI_SERIALIZE(name##Count, 1); \
+		for(SceneComponent & comp : m_componentArrays.get##name##s()) \
+		{ \
+			ANKI_CHECK(serializeComponent(comp)); \
+		} \
+		ANKI_ASSERT(serializationArgs.m_write.m_componentsToBeSerializedCount[SceneComponentType::k##name] == 0); \
+	}
+#include <AnKi/Scene/Components/SceneComponentClasses.def.h>
+
+	const F64 timeDiffMs = F64(HighRezTimer::getCurrentTimeUs() - begin) / 1000.0;
+	ANKI_SCENE_LOGI("Saving scene finished. %fms", timeDiffMs);
+
+	return Error::kNone;
+}
+
+Error SceneGraph::loadFromTextFile(CString filename)
+{
+	ANKI_TRACE_FUNCTION();
+
+	ANKI_LOGI("Loading scene: %s", filename.cstr());
+
+	File file;
+	ANKI_CHECK(file.open(filename, FileOpenFlag::kRead));
+
+	TextSceneSerializer serializer(&file, true);
+
+	// Header
+	Array<Char, 9> magic;
+	ANKI_CHECK(file.read(magic.getBegin(), magic.getSize()));
+	if(CString("ANKISCEN\n") != magic.getBegin())
+	{
+		ANKI_LOGE("Wrong magic value");
+		return Error::kUserData;
+	}
+
+	U32 version = 0;
+	ANKI_SERIALIZE(version, 1);
+	if(version > kSceneBinaryVersion)
+	{
+		ANKI_LOGE("Wrong version number");
+		return Error::kUserData;
+	}
+
+	// Scene nodes
+	SceneNode::SerializeCommonArgs serializationArgs;
+	U32 nodeCount = 0;
+	ANKI_SERIALIZE(nodeCount, 1);
+
+	for(U32 i = 0; i < nodeCount; ++i)
+	{
+		SceneString className;
+		ANKI_SERIALIZE(className, 1);
+		U32 uuid = 0;
+		ANKI_SERIALIZE(uuid, 1);
+		SceneString name;
+		ANKI_SERIALIZE(name, 1);
+
+		SceneNode* node;
+		if(className != "SceneNode")
+		{
+			// Derived SceneNode class
+			const SceneNodeRegistryRecord& record = *static_cast<SceneNodeRegistryRecord*>(GlobalRegistry::getSingleton().findRecord(className));
+
+			void* mem = SceneMemoryPool::getSingleton().allocate(record.m_getClassSizeCallback(), ANKI_SAFE_ALIGNMENT);
+			record.m_constructCallback(mem, name);
+
+			node = static_cast<SceneNode*>(mem);
+		}
+		else
+		{
+			node = newInstance<SceneNode>(SceneMemoryPool::getSingleton(), name);
+		}
+
+		node->m_uuid = uuid;
+		ANKI_CHECK(node->serializeCommon(serializer, serializationArgs));
+		ANKI_CHECK(node->serialize(serializer));
+
+		m_nodesForRegistration.pushBack(node);
+	}
+
+	// Components
+	auto serializeComponent = [&](auto& compArray) -> Error {
+		U32 uuid;
+		ANKI_SERIALIZE(uuid, 1);
+
+		auto it2 = serializationArgs.m_read.m_sceneComponentUuidToNode.find(uuid);
+		if(it2 == serializationArgs.m_read.m_sceneComponentUuidToNode.getEnd())
+		{
+			ANKI_SCENE_LOGE("Incorrect UUID");
+			return Error::kUserData;
+		}
+		SceneNode* node = *it2;
+
+		auto it = compArray.emplace(node, uuid);
+		SceneComponent& comp = *it;
+		comp.setArrayIndex(it.getArrayIndex());
+		ANKI_CHECK(comp.serialize(serializer));
+
+		node->addComponent(&comp);
+
+		return Error::kNone;
+	};
+
+#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, sceneNodeCanHaveMany, icon, serializable) \
+	if(serializable) \
+	{ \
+		U32 name##Count = 0; \
+		ANKI_SERIALIZE(name##Count, 1); \
+		for(U32 i = 0; i < name##Count; ++i) \
+		{ \
+			ANKI_CHECK(serializeComponent(SceneGraph::getSingleton().getComponentArrays().get##name##s())); \
+		} \
+	}
+#include <AnKi/Scene/Components/SceneComponentClasses.def.h>
+
 	return Error::kNone;
 }
 

+ 7 - 2
AnKi/Scene/SceneGraph.h

@@ -87,7 +87,7 @@ public:
 
 	U32 getSceneNodesCount() const
 	{
-		return m_nodesCount;
+		return m_nodeCount;
 	}
 
 	EventManager& getEventManager()
@@ -126,6 +126,7 @@ public:
 	TNode* newSceneNode(CString name)
 	{
 		TNode* node = newInstance<TNode>(SceneMemoryPool::getSingleton(), name);
+		node->m_uuid = getNewUuid();
 		LockGuard lock(m_nodesForRegistrationMtx);
 		m_nodesForRegistration.pushBack(node);
 		return node;
@@ -178,6 +179,8 @@ public:
 
 	Error saveToTextFile(CString filename);
 
+	Error loadFromTextFile(CString filename);
+
 private:
 	class UpdateSceneNodesCtx;
 
@@ -195,7 +198,7 @@ private:
 	mutable StackMemoryPool m_framePool;
 
 	IntrusiveList<SceneNode> m_nodes;
-	U32 m_nodesCount = 0;
+	U32 m_nodeCount = 0;
 	GrHashMap<CString, SceneNode*> m_nodesDict;
 
 	SceneNode* m_mainCam = nullptr;
@@ -230,6 +233,8 @@ private:
 	void updateNode(U32 tid, SceneNode& node, UpdateSceneNodesCtx& ctx);
 
 	void sceneNodeChangedName(SceneNode& node, CString oldName);
+
+	static void countSerializableNodes(SceneNode& root, U32& serializableNodeCount);
 };
 
 } // end namespace anki

+ 48 - 14
AnKi/Scene/SceneNode.cpp

@@ -35,21 +35,20 @@ namespace anki {
 	template<> \
 	name##Component* SceneNode::newComponent<name##Component>() \
 	{ \
-		if(hasComponent<name##Component>() && !kSceneComponentSceneNodeCanHaveMany[SceneComponentType::k##name]) \
+		if(hasComponent<name##Component>() && !kSceneComponentTypeInfos[SceneComponentType::k##name].m_sceneNodeCanHaveMany) \
 		{ \
-			ANKI_SCENE_LOGE("Can't have many %s components in scene node %s", kSceneComponentTypeName[SceneComponentType::k##name], \
+			ANKI_SCENE_LOGE("Can't have many %s components in scene node %s", kSceneComponentTypeInfos[SceneComponentType::k##name].m_name, \
 							getName().cstr()); \
 			return nullptr; \
 		} \
-		auto it = SceneGraph::getSingleton().getComponentArrays().get##name##s().emplace(this); \
+		auto it = SceneGraph::getSingleton().getComponentArrays().get##name##s().emplace(this, SceneGraph::getSingleton().getNewUuid()); \
 		it->setArrayIndex(it.getArrayIndex()); \
-		newComponentInternal(&(*it)); \
+		addComponent(&(*it)); \
 		return &(*it); \
 	}
 #include <AnKi/Scene/Components/SceneComponentClasses.def.h>
 
 SceneNode::SceneNode(CString name)
-	: m_uuid(SceneGraph::getSingleton().getNewUuid())
 {
 	if(name)
 	{
@@ -87,7 +86,7 @@ void SceneNode::markForDeletion()
 	});
 }
 
-void SceneNode::newComponentInternal(SceneComponent* newc)
+void SceneNode::addComponent(SceneComponent* newc)
 {
 	m_componentTypeMask |= 1 << SceneComponentTypeMask(newc->getType());
 
@@ -166,11 +165,11 @@ void SceneNode::setName(CString name)
 	SceneGraph::getSingleton().sceneNodeChangedName(*this, oldName);
 }
 
-Error SceneNode::serializeCommon(SceneSerializer& serializer)
+Error SceneNode::serializeCommon(SceneSerializer& serializer, SerializeCommonArgs& args)
 {
-	ANKI_SERIALIZE(m_uuid, 1);
-	ANKI_SERIALIZE(m_name, 1);
+	ANKI_ASSERT(m_serialize);
 
+	// Trf
 	Vec3 origin = m_ltrf.getOrigin().xyz;
 	ANKI_SERIALIZE(origin, 1);
 	m_ltrf.setOrigin(origin);
@@ -183,7 +182,20 @@ Error SceneNode::serializeCommon(SceneSerializer& serializer)
 	ANKI_SERIALIZE(scale, 1);
 	m_ltrf.setScale(scale);
 
-	U32 componentCount = m_components.getSize();
+	// Components
+	U32 componentCount = 0;
+
+	if(serializer.isInWriteMode())
+	{
+		for(SceneComponent* comp : m_components)
+		{
+			if(comp->getSerialization())
+			{
+				++componentCount;
+			}
+		}
+	}
+
 	ANKI_SERIALIZE(componentCount, 1);
 
 	SceneDynamicArray<U32> componentUuids;
@@ -191,24 +203,46 @@ Error SceneNode::serializeCommon(SceneSerializer& serializer)
 	{
 		for(SceneComponent* comp : m_components)
 		{
-			componentUuids.emplaceBack(comp->getUuid());
+			if(comp->getSerialization())
+			{
+				componentUuids.emplaceBack(comp->getUuid());
+
+				args.m_write.m_serializableComponentMask[comp->getType()].setBit(comp->getArrayIndex());
+				++args.m_write.m_componentsToBeSerializedCount[comp->getType()];
+			}
 		}
 	}
 	else
 	{
-		ANKI_ASSERT(!"TODO");
+		componentUuids.resize(componentCount, 0);
 	}
 
 	ANKI_SERIALIZE(componentUuids, 1);
 
+	if(serializer.isInReadMode())
+	{
+		for(U32 componentUuid : componentUuids)
+		{
+			args.m_read.m_sceneComponentUuidToNode.emplace(componentUuid, this);
+		}
+	}
+
 	// Parent
 	SceneNode* parent = getParent();
 	U32 parentUuid = (parent) ? parent->getUuid() : 0;
 	ANKI_SERIALIZE(parentUuid, 1);
 
-	if(serializer.isInReadMode())
+	if(serializer.isInReadMode() && parentUuid > 0)
 	{
-		ANKI_ASSERT(!"TODO");
+		auto it = args.m_read.m_sceneNodeUuidToNode.find(parentUuid);
+		if(it == args.m_read.m_sceneNodeUuidToNode.getEnd())
+		{
+			ANKI_SCENE_LOGE("Failed to find parent UUID: %u", parentUuid);
+			return Error::kUserData;
+		}
+
+		SceneNode* parent = *it;
+		parent->addChild(this);
 	}
 
 	return Error::kNone;

+ 52 - 7
AnKi/Scene/SceneNode.h

@@ -11,6 +11,7 @@
 #include <AnKi/Util/BitSet.h>
 #include <AnKi/Util/List.h>
 #include <AnKi/Util/Enum.h>
+#include <AnKi/Util/DynamicBitSet.h>
 
 namespace anki {
 
@@ -83,6 +84,17 @@ public:
 
 	void markForDeletion();
 
+	// Enable serialization for this node, its components and its children
+	void setSerialization(Bool enable)
+	{
+		m_serialize = enable;
+	}
+
+	Bool getSerialization() const
+	{
+		return m_serialize;
+	}
+
 	Timestamp getComponentMaxTimestamp() const
 	{
 		return m_maxComponentTimestamp;
@@ -128,7 +140,10 @@ public:
 	{
 		for(U32 i = 0; i < m_components.getSize(); ++i)
 		{
-			func(*m_components[i]);
+			if(func(*m_components[i]) == FunctorContinue::kStop)
+			{
+				break;
+			}
 		}
 	}
 
@@ -138,7 +153,10 @@ public:
 	{
 		for(U32 i = 0; i < m_components.getSize(); ++i)
 		{
-			func(*m_components[i]);
+			if(func(*m_components[i]) == FunctorContinue::kStop)
+			{
+				break;
+			}
 		}
 	}
 
@@ -152,7 +170,10 @@ public:
 			{
 				if(m_components[i]->getType() == TComponent::kClassType)
 				{
-					func(static_cast<const TComponent&>(*m_components[i]));
+					if(func(static_cast<const TComponent&>(*m_components[i])) == FunctorContinue::kStop)
+					{
+						break;
+					}
 				}
 			}
 		}
@@ -168,7 +189,10 @@ public:
 			{
 				if(m_components[i]->getType() == TComponent::kClassType)
 				{
-					func(static_cast<TComponent&>(*m_components[i]));
+					if(func(static_cast<TComponent&>(*m_components[i])) == FunctorContinue::kStop)
+					{
+						break;
+					}
 				}
 			}
 		}
@@ -441,8 +465,28 @@ public:
 	// End movement methods //
 
 private:
+	class SerializeCommonArgs
+	{
+	public:
+		class
+		{
+		public:
+			// Given an index to the component array find out if the component should be serialized
+			Array<SceneDynamicBitSet<U32>, U32(SceneComponentType::kCount)> m_serializableComponentMask;
+
+			Array<U32, U32(SceneComponentType::kCount)> m_componentsToBeSerializedCount = {};
+		} m_write;
+
+		class
+		{
+		public:
+			SceneHashMap<U32, SceneNode*> m_sceneNodeUuidToNode;
+			SceneHashMap<U32, SceneNode*> m_sceneComponentUuidToNode;
+		} m_read;
+	};
+
 	SceneString m_name; // A unique name.
-	U32 m_uuid;
+	U32 m_uuid = 0;
 
 	SceneComponentTypeMask m_componentTypeMask = SceneComponentTypeMask::kNone;
 
@@ -459,10 +503,11 @@ private:
 	Bool m_localTransformDirty : 1 = true;
 	Bool m_ignoreParentNodeTransform : 1 = false;
 	Bool m_transformUpdatedThisFrame : 1 = true;
+	Bool m_serialize : 1 = true;
 
-	void newComponentInternal(SceneComponent* newc);
+	void addComponent(SceneComponent* newc);
 
-	Error serializeCommon(SceneSerializer& serializer);
+	Error serializeCommon(SceneSerializer& serializer, SerializeCommonArgs& args);
 };
 
 } // end namespace anki

+ 48 - 2
AnKi/Scene/SceneSerializer.cpp

@@ -28,9 +28,55 @@ Error SceneSerializer::write(CString name, ConstWeakArray<F64> values)
 	return write(name, arr);
 }
 
-Error SceneSerializer::read([[maybe_unused]] CString name, [[maybe_unused]] WeakArray<F64> values)
+Error SceneSerializer::read(CString name, WeakArray<F64> values)
 {
-	ANKI_ASSERT(!"TODO");
+	Array<F32, 32> tmpArray;
+	WeakArray<F32> arr;
+	if(values.getSize() < tmpArray.getSize())
+	{
+		arr = {tmpArray.getBegin(), values.getSize()};
+	}
+	else
+	{
+		ANKI_ASSERT(!"TODO");
+	}
+
+	return read(name, arr);
+}
+
+Error TextSceneSerializer::parseCurrentLine(SceneStringList& tokens, CString fieldName, U32 checkTokenCount)
+{
+	if(m_linesIt == m_lines.getEnd())
+	{
+		ANKI_SERIALIZER_LOGE("Can't read next line");
+		return Error::kUserData;
+	}
+
+	const SceneString& line = *m_linesIt;
+	tokens.splitString(line, ' ');
+	if(tokens.getSize() == 0)
+	{
+		ANKI_SERIALIZER_LOGE("Line empty");
+		return Error::kUserData;
+	}
+
+	if(*tokens.getBegin() != fieldName)
+	{
+		ANKI_SERIALIZER_LOGE("Wrong token. Got: %s, expecting: %s", tokens.getBegin()->cstr(), fieldName.cstr());
+		return Error::kUserData;
+	}
+
+	tokens.popFront();
+
+	if(checkTokenCount < kMaxU32 && tokens.getSize() != checkTokenCount)
+	{
+		ANKI_SERIALIZER_LOGE("Incorrect number of tokens");
+		return Error::kUserData;
+	}
+
+	++m_linesIt;
+	++m_lineno;
+
 	return Error::kNone;
 }
 

+ 65 - 7
AnKi/Scene/SceneSerializer.h

@@ -180,6 +180,18 @@ public:
 	}
 };
 
+#define ANKI_SERIALIZER_LOGE(...) ANKI_SCENE_LOGE("%s", (SceneString().sprintf(__VA_ARGS__) + SceneString().sprintf(". Line %u: ", m_lineno)).cstr())
+#define ANKI_SERIALIZER_CHECK(expr) \
+	do \
+	{ \
+		const Error retError = expr; \
+		if(retError) \
+		{ \
+			ANKI_SERIALIZER_LOGE("Expression failed: " ANKI_STRINGIZE(expr)); \
+			return retError; \
+		} \
+	} while(0)
+
 // Serialize in a custom text format
 class TextSceneSerializer : public SceneSerializer
 {
@@ -188,6 +200,14 @@ public:
 		: SceneSerializer(writingMode)
 		, m_file(*file)
 	{
+		if(!m_writeMode)
+		{
+			SceneString txt;
+			ANKI_CHECKF(file->readAllText(txt));
+
+			m_lines.splitString(txt, '\n');
+			m_linesIt = m_lines.getBegin();
+		}
 	}
 
 	Error write(CString name, ConstWeakArray<U32> values) final
@@ -207,9 +227,17 @@ public:
 		return Error::kNone;
 	}
 
-	Error read([[maybe_unused]] CString name, [[maybe_unused]] WeakArray<U32> values) final
+	Error read(CString name, WeakArray<U32> values) final
 	{
-		ANKI_ASSERT(!"TODO");
+		SceneStringList tokens;
+		ANKI_CHECK(parseCurrentLine(tokens, name, values.getSize()));
+
+		U32 count = 0;
+		for(const SceneString& str : tokens)
+		{
+			ANKI_SERIALIZER_CHECK(str.toNumber(values[count++]));
+		}
+
 		return Error::kNone;
 	}
 
@@ -230,9 +258,17 @@ public:
 		return Error::kNone;
 	}
 
-	Error read([[maybe_unused]] CString name, [[maybe_unused]] WeakArray<I32> values) final
+	Error read(CString name, WeakArray<I32> values) final
 	{
-		ANKI_ASSERT(!"TODO");
+		SceneStringList tokens;
+		ANKI_CHECK(parseCurrentLine(tokens, name, values.getSize()));
+
+		U32 count = 0;
+		for(const SceneString& str : tokens)
+		{
+			ANKI_SERIALIZER_CHECK(str.toNumber(values[count++]));
+		}
+
 		return Error::kNone;
 	}
 
@@ -253,9 +289,17 @@ public:
 		return Error::kNone;
 	}
 
-	Error read([[maybe_unused]] CString name, [[maybe_unused]] WeakArray<F32> values)
+	Error read(CString name, WeakArray<F32> values)
 	{
-		ANKI_ASSERT(!"TODO");
+		SceneStringList tokens;
+		ANKI_CHECK(parseCurrentLine(tokens, name, values.getSize()));
+
+		U32 count = 0;
+		for(const SceneString& str : tokens)
+		{
+			ANKI_SERIALIZER_CHECK(str.toNumber(values[count++]));
+		}
+
 		return Error::kNone;
 	}
 
@@ -266,12 +310,26 @@ public:
 
 	Error read([[maybe_unused]] CString name, [[maybe_unused]] SceneString& value) final
 	{
-		ANKI_ASSERT(!"TODO");
+		SceneStringList tokens;
+		ANKI_CHECK(parseCurrentLine(tokens, name));
+
+		value = "";
+		for(const SceneString& str : tokens)
+		{
+			value += str;
+		}
+
 		return Error::kNone;
 	}
 
 private:
 	File& m_file;
+
+	SceneStringList m_lines;
+	SceneStringList::Iterator m_linesIt;
+	U32 m_lineno = 0;
+
+	Error parseCurrentLine(SceneStringList& tokens, CString fieldName, U32 checkTokenCount = kMaxU32);
 };
 
 } // end namespace anki

+ 29 - 33
AnKi/Util/BitSet.h

@@ -11,26 +11,23 @@
 
 namespace anki {
 
-/// @addtogroup util_containers
-/// @{
-
-/// Easy bit manipulation.
-/// @tparam kBitCount The number of bits.
-/// @tparam TChunkType The type of the chunks that the bitset consists. By default it's U8.
+// Easy bit manipulation.
+// kBitCount: The number of bits.
+// TChunkType: The type of the chunks that the bitset consists. By default it's U8.
 template<U32 kBitCount, typename TChunkType = U8>
 class BitSet
 {
 private:
 	using ChunkType = TChunkType;
 
-	/// Number of bits a chunk holds.
+	// Number of bits a chunk holds.
 	static constexpr U32 kChunkBitCount = sizeof(ChunkType) * 8;
 
-	/// Number of chunks.
+	// Number of chunks.
 	static constexpr U32 kChunkCount = (kBitCount + (kChunkBitCount - 1)) / kChunkBitCount;
 
 public:
-	/// Constructor. It will set all the bits or unset them.
+	// Constructor. It will set all the bits or unset them.
 	BitSet(Bool set)
 	{
 		ANKI_ASSERT(set == 0 || set == 1);
@@ -44,20 +41,20 @@ public:
 		}
 	}
 
-	/// Copy.
+	// Copy.
 	BitSet(const BitSet& b)
 		: m_chunks(b.m_chunks)
 	{
 	}
 
-	/// Copy.
+	// Copy.
 	BitSet& operator=(const BitSet& b)
 	{
 		m_chunks = b.m_chunks;
 		return *this;
 	}
 
-	/// Bitwise or between this and @a b sets.
+	// Bitwise or between this and @a b sets.
 	BitSet operator|(const BitSet& b) const
 	{
 		BitSet out;
@@ -68,7 +65,7 @@ public:
 		return out;
 	}
 
-	/// Bitwise or between this and @a b sets.
+	// Bitwise or between this and @a b sets.
 	BitSet& operator|=(const BitSet& b)
 	{
 		for(U32 i = 0; i < kChunkCount; ++i)
@@ -78,7 +75,7 @@ public:
 		return *this;
 	}
 
-	/// Bitwise and between this and @a b sets.
+	// Bitwise and between this and @a b sets.
 	BitSet operator&(const BitSet& b) const
 	{
 		BitSet out;
@@ -89,7 +86,7 @@ public:
 		return out;
 	}
 
-	/// Bitwise and between this and @a b sets.
+	// Bitwise and between this and @a b sets.
 	BitSet& operator&=(const BitSet& b)
 	{
 		for(U32 i = 0; i < kChunkCount; ++i)
@@ -99,7 +96,7 @@ public:
 		return *this;
 	}
 
-	/// Bitwise xor between this and @a b sets.
+	// Bitwise xor between this and @a b sets.
 	BitSet operator^(const BitSet& b) const
 	{
 		BitSet out;
@@ -110,7 +107,7 @@ public:
 		return out;
 	}
 
-	/// Bitwise xor between this and @a b sets.
+	// Bitwise xor between this and @a b sets.
 	BitSet& operator^=(const BitSet& b)
 	{
 		for(U32 i = 0; i < kChunkCount; ++i)
@@ -120,7 +117,7 @@ public:
 		return *this;
 	}
 
-	/// Bitwise not of self.
+	// Bitwise not of self.
 	BitSet operator~() const
 	{
 		BitSet out;
@@ -157,7 +154,7 @@ public:
 		return getAnySet();
 	}
 
-	/// Set or unset a bit at the given position.
+	// Set or unset a bit at the given position.
 	template<typename TInt>
 	BitSet& set(TInt pos, Bool setBit = true)
 	{
@@ -168,7 +165,7 @@ public:
 		return *this;
 	}
 
-	/// Set multiple bits.
+	// Set multiple bits.
 	template<typename TInt>
 	BitSet& set(std::initializer_list<TInt> list, Bool setBits = true)
 	{
@@ -179,7 +176,7 @@ public:
 		return *this;
 	}
 
-	/// Set all bits.
+	// Set all bits.
 	BitSet& setAll()
 	{
 		memset(&m_chunks[0], 0xFF, sizeof(m_chunks));
@@ -187,28 +184,28 @@ public:
 		return *this;
 	}
 
-	/// Unset a bit (set to zero) at the given position.
+	// Unset a bit (set to zero) at the given position.
 	template<typename TInt>
 	BitSet& unset(TInt pos)
 	{
 		return set(pos, false);
 	}
 
-	/// Unset multiple bits.
+	// Unset multiple bits.
 	template<typename TInt>
 	BitSet& unset(std::initializer_list<TInt> list)
 	{
 		return set(list, false);
 	}
 
-	/// Unset all bits.
+	// Unset all bits.
 	BitSet& unsetAll()
 	{
 		memset(&m_chunks[0], 0, sizeof(m_chunks));
 		return *this;
 	}
 
-	/// Flip the bits at the given position. It will go from 1 to 0 or from 0 to 1.
+	// Flip the bits at the given position. It will go from 1 to 0 or from 0 to 1.
 	template<typename TInt>
 	BitSet& flip(TInt pos)
 	{
@@ -219,7 +216,7 @@ public:
 		return *this;
 	}
 
-	/// Return true if the bit is set or false if it's not.
+	// Return true if the bit is set or false if it's not.
 	template<typename TInt>
 	Bool get(TInt pos) const
 	{
@@ -229,14 +226,14 @@ public:
 		return (m_chunks[high] & mask) != 0;
 	}
 
-	/// Any are enabled.
+	// Any are enabled.
 	Bool getAnySet() const
 	{
 		const BitSet kZero(false);
 		return *this != kZero;
 	}
 
-	/// Count bits.
+	// Count bits.
 	U32 getSetBitCount() const
 	{
 		U32 count = 0;
@@ -247,7 +244,7 @@ public:
 		return count;
 	}
 
-	/// Get the most significant bit that is enabled. Or kMaxU32 if all is zero.
+	// Get the most significant bit that is enabled. Or kMaxU32 if all is zero.
 	U32 getMostSignificantBit() const
 	{
 		U32 i = kChunkCount;
@@ -264,7 +261,7 @@ public:
 		return kMaxU32;
 	}
 
-	/// Get the least significant bit that is enabled. Or kMaxU32 if all is zero.
+	// Get the least significant bit that is enabled. Or kMaxU32 if all is zero.
 	U32 getLeastSignificantBit() const
 	{
 		for(U32 i = 0; i < kChunkCount; ++i)
@@ -285,7 +282,7 @@ public:
 		return m_chunks;
 	}
 
-	/// Unset the N least significant bits of the bitset.
+	// Unset the N least significant bits of the bitset.
 	BitSet& unsetNLeastSignificantBits(U32 n)
 	{
 		ANKI_ASSERT(n);
@@ -323,7 +320,7 @@ private:
 		ANKI_ASSERT(low < kChunkBitCount);
 	}
 
-	/// Zero the unused bits.
+	// Zero the unused bits.
 	void zeroUnusedBits()
 	{
 		constexpr ChunkType kUnusedBits = kChunkCount * kChunkBitCount - kBitCount;
@@ -334,6 +331,5 @@ private:
 		}
 	}
 };
-/// @}
 
 } // end namespace anki

+ 77 - 0
AnKi/Util/DynamicBitSet.h

@@ -0,0 +1,77 @@
+// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <AnKi/Util/DynamicArray.h>
+
+namespace anki {
+
+// A dynamic array of bits.
+template<typename TMemoryPool, typename TElementType>
+class DynamicBitSet
+{
+public:
+	DynamicBitSet() = default;
+
+	DynamicBitSet(const DynamicBitSet& b)
+		: m_storage(b.m_storage)
+	{
+	}
+
+	DynamicBitSet(DynamicBitSet&& b)
+		: m_storage(std::move(b.m_storage))
+	{
+	}
+
+	DynamicBitSet& operator=(const DynamicBitSet& b)
+	{
+		m_storage = b.m_storage;
+		return *this;
+	}
+
+	DynamicBitSet& operator=(DynamicBitSet&& b)
+	{
+		m_storage = std::move(b.m_storage);
+		return *this;
+	}
+
+	DynamicBitSet& setBit(U32 pos)
+	{
+		U32 index, bit;
+		decode(pos, index, bit);
+		if(index >= m_storage.getSize())
+		{
+			m_storage.resize(index + 1, 0u);
+		}
+
+		m_storage[index] |= TElementType(1) << TElementType(bit);
+		return *this;
+	}
+
+	Bool getBit(U32 pos) const
+	{
+		U32 index, bit;
+		decode(pos, index, bit);
+		if(index >= m_storage.getSize())
+		{
+			return false;
+		}
+
+		const TElementType mask = TElementType(1) << TElementType(bit);
+		return (m_storage[index] & mask) != 0;
+	}
+
+private:
+	DynamicArray<TElementType, TMemoryPool> m_storage;
+
+	static void decode(U32 pos, U32& index, U32& bit)
+	{
+		index = pos / sizeof(TElementType);
+		bit = pos % sizeof(TElementType);
+	}
+};
+
+} // end namespace anki

+ 1 - 1
AnKi/Util/File.cpp

@@ -260,7 +260,7 @@ Error File::read(void* buff, PtrSize size)
 	}
 
 	Error err = Error::kNone;
-	if(static_cast<I64>(size) != readSize)
+	if(I64(size) != readSize)
 	{
 		ANKI_UTIL_LOGE("File read failed");
 		err = Error::kFileAccess;

+ 38 - 42
AnKi/Util/File.h

@@ -11,11 +11,7 @@
 
 namespace anki {
 
-/// @addtogroup util_file
-/// @{
-
-/// Open mode
-/// @memberof File
+// Open mode
 enum class FileOpenFlag : U8
 {
 	kNone = 0,
@@ -23,14 +19,13 @@ enum class FileOpenFlag : U8
 	kWrite = 1 << 1,
 	kAppend = kWrite | (1 << 3),
 	kBinary = 1 << 4,
-	kLittleEndian = 1 << 5, ///< The default
+	kLittleEndian = 1 << 5, // The default
 	kBigEndian = 1 << 6,
-	kSpecial = 1 << 7, ///< Android package file.
+	kSpecial = 1 << 7, // Android package file.
 };
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(FileOpenFlag)
 
-/// Passed to seek function
-/// @memberof File
+// Passed to seek function
 enum class FileSeekOrigin
 {
 	kBeginning = SEEK_SET,
@@ -38,54 +33,54 @@ enum class FileSeekOrigin
 	kEnd = SEEK_END
 };
 
-/// An abstraction over typical files and files in ziped archives. This class can read from regular C files, zip files
-/// and on Android from the packed asset files.
-/// To identify the file:
-/// - If the filename starts with '$' it will try to load a system specific file. For Android this is a file in the .apk
-/// - If the above are false then try to load a regular C file
+// An abstraction over typical files and files in ziped archives. This class can read from regular C files, zip files
+// and on Android from the packed asset files.
+// To identify the file:
+// - If the filename starts with '$' it will try to load a system specific file. For Android this is a file in the .apk
+// - If the above are false then try to load a regular C file
 class File
 {
 public:
-	/// Default constructor
+	// Default constructor
 	File() = default;
 
 	File(const File&) = delete; // Non-copyable
 
 	File& operator=(const File&) = delete; // Non-copyable
 
-	/// Move
+	// Move
 	File(File&& b)
 	{
 		*this = std::move(b);
 	}
 
-	/// Closes the file if it's open
+	// Closes the file if it's open
 	~File();
 
-	/// Move
+	// Move
 	File& operator=(File&& b);
 
-	/// Open a file.
-	/// @param[in] filename The file to open
-	/// @param[in] openMask The open flags. It's a combination of FileOpenFlag enum
+	// Open a file.
+	// filename: The file to open
+	// openMask: The open flags. It's a combination of FileOpenFlag enum
 	Error open(const CString& filename, FileOpenFlag openMask);
 
-	/// Return true if the file is oppen
+	// Return true if the file is oppen
 	Bool isOpen() const
 	{
 		return m_file != nullptr;
 	}
 
-	/// Close the file
+	// Close the file
 	void close();
 
-	/// Flush pending operations
+	// Flush pending operations
 	Error flush();
 
-	/// Read data from the file
+	// Read data from the file
 	Error read(void* buff, PtrSize size);
 
-	/// Read all the contents of a text file. If the file is not rewined it will probably fail.
+	// Read all the contents of a text file. If the file is not rewined it will probably fail.
 	template<typename TMemPool>
 	Error readAllText(BaseString<TMemPool>& out)
 	{
@@ -105,31 +100,31 @@ public:
 		return err;
 	}
 
-	/// Read 32bit unsigned integer. Set the endianness if the file's endianness is different from the machine's.
+	// Read 32bit unsigned integer. Set the endianness if the file's endianness is different from the machine's.
 	Error readU32(U32& u);
 
-	/// Read 32bit float. Set the endianness if the file's endianness is different from the machine's.
+	// Read 32bit float. Set the endianness if the file's endianness is different from the machine's.
 	Error readF32(F32& f);
 
-	/// Write data to the file
+	// Write data to the file
 	Error write(const void* buff, PtrSize size);
 
-	/// Write formated text
+	// Write formated text
 	ANKI_CHECK_FORMAT(1, 2)
 	Error writeTextf(const Char* format, ...);
 
-	/// Write plain text.
+	// Write plain text.
 	Error writeText(CString text);
 
-	/// Set the position indicator to a new position.
-	/// @param offset Number of bytes to offset from origin
-	/// @param origin Position used as reference for the offset
+	// Set the position indicator to a new position
+	// offset: Number of bytes to offset from origin
+	// origin: Position used as reference for the offset
 	Error seek(PtrSize offset, FileSeekOrigin origin);
 
-	/// Return the position indicator inside the file.
+	// Return the position indicator inside the file.
 	PtrSize tell();
 
-	/// The the size of the file.
+	// The the size of the file.
 	PtrSize getSize() const
 	{
 		ANKI_ASSERT(!(m_flags & FileOpenFlag::kWrite));
@@ -137,18 +132,20 @@ public:
 	}
 
 private:
-	void* m_file = nullptr; ///< A native file type
-	FileOpenFlag m_flags = FileOpenFlag::kNone; ///< All the flags. Set on open
+	static constexpr U32 kReadLineInitialSize = 256;
+
+	void* m_file = nullptr; // A native file type
 	PtrSize m_size = 0;
+	FileOpenFlag m_flags = FileOpenFlag::kNone; // All the flags. Set on open
 
-	/// Get the current machine's endianness
+	// Get the current machine's endianness
 	static FileOpenFlag getMachineEndianness();
 
-	/// Open a C file
+	// Open a C file
 	Error openCFile(const CString& filename, FileOpenFlag flags);
 
 #if ANKI_OS_ANDROID
-	/// Open an Android file
+	// Open an Android file
 	Error openAndroidFile(const CString& filename, FileOpenFlag flags);
 #endif
 
@@ -159,6 +156,5 @@ private:
 		m_size = 0;
 	}
 };
-/// @}
 
 } // end namespace anki

+ 6 - 1
AnKi/Util/Forward.h

@@ -70,6 +70,9 @@ class ConstWeakArray;
 template<typename T, typename TMemoryPool, typename TSize>
 class DynamicArray;
 
+template<typename TMemoryPool = SingletonMemoryPoolWrapper<DefaultMemoryPool>, typename T = U32>
+class DynamicBitSet;
+
 class F16;
 
 template<typename TMemoryPool>
@@ -93,6 +96,8 @@ class XmlDocument;
 	template<typename T> \
 	using submoduleName##Hierarchy = Hierarchy<T, submoduleName##MemPoolWrapper>; \
 	template<typename T, typename TConfig = BlockArrayDefaultConfig> \
-	using submoduleName##BlockArray = BlockArray<T, submoduleName##MemPoolWrapper, TConfig>;
+	using submoduleName##BlockArray = BlockArray<T, submoduleName##MemPoolWrapper, TConfig>; \
+	template<typename T = U32> \
+	using submoduleName##DynamicBitSet = DynamicBitSet<submoduleName##MemPoolWrapper, T>;
 
 } // end namespace anki

+ 2 - 0
Samples/PhysicsPlayground/FpsCharacterNode.cpp

@@ -52,6 +52,7 @@ FpsCharacter::FpsCharacter(CString name)
 	CameraComponent* camc = cam->newComponent<CameraComponent>();
 	camc->setPerspective(0.1f, 1000.0f, Renderer::getSingleton().getAspectRatio() * toRad<F32>(g_cvarGameFov), toRad<F32>(g_cvarGameFov));
 	addChild(cam);
+	cam->setSerialization(false);
 	m_cameraNode = cam;
 
 	SceneNode* shotgun = SceneGraph::getSingleton().newSceneNode<SceneNode>("Shotgun");
@@ -59,6 +60,7 @@ FpsCharacter::FpsCharacter(CString name)
 	shotgun->newComponent<MeshComponent>()->setMeshFilename("Assets/sleevegloveLOW.001_893660395596b206.ankimesh");
 	shotgun->newComponent<MaterialComponent>()->setMaterialFilename("Assets/arms_3a4232ebbd425e7a.ankimtl").setSubmeshIndex(0);
 	shotgun->newComponent<MaterialComponent>()->setMaterialFilename("Assets/boomstick_89a614a521ace7fd.ankimtl").setSubmeshIndex(1);
+	shotgun->setSerialization(false);
 	cam->addChild(shotgun);
 	m_shotgunNode = shotgun;
 }