Browse Source

Some scene refactoring

Panagiotis Christopoulos Charitos 9 months ago
parent
commit
45bfda6d2d
70 changed files with 719 additions and 845 deletions
  1. 59 0
      AnKi/Config.h.cmake
  2. 4 29
      AnKi/Core/App.cpp
  3. 3 4
      AnKi/Gr/Common.h
  4. 1 1
      AnKi/Gr/D3D/D3DCommandBuffer.cpp
  5. 1 1
      AnKi/Math/Mat.h
  6. 2 2
      AnKi/Scene/Common.h
  7. 8 6
      AnKi/Scene/Components/BodyComponent.cpp
  8. 5 5
      AnKi/Scene/Components/BodyComponent.h
  9. 1 3
      AnKi/Scene/Components/CameraComponent.cpp
  10. 5 5
      AnKi/Scene/Components/CameraComponent.h
  11. 4 4
      AnKi/Scene/Components/DecalComponent.cpp
  12. 3 1
      AnKi/Scene/Components/DecalComponent.h
  13. 2 7
      AnKi/Scene/Components/FogDensityComponent.cpp
  14. 8 6
      AnKi/Scene/Components/FogDensityComponent.h
  15. 2 4
      AnKi/Scene/Components/GlobalIlluminationProbeComponent.cpp
  16. 3 3
      AnKi/Scene/Components/GlobalIlluminationProbeComponent.h
  17. 2 4
      AnKi/Scene/Components/JointComponent.cpp
  18. 1 1
      AnKi/Scene/Components/JointComponent.h
  19. 1 3
      AnKi/Scene/Components/LensFlareComponent.cpp
  20. 1 1
      AnKi/Scene/Components/LensFlareComponent.h
  21. 1 3
      AnKi/Scene/Components/LightComponent.cpp
  22. 1 1
      AnKi/Scene/Components/LightComponent.h
  23. 2 4
      AnKi/Scene/Components/ModelComponent.cpp
  24. 1 1
      AnKi/Scene/Components/ModelComponent.h
  25. 1 2
      AnKi/Scene/Components/MoveComponent.cpp
  26. 1 1
      AnKi/Scene/Components/MoveComponent.h
  27. 1 3
      AnKi/Scene/Components/ParticleEmitterComponent.cpp
  28. 1 1
      AnKi/Scene/Components/ParticleEmitterComponent.h
  29. 1 3
      AnKi/Scene/Components/PlayerControllerComponent.cpp
  30. 1 1
      AnKi/Scene/Components/PlayerControllerComponent.h
  31. 1 3
      AnKi/Scene/Components/ReflectionProbeComponent.cpp
  32. 1 1
      AnKi/Scene/Components/ReflectionProbeComponent.h
  33. 1 1
      AnKi/Scene/Components/SceneComponent.h
  34. 6 25
      AnKi/Scene/Components/ScriptComponent.cpp
  35. 1 1
      AnKi/Scene/Components/ScriptComponent.h
  36. 2 4
      AnKi/Scene/Components/SkinComponent.cpp
  37. 1 1
      AnKi/Scene/Components/SkinComponent.h
  38. 1 2
      AnKi/Scene/Components/SkyboxComponent.cpp
  39. 1 1
      AnKi/Scene/Components/SkyboxComponent.h
  40. 2 4
      AnKi/Scene/Components/TriggerComponent.cpp
  41. 1 1
      AnKi/Scene/Components/TriggerComponent.h
  42. 1 2
      AnKi/Scene/Components/UiComponent.h
  43. 2 6
      AnKi/Scene/DeveloperConsoleUiNode.cpp
  44. 0 2
      AnKi/Scene/DeveloperConsoleUiNode.h
  45. 17 10
      AnKi/Scene/Events/AnimationEvent.cpp
  46. 2 3
      AnKi/Scene/Events/AnimationEvent.h
  47. 0 5
      AnKi/Scene/Events/Event.cpp
  48. 21 11
      AnKi/Scene/Events/Event.h
  49. 33 71
      AnKi/Scene/Events/EventManager.cpp
  50. 13 24
      AnKi/Scene/Events/EventManager.h
  51. 12 11
      AnKi/Scene/Events/JitterMoveEvent.cpp
  52. 5 6
      AnKi/Scene/Events/JitterMoveEvent.h
  53. 13 11
      AnKi/Scene/Events/LightEvent.cpp
  54. 11 6
      AnKi/Scene/Events/LightEvent.h
  55. 28 61
      AnKi/Scene/Events/ScriptEvent.cpp
  56. 3 8
      AnKi/Scene/Events/ScriptEvent.h
  57. 110 119
      AnKi/Scene/SceneGraph.cpp
  58. 20 92
      AnKi/Scene/SceneGraph.h
  59. 6 13
      AnKi/Scene/SceneNode.cpp
  60. 17 25
      AnKi/Scene/SceneNode.h
  61. 2 6
      AnKi/Scene/StatsUiNode.cpp
  62. 0 2
      AnKi/Scene/StatsUiNode.h
  63. 42 61
      AnKi/Script/Scene.cpp
  64. 5 25
      AnKi/Script/Scene.xml
  65. 4 4
      AnKi/Util/Hierarchy.h
  66. 26 28
      AnKi/Util/Hierarchy.inl.h
  67. BIN
      Samples/PhysicsPlayground/Assets/bullet_hole_decal.ankitex
  68. 110 0
      Samples/PhysicsPlayground/FpsCharacter.h
  69. 69 74
      Samples/PhysicsPlayground/Main.cpp
  70. 2 5
      Tools/Image/ImageViewerMain.cpp

+ 59 - 0
AnKi/Config.h.cmake

@@ -15,6 +15,7 @@
 
 #define ANKI_VERSION_MINOR ${ANKI_VERSION_MINOR}
 #define ANKI_VERSION_MAJOR ${ANKI_VERSION_MAJOR}
+#define ANKI_VERSION_STR "${ANKI_VERSION_MAJOR}.${ANKI_VERSION_MINOR}"
 #define ANKI_REVISION ${ANKI_REVISION}
 
 #define ANKI_EXTRA_CHECKS ${_ANKI_EXTRA_CHECKS}
@@ -132,23 +133,28 @@
 #	define ANKI_SIMD_NONE 1
 #	define ANKI_SIMD_SSE 0
 #	define ANKI_SIMD_NEON 0
+#	define ANKI_SIMD_STR "None"
 #elif ANKI_CPU_ARCH_X86
 #	define ANKI_SIMD_NONE 0
 #	define ANKI_SIMD_SSE 1
 #	define ANKI_SIMD_NEON 0
+#	define ANKI_SIMD_STR "SSE"
 #else
 #	define ANKI_SIMD_NONE 0
 #	define ANKI_SIMD_SSE 0
 #	define ANKI_SIMD_NEON 1
+#	define ANKI_SIMD_STR "Neon"
 #endif
 
 // Graphics backend
 #if ${_ANKI_GR_BACKEND} == 0
 #	define ANKI_GR_BACKEND_VULKAN 1
 #	define ANKI_GR_BACKEND_DIRECT3D 0
+#	define ANKI_GR_BACKEND_STR "Vulkan"
 #else
 #	define ANKI_GR_BACKEND_VULKAN 0
 #	define ANKI_GR_BACKEND_DIRECT3D 1
+#	define ANKI_GR_BACKEND_STR "D3D"
 #endif
 
 // Windowing system
@@ -156,14 +162,17 @@
 #	define ANKI_WINDOWING_SYSTEM_HEADLESS 1
 #	define ANKI_WINDOWING_SYSTEM_SDL 0
 #	define ANKI_WINDOWING_SYSTEM_ANDROID 0
+#	define ANKI_WINDOWING_SYSTEM_STR "Headless"
 #elif ${_ANKI_WINDOWING_SYSTEM} == 1
 #	define ANKI_WINDOWING_SYSTEM_HEADLESS 0
 #	define ANKI_WINDOWING_SYSTEM_SDL 1
 #	define ANKI_WINDOWING_SYSTEM_ANDROID 0
+#	define ANKI_WINDOWING_SYSTEM_STR "SDL"
 #elif ${_ANKI_WINDOWING_SYSTEM} == 2
 #	define ANKI_WINDOWING_SYSTEM_HEADLESS 0
 #	define ANKI_WINDOWING_SYSTEM_SDL 0
 #	define ANKI_WINDOWING_SYSTEM_ANDROID 1
+#	define ANKI_WINDOWING_SYSTEM_STR "Android"
 #endif
 
 // Mobile or not
@@ -205,6 +214,56 @@
 #	define ANKI_PURE
 #endif
 
+namespace anki {
+static inline const char* kAnKiBuildConfigString = 
+"ver " ANKI_VERSION_STR 
+
+", git hash " ANKI_REVISION
+
+", " ANKI_COMPILER_STR
+
+", " ANKI_OS_STR " OS"
+
+", " ANKI_GPU_ARCH_STR
+
+", " ANKI_SIMD_STR " SIMD"
+
+", " ANKI_GR_BACKEND_STR " GFX backend"
+
+", " ANKI_WINDOWING_SYSTEM_STR " window system"
+
+#if ANKI_EXTRA_CHECKS
+", extra checks ON"
+#else
+", extra checks OFF"
+#endif
+
+#if ANKI_DEBUG_SYMBOLS
+", debug symbols ON"
+#else
+", debug symbols OFF"
+#endif
+
+#if ANKI_OPTIMIZE
+", optimizations ON"
+#else
+", optimizations OFF"
+#endif
+
+#if ANKI_TRACING_ENABLED
+", tracing ON"
+#else
+", tracing OFF"
+#endif
+
+#if ANKI_STATS_ENABLED
+", stats ON"
+#else
+", stats OFF"
+#endif
+;
+}
+
 // Pack structs
 #if ANKI_COMPILER_MSVC
 #	define ANKI_BEGIN_PACKED_STRUCT __pragma(pack (push, 1))

+ 4 - 29
AnKi/Core/App.cpp

@@ -154,6 +154,8 @@ void App::cleanup()
 
 	CoreMemoryPool::freeSingleton();
 	DefaultMemoryPool::freeSingleton();
+
+	ANKI_CORE_LOGI("Application finished shutting down");
 }
 
 Error App::init()
@@ -182,34 +184,7 @@ Error App::initInternal()
 
 	ANKI_CHECK(initDirs());
 
-	// Print a message
-	const char* buildType =
-#if ANKI_OPTIMIZE
-		"optimized, "
-#else
-		"NOT optimized, "
-#endif
-#if ANKI_DEBUG_SYMBOLS
-		"dbg symbols, "
-#else
-		"NO dbg symbols, "
-#endif
-#if ANKI_EXTRA_CHECKS
-		"extra checks, "
-#else
-		"NO extra checks, "
-#endif
-#if ANKI_TRACING_ENABLED
-		"built with tracing";
-#else
-		"NOT built with tracing";
-#endif
-
-	ANKI_CORE_LOGI("Initializing application");
-	ANKI_CORE_LOGI("\tBuild type %s", buildType);
-	ANKI_CORE_LOGI("\tBuild time %s %s", __DATE__, __TIME__);
-	ANKI_CORE_LOGI("\tCompiler %s", ANKI_COMPILER_STR);
-	ANKI_CORE_LOGI("\tCommit %s", ANKI_REVISION);
+	ANKI_CORE_LOGI("Initializing application. Build config: %s", kAnKiBuildConfigString);
 
 // Check SIMD support
 #if ANKI_SIMD_SSE && ANKI_COMPILER_GCC_COMPATIBLE
@@ -428,7 +403,7 @@ Error App::mainLoop()
 			// User update
 			ANKI_CHECK(userMainLoop(quit, crntTime - prevUpdateTime));
 
-			ANKI_CHECK(SceneGraph::getSingleton().update(prevUpdateTime, crntTime));
+			SceneGraph::getSingleton().update(prevUpdateTime, crntTime);
 
 			// Render
 			TexturePtr presentableTex = GrManager::getSingleton().acquireNextPresentableTexture();

+ 3 - 4
AnKi/Gr/Common.h

@@ -47,7 +47,9 @@ inline BoolCVar g_workGraphcsCVar("Gr", "WorkGraphs", false, "Enable or not Work
 inline NumericCVar<U32> g_maxBindlessSampledTextureCountCVar("Gr", "MaxBindlessSampledTextureCountCVar", 512, 16, kMaxU16);
 inline NumericCVar<Second> g_gpuTimeoutCVar("Gr", "GpuTimeout", 120.0, 0.0, 24.0 * 60.0,
 											"Max time to wait for GPU fences or semaphores. More than that it must be a GPU timeout");
-
+inline NumericCVar<U8> g_asyncComputeCVar("Gr", "AsyncCompute", 0, 0, 2,
+										  "Control the async compute behaviour: 0: Try use separate queue family, 1: Use lower priority queue in the "
+										  "general's queue family, 2: Use the general queue");
 #if ANKI_GR_BACKEND_DIRECT3D
 inline NumericCVar<U16> g_maxRtvDescriptorsCVar("Gr", "MaxRvtDescriptors", 1024, 8, kMaxU16, "Max number of RTVs");
 inline NumericCVar<U16> g_maxDsvDescriptorsCVar("Gr", "MaxDsvDescriptors", 512, 8, kMaxU16, "Max number of DSVs");
@@ -63,9 +65,6 @@ inline BoolCVar g_dredCVar("Gr", "Dred", false, "Enable DRED");
 inline NumericCVar<PtrSize> g_diskShaderCacheMaxSizeCVar("Gr", "DiskShaderCacheMaxSize", 128_MB, 1_MB, 1_GB, "Max size of the pipeline cache file");
 inline BoolCVar g_debugPrintfCVar("Gr", "DebugPrintf", false, "Enable or not debug printf");
 inline BoolCVar g_samplerFilterMinMaxCVar("Gr", "SamplerFilterMinMax", true, "Enable or not min/max sample filtering");
-inline NumericCVar<U8> g_asyncComputeCVar("Gr", "AsyncCompute", 0, 0, 2,
-										  "Control the async compute behaviour: 0: Try use separate queue family, 1: Use lower priority queue in the "
-										  "general's queue family, 2: Use the general queue");
 inline StringCVar g_vkLayersCVar("Gr", "VkLayers", "", "VK layers to enable. Seperated by :");
 #endif
 

+ 1 - 1
AnKi/Gr/D3D/D3DCommandBuffer.cpp

@@ -641,7 +641,7 @@ void CommandBuffer::zeroBuffer(const BufferView& buff)
 	Buffer& zeroBuff = getGrManagerImpl().getZeroBuffer();
 	const PtrSize zeroBuffSize = zeroBuff.getSize();
 
-	const U32 copyRangeCount = (buff.getRange() + (zeroBuffSize - 1)) / zeroBuffSize;
+	const PtrSize copyRangeCount = (buff.getRange() + (zeroBuffSize - 1)) / zeroBuffSize;
 	WeakArray<CopyBufferToBufferInfo> copyRanges;
 	CopyBufferToBufferInfo defaultCopyRange;
 	if(copyRangeCount > 1)

+ 1 - 1
AnKi/Math/Mat.h

@@ -150,7 +150,7 @@ public:
 	{
 		TMat& m = *this;
 		// Not normalized axis
-		ANKI_ASSERT(isZero<T>(T(1) - axisang.getAxis().getLength()));
+		ANKI_ASSERT(isZero<T>(T(1) - axisang.getAxis().length()));
 
 		T c, s;
 		sinCos(axisang.getAngle(), s, c);

+ 2 - 2
AnKi/Scene/Common.h

@@ -44,9 +44,9 @@ private:
 
 ANKI_DEFINE_SUBMODULE_UTIL_CONTAINERS(Scene, SceneMemoryPool)
 
-#define ANKI_SCENE_ASSERT(expression) \
+#define ANKI_EXPECT(expression) \
 	std::invoke([&]() -> Bool { \
-		const Bool ok = (expression); \
+		const Bool ok = !!(expression); \
 		if(!ok) [[unlikely]] \
 		{ \
 			ANKI_SCENE_LOGE("Expression failed: " #expression); \

+ 8 - 6
AnKi/Scene/Components/BodyComponent.cpp

@@ -36,13 +36,13 @@ void BodyComponent::teleportTo(Vec3 position, const Mat3& rotation)
 	m_node->setLocalRotation(rotation);
 }
 
-Error BodyComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
+void BodyComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 	if(m_shapeType == BodyComponentCollisionShapeType::kCount
 	   || (m_shapeType == BodyComponentCollisionShapeType::kFromModelComponent && !m_mesh.m_modelc))
 	{
 		ANKI_ASSERT(!m_body);
-		return Error::kNone;
+		return;
 	}
 
 	const Bool shapeDirty =
@@ -77,8 +77,12 @@ Error BodyComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 		{
 			m_mesh.m_modelcUuid = m_mesh.m_modelc->getUuid();
 
-			ANKI_CHECK(m_mesh.m_modelc->getModelResource()->getModelPatches()[0].getMesh()->getOrCreateCollisionShape(
-				m_mass == 0.0f, kMaxLodCount - 1, m_collisionShape));
+			if(m_mesh.m_modelc->getModelResource()->getModelPatches()[0].getMesh()->getOrCreateCollisionShape(m_mass == 0.0f, kMaxLodCount - 1,
+																											  m_collisionShape))
+			{
+				ANKI_SCENE_LOGE("BodyComponent::update failed");
+				return;
+			}
 		}
 		else if(m_shapeType == BodyComponentCollisionShapeType::kAabb)
 		{
@@ -124,8 +128,6 @@ Error BodyComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 		}
 		m_force = Vec3(0.0f);
 	}
-
-	return Error::kNone;
 }
 
 void BodyComponent::onOtherComponentRemovedOrAdded(SceneComponent* other, Bool added)

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

@@ -36,7 +36,7 @@ public:
 
 	void setBoxExtend(Vec3 extend)
 	{
-		if(ANKI_SCENE_ASSERT(extend > 0.01f) && extend != m_box.m_extend)
+		if(ANKI_EXPECT(extend > 0.01f) && extend != m_box.m_extend)
 		{
 			m_box.m_extend = extend;
 			if(m_shapeType == BodyComponentCollisionShapeType::kAabb)
@@ -53,7 +53,7 @@ public:
 
 	void setSphereRadius(F32 radius)
 	{
-		if(ANKI_SCENE_ASSERT(radius > 0.01f) && radius != m_sphere.m_radius)
+		if(ANKI_EXPECT(radius > 0.01f) && radius != m_sphere.m_radius)
 		{
 			m_sphere.m_radius = radius;
 			if(m_shapeType == BodyComponentCollisionShapeType::kSphere)
@@ -70,7 +70,7 @@ public:
 
 	void setCollisionShapeType(BodyComponentCollisionShapeType type)
 	{
-		if(ANKI_SCENE_ASSERT(type <= BodyComponentCollisionShapeType::kCount) && m_shapeType != type)
+		if(ANKI_EXPECT(type <= BodyComponentCollisionShapeType::kCount) && m_shapeType != type)
 		{
 			m_shapeType = type;
 			m_body.reset(nullptr); // Force recreate
@@ -79,7 +79,7 @@ public:
 
 	void setMass(F32 mass)
 	{
-		if(ANKI_SCENE_ASSERT(mass >= 0.0f) && m_mass != mass)
+		if(ANKI_EXPECT(mass >= 0.0f) && m_mass != mass)
 		{
 			m_mass = mass;
 			m_body.reset(nullptr); // Force recreate
@@ -148,7 +148,7 @@ private:
 
 	BodyComponentCollisionShapeType m_shapeType = BodyComponentCollisionShapeType::kCount;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
+	void update(SceneComponentUpdateInfo& info, Bool& updated) override;
 
 	void onOtherComponentRemovedOrAdded(SceneComponent* other, Bool added) override;
 };

+ 1 - 3
AnKi/Scene/Components/CameraComponent.cpp

@@ -24,7 +24,7 @@ CameraComponent::~CameraComponent()
 {
 }
 
-Error CameraComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
+void CameraComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 	if(info.m_node->movedThisFrame())
 	{
@@ -32,8 +32,6 @@ Error CameraComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 	}
 
 	updated = m_frustum.update();
-
-	return Error::kNone;
 }
 
 } // end namespace anki

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

@@ -25,7 +25,7 @@ public:
 
 	void setNear(F32 near)
 	{
-		if(ANKI_SCENE_ASSERT(near > 0.0f))
+		if(ANKI_EXPECT(near > 0.0f))
 		{
 			m_frustum.setNear(near);
 		}
@@ -38,7 +38,7 @@ public:
 
 	void setFar(F32 far)
 	{
-		if(ANKI_SCENE_ASSERT(far > 0.0f))
+		if(ANKI_EXPECT(far > 0.0f))
 		{
 			m_frustum.setFar(far);
 		}
@@ -51,7 +51,7 @@ public:
 
 	void setFovX(F32 fovx)
 	{
-		if(ANKI_SCENE_ASSERT(fovx > 0.0f && fovx < kPi))
+		if(ANKI_EXPECT(fovx > 0.0f && fovx < kPi))
 		{
 			m_frustum.setFovX(fovx);
 		}
@@ -64,7 +64,7 @@ public:
 
 	void setFovY(F32 fovy)
 	{
-		if(ANKI_SCENE_ASSERT(fovy > 0.0f && fovy < kPi))
+		if(ANKI_EXPECT(fovy > 0.0f && fovy < kPi))
 		{
 			m_frustum.setFovY(fovy);
 		}
@@ -93,7 +93,7 @@ public:
 private:
 	Frustum m_frustum;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
+	void update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
 /// @}
 

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

@@ -16,6 +16,8 @@ DecalComponent::DecalComponent(SceneNode* node)
 {
 	m_gpuSceneDecal.allocate();
 	loadDiffuseImageResource("EngineAssets/DefaultDecal.png", 0.9f);
+
+	m_defaultDecalImage = m_layers[LayerType::kDiffuse].m_image;
 }
 
 DecalComponent::~DecalComponent()
@@ -40,13 +42,13 @@ void DecalComponent::setLayer(CString fname, F32 blendFactor, LayerType type)
 	l.m_blendFactor = clamp(blendFactor, 0.0f, 1.0f);
 }
 
-Error DecalComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
+void DecalComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 	updated = m_dirty || info.m_node->movedThisFrame();
 
 	if(!updated) [[likely]]
 	{
-		return Error::kNone;
+		return;
 	}
 
 	m_dirty = false;
@@ -76,8 +78,6 @@ Error DecalComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 	gpuDecal.m_sphereRadius = halfBoxSize.length();
 
 	m_gpuSceneDecal.uploadToGpuScene(gpuDecal);
-
-	return Error::kNone;
 }
 
 } // end namespace anki

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

@@ -59,6 +59,8 @@ private:
 		U32 m_bindlessTextureIndex = kMaxU32;
 	};
 
+	ImageResourcePtr m_defaultDecalImage; ///< Keep that loaded to avoid loading it all the time when a new decal is constructed.
+
 	Array<Layer, U(LayerType::kCount)> m_layers;
 
 	GpuSceneArrays::Decal::Allocation m_gpuSceneDecal;
@@ -67,7 +69,7 @@ private:
 
 	void setLayer(CString fname, F32 blendFactor, LayerType type);
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
+	void update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
 /// @}
 

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

@@ -20,12 +20,9 @@ FogDensityComponent ::~FogDensityComponent()
 {
 }
 
-Error FogDensityComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
+void FogDensityComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
-	if(m_type == FogDensityComponentShape::kCount)
-	{
-		return Error::kNone;
-	}
+	ANKI_ASSERT(m_type < FogDensityComponentShape::kCount);
 
 	updated = m_dirty || info.m_node->movedThisFrame();
 
@@ -52,8 +49,6 @@ Error FogDensityComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 
 		m_gpuSceneVolume.uploadToGpuScene(gpuVolume);
 	}
-
-	return Error::kNone;
 }
 
 } // end namespace anki

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

@@ -37,7 +37,7 @@ public:
 
 	void setShapeType(FogDensityComponentShape type)
 	{
-		if(type != m_type)
+		if(ANKI_EXPECT(type < FogDensityComponentShape::kCount) && type != m_type)
 		{
 			m_type = type;
 			m_dirty = true;
@@ -49,11 +49,13 @@ public:
 		return m_type;
 	}
 
-	void setDensity(F32 d)
+	void setDensity(F32 density)
 	{
-		ANKI_ASSERT(d >= 0.0f);
-		m_dirty = true;
-		m_density = d;
+		if(ANKI_EXPECT(density >= 0.0f) && m_density != density)
+		{
+			m_dirty = true;
+			m_density = density;
+		}
 	}
 
 	F32 getDensity() const
@@ -70,7 +72,7 @@ private:
 
 	Bool m_dirty = true;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
+	void update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
 
 } // end namespace anki

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

@@ -30,13 +30,13 @@ GlobalIlluminationProbeComponent::~GlobalIlluminationProbeComponent()
 {
 }
 
-Error GlobalIlluminationProbeComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
+void GlobalIlluminationProbeComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 	const Bool moved = info.m_node->movedThisFrame();
 
 	if(!m_dirty && !moved) [[likely]]
 	{
-		return Error::kNone;
+		return;
 	}
 
 	updated = true;
@@ -154,8 +154,6 @@ Error GlobalIlluminationProbeComponent::update(SceneComponentUpdateInfo& info, B
 	}
 
 	m_dirty = false;
-
-	return Error::kNone;
 }
 
 F32 GlobalIlluminationProbeComponent::getRenderRadius() const

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

@@ -28,7 +28,7 @@ public:
 	/// Set the cell size in meters.
 	void setCellSize(F32 cellSize)
 	{
-		if(ANKI_SCENE_ASSERT(cellSize > 0.0f) && m_cellSize != cellSize)
+		if(ANKI_EXPECT(cellSize > 0.0f) && m_cellSize != cellSize)
 		{
 			m_cellSize = cellSize;
 			m_dirty = true;
@@ -47,7 +47,7 @@ public:
 
 	void setFadeDistance(F32 dist)
 	{
-		if(ANKI_SCENE_ASSERT(dist > 0.0f) && m_fadeDistance != dist)
+		if(ANKI_EXPECT(dist > 0.0f) && m_fadeDistance != dist)
 		{
 			m_fadeDistance = dist;
 			m_dirty = true;
@@ -129,7 +129,7 @@ private:
 
 	Bool m_dirty = true;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
+	void update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
 /// @}
 

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

@@ -19,7 +19,7 @@ JointComponent::~JointComponent()
 {
 }
 
-Error JointComponent::update([[maybe_unused]] SceneComponentUpdateInfo& info, Bool& updated)
+void JointComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 	SceneNode& node = *info.m_node;
 
@@ -35,7 +35,7 @@ Error JointComponent::update([[maybe_unused]] SceneComponentUpdateInfo& info, Bo
 	if(!body1 || !body2 || m_type == JointType::kCount)
 	{
 		m_joint.reset(nullptr);
-		return Error::kNone;
+		return;
 	}
 
 	const Bool parentChanged = parent && m_parentNodeUuid != parent->getUuid();
@@ -66,8 +66,6 @@ Error JointComponent::update([[maybe_unused]] SceneComponentUpdateInfo& info, Bo
 			ANKI_ASSERT(0);
 		}
 	}
-
-	return Error::kNone;
 }
 
 } // end namespace anki

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

@@ -46,7 +46,7 @@ private:
 
 	JointType m_type = JointType::kCount;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
+	void update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
 /// @}
 

+ 1 - 3
AnKi/Scene/Components/LensFlareComponent.cpp

@@ -32,7 +32,7 @@ void LensFlareComponent::loadImageResource(CString filename)
 	m_image = std::move(image);
 }
 
-Error LensFlareComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
+void LensFlareComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 	updated = m_dirty || info.m_node->movedThisFrame();
 
@@ -42,8 +42,6 @@ Error LensFlareComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 
 		m_worldPosition = info.m_node->getWorldTransform().getOrigin().xyz();
 	}
-
-	return Error::kNone;
 }
 
 } // end namespace anki

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

@@ -89,7 +89,7 @@ private:
 
 	Bool m_dirty = true;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
+	void update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
 /// @}
 

+ 1 - 3
AnKi/Scene/Components/LightComponent.cpp

@@ -60,7 +60,7 @@ void LightComponent::setLightComponentType(LightComponentType newType)
 	}
 }
 
-Error LightComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
+void LightComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 	const Bool moveUpdated = info.m_node->movedThisFrame();
 	updated = moveUpdated || m_shapeDirty || m_otherDirty;
@@ -200,8 +200,6 @@ Error LightComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 
 	m_shapeDirty = false;
 	m_otherDirty = false;
-
-	return Error::kNone;
 }
 
 void LightComponent::computeCascadeFrustums(const Frustum& primaryFrustum, ConstWeakArray<F32> cascadeDistances, WeakArray<Mat4> cascadeProjMats,

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

@@ -200,7 +200,7 @@ private:
 	U8 m_otherDirty : 1 = true;
 	U8 m_shadowAtlasUvViewportCount : 3 = 0;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
+	void update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
 /// @}
 

+ 2 - 4
AnKi/Scene/Components/ModelComponent.cpp

@@ -117,12 +117,12 @@ void ModelComponent::loadModelResource(CString filename)
 	}
 }
 
-Error ModelComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
+void ModelComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 	if(!isEnabled()) [[unlikely]]
 	{
 		updated = false;
-		return Error::kNone;
+		return;
 	}
 
 	const Bool resourceUpdated = m_resourceChanged;
@@ -347,8 +347,6 @@ Error ModelComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 			}
 		}
 	}
-
-	return Error::kNone;
 }
 
 void ModelComponent::onOtherComponentRemovedOrAdded(SceneComponent* other, Bool added)

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

@@ -82,7 +82,7 @@ private:
 
 	void freeGpuScene();
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
+	void update(SceneComponentUpdateInfo& info, Bool& updated) override;
 
 	void onOtherComponentRemovedOrAdded(SceneComponent* other, Bool added) override;
 

+ 1 - 2
AnKi/Scene/Components/MoveComponent.cpp

@@ -8,10 +8,9 @@
 
 namespace anki {
 
-Error MoveComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
+void MoveComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 	updated = info.m_node->updateTransform();
-	return Error::kNone;
 }
 
 } // end namespace anki

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

@@ -26,7 +26,7 @@ public:
 	~MoveComponent() = default;
 
 private:
-	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
+	void update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
 /// @}
 

+ 1 - 3
AnKi/Scene/Components/ParticleEmitterComponent.cpp

@@ -321,12 +321,11 @@ void ParticleEmitterComponent::loadParticleEmitterResource(CString filename)
 	}
 }
 
-Error ParticleEmitterComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
+void ParticleEmitterComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 	if(!m_particleEmitterResource.isCreated()) [[unlikely]]
 	{
 		updated = false;
-		return Error::kNone;
 	}
 
 	updated = true;
@@ -482,7 +481,6 @@ Error ParticleEmitterComponent::update(SceneComponentUpdateInfo& info, Bool& upd
 	}
 
 	m_resourceUpdated = false;
-	return Error::kNone;
 }
 
 template<typename TParticle>

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

@@ -78,7 +78,7 @@ private:
 	Bool m_resourceUpdated = true;
 	SimulationType m_simulationType = SimulationType::kUndefined;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
+	void update(SceneComponentUpdateInfo& info, Bool& updated) override;
 
 	template<typename TParticle>
 	void simulate(Second prevUpdateTime, Second crntTime, const Transform& worldTransform, WeakArray<TParticle> particles, Vec3*& positions,

+ 1 - 3
AnKi/Scene/Components/PlayerControllerComponent.cpp

@@ -21,7 +21,7 @@ PlayerControllerComponent::PlayerControllerComponent(SceneNode* node)
 	node->setIgnoreParentTransform(true);
 }
 
-Error PlayerControllerComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
+void PlayerControllerComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 	U32 posVersion;
 	const Vec3 newPos = m_player->getPosition(&posVersion);
@@ -33,8 +33,6 @@ Error PlayerControllerComponent::update(SceneComponentUpdateInfo& info, Bool& up
 
 		info.m_node->setLocalOrigin(newPos);
 	}
-
-	return Error::kNone;
 }
 
 } // end namespace anki

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

@@ -40,7 +40,7 @@ private:
 	PhysicsPlayerControllerPtr m_player;
 	U32 m_positionVersion = kMaxU32;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
+	void update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
 /// @}
 

+ 1 - 3
AnKi/Scene/Components/ReflectionProbeComponent.cpp

@@ -36,7 +36,7 @@ ReflectionProbeComponent::~ReflectionProbeComponent()
 {
 }
 
-Error ReflectionProbeComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
+void ReflectionProbeComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 	const Bool moved = info.m_node->movedThisFrame();
 	updated = moved || m_dirty;
@@ -68,8 +68,6 @@ Error ReflectionProbeComponent::update(SceneComponentUpdateInfo& info, Bool& upd
 		gpuProbe.m_componentArrayIndex = getArrayIndex();
 		m_gpuSceneProbe.uploadToGpuScene(gpuProbe);
 	}
-
-	return Error::kNone;
 }
 
 F32 ReflectionProbeComponent::getRenderRadius() const

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

@@ -77,7 +77,7 @@ private:
 	Bool m_dirty = true;
 	Bool m_reflectionNeedsRefresh = true;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
+	void update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
 /// @}
 

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

@@ -107,7 +107,7 @@ public:
 	{
 	}
 
-	ANKI_INTERNAL virtual Error update(SceneComponentUpdateInfo& info, Bool& updated) = 0;
+	ANKI_INTERNAL virtual void update(SceneComponentUpdateInfo& info, Bool& updated) = 0;
 
 	ANKI_INTERNAL virtual void onOtherComponentRemovedOrAdded([[maybe_unused]] SceneComponent* other, [[maybe_unused]] Bool added)
 	{

+ 6 - 25
AnKi/Scene/Components/ScriptComponent.cpp

@@ -56,12 +56,12 @@ void ScriptComponent::loadScriptResource(CString fname)
 	}
 }
 
-Error ScriptComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
+void ScriptComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 	updated = false;
 	if(m_env == nullptr)
 	{
-		return Error::kNone;
+		return;
 	}
 
 	lua_State* lua = &m_env->getLuaState();
@@ -74,33 +74,14 @@ Error ScriptComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 	lua_pushnumber(lua, info.m_previousTime);
 	lua_pushnumber(lua, info.m_currentTime);
 
-	// Do the call (3 arguments, 1 result)
-	if(lua_pcall(lua, 3, 1, 0) != 0)
+	// Do the call (3 arguments, no result)
+	if(lua_pcall(lua, 3, 0, 0) != 0)
 	{
 		ANKI_SCENE_LOGE("Error running ScriptComponent's \"update\": %s", lua_tostring(lua, -1));
-		return Error::kUserData;
+		return;
 	}
 
-	if(!lua_isnumber(lua, -1))
-	{
-		ANKI_SCENE_LOGE("ScriptComponent's \"update\" should return a number");
-		lua_pop(lua, 1);
-		return Error::kUserData;
-	}
-
-	// Get the result
-	lua_Number result = lua_tonumber(lua, -1);
-	lua_pop(lua, 1);
-
-	if(result < 0)
-	{
-		ANKI_SCENE_LOGE("ScriptComponent's \"update\" return an error code");
-		return Error::kUserData;
-	}
-
-	updated = (result != 0);
-
-	return Error::kNone;
+	updated = true;
 }
 
 } // end namespace anki

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

@@ -35,7 +35,7 @@ private:
 	ScriptResourcePtr m_script;
 	ScriptEnvironment* m_env = nullptr;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
+	void update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
 /// @}
 

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

@@ -71,12 +71,12 @@ void SkinComponent::playAnimation(U32 track, AnimationResourcePtr anim, const An
 	m_tracks[track].m_repeatTimes = info.m_repeatTimes;
 }
 
-Error SkinComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
+void SkinComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 	updated = false;
 	if(!m_skeleton.isCreated())
 	{
-		return Error::kNone;
+		return;
 	}
 
 	const Second dt = info.m_dt;
@@ -205,8 +205,6 @@ Error SkinComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 	}
 
 	m_absoluteTime += dt;
-
-	return Error::kNone;
 }
 
 void SkinComponent::visitBones(const Bone& bone, const Mat3x4& parentTrf, const BitSet<128>& bonesAnimated, Vec4& minExtend, Vec4& maxExtend)

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

@@ -115,7 +115,7 @@ private:
 
 	GpuSceneBufferAllocation m_gpuSceneBoneTransforms;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
+	void update(SceneComponentUpdateInfo& info, Bool& updated) override;
 
 	void visitBones(const Bone& bone, const Mat3x4& parentTrf, const BitSet<128, U8>& bonesAnimated, Vec4& minExtend, Vec4& maxExtend);
 };

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

@@ -36,10 +36,9 @@ void SkyboxComponent::loadImageResource(CString filename)
 	m_type = SkyboxType::kImage2D;
 }
 
-Error SkyboxComponent::update([[maybe_unused]] SceneComponentUpdateInfo& info, Bool& updated)
+void SkyboxComponent::update([[maybe_unused]] SceneComponentUpdateInfo& info, Bool& updated)
 {
 	updated = false;
-	return Error::kNone;
 }
 
 } // end namespace anki

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

@@ -177,7 +177,7 @@ private:
 		Vec3 m_diffuseColor = Vec3(1.0f);
 	} m_fog;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
+	void update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
 /// @}
 

+ 2 - 4
AnKi/Scene/Components/TriggerComponent.cpp

@@ -80,11 +80,11 @@ void TriggerComponent::setType(TriggerComponentShapeType type)
 	}
 }
 
-Error TriggerComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
+void TriggerComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 	if(m_type == TriggerComponentShapeType::kCount)
 	{
-		return Error::kNone;
+		return;
 	}
 
 	if(m_trigger && info.m_node->movedThisFrame() && info.m_node->getWorldTransform().getScale() != m_trigger->getTransform().getScale())
@@ -145,8 +145,6 @@ Error TriggerComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 	// Prepare them for the next frame
 	m_resetEnter = true;
 	m_resetExit = true;
-
-	return Error::kNone;
 }
 
 } // end namespace anki

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

@@ -61,7 +61,7 @@ private:
 
 	static MyPhysicsTriggerCallbacks m_callbacks;
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
+	void update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
 /// @}
 

+ 1 - 2
AnKi/Scene/Components/UiComponent.h

@@ -61,10 +61,9 @@ private:
 	void* m_userData = nullptr;
 	Bool m_enabled = true;
 
-	Error update([[maybe_unused]] SceneComponentUpdateInfo& info, Bool& updated) override
+	void update([[maybe_unused]] SceneComponentUpdateInfo& info, Bool& updated) override
 	{
 		updated = false;
-		return Error::kNone;
 	}
 };
 /// @}

+ 2 - 6
AnKi/Scene/DeveloperConsoleUiNode.cpp

@@ -19,6 +19,8 @@ DeveloperConsoleUiNode::DeveloperConsoleUiNode(CString name)
 		},
 		this);
 	uic->setEnabled(false);
+
+	ANKI_CHECKF(UiManager::getSingleton().newInstance(m_font, "EngineAssets/UbuntuMonoRegular.ttf", Array<U32, 1>{16}));
 }
 
 DeveloperConsoleUiNode::~DeveloperConsoleUiNode()
@@ -34,12 +36,6 @@ DeveloperConsoleUiNode::~DeveloperConsoleUiNode()
 	}
 }
 
-Error DeveloperConsoleUiNode::init()
-{
-	ANKI_CHECK(UiManager::getSingleton().newInstance(m_font, "EngineAssets/UbuntuMonoRegular.ttf", Array<U32, 1>{16}));
-	return Error::kNone;
-}
-
 void DeveloperConsoleUiNode::toggleConsole()
 {
 	m_enabled = !m_enabled;

+ 0 - 2
AnKi/Scene/DeveloperConsoleUiNode.h

@@ -22,8 +22,6 @@ public:
 
 	~DeveloperConsoleUiNode();
 
-	Error init();
-
 	void toggleConsole();
 
 	Bool isConsoleEnabled() const

+ 17 - 10
AnKi/Scene/Events/AnimationEvent.cpp

@@ -10,10 +10,20 @@
 
 namespace anki {
 
-Error AnimationEvent::init(CString animationFilename, CString channelName, SceneNode* movableSceneNode)
+AnimationEvent::AnimationEvent(CString animationFilename, CString channelName, SceneNode* movableSceneNode)
+	: Event(0.0, 1.0)
 {
-	ANKI_ASSERT(movableSceneNode);
-	ANKI_CHECK(ResourceManager::getSingleton().loadResource(animationFilename, m_anim));
+	if(!ANKI_EXPECT(movableSceneNode))
+	{
+		markForDeletion();
+		return;
+	}
+
+	if(!ANKI_EXPECT(!ResourceManager::getSingleton().loadResource(animationFilename, m_anim)))
+	{
+		markForDeletion();
+		return;
+	}
 
 	m_channelIndex = 0;
 	for(const AnimationChannel& channel : m_anim->getChannels())
@@ -28,17 +38,16 @@ Error AnimationEvent::init(CString animationFilename, CString channelName, Scene
 	if(m_channelIndex == m_anim->getChannels().getSize())
 	{
 		ANKI_SCENE_LOGE("Can't initialize AnimationEvent. Channel not found: %s", channelName.cstr());
-		return Error::kUserData;
+		markForDeletion();
+		return;
 	}
 
-	Event::init(m_anim->getStartingTime(), m_anim->getDuration());
+	init(m_anim->getStartingTime(), m_anim->getDuration());
 	m_reanimate = true;
 	m_associatedNodes.emplaceBack(movableSceneNode);
-
-	return Error::kNone;
 }
 
-Error AnimationEvent::update([[maybe_unused]] Second prevUpdateTime, Second crntTime)
+void AnimationEvent::update([[maybe_unused]] Second prevUpdateTime, Second crntTime)
 {
 	Vec3 pos;
 	Quat rot;
@@ -53,8 +62,6 @@ Error AnimationEvent::update([[maybe_unused]] Second prevUpdateTime, Second crnt
 	trf.setScale(Vec4(scale, scale, scale, 0.0f));
 
 	m_associatedNodes[0]->setLocalTransform(trf);
-
-	return Error::kNone;
 }
 
 } // end namespace anki

+ 2 - 3
AnKi/Scene/Events/AnimationEvent.h

@@ -17,10 +17,9 @@ namespace anki {
 class AnimationEvent : public Event
 {
 public:
-	Error init(CString animationFilename, CString channel, SceneNode* movableSceneNode);
+	AnimationEvent(CString animationFilename, CString channel, SceneNode* movableSceneNode);
 
-	/// Implements Event::update
-	Error update(Second prevUpdateTime, Second crntTime) override;
+	void update(Second prevUpdateTime, Second crntTime) override;
 
 private:
 	AnimationResourcePtr m_anim;

+ 0 - 5
AnKi/Scene/Events/Event.cpp

@@ -28,9 +28,4 @@ Second Event::getDelta(Second crntTime) const
 	return dp;
 }
 
-void Event::setMarkedForDeletion()
-{
-	SceneGraph::getSingleton().getEventManager().markEventForDeletion(this);
-}
-
 } // end namespace anki

+ 21 - 11
AnKi/Scene/Events/Event.h

@@ -20,7 +20,12 @@ class Event : public IntrusiveListEnabled<Event>
 	friend class EventManager;
 
 public:
-	Event() = default;
+	/// @param startTime The time the event will start. If it's < 0 then start the event now.
+	/// @param duration The duration of the event.
+	Event(Second startTime, Second duration)
+	{
+		init(startTime, duration);
+	}
 
 	virtual ~Event() = default;
 
@@ -39,10 +44,12 @@ public:
 		return crntTime >= m_startTime + m_duration;
 	}
 
-	/// @note It's thread safe.
-	void setMarkedForDeletion();
+	void markForDeletion()
+	{
+		m_markedForDeletion = true;
+	}
 
-	Bool getMarkedForDeletion() const
+	Bool isMarkedForDeletion() const
 	{
 		return m_markedForDeletion;
 	}
@@ -63,6 +70,12 @@ public:
 												  : WeakArray<SceneNode*>(&m_associatedNodes[0], m_associatedNodes.getSize());
 	}
 
+	ConstWeakArray<SceneNode*> getAssociatedSceneNodes() const
+	{
+		return (m_associatedNodes.getSize() == 0) ? ConstWeakArray<SceneNode*>()
+												  : ConstWeakArray<SceneNode*>(&m_associatedNodes[0], m_associatedNodes.getSize());
+	}
+
 	void addAssociatedSceneNode(SceneNode* node)
 	{
 		ANKI_ASSERT(node);
@@ -72,14 +85,13 @@ public:
 	/// This method should be implemented by the derived classes
 	/// @param prevUpdateTime The time of the previous update (sec)
 	/// @param crntTime The current time (sec)
-	virtual Error update(Second prevUpdateTime, Second crntTime) = 0;
+	virtual void update(Second prevUpdateTime, Second crntTime) = 0;
 
 	/// This is called when the event is killed
 	/// @param prevUpdateTime The time of the previous update (sec)
 	/// @param crntTime The current time (sec)
-	virtual Error onKilled([[maybe_unused]] Second prevUpdateTime, [[maybe_unused]] Second crntTime)
+	virtual void onKilled([[maybe_unused]] Second prevUpdateTime, [[maybe_unused]] Second crntTime)
 	{
-		return Error::kNone;
 	}
 
 protected:
@@ -91,13 +103,11 @@ protected:
 
 	SceneDynamicArray<SceneNode*> m_associatedNodes;
 
-	/// @param startTime The time the event will start. If it's < 0 then start the event now.
-	/// @param duration The duration of the event.
-	void init(Second startTime, Second duration);
-
 	/// Return the u between current time and when the event started
 	/// @return A number [0.0, 1.0]
 	Second getDelta(Second crntTime) const;
+
+	void init(Second startTime, Second duration);
 };
 /// @}
 

+ 33 - 71
AnKi/Scene/Events/EventManager.cpp

@@ -15,46 +15,39 @@ EventManager::EventManager()
 
 EventManager::~EventManager()
 {
-	while(!m_events.isEmpty())
+	while(!m_eventsForRegistration.isEmpty())
 	{
-		Event* event = &m_events.getFront();
-		event->setMarkedForDeletion();
+		deleteInstance(SceneMemoryPool::getSingleton(), m_eventsForRegistration.popFront());
 	}
 
-	deleteEventsMarkedForDeletion(false);
+	while(!m_events.isEmpty())
+	{
+		deleteInstance(SceneMemoryPool::getSingleton(), m_events.popFront());
+	}
 }
 
-Error EventManager::updateAllEvents(Second prevUpdateTime, Second crntTime)
+void EventManager::updateAllEvents(Second prevUpdateTime, Second crntTime)
 {
-	Error err = Error::kNone;
+	// Register new events
+	while(!m_eventsForRegistration.isEmpty())
+	{
+		Event* e = m_eventsForRegistration.popFront();
+		m_events.pushBack(e);
+	}
 
+	DynamicArray<Event*, MemoryPoolPtrWrapper<StackMemoryPool>> eventsForDeletion(&SceneGraph::getSingleton().getFrameMemoryPool());
 	for(Event& event : m_events)
 	{
-		// If event or the node's event is marked for deletion then dont do anything else for that event
-		if(event.getMarkedForDeletion())
-		{
-			continue;
-		}
-
-		// Check if the associated scene nodes are marked for deletion
-		Bool skip = false;
-		for(SceneNode* node : event.m_associatedNodes)
-		{
-			if(node->getMarkedForDeletion())
-			{
-				skip = true;
-				break;
-			}
-		}
-
-		if(skip)
+		// If event or the event's nodes are marked for deletion, destoy it
+		if(event.isMarkedForDeletion() || assosiatedNodesMarkedForDeletion(event))
 		{
+			eventsForDeletion.emplaceBack(&event);
 			continue;
 		}
 
-		// Audjust starting time
 		if(event.m_startTime < 0.0)
 		{
+			// Start the event now
 			event.m_startTime = crntTime;
 		}
 
@@ -65,79 +58,48 @@ Error EventManager::updateAllEvents(Second prevUpdateTime, Second crntTime)
 
 			if(event.getStartTime() <= crntTime)
 			{
-				err = event.update(prevUpdateTime, crntTime);
+				event.update(prevUpdateTime, crntTime);
 			}
 		}
 		else
 		{
-			// Dead
+			// Dead, reanimate or kill
 
 			if(event.getReanimate())
 			{
 				event.m_startTime = prevUpdateTime;
-				err = event.update(prevUpdateTime, crntTime);
+				event.update(prevUpdateTime, crntTime);
 			}
 			else
 			{
-				err = event.onKilled(prevUpdateTime, crntTime);
-				if(err || !event.getReanimate())
+				event.onKilled(prevUpdateTime, crntTime);
+				if(!event.getReanimate())
 				{
-					event.setMarkedForDeletion();
+					eventsForDeletion.emplaceBack(&event);
 				}
 			}
 		}
 	}
 
-	return err;
-}
-
-void EventManager::markEventForDeletion(Event* event)
-{
-	ANKI_ASSERT(event);
-	if(event->m_markedForDeletion)
+	for(Event* e : eventsForDeletion)
 	{
-		return;
+		m_events.erase(e);
+		deleteInstance(SceneMemoryPool::getSingleton(), e);
 	}
-
-	LockGuard<Mutex> lock(m_mtx);
-	event->m_markedForDeletion = true;
-	m_events.erase(event);
-	m_eventsMarkedForDeletion.pushBack(event);
+	eventsForDeletion.destroy();
 }
 
-void EventManager::deleteEventsMarkedForDeletion(Bool fullCleanup)
+Bool EventManager::assosiatedNodesMarkedForDeletion(const Event& e)
 {
-	// Mark events with to-be-deleted nodes as also to be deleted
-	if(fullCleanup)
+	for(const SceneNode* node : e.getAssociatedSceneNodes())
 	{
-		// Gather in an array because we can't call setMarkedForDeletion while iterating m_events
-		DynamicArray<Event*, MemoryPoolPtrWrapper<StackMemoryPool>> markedForDeletion(&SceneGraph::getSingleton().getFrameMemoryPool());
-		for(Event& event : m_events)
-		{
-			for(SceneNode* node : event.m_associatedNodes)
-			{
-				if(node->getMarkedForDeletion())
-				{
-					markedForDeletion.emplaceBack(&event);
-					break;
-				}
-			}
-		}
-
-		for(Event* event : markedForDeletion)
+		if(node->isMarkedForDeletion())
 		{
-			event->setMarkedForDeletion();
+			return true;
 		}
 	}
 
-	// Gather events for deletion
-	while(!m_eventsMarkedForDeletion.isEmpty())
-	{
-		Event* event = &m_eventsMarkedForDeletion.getFront();
-		m_eventsMarkedForDeletion.popFront();
-
-		deleteInstance(SceneMemoryPool::getSingleton(), event);
-	}
+	return false;
 }
 
 } // end namespace anki

+ 13 - 24
AnKi/Scene/Events/EventManager.h

@@ -22,37 +22,26 @@ public:
 	~EventManager();
 
 	/// Create a new event
-	/// @note It's thread-safe against itself.
-	template<typename T, typename... Args>
-	Error newEvent(T*& event, Args... args)
+	/// @note It's thread-safe against itself. Can be called in Event::update.
+	template<typename T, typename... TArgs>
+	T* newEvent(TArgs&&... args)
 	{
-		event = newInstance<T>(SceneMemoryPool::getSingleton());
-		Error err = event->init(std::forward<Args>(args)...);
-		if(err)
-		{
-			deleteInstance(SceneMemoryPool::getSingleton(), event);
-		}
-		else
-		{
-			LockGuard<Mutex> lock(m_mtx);
-			m_events.pushBack(event);
-		}
-		return err;
+		T* event = newInstance<T>(SceneMemoryPool::getSingleton(), std::forward<TArgs>(args)...);
+		LockGuard lock(m_eventsForRegistrationMtx);
+		m_eventsForRegistration.pushBack(event);
+		return event;
 	}
 
 	/// Update
-	Error updateAllEvents(Second prevUpdateTime, Second crntTime);
-
-	/// Delete events that pending deletion
-	void deleteEventsMarkedForDeletion(Bool fullCleanup);
-
-	/// @note It's thread-safe against itself.
-	void markEventForDeletion(Event* event);
+	ANKI_INTERNAL void updateAllEvents(Second prevUpdateTime, Second crntTime);
 
 private:
 	IntrusiveList<Event> m_events;
-	IntrusiveList<Event> m_eventsMarkedForDeletion;
-	Mutex m_mtx;
+
+	IntrusiveList<Event> m_eventsForRegistration;
+	SpinLock m_eventsForRegistrationMtx;
+
+	static Bool assosiatedNodesMarkedForDeletion(const Event& e);
 };
 /// @}
 

+ 12 - 11
AnKi/Scene/Events/JitterMoveEvent.cpp

@@ -9,17 +9,20 @@
 
 namespace anki {
 
-Error JitterMoveEvent::init(Second startTime, Second duration, SceneNode* node)
+JitterMoveEvent::JitterMoveEvent(Second startTime, Second duration, SceneNode* node)
+	: Event(startTime, duration)
 {
-	ANKI_ASSERT(node);
-	Event::init(startTime, duration);
-	m_associatedNodes.emplaceBack(node);
-	m_originalPos = node->getWorldTransform().getOrigin();
+	if(!ANKI_EXPECT(node))
+	{
+		markForDeletion();
+		return;
+	}
 
-	return Error::kNone;
+	m_associatedNodes.emplaceBack(node);
+	m_originalPos = node->getWorldTransform().getOrigin().xyz();
 }
 
-void JitterMoveEvent::setPositionLimits(const Vec4& posMin, const Vec4& posMax)
+void JitterMoveEvent::setPositionLimits(Vec3 posMin, Vec3 posMax)
 {
 	for(U i = 0; i < 3; i++)
 	{
@@ -30,19 +33,17 @@ void JitterMoveEvent::setPositionLimits(const Vec4& posMin, const Vec4& posMax)
 	m_newPos += m_originalPos;
 }
 
-Error JitterMoveEvent::update([[maybe_unused]] Second prevUpdateTime, Second crntTime)
+void JitterMoveEvent::update([[maybe_unused]] Second prevUpdateTime, Second crntTime)
 {
 	SceneNode* node = m_associatedNodes[0];
 
 	Transform trf = node->getLocalTransform();
 
-	F32 factor = F32(sin(getDelta(crntTime) * kPi));
+	const F32 factor = F32(sin(getDelta(crntTime) * kPi));
 
 	trf.setOrigin(linearInterpolate(m_originalPos, m_newPos, factor));
 
 	node->setLocalTransform(trf);
-
-	return Error::kNone;
 }
 
 } // end namespace anki

+ 5 - 6
AnKi/Scene/Events/JitterMoveEvent.h

@@ -17,16 +17,15 @@ namespace anki {
 class JitterMoveEvent : public Event
 {
 public:
-	Error init(Second startTime, Second duration, SceneNode* movableSceneNode);
+	JitterMoveEvent(Second startTime, Second duration, SceneNode* movableSceneNode);
 
-	/// Implements Event::update
-	Error update(Second prevUpdateTime, Second crntTime);
+	void update(Second prevUpdateTime, Second crntTime) override;
 
-	void setPositionLimits(const Vec4& posMin, const Vec4& posMax);
+	void setPositionLimits(Vec3 posMin, Vec3 posMax);
 
 private:
-	Vec4 m_originalPos;
-	Vec4 m_newPos;
+	Vec3 m_originalPos;
+	Vec3 m_newPos;
 };
 /// @}
 

+ 13 - 11
AnKi/Scene/Events/LightEvent.cpp

@@ -9,9 +9,15 @@
 
 namespace anki {
 
-Error LightEvent::init(Second startTime, Second duration, SceneNode* light)
+LightEvent::LightEvent(Second startTime, Second duration, SceneNode* light)
+	: Event(startTime, duration)
 {
-	Event::init(startTime, duration);
+	if(!ANKI_EXPECT(light))
+	{
+		markForDeletion();
+		return;
+	}
+
 	m_associatedNodes.emplaceBack(light);
 
 	LightComponent& lightc = light->getFirstComponentOfType<LightComponent>();
@@ -22,7 +28,7 @@ Error LightEvent::init(Second startTime, Second duration, SceneNode* light)
 		m_originalRadius = lightc.getRadius();
 		break;
 	case LightComponentType::kSpot:
-		ANKI_ASSERT("TODO");
+		m_originalRadius = lightc.getDistance();
 		break;
 	default:
 		ANKI_ASSERT(0);
@@ -30,15 +36,13 @@ Error LightEvent::init(Second startTime, Second duration, SceneNode* light)
 	}
 
 	m_originalDiffColor = lightc.getDiffuseColor();
-
-	return Error::kNone;
 }
 
-Error LightEvent::update([[maybe_unused]] Second prevUpdateTime, Second crntTime)
+void LightEvent::update([[maybe_unused]] Second prevUpdateTime, Second crntTime)
 {
 	const F32 freq = getRandomRange(m_freq - m_freqDeviation, m_freq + m_freqDeviation);
 
-	F32 factor = F32(sin(crntTime * freq * kPi)) / 2.0f + 0.5f;
+	const F32 factor = F32(sin(crntTime * freq * kPi)) / 2.0f + 0.5f;
 	LightComponent& lightc = m_associatedNodes[0]->getFirstComponentOfType<LightComponent>();
 
 	// Update radius
@@ -50,7 +54,7 @@ Error LightEvent::update([[maybe_unused]] Second prevUpdateTime, Second crntTime
 			lightc.setRadius(m_originalRadius + factor * m_radiusMultiplier);
 			break;
 		case LightComponentType::kSpot:
-			ANKI_ASSERT("TODO");
+			lightc.setDistance(m_originalRadius + factor * m_radiusMultiplier);
 			break;
 		default:
 			ANKI_ASSERT(0);
@@ -61,7 +65,7 @@ Error LightEvent::update([[maybe_unused]] Second prevUpdateTime, Second crntTime
 	// Update the color and the lens flare's color if they are the same
 	if(m_intensityMultiplier != Vec4(0.0))
 	{
-		Vec4 outCol = m_originalDiffColor + factor * m_intensityMultiplier;
+		const Vec4 outCol = m_originalDiffColor + factor * m_intensityMultiplier;
 
 		LensFlareComponent* lfc = m_associatedNodes[0]->tryGetFirstComponentOfType<LensFlareComponent>();
 
@@ -72,8 +76,6 @@ Error LightEvent::update([[maybe_unused]] Second prevUpdateTime, Second crntTime
 
 		lightc.setDiffuseColor(outCol);
 	}
-
-	return Error::kNone;
 }
 
 } // end namespace anki

+ 11 - 6
AnKi/Scene/Events/LightEvent.h

@@ -17,10 +17,10 @@ namespace anki {
 class LightEvent : public Event
 {
 public:
-	Error init(Second startTime, Second duration, SceneNode* light);
+	LightEvent(Second startTime, Second duration, SceneNode* light);
 
 	/// Implements Event::update
-	Error update(Second prevUpdateTime, Second crntTime) override;
+	void update(Second prevUpdateTime, Second crntTime) override;
 
 	void setRadiusMultiplier(F32 v)
 	{
@@ -37,10 +37,15 @@ public:
 	/// @param deviation Add a randomization to the frequency.
 	void setFrequency(F32 freq, F32 deviation)
 	{
-		ANKI_ASSERT(freq > 0.0);
-		ANKI_ASSERT(freq > deviation);
-		m_freq = freq;
-		m_freqDeviation = deviation;
+		if(ANKI_EXPECT(freq > 0.0))
+		{
+			m_freq = freq;
+		}
+
+		if(ANKI_EXPECT(freq > deviation))
+		{
+			m_freqDeviation = deviation;
+		}
 	}
 
 private:

+ 28 - 61
AnKi/Scene/Events/ScriptEvent.cpp

@@ -13,18 +13,9 @@
 
 namespace anki {
 
-ScriptEvent::ScriptEvent()
+ScriptEvent::ScriptEvent(Second startTime, Second duration, CString script)
+	: Event(startTime, duration)
 {
-}
-
-ScriptEvent::~ScriptEvent()
-{
-}
-
-Error ScriptEvent::init(Second startTime, Second duration, CString script)
-{
-	Event::init(startTime, duration);
-
 	// Do the rest
 	String extension;
 	getFilepathExtension(script, extension);
@@ -32,10 +23,18 @@ Error ScriptEvent::init(Second startTime, Second duration, CString script)
 	if(!extension.isEmpty() && extension == "lua")
 	{
 		// It's a file
-		ANKI_CHECK(ResourceManager::getSingleton().loadResource(script, m_scriptRsrc));
+		if(!ANKI_EXPECT(!ResourceManager::getSingleton().loadResource(script, m_scriptRsrc)))
+		{
+			markForDeletion();
+			return;
+		}
 
 		// Exec the script
-		ANKI_CHECK(m_env.evalString(m_scriptRsrc->getSource()));
+		if(!ANKI_EXPECT(!m_env.evalString(m_scriptRsrc->getSource())))
+		{
+			markForDeletion();
+			return;
+		}
 	}
 	else
 	{
@@ -43,13 +42,19 @@ Error ScriptEvent::init(Second startTime, Second duration, CString script)
 		m_script = script;
 
 		// Exec the script
-		ANKI_CHECK(m_env.evalString(m_script.toCString()));
+		if(!ANKI_EXPECT(!m_env.evalString(m_script.toCString())))
+		{
+			markForDeletion();
+			return;
+		}
 	}
+}
 
-	return Error::kNone;
+ScriptEvent::~ScriptEvent()
+{
 }
 
-Error ScriptEvent::update(Second prevUpdateTime, Second crntTime)
+void ScriptEvent::update(Second prevUpdateTime, Second crntTime)
 {
 	lua_State* lua = &m_env.getLuaState();
 
@@ -61,34 +66,15 @@ Error ScriptEvent::update(Second prevUpdateTime, Second crntTime)
 	lua_pushnumber(lua, prevUpdateTime);
 	lua_pushnumber(lua, crntTime);
 
-	// Do the call (3 arguments, 1 result)
-	if(lua_pcall(lua, 3, 1, 0) != 0)
+	// Do the call (3 arguments, no result)
+	if(lua_pcall(lua, 3, 0, 0) != 0)
 	{
 		ANKI_SCENE_LOGE("Error running ScriptEvent's \"update\": %s", lua_tostring(lua, -1));
-		return Error::kUserData;
-	}
-
-	if(!lua_isnumber(lua, -1))
-	{
-		ANKI_SCENE_LOGE("ScriptEvent's \"update\" should return a number");
-		lua_pop(lua, 1);
-		return Error::kUserData;
+		return;
 	}
-
-	// Get the result
-	lua_Number result = lua_tonumber(lua, -1);
-	lua_pop(lua, 1);
-
-	if(result < 0)
-	{
-		ANKI_SCENE_LOGE("ScriptEvent's \"update\" return an error code");
-		return Error::kUserData;
-	}
-
-	return Error::kNone;
 }
 
-Error ScriptEvent::onKilled(Second prevUpdateTime, Second crntTime)
+void ScriptEvent::onKilled(Second prevUpdateTime, Second crntTime)
 {
 	lua_State* lua = &m_env.getLuaState();
 
@@ -100,31 +86,12 @@ Error ScriptEvent::onKilled(Second prevUpdateTime, Second crntTime)
 	lua_pushnumber(lua, prevUpdateTime);
 	lua_pushnumber(lua, crntTime);
 
-	// Do the call (3 arguments, 1 result)
-	if(lua_pcall(lua, 3, 1, 0) != 0)
+	// Do the call (3 arguments, no result)
+	if(lua_pcall(lua, 3, 0, 0) != 0)
 	{
 		ANKI_SCENE_LOGE("Error running ScriptEvent's \"onKilled\": %s", lua_tostring(lua, -1));
-		return Error::kUserData;
+		return;
 	}
-
-	if(!lua_isnumber(lua, -1))
-	{
-		ANKI_SCENE_LOGE("ScriptEvent's \"onKilled\" should return a number");
-		lua_pop(lua, 1);
-		return Error::kUserData;
-	}
-
-	// Get the result
-	lua_Number result = lua_tonumber(lua, -1);
-	lua_pop(lua, 1);
-
-	if(result < 0)
-	{
-		ANKI_SCENE_LOGE("ScriptEvent's \"onKilled\" return an error code");
-		return Error::kUserData;
-	}
-
-	return Error::kNone;
 }
 
 } // end namespace anki

+ 3 - 8
AnKi/Scene/Events/ScriptEvent.h

@@ -18,27 +18,22 @@ namespace anki {
 /// @code
 /// function update(event, prevTime, crntTime)
 /// 	-- Do something
-/// 	return 1
 /// end
 ///
 /// function onKilled(event, prevTime, crntTime)
 /// 	-- Do something
-/// 	return 1
 /// end
 /// @endcode
 class ScriptEvent : public Event
 {
 public:
-	ScriptEvent();
+	ScriptEvent(Second startTime, Second duration, CString script);
 
 	~ScriptEvent();
 
-	/// @param script It's a script or a filename to a script.
-	Error init(Second startTime, Second duration, CString script);
+	void update(Second prevUpdateTime, Second crntTime) override;
 
-	Error update(Second prevUpdateTime, Second crntTime) override;
-
-	Error onKilled(Second prevUpdateTime, Second crntTime) override;
+	void onKilled(Second prevUpdateTime, Second crntTime) override;
 
 private:
 	ScriptResourcePtr m_scriptRsrc;

+ 110 - 119
AnKi/Scene/SceneGraph.cpp

@@ -49,6 +49,13 @@ public:
 
 	Second m_prevUpdateTime;
 	Second m_crntTime;
+
+	DynamicArray<SceneNode*, MemoryPoolPtrWrapper<StackMemoryPool>> m_nodesForDeletion;
+
+	UpdateSceneNodesCtx()
+		: m_nodesForDeletion(&SceneGraph::getSingleton().m_framePool)
+	{
+	}
 };
 
 SceneGraph::SceneGraph()
@@ -57,12 +64,15 @@ SceneGraph::SceneGraph()
 
 SceneGraph::~SceneGraph()
 {
-	[[maybe_unused]] const Error err = iterateSceneNodes([&](SceneNode& s) -> Error {
-		s.setMarkedForDeletion();
-		return Error::kNone;
-	});
+	while(!m_nodesForRegistration.isEmpty())
+	{
+		deleteInstance(SceneMemoryPool::getSingleton(), m_nodesForRegistration.popBack());
+	}
 
-	deleteNodesMarkedForDeletion();
+	while(!m_nodes.isEmpty())
+	{
+		deleteInstance(SceneMemoryPool::getSingleton(), m_nodes.popBack());
+	}
 
 #define ANKI_CAT_TYPE(arrayName, gpuSceneType, id, cvarName) GpuSceneArrays::arrayName::freeSingleton();
 #include <AnKi/Scene/GpuSceneArrays.def.h>
@@ -77,7 +87,7 @@ Error SceneGraph::init(AllocAlignedCallback allocCallback, void* allocCallbackDa
 	m_framePool.init(allocCallback, allocCallbackData, 1_MB, 2.0, 0, true, "SceneGraphFramePool");
 
 	// Init the default main camera
-	ANKI_CHECK(newSceneNode<SceneNode>("mainCamera", m_defaultMainCam));
+	m_defaultMainCam = newSceneNode<SceneNode>("mainCamera");
 	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;
@@ -90,60 +100,15 @@ Error SceneGraph::init(AllocAlignedCallback allocCallback, void* allocCallbackDa
 	// Construct a few common nodex
 	if(g_displayStatsCVar > 0)
 	{
-		StatsUiNode* statsNode;
-		ANKI_CHECK(newSceneNode("_StatsUi", statsNode));
+		StatsUiNode* statsNode = newSceneNode<StatsUiNode>("_StatsUi");
 		statsNode->setFpsOnly(g_displayStatsCVar == 1);
 	}
 
-	DeveloperConsoleUiNode* consoleNode;
-	ANKI_CHECK(newSceneNode("_DevConsole", consoleNode));
+	newSceneNode<DeveloperConsoleUiNode>("_DevConsole");
 
 	return Error::kNone;
 }
 
-Error SceneGraph::registerNode(SceneNode* node)
-{
-	ANKI_ASSERT(node);
-
-	// Add to dict if it has a name
-	if(node->getName())
-	{
-		if(tryFindSceneNode(node->getName()))
-		{
-			ANKI_SCENE_LOGE("Node with the same name already exists");
-			return Error::kUserData;
-		}
-
-		m_nodesDict.emplace(node->getName(), node);
-	}
-
-	// Add to vector
-	m_nodes.pushBack(node);
-	++m_nodesCount;
-
-	return Error::kNone;
-}
-
-void SceneGraph::unregisterNode(SceneNode* node)
-{
-	// Remove from the graph
-	m_nodes.erase(node);
-	--m_nodesCount;
-
-	if(m_mainCam != m_defaultMainCam && m_mainCam == node)
-	{
-		m_mainCam = m_defaultMainCam;
-	}
-
-	// Remove from dict
-	if(node->getName())
-	{
-		auto it = m_nodesDict.find(node->getName());
-		ANKI_ASSERT(it != m_nodesDict.getEnd());
-		m_nodesDict.erase(it);
-	}
-}
-
 SceneNode& SceneGraph::findSceneNode(const CString& name)
 {
 	SceneNode* node = tryFindSceneNode(name);
@@ -153,39 +118,27 @@ SceneNode& SceneGraph::findSceneNode(const CString& name)
 
 SceneNode* SceneGraph::tryFindSceneNode(const CString& name)
 {
+	// Search registered nodes
 	auto it = m_nodesDict.find(name);
-	return (it == m_nodesDict.getEnd()) ? nullptr : (*it);
-}
+	if(it != m_nodesDict.getEnd())
+	{
+		return *it;
+	}
 
-void SceneGraph::deleteNodesMarkedForDeletion()
-{
-	/// Delete all nodes pending deletion. At this point all scene threads
-	/// should have finished their tasks
-	while(m_objectsMarkedForDeletionCount.load() > 0)
+	// Didn't found it, search those up for registration
+	LockGuard lock(m_nodesForRegistrationMtx);
+	for(SceneNode& node : m_nodesForRegistration)
 	{
-		[[maybe_unused]] Bool found = false;
-		auto it = m_nodes.begin();
-		auto end = m_nodes.end();
-		for(; it != end; ++it)
+		if(node.getName() == name)
 		{
-			SceneNode& node = *it;
-
-			if(node.getMarkedForDeletion())
-			{
-				// Delete node
-				unregisterNode(&node);
-				deleteInstance(SceneMemoryPool::getSingleton(), &node);
-				m_objectsMarkedForDeletionCount.fetchSub(1);
-				found = true;
-				break;
-			}
+			return &node;
 		}
-
-		ANKI_ASSERT(found && "Something is wrong with marked for deletion");
 	}
+
+	return nullptr;
 }
 
-Error SceneGraph::update(Second prevUpdateTime, Second crntTime)
+void SceneGraph::update(Second prevUpdateTime, Second crntTime)
 {
 	ANKI_ASSERT(m_mainCam);
 	ANKI_TRACE_SCOPED_EVENT(SceneUpdate);
@@ -195,24 +148,44 @@ Error SceneGraph::update(Second prevUpdateTime, Second crntTime)
 	// Reset the framepool
 	m_framePool.reset();
 
-	// Delete stuff
+	// Register new nodes
+	while(!m_nodesForRegistration.isEmpty())
 	{
-		ANKI_TRACE_SCOPED_EVENT(SceneRemoveMarkedForDeletion);
-		const Bool fullCleanup = m_objectsMarkedForDeletionCount.load() != 0;
-		m_events.deleteEventsMarkedForDeletion(fullCleanup);
-		deleteNodesMarkedForDeletion();
+		SceneNode* node = m_nodesForRegistration.popFront();
+
+		// Add to dict if it has a name
+		if(node->getName())
+		{
+			if(tryFindSceneNode(node->getName()))
+			{
+				ANKI_SCENE_LOGE("Node with the same name already exists. New node will not be searchable: %s", node->getName().cstr());
+			}
+			else
+			{
+				m_nodesDict.emplace(node->getName(), node);
+			}
+		}
+
+		// Add to list
+		m_nodes.pushBack(node);
+		++m_nodesCount;
 	}
 
-	// Update
+	// Update physics
+	PhysicsWorld::getSingleton().update(crntTime - prevUpdateTime);
+
+	// Before the update wake some threads with dummy work
+	for(U i = 0; i < 2; i++)
 	{
-		PhysicsWorld::getSingleton().update(crntTime - prevUpdateTime);
+		CoreThreadJobManager::getSingleton().dispatchTask([]([[maybe_unused]] U32 tid) {});
 	}
 
+	// Update events and scene nodes
+	UpdateSceneNodesCtx updateCtx;
 	{
 		ANKI_TRACE_SCOPED_EVENT(SceneNodesUpdate);
-		ANKI_CHECK(m_events.updateAllEvents(prevUpdateTime, crntTime));
+		m_events.updateAllEvents(prevUpdateTime, crntTime);
 
-		UpdateSceneNodesCtx updateCtx;
 		updateCtx.m_crntNode = m_nodes.getBegin();
 		updateCtx.m_prevUpdateTime = prevUpdateTime;
 		updateCtx.m_crntTime = crntTime;
@@ -220,43 +193,60 @@ Error SceneGraph::update(Second prevUpdateTime, Second crntTime)
 		for(U i = 0; i < CoreThreadJobManager::getSingleton().getThreadCount(); i++)
 		{
 			CoreThreadJobManager::getSingleton().dispatchTask([this, &updateCtx]([[maybe_unused]] U32 tid) {
-				if(updateNodes(updateCtx))
-				{
-					ANKI_SCENE_LOGF("Will not recover");
-				}
+				updateNodes(updateCtx);
 			});
 		}
 
 		CoreThreadJobManager::getSingleton().waitForAllTasksToFinish();
 	}
 
+	// Cleanup
+	for(SceneNode* node : updateCtx.m_nodesForDeletion)
+	{
+		// Remove from the graph
+		m_nodes.erase(node);
+		ANKI_ASSERT(m_nodesCount > 0);
+		--m_nodesCount;
+
+		if(m_mainCam != m_defaultMainCam && m_mainCam == node)
+		{
+			m_mainCam = m_defaultMainCam;
+		}
+
+		// Remove from dict
+		if(node->getName())
+		{
+			auto it = m_nodesDict.find(node->getName());
+			ANKI_ASSERT(it != m_nodesDict.getEnd());
+			if(*it == node)
+			{
+				m_nodesDict.erase(it);
+			}
+		}
+
+		deleteInstance(SceneMemoryPool::getSingleton(), node);
+	}
+	updateCtx.m_nodesForDeletion.destroy();
+
 #define ANKI_CAT_TYPE(arrayName, gpuSceneType, id, cvarName) GpuSceneArrays::arrayName::getSingleton().flush();
 #include <AnKi/Scene/GpuSceneArrays.def.h>
 
 	g_sceneUpdateTimeStatVar.set((HighRezTimer::getCurrentTime() - startUpdateTime) * 1000.0);
-	return Error::kNone;
 }
 
-Error SceneGraph::updateNode(Second prevTime, Second crntTime, SceneNode& node)
+void SceneGraph::updateNode(Second prevTime, Second crntTime, SceneNode& node)
 {
 	ANKI_TRACE_INC_COUNTER(SceneNodeUpdated, 1);
 
-	Error err = Error::kNone;
-
 	// Components update
 	SceneComponentUpdateInfo componentUpdateInfo(prevTime, crntTime);
 	componentUpdateInfo.m_framePool = &m_framePool;
 
 	Bool atLeastOneComponentUpdated = false;
 	node.iterateComponents([&](SceneComponent& comp) {
-		if(err)
-		{
-			return;
-		}
-
 		componentUpdateInfo.m_node = &node;
 		Bool updated = false;
-		err = comp.update(componentUpdateInfo, updated);
+		comp.update(componentUpdateInfo, updated);
 
 		if(updated)
 		{
@@ -267,15 +257,12 @@ Error SceneGraph::updateNode(Second prevTime, Second crntTime, SceneNode& node)
 	});
 
 	// Update children
-	if(!err)
-	{
-		err = node.visitChildrenMaxDepth(0, [&](SceneNode& child) -> Error {
-			return updateNode(prevTime, crntTime, child);
-		});
-	}
+	node.visitChildrenMaxDepth(0, [&](SceneNode& child) {
+		updateNode(prevTime, crntTime, child);
+		return true;
+	});
 
 	// Frame update
-	if(!err)
 	{
 		if(atLeastOneComponentUpdated)
 		{
@@ -286,21 +273,18 @@ Error SceneGraph::updateNode(Second prevTime, Second crntTime, SceneNode& node)
 			// No components or nothing updated, don't change the timestamp
 		}
 
-		err = node.frameUpdate(prevTime, crntTime);
+		node.frameUpdate(prevTime, crntTime);
 	}
-
-	return err;
 }
 
-Error SceneGraph::updateNodes(UpdateSceneNodesCtx& ctx)
+void SceneGraph::updateNodes(UpdateSceneNodesCtx& ctx)
 {
 	ANKI_TRACE_SCOPED_EVENT(SceneNodeUpdate);
 
 	IntrusiveList<SceneNode>::ConstIterator end = m_nodes.getEnd();
 
 	Bool quit = false;
-	Error err = Error::kNone;
-	while(!quit && !err)
+	while(!quit)
 	{
 		// Fetch a batch of scene nodes that don't have parent
 		Array<SceneNode*, kUpdateNodeBatchSize> batch;
@@ -323,23 +307,30 @@ Error SceneGraph::updateNodes(UpdateSceneNodesCtx& ctx)
 				}
 
 				SceneNode& node = *ctx.m_crntNode;
-				if(node.getParent() == nullptr)
+
+				if(node.isMarkedForDeletion())
+				{
+					ctx.m_nodesForDeletion.emplaceBack(&node);
+				}
+				else if(node.getParent() == nullptr)
 				{
 					batch[batchSize++] = &node;
 				}
+				else
+				{
+					// Ignore
+				}
 
 				++ctx.m_crntNode;
 			}
 		}
 
 		// Process nodes
-		for(U i = 0; i < batchSize && !err; ++i)
+		for(U i = 0; i < batchSize; ++i)
 		{
-			err = updateNode(ctx.m_prevUpdateTime, ctx.m_crntTime, *batch[i]);
+			updateNode(ctx.m_prevUpdateTime, ctx.m_crntTime, *batch[i]);
 		}
 	}
-
-	return err;
 }
 
 LightComponent* SceneGraph::getDirectionalLight() const

+ 20 - 92
AnKi/Scene/SceneGraph.h

@@ -89,11 +89,6 @@ public:
 	void setActiveCameraNode(SceneNode* cam)
 	{
 		m_mainCam = cam;
-		m_activeCameraChangeTimestamp = GlobalFrameIndex::getSingleton().m_value;
-	}
-	Timestamp getActiveCameraNodeChangeTimestamp() const
-	{
-		return m_activeCameraChangeTimestamp;
 	}
 
 	U32 getSceneNodesCount() const
@@ -110,44 +105,37 @@ public:
 		return m_events;
 	}
 
-	Error update(Second prevUpdateTime, Second crntTime);
+	void update(Second prevUpdateTime, Second crntTime);
 
-	SceneNode& findSceneNode(const CString& name);
+	/// @note Thread-safe against itself. Can be called by SceneNode::update
 	SceneNode* tryFindSceneNode(const CString& name);
 
+	/// @note Thread-safe against itself. Can be called by SceneNode::update
+	SceneNode& findSceneNode(const CString& name);
+
 	/// Iterate the scene nodes using a lambda
 	template<typename Func>
-	Error iterateSceneNodes(Func func)
+	void iterateSceneNodes(Func func)
 	{
 		for(SceneNode& psn : m_nodes)
 		{
-			Error err = func(psn);
-			if(err)
+			const Bool continue_ = func(psn);
+			if(!continue_)
 			{
-				return err;
+				break;
 			}
 		}
-
-		return Error::kNone;
 	}
 
-	/// Iterate a range of scene nodes using a lambda
-	template<typename Func>
-	Error iterateSceneNodes(PtrSize begin, PtrSize end, Func func);
-
 	/// Create a new SceneNode
-	template<typename Node, typename... Args>
-	Error newSceneNode(const CString& name, Node*& node, Args&&... args);
-
-	/// Delete a scene node. It actualy marks it for deletion
-	void deleteSceneNode(SceneNode* node)
+	/// @note Thread-safe against itself. Can be called by SceneNode::update
+	template<typename TNode>
+	TNode* newSceneNode(CString name)
 	{
-		node->setMarkedForDeletion();
-	}
-
-	void increaseObjectsMarkedForDeletion()
-	{
-		m_objectsMarkedForDeletionCount.fetchAdd(1);
+		TNode* node = newInstance<TNode>(SceneMemoryPool::getSingleton(), name);
+		LockGuard lock(m_nodesForRegistrationMtx);
+		m_nodesForRegistration.pushBack(node);
+		return node;
 	}
 
 	const Vec3& getSceneMin() const
@@ -248,7 +236,6 @@ private:
 	GrHashMap<CString, SceneNode*> m_nodesDict;
 
 	SceneNode* m_mainCam = nullptr;
-	Timestamp m_activeCameraChangeTimestamp = 0;
 	SceneNode* m_defaultMainCam = nullptr;
 
 	EventManager m_events;
@@ -257,7 +244,8 @@ private:
 	Vec3 m_sceneMax = Vec3(kMinF32);
 	mutable SpinLock m_sceneBoundsMtx;
 
-	Atomic<U32> m_objectsMarkedForDeletionCount = {0};
+	IntrusiveList<SceneNode> m_nodesForRegistration;
+	SpinLock m_nodesForRegistrationMtx;
 
 	Atomic<U32> m_nodesUuid = {1};
 
@@ -270,69 +258,9 @@ private:
 
 	~SceneGraph();
 
-	/// Put a node in the appropriate containers
-	Error registerNode(SceneNode* node);
-	void unregisterNode(SceneNode* node);
-
-	/// Delete the nodes that are marked for deletion
-	void deleteNodesMarkedForDeletion();
-
-	Error updateNodes(UpdateSceneNodesCtx& ctx);
-	Error updateNode(Second prevTime, Second crntTime, SceneNode& node);
+	void updateNodes(UpdateSceneNodesCtx& ctx);
+	void updateNode(Second prevTime, Second crntTime, SceneNode& node);
 };
-
-template<typename Node, typename... Args>
-inline Error SceneGraph::newSceneNode(const CString& name, Node*& node, Args&&... args)
-{
-	Error err = Error::kNone;
-
-	node = newInstance<Node>(SceneMemoryPool::getSingleton(), name);
-	if(node)
-	{
-		err = node->init(std::forward<Args>(args)...);
-	}
-	else
-	{
-		err = Error::kOutOfMemory;
-	}
-
-	if(!err)
-	{
-		err = registerNode(node);
-	}
-
-	if(err)
-	{
-		ANKI_SCENE_LOGE("Failed to create scene node: %s", (name.isEmpty()) ? "unnamed" : &name[0]);
-
-		if(node)
-		{
-			deleteInstance(SceneMemoryPool::getSingleton(), node);
-			node = nullptr;
-		}
-	}
-
-	return err;
-}
-
-template<typename Func>
-Error SceneGraph::iterateSceneNodes(PtrSize begin, PtrSize end, Func func)
-{
-	ANKI_ASSERT(begin < m_nodesCount && end <= m_nodesCount);
-	auto it = m_nodes.getBegin() + begin;
-
-	PtrSize count = end - begin;
-	Error err = Error::kNone;
-	while(count-- != 0 && !err)
-	{
-		ANKI_ASSERT(it != m_nodes.getEnd());
-		err = func(*it);
-
-		++it;
-	}
-
-	return Error::kNone;
-}
 /// @}
 
 } // end namespace anki

+ 6 - 13
AnKi/Scene/SceneNode.cpp

@@ -72,18 +72,11 @@ SceneNode::~SceneNode()
 	}
 }
 
-void SceneNode::setMarkedForDeletion()
+void SceneNode::markForDeletion()
 {
-	// Mark for deletion only when it's not already marked because we don't want to increase the counter again
-	if(!getMarkedForDeletion())
-	{
-		m_markedForDeletion = true;
-		SceneGraph::getSingleton().increaseObjectsMarkedForDeletion();
-	}
-
-	[[maybe_unused]] const Error err = visitChildren([](SceneNode& obj) -> Error {
-		obj.setMarkedForDeletion();
-		return Error::kNone;
+	visitThisAndChildren([](SceneNode& obj) {
+		obj.m_markedForDeletion = true;
+		return true;
 	});
 }
 
@@ -147,12 +140,12 @@ Bool SceneNode::updateTransform()
 		}
 
 		// Make children dirty as well. Don't walk the whole tree because you will re-walk it later
-		[[maybe_unused]] const Error err = visitChildrenMaxDepth(1, [](SceneNode& childNode) -> Error {
+		visitChildrenMaxDepth(1, [](SceneNode& childNode) {
 			if(!childNode.m_ignoreParentNodeTransform)
 			{
 				childNode.m_localTransformDirty = true;
 			}
-			return Error::kNone;
+			return true;
 		});
 	}
 

+ 17 - 25
AnKi/Scene/SceneNode.h

@@ -26,19 +26,11 @@ public:
 	using Base = SceneHierarchy<SceneNode>;
 
 	/// The one and only constructor.
-	/// @param scene The owner scene.
 	/// @param name The unique name of the node. If it's empty the the node is not searchable.
 	SceneNode(CString name);
 
-	/// Unregister node
 	virtual ~SceneNode();
 
-	/// A dummy init for those scene nodes that don't need it.
-	Error init()
-	{
-		return Error::kNone;
-	}
-
 	/// Return the name. It may be empty for nodes that we don't want to track.
 	CString getName() const
 	{
@@ -50,12 +42,12 @@ public:
 		return m_uuid;
 	}
 
-	Bool getMarkedForDeletion() const
+	Bool isMarkedForDeletion() const
 	{
 		return m_markedForDeletion;
 	}
 
-	void setMarkedForDeletion();
+	void markForDeletion();
 
 	Timestamp getComponentMaxTimestamp() const
 	{
@@ -81,9 +73,8 @@ public:
 	/// This is called by the scenegraph every frame after all component updates. By default it does nothing.
 	/// @param prevUpdateTime Timestamp of the previous update
 	/// @param crntTime Timestamp of this update
-	virtual Error frameUpdate([[maybe_unused]] Second prevUpdateTime, [[maybe_unused]] Second crntTime)
+	virtual void frameUpdate([[maybe_unused]] Second prevUpdateTime, [[maybe_unused]] Second crntTime)
 	{
-		return Error::kNone;
 	}
 
 	/// Iterate all components.
@@ -255,14 +246,25 @@ public:
 		return count;
 	}
 
+	/// Create and append a component to the components container. The SceneNode has the ownership.
+	template<typename TComponent>
+	TComponent* newComponent();
+
+	template<typename TComponent>
+	Bool hasComponent() const
+	{
+		return !!((1u << SceneComponentTypeMask(TComponent::kClassType)) & m_componentTypeMask);
+	}
+
+	/// @name Movement
+	/// @{
+
 	/// Ignore parent nodes's transform.
 	void setIgnoreParentTransform(Bool ignore)
 	{
 		m_ignoreParentNodeTransform = ignore;
 	}
 
-	/// @name Mess with the local transform
-	/// @{
 	const Transform& getLocalTransform() const
 	{
 		return m_ltrf;
@@ -373,7 +375,6 @@ public:
 		m_ltrf = m_ltrf.lookAt(point, Vec4::yAxis());
 		m_localTransformDirty = true;
 	}
-	/// @}
 
 	Bool movedThisFrame() const
 	{
@@ -381,16 +382,7 @@ public:
 	}
 
 	ANKI_INTERNAL Bool updateTransform();
-
-	/// Create and append a component to the components container. The SceneNode has the ownership.
-	template<typename TComponent>
-	TComponent* newComponent();
-
-	template<typename TComponent>
-	Bool hasComponent() const
-	{
-		return !!((1u << SceneComponentTypeMask(TComponent::kClassType)) & m_componentTypeMask);
-	}
+	/// @}
 
 private:
 	SceneString m_name; ///< A unique name.

+ 2 - 6
AnKi/Scene/StatsUiNode.cpp

@@ -80,16 +80,12 @@ StatsUiNode::StatsUiNode(CString name)
 	{
 		m_averageValues.resize(StatsSet::getSingleton().getCounterCount());
 	}
-}
 
-StatsUiNode::~StatsUiNode()
-{
+	ANKI_CHECKF(UiManager::getSingleton().newInstance(m_font, "EngineAssets/UbuntuMonoRegular.ttf", Array<U32, 1>{24}));
 }
 
-Error StatsUiNode::init()
+StatsUiNode::~StatsUiNode()
 {
-	ANKI_CHECK(UiManager::getSingleton().newInstance(m_font, "EngineAssets/UbuntuMonoRegular.ttf", Array<U32, 1>{24}));
-	return Error::kNone;
 }
 
 void StatsUiNode::draw(CanvasPtr& canvas)

+ 0 - 2
AnKi/Scene/StatsUiNode.h

@@ -21,8 +21,6 @@ public:
 
 	~StatsUiNode();
 
-	Error init();
-
 	void setFpsOnly(Bool fpsOnly)
 	{
 		m_fpsOnly = fpsOnly;

+ 42 - 61
AnKi/Script/Scene.cpp

@@ -14,33 +14,13 @@ namespace anki {
 template<typename T, typename... TArgs>
 static T* newSceneNode(SceneGraph* scene, CString name, TArgs... args)
 {
-	T* ptr;
-	Error err = scene->template newSceneNode<T>(name, ptr, std::forward<TArgs>(args)...);
-
-	if(!err)
-	{
-		return ptr;
-	}
-	else
-	{
-		return nullptr;
-	}
+	return scene->template newSceneNode<T>(name, std::forward<TArgs>(args)...);
 }
 
 template<typename T, typename... TArgs>
 static T* newEvent(EventManager* eventManager, TArgs... args)
 {
-	T* ptr;
-	Error err = eventManager->template newEvent<T>(ptr, std::forward<TArgs>(args)...);
-
-	if(!err)
-	{
-		return ptr;
-	}
-	else
-	{
-		return nullptr;
-	}
+	return eventManager->template newEvent<T>(std::forward<TArgs>(args)...);
 }
 
 static SceneGraph* getSceneGraph(lua_State* l)
@@ -60,7 +40,7 @@ static EventManager* getEventManager(lua_State* l)
 
 using WeakArraySceneNodePtr = WeakArray<SceneNode*>;
 
-LuaUserDataTypeInfo luaUserDataTypeInfoLightComponentType = {9161731750228934784, "LightComponentType", 0, nullptr, nullptr};
+LuaUserDataTypeInfo luaUserDataTypeInfoLightComponentType = {-422043100225580406, "LightComponentType", 0, nullptr, nullptr};
 
 template<>
 const LuaUserDataTypeInfo& LuaUserData::getDataTypeInfoFor<LightComponentType>()
@@ -95,7 +75,7 @@ static inline void wrapLightComponentType(lua_State* l)
 	lua_settop(l, 0);
 }
 
-LuaUserDataTypeInfo luaUserDataTypeInfoBodyComponentCollisionShapeType = {-7069966657913140379, "BodyComponentCollisionShapeType", 0, nullptr,
+LuaUserDataTypeInfo luaUserDataTypeInfoBodyComponentCollisionShapeType = {3195632967110394111, "BodyComponentCollisionShapeType", 0, nullptr,
 																		  nullptr};
 
 template<>
@@ -140,7 +120,7 @@ static inline void wrapBodyComponentCollisionShapeType(lua_State* l)
 }
 
 LuaUserDataTypeInfo luaUserDataTypeInfoWeakArraySceneNodePtr = {
-	1283006476289129714, "WeakArraySceneNodePtr", LuaUserData::computeSizeForGarbageCollected<WeakArraySceneNodePtr>(), nullptr, nullptr};
+	-7373253839206137444, "WeakArraySceneNodePtr", LuaUserData::computeSizeForGarbageCollected<WeakArraySceneNodePtr>(), nullptr, nullptr};
 
 template<>
 const LuaUserDataTypeInfo& LuaUserData::getDataTypeInfoFor<WeakArraySceneNodePtr>()
@@ -258,7 +238,7 @@ static inline void wrapWeakArraySceneNodePtr(lua_State* l)
 	lua_settop(l, 0);
 }
 
-LuaUserDataTypeInfo luaUserDataTypeInfoLightComponent = {8699100876815061903, "LightComponent",
+LuaUserDataTypeInfo luaUserDataTypeInfoLightComponent = {-9073978231010587683, "LightComponent",
 														 LuaUserData::computeSizeForGarbageCollected<LightComponent>(), nullptr, nullptr};
 
 template<>
@@ -869,7 +849,7 @@ static inline void wrapLightComponent(lua_State* l)
 	lua_settop(l, 0);
 }
 
-LuaUserDataTypeInfo luaUserDataTypeInfoDecalComponent = {-6169088492441751862, "DecalComponent",
+LuaUserDataTypeInfo luaUserDataTypeInfoDecalComponent = {-2335717325099644994, "DecalComponent",
 														 LuaUserData::computeSizeForGarbageCollected<DecalComponent>(), nullptr, nullptr};
 
 template<>
@@ -991,7 +971,7 @@ static inline void wrapDecalComponent(lua_State* l)
 	lua_settop(l, 0);
 }
 
-LuaUserDataTypeInfo luaUserDataTypeInfoLensFlareComponent = {4995731539995318563, "LensFlareComponent",
+LuaUserDataTypeInfo luaUserDataTypeInfoLensFlareComponent = {-8264422868601051319, "LensFlareComponent",
 															 LuaUserData::computeSizeForGarbageCollected<LensFlareComponent>(), nullptr, nullptr};
 
 template<>
@@ -1154,7 +1134,7 @@ static inline void wrapLensFlareComponent(lua_State* l)
 	lua_settop(l, 0);
 }
 
-LuaUserDataTypeInfo luaUserDataTypeInfoBodyComponent = {8099732141277801003, "BodyComponent",
+LuaUserDataTypeInfo luaUserDataTypeInfoBodyComponent = {-3002233952553439959, "BodyComponent",
 														LuaUserData::computeSizeForGarbageCollected<BodyComponent>(), nullptr, nullptr};
 
 template<>
@@ -1464,7 +1444,7 @@ static inline void wrapBodyComponent(lua_State* l)
 	lua_settop(l, 0);
 }
 
-LuaUserDataTypeInfo luaUserDataTypeInfoTriggerComponent = {6598736644455803043, "TriggerComponent",
+LuaUserDataTypeInfo luaUserDataTypeInfoTriggerComponent = {7726514997328849856, "TriggerComponent",
 														   LuaUserData::computeSizeForGarbageCollected<TriggerComponent>(), nullptr, nullptr};
 
 template<>
@@ -1578,7 +1558,7 @@ static inline void wrapTriggerComponent(lua_State* l)
 	lua_settop(l, 0);
 }
 
-LuaUserDataTypeInfo luaUserDataTypeInfoFogDensityComponent = {1558360313688010235, "FogDensityComponent",
+LuaUserDataTypeInfo luaUserDataTypeInfoFogDensityComponent = {-6644828763714815062, "FogDensityComponent",
 															  LuaUserData::computeSizeForGarbageCollected<FogDensityComponent>(), nullptr, nullptr};
 
 template<>
@@ -1684,7 +1664,7 @@ static inline void wrapFogDensityComponent(lua_State* l)
 	lua_settop(l, 0);
 }
 
-LuaUserDataTypeInfo luaUserDataTypeInfoCameraComponent = {7240058762383993623, "CameraComponent",
+LuaUserDataTypeInfo luaUserDataTypeInfoCameraComponent = {2204986172338020118, "CameraComponent",
 														  LuaUserData::computeSizeForGarbageCollected<CameraComponent>(), nullptr, nullptr};
 
 template<>
@@ -1766,7 +1746,7 @@ static inline void wrapCameraComponent(lua_State* l)
 }
 
 LuaUserDataTypeInfo luaUserDataTypeInfoGlobalIlluminationProbeComponent = {
-	7786098780583308602, "GlobalIlluminationProbeComponent", LuaUserData::computeSizeForGarbageCollected<GlobalIlluminationProbeComponent>(), nullptr,
+	1351895094142149124, "GlobalIlluminationProbeComponent", LuaUserData::computeSizeForGarbageCollected<GlobalIlluminationProbeComponent>(), nullptr,
 	nullptr};
 
 template<>
@@ -1963,7 +1943,7 @@ static inline void wrapGlobalIlluminationProbeComponent(lua_State* l)
 }
 
 LuaUserDataTypeInfo luaUserDataTypeInfoReflectionProbeComponent = {
-	3728015256035918590, "ReflectionProbeComponent", LuaUserData::computeSizeForGarbageCollected<ReflectionProbeComponent>(), nullptr, nullptr};
+	5326043231317868834, "ReflectionProbeComponent", LuaUserData::computeSizeForGarbageCollected<ReflectionProbeComponent>(), nullptr, nullptr};
 
 template<>
 const LuaUserDataTypeInfo& LuaUserData::getDataTypeInfoFor<ReflectionProbeComponent>()
@@ -1979,7 +1959,7 @@ static inline void wrapReflectionProbeComponent(lua_State* l)
 }
 
 LuaUserDataTypeInfo luaUserDataTypeInfoParticleEmitterComponent = {
-	8545543429504863184, "ParticleEmitterComponent", LuaUserData::computeSizeForGarbageCollected<ParticleEmitterComponent>(), nullptr, nullptr};
+	3473927078792861447, "ParticleEmitterComponent", LuaUserData::computeSizeForGarbageCollected<ParticleEmitterComponent>(), nullptr, nullptr};
 
 template<>
 const LuaUserDataTypeInfo& LuaUserData::getDataTypeInfoFor<ParticleEmitterComponent>()
@@ -2041,7 +2021,7 @@ static inline void wrapParticleEmitterComponent(lua_State* l)
 	lua_settop(l, 0);
 }
 
-LuaUserDataTypeInfo luaUserDataTypeInfoModelComponent = {2794397331070110609, "ModelComponent",
+LuaUserDataTypeInfo luaUserDataTypeInfoModelComponent = {-4163551330392490848, "ModelComponent",
 														 LuaUserData::computeSizeForGarbageCollected<ModelComponent>(), nullptr, nullptr};
 
 template<>
@@ -2104,7 +2084,7 @@ static inline void wrapModelComponent(lua_State* l)
 	lua_settop(l, 0);
 }
 
-LuaUserDataTypeInfo luaUserDataTypeInfoSkinComponent = {6453635927704179480, "SkinComponent",
+LuaUserDataTypeInfo luaUserDataTypeInfoSkinComponent = {7633077660147082214, "SkinComponent",
 														LuaUserData::computeSizeForGarbageCollected<SkinComponent>(), nullptr, nullptr};
 
 template<>
@@ -2167,7 +2147,7 @@ static inline void wrapSkinComponent(lua_State* l)
 	lua_settop(l, 0);
 }
 
-LuaUserDataTypeInfo luaUserDataTypeInfoSkyboxComponent = {9078636318267408065, "SkyboxComponent",
+LuaUserDataTypeInfo luaUserDataTypeInfoSkyboxComponent = {5640611622526745985, "SkyboxComponent",
 														  LuaUserData::computeSizeForGarbageCollected<SkyboxComponent>(), nullptr, nullptr};
 
 template<>
@@ -2658,7 +2638,7 @@ static inline void wrapSkyboxComponent(lua_State* l)
 	lua_settop(l, 0);
 }
 
-LuaUserDataTypeInfo luaUserDataTypeInfoSceneNode = {8924349910587267929, "SceneNode", LuaUserData::computeSizeForGarbageCollected<SceneNode>(),
+LuaUserDataTypeInfo luaUserDataTypeInfoSceneNode = {8031015106478875500, "SceneNode", LuaUserData::computeSizeForGarbageCollected<SceneNode>(),
 													nullptr, nullptr};
 
 template<>
@@ -2758,8 +2738,8 @@ static int wrapSceneNodeaddChild(lua_State* l)
 	return 0;
 }
 
-/// Pre-wrap method SceneNode::setMarkedForDeletion.
-static inline int pwrapSceneNodesetMarkedForDeletion(lua_State* l)
+/// Pre-wrap method SceneNode::markForDeletion.
+static inline int pwrapSceneNodemarkForDeletion(lua_State* l)
 {
 	[[maybe_unused]] LuaUserData* ud;
 	[[maybe_unused]] void* voidp;
@@ -2779,15 +2759,15 @@ static inline int pwrapSceneNodesetMarkedForDeletion(lua_State* l)
 	SceneNode* self = ud->getData<SceneNode>();
 
 	// Call the method
-	self->setMarkedForDeletion();
+	self->markForDeletion();
 
 	return 0;
 }
 
-/// Wrap method SceneNode::setMarkedForDeletion.
-static int wrapSceneNodesetMarkedForDeletion(lua_State* l)
+/// Wrap method SceneNode::markForDeletion.
+static int wrapSceneNodemarkForDeletion(lua_State* l)
 {
-	int res = pwrapSceneNodesetMarkedForDeletion(l);
+	int res = pwrapSceneNodemarkForDeletion(l);
 	if(res >= 0)
 	{
 		return res;
@@ -4463,7 +4443,7 @@ static inline void wrapSceneNode(lua_State* l)
 	LuaBinder::createClass(l, &luaUserDataTypeInfoSceneNode);
 	LuaBinder::pushLuaCFuncMethod(l, "getName", wrapSceneNodegetName);
 	LuaBinder::pushLuaCFuncMethod(l, "addChild", wrapSceneNodeaddChild);
-	LuaBinder::pushLuaCFuncMethod(l, "setMarkedForDeletion", wrapSceneNodesetMarkedForDeletion);
+	LuaBinder::pushLuaCFuncMethod(l, "markForDeletion", wrapSceneNodemarkForDeletion);
 	LuaBinder::pushLuaCFuncMethod(l, "setLocalOrigin", wrapSceneNodesetLocalOrigin);
 	LuaBinder::pushLuaCFuncMethod(l, "getLocalOrigin", wrapSceneNodegetLocalOrigin);
 	LuaBinder::pushLuaCFuncMethod(l, "setLocalRotation", wrapSceneNodesetLocalRotation);
@@ -4501,7 +4481,7 @@ static inline void wrapSceneNode(lua_State* l)
 	lua_settop(l, 0);
 }
 
-LuaUserDataTypeInfo luaUserDataTypeInfoSceneGraph = {-7992083750500404459, "SceneGraph", LuaUserData::computeSizeForGarbageCollected<SceneGraph>(),
+LuaUserDataTypeInfo luaUserDataTypeInfoSceneGraph = {-9169673447479816112, "SceneGraph", LuaUserData::computeSizeForGarbageCollected<SceneGraph>(),
 													 nullptr, nullptr};
 
 template<>
@@ -4687,7 +4667,8 @@ static inline void wrapSceneGraph(lua_State* l)
 	lua_settop(l, 0);
 }
 
-LuaUserDataTypeInfo luaUserDataTypeInfoEvent = {4634393106419300120, "Event", LuaUserData::computeSizeForGarbageCollected<Event>(), nullptr, nullptr};
+LuaUserDataTypeInfo luaUserDataTypeInfoEvent = {-4553391550055383207, "Event", LuaUserData::computeSizeForGarbageCollected<Event>(), nullptr,
+												nullptr};
 
 template<>
 const LuaUserDataTypeInfo& LuaUserData::getDataTypeInfoFor<Event>()
@@ -4751,7 +4732,7 @@ static inline void wrapEvent(lua_State* l)
 	lua_settop(l, 0);
 }
 
-LuaUserDataTypeInfo luaUserDataTypeInfoLightEvent = {5654906403086349064, "LightEvent", LuaUserData::computeSizeForGarbageCollected<LightEvent>(),
+LuaUserDataTypeInfo luaUserDataTypeInfoLightEvent = {-7196089091128304157, "LightEvent", LuaUserData::computeSizeForGarbageCollected<LightEvent>(),
 													 nullptr, nullptr};
 
 template<>
@@ -4870,7 +4851,7 @@ static inline void wrapLightEvent(lua_State* l)
 	lua_settop(l, 0);
 }
 
-LuaUserDataTypeInfo luaUserDataTypeInfoScriptEvent = {2884793170545449902, "ScriptEvent", LuaUserData::computeSizeForGarbageCollected<ScriptEvent>(),
+LuaUserDataTypeInfo luaUserDataTypeInfoScriptEvent = {3704782575420779455, "ScriptEvent", LuaUserData::computeSizeForGarbageCollected<ScriptEvent>(),
 													  nullptr, nullptr};
 
 template<>
@@ -4886,7 +4867,7 @@ static inline void wrapScriptEvent(lua_State* l)
 	lua_settop(l, 0);
 }
 
-LuaUserDataTypeInfo luaUserDataTypeInfoJitterMoveEvent = {-7337813253077277960, "JitterMoveEvent",
+LuaUserDataTypeInfo luaUserDataTypeInfoJitterMoveEvent = {-4564135298097450632, "JitterMoveEvent",
 														  LuaUserData::computeSizeForGarbageCollected<JitterMoveEvent>(), nullptr, nullptr};
 
 template<>
@@ -4916,23 +4897,23 @@ static inline int pwrapJitterMoveEventsetPositionLimits(lua_State* l)
 	JitterMoveEvent* self = ud->getData<JitterMoveEvent>();
 
 	// Pop arguments
-	extern LuaUserDataTypeInfo luaUserDataTypeInfoVec4;
-	if(LuaBinder::checkUserData(l, 2, luaUserDataTypeInfoVec4, ud)) [[unlikely]]
+	extern LuaUserDataTypeInfo luaUserDataTypeInfoVec3;
+	if(LuaBinder::checkUserData(l, 2, luaUserDataTypeInfoVec3, ud)) [[unlikely]]
 	{
 		return -1;
 	}
 
-	Vec4* iarg0 = ud->getData<Vec4>();
-	const Vec4& arg0(*iarg0);
+	Vec3* iarg0 = ud->getData<Vec3>();
+	Vec3 arg0(*iarg0);
 
-	extern LuaUserDataTypeInfo luaUserDataTypeInfoVec4;
-	if(LuaBinder::checkUserData(l, 3, luaUserDataTypeInfoVec4, ud)) [[unlikely]]
+	extern LuaUserDataTypeInfo luaUserDataTypeInfoVec3;
+	if(LuaBinder::checkUserData(l, 3, luaUserDataTypeInfoVec3, ud)) [[unlikely]]
 	{
 		return -1;
 	}
 
-	Vec4* iarg1 = ud->getData<Vec4>();
-	const Vec4& arg1(*iarg1);
+	Vec3* iarg1 = ud->getData<Vec3>();
+	Vec3 arg1(*iarg1);
 
 	// Call the method
 	self->setPositionLimits(arg0, arg1);
@@ -4961,7 +4942,7 @@ static inline void wrapJitterMoveEvent(lua_State* l)
 	lua_settop(l, 0);
 }
 
-LuaUserDataTypeInfo luaUserDataTypeInfoAnimationEvent = {-6138247333310671375, "AnimationEvent",
+LuaUserDataTypeInfo luaUserDataTypeInfoAnimationEvent = {9125743954504628346, "AnimationEvent",
 														 LuaUserData::computeSizeForGarbageCollected<AnimationEvent>(), nullptr, nullptr};
 
 template<>
@@ -4977,7 +4958,7 @@ static inline void wrapAnimationEvent(lua_State* l)
 	lua_settop(l, 0);
 }
 
-LuaUserDataTypeInfo luaUserDataTypeInfoEventManager = {9107446086384125482, "EventManager",
+LuaUserDataTypeInfo luaUserDataTypeInfoEventManager = {5839939442792142754, "EventManager",
 													   LuaUserData::computeSizeForGarbageCollected<EventManager>(), nullptr, nullptr};
 
 template<>

+ 5 - 25
AnKi/Script/Scene.xml

@@ -15,33 +15,13 @@ namespace anki {
 template<typename T, typename... TArgs>
 static T* newSceneNode(SceneGraph* scene, CString name, TArgs... args)
 {
-	T* ptr;
-	Error err = scene->template newSceneNode<T>(name, ptr, std::forward<TArgs>(args)...);
-
-	if(!err)
-	{
-		return ptr;
-	}
-	else
-	{
-		return nullptr;
-	}
+	return scene->template newSceneNode<T>(name, std::forward<TArgs>(args)...);
 }
 
 template<typename T, typename... TArgs>
 static T* newEvent(EventManager* eventManager, TArgs... args)
 {
-	T* ptr;
-	Error err = eventManager->template newEvent<T>(ptr, std::forward<TArgs>(args)...);
-
-	if(!err)
-	{
-		return ptr;
-	}
-	else
-	{
-		return nullptr;
-	}
+	return eventManager->template newEvent<T>(std::forward<TArgs>(args)...);
 }
 
 static SceneGraph* getSceneGraph(lua_State* l)
@@ -376,7 +356,7 @@ using WeakArraySceneNodePtr = WeakArray<SceneNode*>;
 						<arg>SceneNode*</arg>
 					</args>
 				</method>
-				<method name="setMarkedForDeletion"></method>
+				<method name="markForDeletion"></method>
 
 				<method name="setLocalOrigin">
 					<args>
@@ -544,8 +524,8 @@ using WeakArraySceneNodePtr = WeakArray<SceneNode*>;
 			<methods>
 				<method name="setPositionLimits">
 					<args>
-						<arg>const Vec4&amp;</arg>
-						<arg>const Vec4&amp;</arg>
+						<arg>Vec3</arg>
+						<arg>Vec3</arg>
 					</args>
 				</method>
 			</methods>

+ 4 - 4
AnKi/Util/Hierarchy.h

@@ -86,19 +86,19 @@ public:
 
 	/// Visit the children and the children's children. Use it with lambda
 	template<typename TVisitorFunc>
-	Error visitChildren(TVisitorFunc vis);
+	Bool visitChildren(TVisitorFunc vis);
 
 	/// Visit this object and move to the children. Use it with lambda
 	template<typename TVisitorFunc>
-	Error visitThisAndChildren(TVisitorFunc vis);
+	void visitThisAndChildren(TVisitorFunc vis);
 
 	/// Visit the whole tree. Use it with lambda
 	template<typename TVisitorFunc>
-	Error visitTree(TVisitorFunc vis);
+	void visitTree(TVisitorFunc vis);
 
 	/// Visit the children and limit the depth. Use it with lambda.
 	template<typename TVisitorFunc>
-	Error visitChildrenMaxDepth(I maxDepth, TVisitorFunc vis);
+	Bool visitChildrenMaxDepth(I maxDepth, TVisitorFunc vis);
 
 private:
 	Value* m_parent = nullptr; ///< May be nullptr

+ 26 - 28
AnKi/Util/Hierarchy.inl.h

@@ -54,41 +54,39 @@ void Hierarchy<T, TMemoryPool>::removeChild(Value* child)
 }
 
 template<typename T, typename TMemoryPool>
-template<typename VisitorFunc>
-Error Hierarchy<T, TMemoryPool>::visitChildren(VisitorFunc vis)
+template<typename TVisitorFunc>
+Bool Hierarchy<T, TMemoryPool>::visitChildren(TVisitorFunc vis)
 {
-	Error err = Error::kNone;
-	typename Container::Iterator it = m_children.getBegin();
-	for(; it != m_children.getEnd() && !err; it++)
+	auto it = m_children.getBegin();
+	Bool continue_ = true;
+	for(; it != m_children.getEnd() && continue_; it++)
 	{
-		err = vis(*(*it));
+		continue_ = vis(*(*it));
 
-		if(!err)
+		if(continue_)
 		{
-			err = (*it)->visitChildren(vis);
+			continue_ = (*it)->visitChildren(vis);
 		}
 	}
 
-	return err;
+	return continue_;
 }
 
 template<typename T, typename TMemoryPool>
-template<typename VisitorFunc>
-Error Hierarchy<T, TMemoryPool>::visitThisAndChildren(VisitorFunc vis)
+template<typename TVisitorFunc>
+void Hierarchy<T, TMemoryPool>::visitThisAndChildren(TVisitorFunc vis)
 {
-	Error err = vis(*getSelf());
+	const Bool continue_ = vis(*getSelf());
 
-	if(!err)
+	if(continue_)
 	{
-		err = visitChildren(vis);
+		visitChildren(vis);
 	}
-
-	return err;
 }
 
 template<typename T, typename TMemoryPool>
-template<typename VisitorFunc>
-Error Hierarchy<T, TMemoryPool>::visitTree(VisitorFunc vis)
+template<typename TVisitorFunc>
+void Hierarchy<T, TMemoryPool>::visitTree(TVisitorFunc vis)
 {
 	// Move to root
 	Value* root = getSelf();
@@ -97,29 +95,29 @@ Error Hierarchy<T, TMemoryPool>::visitTree(VisitorFunc vis)
 		root = root->m_parent;
 	}
 
-	return root->visitThisAndChildren(vis);
+	root->visitThisAndChildren(vis);
 }
 
 template<typename T, typename TMemoryPool>
-template<typename VisitorFunc>
-Error Hierarchy<T, TMemoryPool>::visitChildrenMaxDepth(I maxDepth, VisitorFunc vis)
+template<typename TVisitorFunc>
+Bool Hierarchy<T, TMemoryPool>::visitChildrenMaxDepth(I maxDepth, TVisitorFunc vis)
 {
 	ANKI_ASSERT(maxDepth >= 0);
-	Error err = Error::kNone;
 	--maxDepth;
 
-	typename Container::Iterator it = m_children.getBegin();
-	for(; it != m_children.getEnd() && !err; ++it)
+	Bool continue_ = true;
+	auto it = m_children.getBegin();
+	for(; it != m_children.getEnd() && continue_; ++it)
 	{
-		err = vis(*(*it));
+		continue_ = vis(*(*it));
 
-		if(!err && maxDepth >= 0)
+		if(continue_ && maxDepth >= 0)
 		{
-			err = (*it)->visitChildrenMaxDepth(maxDepth, vis);
+			continue_ = (*it)->visitChildrenMaxDepth(maxDepth, vis);
 		}
 	}
 
-	return err;
+	return continue_;
 }
 
 } // end namespace anki

BIN
Samples/PhysicsPlayground/Assets/bullet_hole_decal.ankitex


+ 110 - 0
Samples/PhysicsPlayground/FpsCharacter.h

@@ -0,0 +1,110 @@
+// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <AnKi/AnKi.h>
+
+using namespace anki;
+
+class FpsCharacter : public SceneNode
+{
+public:
+	F32 m_mouseLookPower = toRad(7.0f);
+	F32 m_walkingSpeed = 8.5f;
+	F32 m_jumpSpeed = 8.0f;
+	Bool m_crouching = false;
+
+	FpsCharacter()
+		: SceneNode("FpsCharacter")
+	{
+		PlayerControllerComponent* playerc = newComponent<PlayerControllerComponent>();
+
+		SceneNode* cam = SceneGraph::getSingleton().newSceneNode<SceneNode>("FpsCharacterCam");
+		cam->setLocalTransform(Transform(Vec3(0.0f, 2.0f, 0.0f), Mat3::getIdentity(), Vec3(1.0f)));
+		addChild(cam);
+
+		SceneNode* shotgun = SceneGraph::getSingleton().newSceneNode<SceneNode>("Shotgun");
+		shotgun->setLocalTransform(Transform(Vec3(0.065f, -0.13f, -0.4f), Mat3(Euler(0.0f, kPi, 0.0f)), Vec3(1.0f)));
+		ModelComponent* modelc = shotgun->newComponent<ModelComponent>();
+		modelc->loadModelResource("sleevegloveLOW.001_arms_boomstick_76e1c49d9efa9619.ankimdl");
+		cam->addChild(shotgun);
+	}
+
+	void frameUpdate([[maybe_unused]] Second prevUpdateTime, [[maybe_unused]] Second crntTime) override
+	{
+		PlayerControllerComponent& playerc = getFirstComponentOfType<PlayerControllerComponent>();
+
+		// Rotate
+		F32 y = Input::getSingleton().getMousePosition().y();
+		F32 x = Input::getSingleton().getMousePosition().x();
+		if(y != 0.0 || x != 0.0)
+		{
+			// Set rotation
+			Mat3 rot(Euler(m_mouseLookPower * y, m_mouseLookPower * x, 0.0f));
+
+			rot = getLocalRotation() * rot;
+
+			const Vec3 newz = rot.getColumn(2).normalize();
+			const Vec3 newx = Vec3(0.0, 1.0, 0.0).cross(newz);
+			const Vec3 newy = newz.cross(newx);
+			rot.setColumns(newx, newy, newz);
+			rot = rot.reorthogonalize();
+
+			// Update move
+			setLocalRotation(rot);
+		}
+
+		Vec3 moveVec(0.0);
+		if(Input::getSingleton().getKey(KeyCode::kW))
+		{
+			moveVec.z() += 1.0f;
+		}
+
+		if(Input::getSingleton().getKey(KeyCode::kA))
+		{
+			moveVec.x() += 1.0f;
+		}
+
+		if(Input::getSingleton().getKey(KeyCode::kS))
+		{
+			moveVec.z() -= 1.0f;
+		}
+
+		if(Input::getSingleton().getKey(KeyCode::kD))
+		{
+			moveVec.x() -= 1.0f;
+		}
+
+		F32 jumpSpeed = 0.0f;
+		if(Input::getSingleton().getKey(KeyCode::kSpace))
+		{
+			jumpSpeed += m_jumpSpeed;
+		}
+
+		Bool crouchChanged = false;
+		if(Input::getSingleton().getKey(KeyCode::kC))
+		{
+			m_crouching = !m_crouching;
+			crouchChanged = true;
+		}
+
+		if(moveVec != 0.0f || jumpSpeed != 0.0f || crouchChanged)
+		{
+			Vec3 dir;
+			if(moveVec != 0.0f)
+			{
+				dir = -(getLocalRotation() * moveVec);
+				dir.y() = 0.0f;
+				dir = dir.normalize();
+			}
+
+			F32 speed = m_walkingSpeed;
+			if(Input::getSingleton().getKey(KeyCode::kLeftShift))
+			{
+				speed *= 2.0f;
+			}
+			playerc.setVelocity(speed, jumpSpeed, dir, m_crouching);
+		}
+	}
+};

+ 69 - 74
Samples/PhysicsPlayground/Main.cpp

@@ -5,6 +5,7 @@
 
 #include <cstdio>
 #include <Samples/Common/SampleApp.h>
+#include <Samples/PhysicsPlayground/FpsCharacter.h>
 
 using namespace anki;
 
@@ -13,17 +14,14 @@ static Error createDestructionEvent(SceneNode* node)
 	CString script = R"(
 function update(event, prevTime, crntTime)
 	-- Do nothing
-	return 1
 end
 
 function onKilled(event, prevTime, crntTime)
 	logi(string.format("Will kill %s", event:getAssociatedSceneNodes():getAt(0):getName()))
-	event:getAssociatedSceneNodes():getAt(0):setMarkedForDeletion()
-	return 1
+	event:getAssociatedSceneNodes():getAt(0):markForDeletion()
 end
 	)";
-	ScriptEvent* event;
-	ANKI_CHECK(SceneGraph::getSingleton().getEventManager().newEvent(event, -1, 10.0, script));
+	ScriptEvent* event = SceneGraph::getSingleton().getEventManager().newEvent<ScriptEvent>(-1.0f, 10.0f, script);
 	event->addAssociatedSceneNode(node);
 
 	return Error::kNone;
@@ -49,21 +47,18 @@ function update(event, prevTime, crntTime)
 	node:setLocalOrigin(pos)
 
 	if density <= 0.0 or radius <= 0.0 then
-		node:setMarkedForDeletion()
+		node:markForDeletion()
 	else
 		node:setLocalScale(Vec3.new(radius))
 		fogComponent:setDensity(density)
 	end
-
-	return 1
 end
 
 function onKilled(event, prevTime, crntTime)
-	return 1
+	-- Nothing
 end
 	)";
-	ScriptEvent* event;
-	ANKI_CHECK(SceneGraph::getSingleton().getEventManager().newEvent(event, -1, 10.0, script));
+	ScriptEvent* event = SceneGraph::getSingleton().getEventManager().newEvent<ScriptEvent>(-1, 10.0, script);
 	event->addAssociatedSceneNode(node);
 
 	return Error::kNone;
@@ -92,8 +87,7 @@ Error MyApp::sampleExtraInit()
 		arm.setLocalTransform(Transform(Vec3(0.065f, -0.13f, -0.4f), Mat3(Euler(0.0f, kPi, 0.0f)), Vec3(1.0f, 1.0f, 1.0f)));
 		arm.setParent(&cam);
 
-		SceneNode* player;
-		ANKI_CHECK(SceneGraph::getSingleton().newSceneNode("player", player));
+		SceneNode* player = SceneGraph::getSingleton().newSceneNode<SceneNode>("player");
 		PlayerControllerComponent* playerc = player->newComponent<PlayerControllerComponent>();
 		playerc->moveToPosition(Vec3(0.0f, 10.5f, 0.0f));
 
@@ -103,22 +97,19 @@ Error MyApp::sampleExtraInit()
 	// Create a body component with hinge joint
 	if(1)
 	{
-		SceneNode* base;
-		ANKI_CHECK(SceneGraph::getSingleton().newSceneNode("hingeBase", base));
+		SceneNode* base = SceneGraph::getSingleton().newSceneNode<SceneNode>("hingeBase");
 		BodyComponent* bodyc = base->newComponent<BodyComponent>();
 		bodyc->setBoxExtend(Vec3(0.1f));
 		bodyc->setCollisionShapeType(BodyComponentCollisionShapeType::kAabb);
 		bodyc->teleportTo(Vec3(-0.0f, 5.0f, -3.0f), Mat3::getIdentity());
 
-		SceneNode* joint;
-		ANKI_CHECK(SceneGraph::getSingleton().newSceneNode("hinge", joint));
+		SceneNode* joint = SceneGraph::getSingleton().newSceneNode<SceneNode>("hinge");
 		JointComponent* jointc = joint->newComponent<JointComponent>();
 		jointc->setType(JointType::kHinge);
 		joint->setLocalOrigin(Vec3(-0.0f, 4.8f, -3.0f));
 		base->addChild(joint);
 
-		SceneNode* monkey;
-		ANKI_CHECK(SceneGraph::getSingleton().newSceneNode("monkey_p2p", monkey));
+		SceneNode* monkey = SceneGraph::getSingleton().newSceneNode<SceneNode>("monkey_p2p");
 		ModelComponent* modelc = monkey->newComponent<ModelComponent>();
 		modelc->loadModelResource("Assets/Suzanne_dynamic_36043dae41fe12d5.ankimdl");
 		const Aabb aabb = modelc->getModelResource()->getBoundingVolume();
@@ -139,8 +130,7 @@ Error MyApp::sampleExtraInit()
 
 		Transform trf(Vec4(-4.3f, 12.0f, -3.0f, 0.0f), Mat3x4::getIdentity(), Vec4(1.0, 1.0, 1.0, 0.0));
 
-		SceneNode* base;
-		ANKI_CHECK(SceneGraph::getSingleton().newSceneNode("p2pBase", base));
+		SceneNode* base = SceneGraph::getSingleton().newSceneNode<SceneNode>("p2pBase");
 		BodyComponent* bodyc = base->newComponent<BodyComponent>();
 		bodyc->setBoxExtend(Vec3(0.1f));
 		bodyc->setCollisionShapeType(BodyComponentCollisionShapeType::kAabb);
@@ -152,15 +142,13 @@ Error MyApp::sampleExtraInit()
 
 		for(U32 i = 0; i < linkCount; ++i)
 		{
-			SceneNode* joint;
-			ANKI_CHECK(SceneGraph::getSingleton().newSceneNode(String().sprintf("joint_chain%u", i), joint));
+			SceneNode* joint = SceneGraph::getSingleton().newSceneNode<SceneNode>(String().sprintf("joint_chain%u", i));
 			JointComponent* jointc = joint->newComponent<JointComponent>();
 			jointc->setType(JointType::kPoint);
 			joint->setLocalOrigin(trf.getOrigin().xyz());
 			joint->setParent(prevNode);
 
-			SceneNode* monkey;
-			ANKI_CHECK(SceneGraph::getSingleton().newSceneNode(String().sprintf("monkey_chain%u", i).toCString(), monkey));
+			SceneNode* monkey = SceneGraph::getSingleton().newSceneNode<SceneNode>(String().sprintf("monkey_chain%u", i).toCString());
 			ModelComponent* modelc = monkey->newComponent<ModelComponent>();
 			modelc->loadModelResource("Assets/Suzanne_dynamic_36043dae41fe12d5.ankimdl");
 			const Aabb aabb = modelc->getModelResource()->getBoundingVolume();
@@ -183,8 +171,7 @@ Error MyApp::sampleExtraInit()
 	// Trigger
 	if(1)
 	{
-		SceneNode* node;
-		ANKI_CHECK(SceneGraph::getSingleton().newSceneNode("trigger", node));
+		SceneNode* node = SceneGraph::getSingleton().newSceneNode<SceneNode>("trigger");
 		TriggerComponent* triggerc = node->newComponent<TriggerComponent>();
 		triggerc->setType(TriggerComponentShapeType::kSphere);
 		node->setLocalScale(Vec3(1.8f, 1.8f, 1.8f));
@@ -361,7 +348,7 @@ Error MyApp::userMainLoop(Bool& quit, [[maybe_unused]] Second elapsedTime)
 		}
 	}
 
-	if(Input::getSingleton().getMouseButton(MouseButton::kLeft) == 1)
+	if(Input::getSingleton().getMouseButton(MouseButton::kRight) == 1)
 	{
 		ANKI_LOGI("Firing a grenade");
 
@@ -371,8 +358,7 @@ Error MyApp::userMainLoop(Bool& quit, [[maybe_unused]] Second elapsedTime)
 		const Vec3 newPos = camTrf.getOrigin().xyz() + camTrf.getRotation().getZAxis() * -3.0f;
 		camTrf.setOrigin(newPos.xyz0());
 
-		SceneNode* grenade;
-		ANKI_CHECK(SceneGraph::getSingleton().newSceneNode(String().sprintf("Grenade%u", instance++).toCString(), grenade));
+		SceneNode* grenade = SceneGraph::getSingleton().newSceneNode<SceneNode>(String().sprintf("Grenade%u", instance++).toCString());
 		grenade->setLocalScale(Vec3(2.8f));
 		ModelComponent* modelc = grenade->newComponent<ModelComponent>();
 		modelc->loadModelResource("Assets/MESH_grenade_MTL_grenade_85852a78645563d8.ankimdl");
@@ -389,63 +375,72 @@ Error MyApp::userMainLoop(Bool& quit, [[maybe_unused]] Second elapsedTime)
 		ANKI_CHECK(createDestructionEvent(grenade));
 	}
 
-	if(Input::getSingleton().getMouseButton(MouseButton::kRight) == 1)
+	if(Input::getSingleton().getMouseButton(MouseButton::kLeft) == 1)
 	{
-		Transform camTrf = SceneGraph::getSingleton().getActiveCameraNode().getWorldTransform();
-		Vec3 from = camTrf.getOrigin().xyz();
-		Vec3 to = from + -camTrf.getRotation().getZAxis() * 100.0f;
+		const Transform camTrf = SceneGraph::getSingleton().getActiveCameraNode().getWorldTransform();
 
-		RayHitResult result;
-		const Bool hit = PhysicsWorld::getSingleton().castRayClosestHit(from, to, PhysicsLayerBit::kStatic, result);
-
-		if(hit)
+		for(U32 i = 0; i < 8; ++i)
 		{
-			// Create rotation
-			const Vec3& zAxis = result.m_normal;
-			Vec3 yAxis = Vec3(0, 1, 0.5);
-			Vec3 xAxis = yAxis.cross(zAxis).normalize();
-			yAxis = zAxis.cross(xAxis);
-
-			Mat3x4 rot = Mat3x4::getIdentity();
-			rot.setXAxis(xAxis);
-			rot.setYAxis(yAxis);
-			rot.setZAxis(zAxis);
-
-			Transform trf(result.m_hitPosition.xyz0(), rot, Vec4(1.0f, 1.0f, 1.0f, 0.0f));
-
-			// Create an obj
-			static U32 id = 0;
-			SceneNode* monkey;
-			ANKI_CHECK(SceneGraph::getSingleton().newSceneNode(String().sprintf("decal%u", id++).toCString(), monkey));
-			ModelComponent* modelc = monkey->newComponent<ModelComponent>();
-			modelc->loadModelResource("Assets/Suzanne_dynamic_36043dae41fe12d5.ankimdl");
-			monkey->setLocalTransform(trf);
+			F32 spredAngle = toRad(getRandomRange(-2.0f, 2.0f));
+			Mat3 randDirection(Axisang(spredAngle, Vec3(1.0f, 0.0f, 0.0f)));
+			spredAngle = toRad(getRandomRange(-2.0f, 2.0f));
+			randDirection = randDirection * Mat3(Axisang(spredAngle, Vec3(0.0f, 1.0f, 0.0f)));
+			randDirection = camTrf.getRotation().getRotationPart() * randDirection;
 
-			ANKI_CHECK(createDestructionEvent(monkey));
+			const Vec3 from = camTrf.getOrigin().xyz();
+			const Vec3 to = from + -randDirection.getZAxis() * 100.0f;
 
-#if 1
+			RayHitResult result;
+			const Bool hit = PhysicsWorld::getSingleton().castRayClosestHit(from, to, PhysicsLayerBit::kStatic, result);
+
+			if(hit)
+			{
+				// Create rotation
+				const Vec3& zAxis = result.m_normal;
+				Vec3 yAxis = Vec3(0, 1, 0.5);
+				Vec3 xAxis = yAxis.cross(zAxis).normalize();
+				yAxis = zAxis.cross(xAxis);
+
+				Mat3x4 rot = Mat3x4::getIdentity();
+				rot.setXAxis(xAxis);
+				rot.setYAxis(yAxis);
+				rot.setZAxis(zAxis);
+
+				Transform trf(result.m_hitPosition.xyz0(), rot, Vec4(1.0f, 1.0f, 1.0f, 0.0f));
+
+				// Create an obj
+				static U32 id = 0;
+				SceneNode* bulletDecal = SceneGraph::getSingleton().newSceneNode<SceneNode>(String().sprintf("decal%u", id++).toCString());
+				bulletDecal->setLocalTransform(trf);
+				bulletDecal->setLocalScale(Vec3(0.1f, 0.1f, 0.3f));
+				DecalComponent* decalc = bulletDecal->newComponent<DecalComponent>();
+				decalc->loadDiffuseImageResource("Assets/bullet_hole_decal.ankitex", 1.0f);
+
+				ANKI_CHECK(createDestructionEvent(bulletDecal));
+
+#if 0
 			// Create some particles
 			ParticleEmitterComponent* partc = monkey->newComponent<ParticleEmitterComponent>();
 			partc->loadParticleEmitterResource("Assets/Smoke.ankipart");
 #endif
 
-			// Create some fog volumes
-			for(U i = 0; i < 1; ++i)
-			{
-				static int id = 0;
-				String name;
-				name.sprintf("fog%u", id++);
+				// Create some fog volumes
+				if(i == 0)
+				{
+					static int id = 0;
+					String name;
+					name.sprintf("fog%u", id++);
 
-				SceneNode* fogNode;
-				ANKI_CHECK(SceneGraph::getSingleton().newSceneNode(name.toCString(), fogNode));
-				FogDensityComponent* fogComp = fogNode->newComponent<FogDensityComponent>();
-				fogNode->setLocalScale(Vec3(2.1f));
-				fogComp->setDensity(15.0f);
+					SceneNode* fogNode = SceneGraph::getSingleton().newSceneNode<SceneNode>(name.toCString());
+					FogDensityComponent* fogComp = fogNode->newComponent<FogDensityComponent>();
+					fogNode->setLocalScale(Vec3(2.1f));
+					fogComp->setDensity(15.0f);
 
-				fogNode->setLocalTransform(trf);
+					fogNode->setLocalTransform(trf);
 
-				ANKI_CHECK(createDestructionEvent(fogNode));
-				ANKI_CHECK(createFogVolumeFadeEvent(fogNode));
+					ANKI_CHECK(createDestructionEvent(fogNode));
+					ANKI_CHECK(createFogVolumeFadeEvent(fogNode));
+				}
 			}
 		}
 	}

+ 2 - 5
Tools/Image/ImageViewerMain.cpp

@@ -27,14 +27,12 @@ public:
 		ANKI_CHECK_AND_IGNORE(ResourceManager::getSingleton().loadResource("ShaderBinaries/UiVisualizeImage.ankiprogbin", m_imageProgram));
 	}
 
-	Error frameUpdate([[maybe_unused]] Second prevUpdateTime, [[maybe_unused]] Second crntTime) override
+	void frameUpdate([[maybe_unused]] Second prevUpdateTime, [[maybe_unused]] Second crntTime) override
 	{
 		if(!m_imageIdExtra.m_textureView.isValid())
 		{
 			m_imageIdExtra.m_textureView = TextureView(&m_imageResource->getTexture(), TextureSubresourceDesc::all());
 		}
-
-		return Error::kNone;
 	}
 
 private:
@@ -285,8 +283,7 @@ public:
 		NativeWindow::getSingleton().setWindowTitle(title);
 
 		// Create the node
-		TextureViewerUiNode* node;
-		ANKI_CHECK(SceneGraph::getSingleton().newSceneNode("TextureViewer", node));
+		TextureViewerUiNode* node = SceneGraph::getSingleton().newSceneNode<TextureViewerUiNode>("TextureViewer");
 		node->m_imageResource = std::move(image);
 
 		return Error::kNone;