瀏覽代碼

Scene graph changes. Removing callbacks and adding dummy/feedback components

Panagiotis Christopoulos Charitos 11 年之前
父節點
當前提交
f07de7685e
共有 41 個文件被更改,包括 1009 次插入1024 次删除
  1. 1 1
      include/anki/collision/Frustum.h
  2. 1 1
      include/anki/renderer/Renderer.h
  3. 8 5
      include/anki/renderer/Sm.h
  4. 18 113
      include/anki/scene/Camera.h
  5. 25 49
      include/anki/scene/FrustumComponent.h
  6. 106 167
      include/anki/scene/Light.h
  7. 1 14
      include/anki/scene/Misc.h
  8. 11 42
      include/anki/scene/ModelNode.h
  9. 10 36
      include/anki/scene/ParticleEmitter.h
  10. 2 3
      include/anki/scene/RenderComponent.h
  11. 17 0
      include/anki/scene/SceneComponent.h
  12. 3 3
      include/anki/scene/SceneNode.h
  13. 20 22
      include/anki/scene/SpatialComponent.h
  14. 5 47
      include/anki/scene/StaticGeometryNode.h
  15. 3 4
      include/anki/util/Bitset.h
  16. 15 15
      src/event/LightEvent.cpp
  17. 3 2
      src/renderer/Bs.cpp
  18. 2 1
      src/renderer/Dbg.cpp
  19. 2 4
      src/renderer/Dp.cpp
  20. 3 2
      src/renderer/Ez.cpp
  21. 1 2
      src/renderer/Hdr.cpp
  22. 57 44
      src/renderer/Is.cpp
  23. 7 6
      src/renderer/Lf.cpp
  24. 5 5
      src/renderer/Ms.cpp
  25. 2 2
      src/renderer/Pps.cpp
  26. 4 3
      src/renderer/Renderer.cpp
  27. 1 2
      src/renderer/RenderingPass.cpp
  28. 6 5
      src/renderer/Sm.cpp
  29. 5 4
      src/renderer/Ssao.cpp
  30. 1 2
      src/renderer/Sslr.cpp
  31. 14 9
      src/renderer/Tiler.cpp
  32. 96 38
      src/scene/Camera.cpp
  33. 26 0
      src/scene/FrustumComponent.cpp
  34. 150 82
      src/scene/Light.cpp
  35. 143 88
      src/scene/ModelNode.cpp
  36. 136 90
      src/scene/ParticleEmitter.cpp
  37. 11 1
      src/scene/SceneNode.cpp
  38. 9 6
      src/scene/SpatialComponent.cpp
  39. 45 69
      src/scene/StaticGeometryNode.cpp
  40. 1 3
      src/scene/Visibility.cpp
  41. 33 32
      testapp/Main.cpp

+ 1 - 1
include/anki/collision/Frustum.h

@@ -45,7 +45,7 @@ public:
 	/// @name Constructors
 	/// @{
 	Frustum(Type type)
-		: m_type(type)
+	:	m_type(type)
 	{}
 
 	virtual ~Frustum()

+ 1 - 1
include/anki/renderer/Renderer.h

@@ -223,7 +223,7 @@ public:
 
 	/// Create a framebuffer attachment texture
 	ANKI_USE_RESULT Error createRenderTarget(U32 w, U32 h, 
-		GLenum internalFormat, GLenum format, GLenum type, U32 samples, 
+		GLenum internalFormat, U32 samples, 
 		GlTextureHandle& rt);
 
 	/// Create a pipeline object that has as a vertex shader the m_drawQuadVert

+ 8 - 5
include/anki/renderer/Sm.h

@@ -13,7 +13,8 @@
 
 namespace anki {
 
-class Light;
+// Forward 
+class SceneNode;
 
 /// @addtogroup renderer
 /// @{
@@ -47,7 +48,7 @@ private:
 	{
 		U32 m_layerId;
 		GlFramebufferHandle m_fb;
-		Light* m_light = nullptr;
+		SceneNode* m_light = nullptr;
 		U32 m_timestamp = 0; ///< Timestamp of last render or light change
 	};
 
@@ -75,7 +76,9 @@ private:
 	}
 
 	ANKI_USE_RESULT Error init(const ConfigSet& initializer);
-	ANKI_USE_RESULT Error run(Light* shadowCasters[], U32 shadowCastersCount, 
+	ANKI_USE_RESULT Error run(
+		SceneNode* shadowCasters[], 
+		U32 shadowCastersCount, 
 		GlCommandBufferHandle& cmdBuff);
 
 	/// Get max shadow casters
@@ -88,10 +91,10 @@ private:
 	void finishDraw(GlCommandBufferHandle& cmdBuff);
 
 	/// Find the best shadowmap for that light
-	Shadowmap& bestCandidate(Light& light);
+	Shadowmap& bestCandidate(SceneNode& light);
 
 	ANKI_USE_RESULT Error doLight(
-		Light& light, GlCommandBufferHandle& cmdBuff, Shadowmap*& sm);
+		SceneNode& light, GlCommandBufferHandle& cmdBuff, Shadowmap*& sm);
 };
 
 /// @}

+ 18 - 113
include/anki/scene/Camera.h

@@ -17,9 +17,10 @@ namespace anki {
 /// @{
 
 /// Camera SceneNode interface class
-class Camera: public SceneNode, public MoveComponent, public FrustumComponent,
-	public SpatialComponent
+class Camera: public SceneNode
 {
+	friend class FeedbackComponent;
+
 public:
 	/// @note Don't EVER change the order
 	enum class Type: U8
@@ -29,51 +30,27 @@ public:
 		COUNT
 	};
 
-	Camera(SceneGraph* scene, Type type, Frustum* frustum);
+	Camera(SceneGraph* scene, Type type);
 
 	virtual ~Camera();
 
-	ANKI_USE_RESULT Error create(const CString& name);
+	ANKI_USE_RESULT Error create(const CString& name, Frustum* frustum);
 
 	Type getCameraType() const
 	{
 		return m_type;
 	}
 
-	/// Needed by the renderer
-	F32 getNear() const
-	{
-		return getFrustum().getNear();
-	}
-
-	/// Needed by the renderer
-	F32 getFar() const
-	{
-		return getFrustum().getFar();
-	}
-
-	/// @name MoveComponent virtuals
-	/// @{
-	ANKI_USE_RESULT Error onMoveComponentUpdate(
-		SceneNode& node, F32 prevTime, F32 crntTime) override;
-	/// @}
-
-	/// @name SpatialComponent virtuals
-	/// @{
-	Vec4 getSpatialOrigin()
-	{
-		return getWorldTransform().getOrigin();
-	}
-	/// @}
-
 	void lookAtPoint(const Vec3& point);
 
-protected:
-	/// Called when something changes in the frustum
-	void frustumUpdate();
-
 private:
 	Type m_type;
+	
+	/// Called when moved.
+	void onMoveComponentUpdate(MoveComponent& move);
+
+	/// Called when something changed in the frustum.
+	void onFrustumComponentUpdate(FrustumComponent& fr);
 };
 
 /// Perspective camera
@@ -82,44 +59,14 @@ class PerspectiveCamera: public Camera
 public:
 	PerspectiveCamera(SceneGraph* scene);
 
-	ANKI_USE_RESULT Error create(const CString& name)
-	{
-		return Camera::create(name);
-	}
-
-	F32 getFovX() const
-	{
-		return m_frustum.getFovX();
-	}
-	void setFovX(F32 x)
-	{
-		m_frustum.setFovX(x);
-		frustumUpdate();
-	}
+	~PerspectiveCamera();
 
-	F32 getFovY() const
-	{
-		return m_frustum.getFovY();
-	}
-	void setFovY(F32 x)
-	{
-		m_frustum.setFovY(x);
-		frustumUpdate();
-	}
-
-	void setAll(F32 fovX_, F32 fovY_, F32 near_, F32 far_)
+	ANKI_USE_RESULT Error create(const CString& name)
 	{
-		m_frustum.setAll(fovX_, fovY_, near_, far_);
-		frustumUpdate();
+		return Camera::create(name, &m_frustum);
 	}
 
-	/// @name SpatialComponent virtuals
-	/// @{
-	const CollisionShape& getSpatialCollisionShape()
-	{
-		return m_frustum;
-	}
-	/// @}
+	void setAll(F32 fovX, F32 fovY, F32 near, F32 far);
 
 private:
 	PerspectiveFrustum m_frustum;
@@ -131,54 +78,12 @@ class OrthographicCamera: public Camera
 public:
 	OrthographicCamera(SceneGraph* scene);
 
-	ANKI_USE_RESULT Error create(const CString& name)
-	{
-		return Camera::create(name);
-	}
-
-	F32 getNear() const
-	{
-		return m_frustum.getNear();
-	}
-
-	F32 getFar() const
-	{
-		return m_frustum.getFar();
-	}
-
-	F32 getLeft() const
-	{
-		return m_frustum.getLeft();
-	}
+	~OrthographicCamera();
 
-	F32 getRight() const
-	{
-		return m_frustum.getRight();
-	}
-
-	F32 getBottom() const
-	{
-		return m_frustum.getBottom();
-	}
-
-	F32 getTop() const
-	{
-		return m_frustum.getTop();
-	}
-
-	void setAll(F32 left, F32 right, F32 near, F32 far, F32 top, F32 bottom)
-	{
-		m_frustum.setAll(left, right, near, far, top, bottom);
-		frustumUpdate();
-	}
-
-	/// @name SpatialComponent virtuals
-	/// @{
-	const CollisionShape& getSpatialCollisionShape()
+	ANKI_USE_RESULT Error create(const CString& name)
 	{
-		return m_frustum;
+		return Camera::create(name, &m_frustum);
 	}
-	/// @}
 
 private:
 	OrthographicFrustum m_frustum;

+ 25 - 49
include/anki/scene/FrustumComponent.h

@@ -24,9 +24,8 @@ class VisibilityTestResults;
 class FrustumComponent: public SceneComponent
 {
 public:
-	class VisibilityStats
+	struct VisibilityStats
 	{
-	public:
 		U32 m_renderablesCount = 0;
 		U32 m_lightsCount = 0;
 	};
@@ -38,7 +37,8 @@ public:
 	{
 		// WARNING: Never touch m_frustum in constructor
 		ANKI_ASSERT(frustum);
-		markForUpdate();
+		markShapeForUpdate();
+		markTransformForUpdate();
 	}
 
 	Frustum& getFrustum()
@@ -56,38 +56,20 @@ public:
 		return m_pm;
 	}
 
-	void setProjectionMatrix(const Mat4& m)
-	{
-		m_pm = m;
-		markForUpdate();
-	}
-
 	const Mat4& getViewMatrix() const
 	{
 		return m_vm;
 	}
 
-	void setViewMatrix(const Mat4& m)
-	{
-		m_vm = m;
-		markForUpdate();
-	}
-
 	const Mat4& getViewProjectionMatrix() const
 	{
 		return m_vpm;
 	}
 
-	void setViewProjectionMatrix(const Mat4& m)
-	{
-		m_vpm = m;
-		markForUpdate();
-	}
-
 	/// Get the origin for sorting and visibility tests
 	const Vec4& getFrustumOrigin() const
 	{
-		return getFrustum().getTransform().getOrigin();
+		return m_frustum->getTransform().getOrigin();
 	}
 
 	void setVisibilityTestResults(VisibilityTestResults* visible);
@@ -99,50 +81,38 @@ public:
 		return *m_visible;
 	}
 
-	VisibilityStats getLastVisibilityStats() const
+	const VisibilityStats& getLastVisibilityStats() const
 	{
 		return m_stats;
 	}
 
-	void markForUpdate()
+	/// Call when the shape of the frustum got changed.
+	void markShapeForUpdate()
+	{
+		m_flags |= SHAPE_MARKED_FOR_UPDATE;
+	}
+
+	/// Call when the transformation of the frustum got changed.
+	void markTransformForUpdate()
 	{
-		m_markedForUpdate = true;
+		m_flags |= TRANSFORM_MARKED_FOR_UPDATE;
 	}
 
 	/// Is a spatial inside the frustum?
 	Bool insideFrustum(SpatialComponent& sp)
 	{
-		return getFrustum().insideFrustum(sp.getSpatialCollisionShape());
+		return m_frustum->insideFrustum(sp.getSpatialCollisionShape());
 	}
 
 	/// Is a collision shape inside the frustum?
 	Bool insideFrustum(const CollisionShape& cs)
 	{
-		return getFrustum().insideFrustum(cs);
-	}
-
-	/// Called when the component gets updated. It should be overriden, by 
-	/// default it does nothing.
-	virtual ANKI_USE_RESULT Error onFrustumComponentUpdate(
-		SceneNode& node, F32 prevTime, F32 crntTime)
-	{
-		return ErrorCode::NONE;
+		return m_frustum->insideFrustum(cs);
 	}
 
 	/// @name SceneComponent overrides
 	/// @{
-	ANKI_USE_RESULT Error update(SceneNode&, F32, F32, Bool& updated) override
-	{
-		updated = m_markedForUpdate;
-		m_markedForUpdate = false;
-		return ErrorCode::NONE;
-	}
-
-	ANKI_USE_RESULT Error onUpdate(
-		SceneNode& node, F32 prevTime, F32 crntTime) final
-	{
-		return onFrustumComponentUpdate(node, prevTime, crntTime);
-	}
+	ANKI_USE_RESULT Error update(SceneNode&, F32, F32, Bool& updated) override;
 
 	void reset() override
 	{
@@ -156,17 +126,23 @@ public:
 	}
 
 private:
+	enum Flags
+	{
+		SHAPE_MARKED_FOR_UPDATE = 1 << 0,
+		TRANSFORM_MARKED_FOR_UPDATE = 1 << 1
+	};
+
 	Frustum* m_frustum;
 	Mat4 m_pm = Mat4::getIdentity(); ///< Projection matrix
 	Mat4 m_vm = Mat4::getIdentity(); ///< View matrix
 	Mat4 m_vpm = Mat4::getIdentity(); ///< View projection matrix
-	VisibilityStats m_stats;
 
 	/// Visibility stuff. It's per frame so the pointer is invalid on the next 
 	/// frame and before any visibility tests are run
 	VisibilityTestResults* m_visible = nullptr;
+	VisibilityStats m_stats;
 
-	Bool8 m_markedForUpdate;
+	U8 m_flags;
 };
 /// @}
 

+ 106 - 167
include/anki/scene/Light.h

@@ -7,12 +7,10 @@
 #define ANKI_SCENE_LIGHT_H
 
 #include "anki/scene/SceneNode.h"
-#include "anki/scene/MoveComponent.h"
-#include "anki/scene/FrustumComponent.h"
-#include "anki/scene/SpatialComponent.h"
 #include "anki/scene/Forward.h"
 #include "anki/resource/Resource.h"
 #include "anki/resource/TextureResource.h"
+#include "anki/Collision.h"
 
 namespace anki {
 
@@ -23,87 +21,87 @@ namespace anki {
 class LightComponent: public SceneComponent
 {
 public:
-	LightComponent(Light* node);
+	enum class LightType: U8
+	{
+		POINT,
+		SPOT,
+		COUNT
+ 	};
 
-	static constexpr Type getClassType()
+	LightComponent(SceneNode* node, LightType type);
+
+	LightType getLightType() const
 	{
-		return Type::LIGHT;
+		return m_type;
 	}
 
-private:
-	Vec4 m_diffColor = Vec4(0.5);
-	Vec4 m_specColor = Vec4(0.5);
+	const Vec4& getDiffuseColor() const
+	{
+		return m_diffColor;
+	}
 
-	Bool8 m_shadow = false;
-	U8 m_shadowMapIndex = 0xFF; ///< Used by the renderer
-};
+	void setDiffuseColor(const Vec4& x)
+	{
+		m_diffColor = x;
+	}
 
-/// Light scene node. It can be spot or point
-///
-/// Explaining the lighting model:
-/// @code
-/// Final intensity:                If = Ia + Id + Is
-/// Ambient intensity:              Ia = Al * Am
-/// Ambient intensity of light:     Al
-/// Ambient intensity of material:  Am
-/// Diffuse intensity:              Id = Dl * Dm * LambertTerm
-/// Diffuse intensity of light:     Dl
-/// Diffuse intensity of material:  Dm
-/// LambertTerm:                    max(Normal dot Light, 0.0)
-/// Specular intensity:             Is = Sm * Sl * pow(max(R dot E, 0.0), f)
-/// Specular intensity of light:    Sl
-/// Specular intensity of material: Sm
-/// @endcode
-class Light: public SceneNode, public LightComponent, public MoveComponent, 
-	public SpatialComponent
-{
-public:
-	enum class Type: U8
+	const Vec4& getSpecularColor() const
 	{
-		POINT,
-		SPOT,
-		COUNT
-	};
+		return m_specColor;
+	}
 
-	Light(SceneGraph* scene, Type t);
+	void setSpecularColor(const Vec4& x)
+	{
+		m_specColor = x;
+	}
 
-	virtual ~Light();
+	void setRadius(F32 x)
+	{
+		m_radius = x;
+		m_dirty = true;
+	}
 
-	ANKI_USE_RESULT Error create(const CString& name);
+	F32 getRadius() const
+	{
+		return m_radius;
+	}
 
-	Type getLightType() const
+	void setDistance(F32 x)
 	{
-		return m_type;
+		m_distance = x;
+		m_dirty = true;
 	}
 
-	const Vec4& getDiffuseColor() const
+	F32 getDistance() const
 	{
-		return m_color;
+		return m_distance;
 	}
 
-	Vec4& getDiffuseColor()
+	void setInnerAngle(F32 ang)
 	{
-		return m_color;
+		m_innerAngleCos = cos(ang / 2.0);
+		m_dirty = true;
 	}
 
-	void setDiffuseColor(const Vec4& x)
+	F32 getInnerAngleCos() const
 	{
-		m_color = x;
+		return m_innerAngleCos;
 	}
 
-	const Vec4& getSpecularColor() const
+	void setOuterAngle(F32 ang)
 	{
-		return m_specColor;
+		m_outerAngleCos = cos(ang / 2.0);
+		m_dirty = true;
 	}
 
-	Vec4& getSpecularColor()
+	F32 getOuterAngle() const
 	{
-		return m_specColor;
+		return m_outerAngle;
 	}
 
-	void setSpecularColor(const Vec4& x)
+	F32 getOuterAngleCos() const
 	{
-		m_specColor = x;
+		return m_outerAngleCos;
 	}
 
 	Bool getShadowEnabled() const
@@ -127,30 +125,59 @@ public:
 		m_shadowMapIndex = static_cast<U8>(i);
 	}
 
-	ANKI_USE_RESULT Error loadLensFlare(const CString& filename);
+	ANKI_USE_RESULT Error update(SceneNode&, F32, F32, Bool& updated) override;
 
-	/// @name SpatialComponent virtuals
-	/// @{
-	Vec4 getSpatialOrigin()
+	static constexpr Type getClassType()
 	{
-		return getWorldTransform().getOrigin();
+		return Type::LIGHT;
 	}
-	/// @}
-
-protected:
-	/// One of the frustums got updated
-	void frustumUpdate();
-
-	/// Called when moved
-	void onMoveComponentUpdateCommon();
 
 private:
-	Type m_type;
-	Vec4 m_color = Vec4(1.0);
-	Vec4 m_specColor = Vec4(1.0);
+	LightType m_type;
+	Vec4 m_diffColor = Vec4(0.5);
+	Vec4 m_specColor = Vec4(0.5);
+	union
+	{
+		F32 m_radius;
+		F32 m_distance;
+	};
+	F32 m_innerAngleCos;
+	F32 m_outerAngleCos;
+	F32 m_outerAngle;
 
 	Bool8 m_shadow = false;
 	U8 m_shadowMapIndex = 0xFF; ///< Used by the renderer
+
+	Bool8 m_dirty = true;
+};
+
+/// Light scene node. It can be spot or point.
+class Light: public SceneNode
+{
+	friend class LightFeedbackComponent;
+
+public:
+	Light(SceneGraph* scene);
+
+	virtual ~Light();
+
+	ANKI_USE_RESULT Error create(
+		const CString& name, 
+		LightComponent::LightType type,
+		CollisionShape* shape);
+
+	ANKI_USE_RESULT Error loadLensFlare(const CString& filename);
+
+protected:
+	/// Called when moved
+	void onMoveUpdateCommon(MoveComponent& move);
+
+	/// One of the frustums got updated
+	void onShapeUpdateCommon(LightComponent& light);
+
+	virtual void onMoveUpdate(MoveComponent& move) = 0;
+
+	virtual void onShapeUpdate(LightComponent& light) = 0;
 };
 
 /// Point light
@@ -161,45 +188,23 @@ public:
 
 	ANKI_USE_RESULT Error create(const CString& name);
 
-	F32 getRadius() const
-	{
-		return m_sphereW.getRadius();
-	}
-
-	void setRadius(const F32 x)
-	{
-		m_sphereW.setRadius(x);
-		frustumUpdate();
-	}
-
-	const Sphere& getSphere() const
-	{
-		return m_sphereW;
-	}
-
 	/// @name SceneNode virtuals
 	/// @{
 	ANKI_USE_RESULT Error frameUpdate(
 		F32 prevUpdateTime, F32 crntTime) override;
 	/// @}
 
-	/// @name MoveComponent virtuals
+	/// @privatesection
 	/// @{
-	ANKI_USE_RESULT Error onMoveComponentUpdate(SceneNode&, F32, F32) override;
-	/// @}
-
-	/// @name SpatialComponent virtuals
-	/// @{
-	const CollisionShape& getSpatialCollisionShape()
-	{
-		return m_sphereW;
-	}
+	void onMoveUpdate(MoveComponent& move) override;
+	void onShapeUpdate(LightComponent& light) override;
 	/// @}
 
 public:
 	class ShadowData
 	{
 	public:
+#if 0
 		ShadowData(SceneNode* node)
 		:	m_frustumComps{{
 				{node, &m_frustums[0]}, {node, &m_frustums[1]},
@@ -210,6 +215,7 @@ public:
 		Array<PerspectiveFrustum, 6> m_frustums;
 		Array<FrustumComponent, 6> m_frustumComps;
 		Array<Transform, 6> m_localTrfs;
+#endif
 	};
 
 	Sphere m_sphereW = Sphere(Vec4(0.0), 1.0);
@@ -217,89 +223,22 @@ public:
 };
 
 /// Spot light
-class SpotLight: public Light, public FrustumComponent
+class SpotLight: public Light
 {
 public:
 	SpotLight(SceneGraph* scene);
 
 	ANKI_USE_RESULT Error create(const CString& name);
 
-	GlTextureHandle& getTexture()
-	{
-		return m_tex->getGlTexture();
-	}
-
-	const GlTextureHandle& getTexture() const
-	{
-		return m_tex->getGlTexture();
-	}
-
-	F32 getOuterAngle() const
-	{
-		return m_frustum.getFovX();
-	}
-
-	void setOuterAngle(F32 x)
-	{
-		m_frustum.setFovX(x);
-		m_frustum.setFovY(x);
-		m_cosOuterAngle = cos(x / 2.0);
-		frustumUpdate();
-	}
-
-	F32 getOuterAngleCos() const
-	{
-		return m_cosOuterAngle;
-	}
-
-	void setInnerAngle(F32 ang)
-	{
-		m_cosInnerAngle = cos(ang / 2.0);
-	}
-
-	F32 getInnerAngleCos() const
-	{
-		return m_cosInnerAngle;
-	}
-
-	F32 getDistance() const
-	{
-		return m_frustum.getFar();
-	}
-
-	void setDistance(F32 f)
-	{
-		m_frustum.setFar(f);
-		frustumUpdate();
-	}
-
-	const PerspectiveFrustum& getFrustum() const
-	{
-		return m_frustum;
-	}
-
-	/// @name MoveComponent virtuals
-	/// @{
-	ANKI_USE_RESULT Error onMoveComponentUpdate(SceneNode&, F32, F32) override;
-	/// @}
-
-	/// @name SpatialComponent virtuals
+	/// @privatesection
 	/// @{
-	const CollisionShape& getSpatialCollisionShape()
-	{
-		return m_frustum;
-	}
+	void onMoveUpdate(MoveComponent& move) override;
+	void onShapeUpdate(LightComponent& light) override;
 	/// @}
 
-	ANKI_USE_RESULT Error loadTexture(const CString& filename);
-
 private:
 	PerspectiveFrustum m_frustum;
-	TextureResourcePointer m_tex;
-	F32 m_cosOuterAngle;
-	F32 m_cosInnerAngle;
 };
-
 /// @}
 
 } // end namespace anki

+ 1 - 14
include/anki/scene/Misc.h

@@ -21,22 +21,9 @@ public:
 	Obb m_obb;
 
 	ObbSpatialComponent(SceneNode* node)
-	:	SpatialComponent(node)
+	:	SpatialComponent(node, &m_obb)
 	{}
-
-	/// Implement SpatialComponent::getSpatialCollisionShape
-	const CollisionShape& getSpatialCollisionShape()
-	{
-		return m_obb;
-	}
-
-	/// Implement SpatialComponent::getSpatialOrigin
-	Vec4 getSpatialOrigin()
-	{
-		return m_obb.getCenter();
-	}
 };
-
 /// @}
 
 } // end namespace anki

+ 11 - 42
include/anki/scene/ModelNode.h

@@ -25,10 +25,10 @@ class PhysicsBody;
 /// @{
 
 /// A fragment of the ModelNode
-class ModelPatchNode: public SceneNode, 
-	public RenderComponent, public SpatialComponent
+class ModelPatchNode: public SceneNode
 {
 	friend class ModelNode;
+	friend class ModelPatchRenderComponent;
 
 public:
 	ModelPatchNode(SceneGraph* scene);
@@ -38,39 +38,6 @@ public:
 	ANKI_USE_RESULT Error create(
 		const CString& name, const ModelPatchBase* modelPatch);
 
-	/// @name RenderComponent virtuals
-	/// @{
-
-	/// Implements RenderComponent::buildRendering
-	ANKI_USE_RESULT Error buildRendering(RenderingBuildData& data);
-
-	/// Implements  RenderComponent::getMaterial
-	const Material& getMaterial()
-	{
-		return m_modelPatch->getMaterial();
-	}
-
-	/// Overrides RenderComponent::getRenderComponentWorldTransform
-	void getRenderWorldTransform(U index, Transform& trf) override;
-
-	Bool getHasWorldTransforms() override
-	{
-		return true;
-	}
-	/// @}
-
-	/// Implement SpatialComponent::getSpatialCollisionShape
-	const CollisionShape& getSpatialCollisionShape()
-	{
-		return m_obb;
-	}
-
-	/// Implement SpatialComponent::getSpatialOrigin
-	Vec4 getSpatialOrigin()
-	{
-		return m_obb.getCenter();
-	}
-
 private:
 	Obb m_obb; ///< In world space
 	const ModelPatchBase* m_modelPatch; ///< The resource
@@ -79,17 +46,22 @@ private:
 	ANKI_USE_RESULT Error updateInstanceSpatials(
 		const MoveComponent* instanceMoves[], 
 		U32 instanceMovesCount);
+
+	ANKI_USE_RESULT Error buildRendering(RenderingBuildData& data);
+
+	void getRenderWorldTransform(U index, Transform& trf);
 };
 
 /// The model scene node
-class ModelNode: public SceneNode, public MoveComponent
+class ModelNode: public SceneNode
 {
 	friend class ModelPatchNode;
+	friend class ModelNodeFeedbackComponent;
 
 public:
 	ModelNode(SceneGraph* scene);
 
-	virtual ~ModelNode();
+	~ModelNode();
 
 	ANKI_USE_RESULT Error create(
 		const CString& name, const CString& modelFname);
@@ -102,10 +74,6 @@ public:
 	/// Override SceneNode::frameUpdate
 	ANKI_USE_RESULT Error frameUpdate(F32, F32) override;
 
-	/// Override MoveComponent::onMoveComponentUpdate
-	ANKI_USE_RESULT Error onMoveComponentUpdate(
-		SceneNode& node, F32 prevTime, F32 crntTime) override;
-
 private:
 	ModelResourcePointer m_model; ///< The resource
 	SceneDArray<ModelPatchNode*> m_modelPatches;
@@ -113,8 +81,9 @@ private:
 	Timestamp m_transformsTimestamp;
 	PhysicsBody* m_body = nullptr;
 	BodyComponent* m_bodyComp = nullptr;
-};
 
+	void onMoveComponentUpdate(MoveComponent& move);
+};
 /// @}
 
 } // end namespace anki

+ 10 - 36
include/anki/scene/ParticleEmitter.h

@@ -74,7 +74,7 @@ public:
 	}
 
 	/// Revive the particle
-	virtual void revive(const ParticleEmitter& pe,
+	virtual void revive(const ParticleEmitter& pe, const Transform& trf,
 		F32 prevUpdateTime, F32 crntTime);
 
 	/// Only relevant for non-bullet simulations
@@ -102,7 +102,7 @@ public:
 	ParticleSimple()
 	{}
 
-	void revive(const ParticleEmitter& pe,
+	void revive(const ParticleEmitter& pe, const Transform& trf,
 		F32 prevUpdateTime, F32 crntTime) override;
 
 	void simulate(const ParticleEmitter& pe, F32 prevUpdateTime, 
@@ -147,13 +147,13 @@ private:
 #endif
 
 /// The particle emitter scene node. This scene node emitts
-class ParticleEmitter: public SceneNode, public SpatialComponent, 
-	public MoveComponent, public RenderComponent, 
-	private ParticleEmitterProperties
+class ParticleEmitter: public SceneNode, private ParticleEmitterProperties
 {
 	friend class ParticleBase;
 	friend class Particle;
 	friend class ParticleSimple;
+	friend class ParticleEmitterRenderComponent;
+	friend class MoveFeedbackComponent;
 
 public:
 	ParticleEmitter(SceneGraph* scene);
@@ -169,36 +169,6 @@ public:
 		F32 prevUpdateTime, F32 crntTime) override;
 	/// @}
 
-	/// @name MoveComponent virtuals
-	/// @{
-	ANKI_USE_RESULT Error onMoveComponentUpdate(
-		SceneNode& node, F32 prevTime, F32 crntTime) override;
-	/// @}
-
-	/// @name SpatialComponent virtuals
-	/// @{
-	const CollisionShape& getSpatialCollisionShape()
-	{
-		return m_obb;
-	}
-
-	Vec4 getSpatialOrigin()
-	{
-		return m_obb.getCenter();
-	}
-	/// @}
-
-	/// @name RenderComponent virtuals
-	/// @{
-	ANKI_USE_RESULT Error buildRendering(RenderingBuildData& data);
-
-	const Material& getMaterial();
-
-	void getRenderWorldTransform(U index, Transform& trf) override;
-
-	Bool getHasWorldTransforms() override;
-	/// @}
-
 private:
 	enum class SimulationType: U8
 	{
@@ -229,8 +199,12 @@ private:
 	ANKI_USE_RESULT Error createParticlesSimpleSimulation();
 
 	ANKI_USE_RESULT Error doInstancingCalcs();
-};
 
+	ANKI_USE_RESULT Error buildRendering(RenderingBuildData& data);
+	void getRenderWorldTransform(U index, Transform& trf);
+
+	void onMoveComponentUpdate(MoveComponent& move);
+};
 /// @}
 
 } // end namespace anki

+ 2 - 3
include/anki/scene/RenderComponent.h

@@ -229,6 +229,8 @@ public:
 
 	~RenderComponent();
 
+	ANKI_USE_RESULT Error create();
+
 	Variables::Iterator getVariablesBegin()
 	{
 		return m_vars.begin();
@@ -288,9 +290,6 @@ public:
 		return Type::RENDER;
 	}
 
-protected:
-	ANKI_USE_RESULT Error create();
-
 private:
 	SceneAllocator<U8> m_alloc;
 	Variables m_vars;

+ 17 - 0
include/anki/scene/SceneComponent.h

@@ -9,6 +9,7 @@
 #include "anki/scene/Common.h"
 #include "anki/core/Timestamp.h"
 #include "anki/util/Functions.h"
+#include "anki/util/Bitset.h"
 
 namespace anki {
 
@@ -78,11 +79,27 @@ public:
 		return *out;
 	}
 
+	void setAutomaticCleanup(Bool enable)
+	{
+		m_flags.enableBits(AUTOMATIC_CLEANUP, enable);	
+	}
+
+	Bool getAutomaticCleanup() const
+	{
+		return m_flags.bitsEnabled(AUTOMATIC_CLEANUP);
+	}
+
 protected:
 	Timestamp m_timestamp; ///< Indicates when an update happened
 
 private:
+	enum Flags
+	{
+		AUTOMATIC_CLEANUP = 1 << 0
+	};
+
 	Type m_type;
+	Bitset<U8> m_flags;
 };
 
 } // end namespace anki

+ 3 - 3
include/anki/scene/SceneNode.h

@@ -160,7 +160,7 @@ public:
 	template<typename Component>
 	const Component& getComponent() const
 	{
-		Component* out = tryGetComponent<Component>();
+		const Component* out = tryGetComponent<Component>();
 		ANKI_ASSERT(out != nullptr);
 		return *out;
 	}
@@ -168,7 +168,8 @@ public:
 protected:
 	/// Append a component to the components container. The SceneNode will not
 	/// take ownership
-	ANKI_USE_RESULT Error addComponent(SceneComponent* comp);
+	ANKI_USE_RESULT Error addComponent(
+		SceneComponent* comp, Bool transferOwnership = false);
 
 	/// Remove a component from the container
 	void removeComponent(SceneComponent* comp);
@@ -182,7 +183,6 @@ private:
 	SceneString m_name; ///< A unique name
 	Bool8 m_forDeletion = false;
 };
-
 /// @}
 
 } // end namespace anki

+ 20 - 22
include/anki/scene/SpatialComponent.h

@@ -45,15 +45,17 @@ class SpatialComponent: public SceneComponent,
 public:
 	using Flag = SpatialComponentFlag;
 
-	/// Pass the collision shape here so we can avoid the virtuals
-	/// @param node The scene node. Used only to steal it's allocators
-	/// @param flags A mask of SpatialFlag
-	SpatialComponent(SceneNode* node, Flag flags = Flag::NONE);
+	SpatialComponent(
+		SceneNode* node, 
+		const CollisionShape* shape, 
+		Flag flags = Flag::NONE);
 
-	// Remove from current OctreeNode
 	~SpatialComponent();
 
-	virtual const CollisionShape& getSpatialCollisionShape() = 0;
+	const CollisionShape& getSpatialCollisionShape() const
+	{
+		return *m_shape;
+	}
 
 	const Aabb& getAabb() const
 	{
@@ -76,7 +78,16 @@ public:
 
 	/// Used for sorting spatials. In most object the origin is the center of
 	/// mess but for cameras the origin is the eye point
-	virtual Vec4 getSpatialOrigin() = 0;
+	const Vec4& getSpatialOrigin() const
+	{
+		ANKI_ASSERT(m_origin.x() != MAX_F32);
+		return m_origin;
+	}
+
+	void setSpatialOrigin(const Vec4& origin)
+	{
+		m_origin = origin;
+	}
 
 	/// The derived class has to manually call this method when the collision 
 	/// shape got updated
@@ -85,24 +96,10 @@ public:
 		enableBits(Flag::MARKED_FOR_UPDATE);
 	}
 
-	/// Called when the component gets updated. It should be overriden, by 
-	/// default it does nothing.
-	virtual ANKI_USE_RESULT Error onSpatialComponentUpdate(
-		SceneNode& node, F32 prevTime, F32 crntTime)
-	{
-		return ErrorCode::NONE;
-	}
-
 	/// @name SceneComponent overrides
 	/// @{
 	ANKI_USE_RESULT Error update(SceneNode&, F32, F32, Bool& updated) override;
 
-	ANKI_USE_RESULT Error onUpdate(
-		SceneNode& node, F32 prevTime, F32 crntTime) final
-	{
-		return onSpatialComponentUpdate(node, prevTime, crntTime);
-	}
-
 	/// Disable some flags
 	void reset() override;
 	/// @}
@@ -113,9 +110,10 @@ public:
 	}
 
 private:
+	const CollisionShape* m_shape;
 	Aabb m_aabb; ///< A faster shape
+	Vec4 m_origin = Vec4(MAX_F32, MAX_F32, MAX_F32, 0.0);
 };
-
 /// @}
 
 } // end namespace anki

+ 5 - 47
include/anki/scene/StaticGeometryNode.h

@@ -16,30 +16,11 @@ namespace anki {
 /// @addtogroup scene
 /// @{
 
-/// Part of the static geometry. Used only for visibility tests
-class StaticGeometrySpatial: public SpatialComponent
-{
-public:
-	StaticGeometrySpatial(SceneNode* node, const Obb* obb);
-
-	const CollisionShape& getSpatialCollisionShape()
-	{
-		return *m_obb;
-	}
-
-	Vec4 getSpatialOrigin()
-	{
-		return m_obb->getCenter();
-	}
-
-private:
-	const Obb* m_obb;
-};
-
 /// Static geometry scene node patch
-class StaticGeometryPatchNode: public SceneNode, public SpatialComponent,
-	public RenderComponent
+class StaticGeometryPatchNode: public SceneNode
 {
+	friend class StaticGeometryRenderComponent;
+
 public:
 	StaticGeometryPatchNode(SceneGraph* scene);
 
@@ -48,32 +29,10 @@ public:
 	ANKI_USE_RESULT Error create(
 		const CString& name, const ModelPatchBase* modelPatch);
 
-	/// @name SpatialComponent virtuals
-	/// @{
-	const CollisionShape& getSpatialCollisionShape()
-	{
-		return *m_obb;
-	}
-
-	Vec4 getSpatialOrigin()
-	{
-		return m_obb->getCenter();
-	}
-	/// @}
-
-	/// @name RenderComponent virtuals
-	/// @{
-	ANKI_USE_RESULT Error buildRendering(RenderingBuildData& data);
-
-	const Material& getMaterial()
-	{
-		return m_modelPatch->getMaterial();
-	}
-	/// @}
-
 private:
 	const ModelPatchBase* m_modelPatch;
-	const Obb* m_obb; 
+
+	ANKI_USE_RESULT Error buildRendering(RenderingBuildData& data);
 };
 
 /// Static geometry scene node
@@ -90,7 +49,6 @@ public:
 private:
 	ModelResourcePointer m_model;
 };
-
 /// @}
 
 } // end namespace anki

+ 3 - 4
include/anki/util/Bitset.h

@@ -13,12 +13,12 @@ namespace anki {
 /// @addtogroup util_containers
 /// @{
 
-/// Easy bit manipulation
+/// Easy bit manipulation.
 template<typename T>
 class Bitset
 {
 public:
-	typedef T Value;
+	using Value = T;
 
 	Bitset()
 	:	m_bitmask(static_cast<Value>(0))
@@ -37,7 +37,7 @@ public:
 
 	void enableBits(Value mask, Bool enable)
 	{
-		m_bitmask = (enable) ? m_bitmask | mask : m_bitmask & ~mask;
+		m_bitmask = (enable) ? (m_bitmask | mask) : (m_bitmask & ~mask);
 	}
 
 	void disableBits(Value mask)
@@ -64,7 +64,6 @@ public:
 protected:
 	Value m_bitmask;
 };
-
 /// @}
 
 } // end namespace anki

+ 15 - 15
src/event/LightEvent.cpp

@@ -14,16 +14,17 @@ Error LightEvent::create(EventManager* manager, F32 startTime, F32 duration,
 {
 	Error err = Event::create(manager, startTime, duration, light);
 	if(err) return err;
+	
+	LightComponent& lightc = light->getComponent<LightComponent>();
 
-	switch(light->getLightType())
+	switch(lightc.getLightType())
 	{
-	case Light::Type::POINT:
+	case LightComponent::LightType::POINT:
 		{
-			PointLight* plight = static_cast<PointLight*>(light);
-			m_originalRadius = plight->getRadius();
+			m_originalRadius = lightc.getRadius();
 		}
 		break;
-	case Light::Type::SPOT:
+	case LightComponent::LightType::SPOT:
 		ANKI_ASSERT("TODO");
 		break;
 	default:
@@ -31,8 +32,8 @@ Error LightEvent::create(EventManager* manager, F32 startTime, F32 duration,
 		break;
 	}
 
-	m_originalDiffColor = light->getDiffuseColor();
-	m_originalSpecColor = light->getSpecularColor();
+	m_originalDiffColor = lightc.getDiffuseColor();
+	m_originalSpecColor = lightc.getSpecularColor();
 
 	return err;
 }
@@ -41,18 +42,17 @@ Error LightEvent::create(EventManager* manager, F32 startTime, F32 duration,
 Error LightEvent::update(F32 prevUpdateTime, F32 crntTime)
 {
 	F32 factor = sin(getDelta(crntTime) * getPi<F32>());
-	Light* light = static_cast<Light*>(getSceneNode());
+	LightComponent& lightc = getSceneNode()->getComponent<LightComponent>();
 
-	switch(light->getLightType())
+	switch(lightc.getLightType())
 	{
-	case Light::Type::POINT:
+	case LightComponent::LightType::POINT:
 		{
-			PointLight* plight = static_cast<PointLight*>(light);
-			plight->setRadius(
+			lightc.setRadius(
 				factor * m_radiusMultiplier + m_originalRadius);
 		}
 		break;
-	case Light::Type::SPOT:
+	case LightComponent::LightType::SPOT:
 		ANKI_ASSERT("TODO");
 		break;
 	default:
@@ -60,9 +60,9 @@ Error LightEvent::update(F32 prevUpdateTime, F32 crntTime)
 		break;
 	}
 
-	light->setDiffuseColor(
+	lightc.setDiffuseColor(
 		m_originalDiffColor + factor * m_intensityMultiplier);
-	light->setSpecularColor(
+	lightc.setSpecularColor(
 		m_originalSpecColor + factor * m_specularIntensityMultiplier);
 
 	return ErrorCode::NONE;

+ 3 - 2
src/renderer/Bs.cpp

@@ -33,9 +33,10 @@ Error Bs::run(GlCommandBufferHandle& cmdb)
 	drawer.prepareDraw(RenderingStage::BLEND, Pass::COLOR, cmdb);
 
 	Camera& cam = m_r->getSceneGraph().getActiveCamera();
+	FrustumComponent& camFr = cam.getComponent<FrustumComponent>();
 
-	auto it = cam.getVisibilityTestResults().getRenderablesBegin();
-	auto end = cam.getVisibilityTestResults().getRenderablesEnd();
+	auto it = camFr.getVisibilityTestResults().getRenderablesBegin();
+	auto end = camFr.getVisibilityTestResults().getRenderablesEnd();
 	for(; !err && it != end; ++it)
 	{
 		err = drawer.render(cam, *it);

+ 2 - 1
src/renderer/Dbg.cpp

@@ -103,8 +103,9 @@ Error Dbg::run(GlCommandBufferHandle& cmdb)
 	cmdb.enableDepthTest(m_depthTest);
 
 	Camera& cam = scene.getActiveCamera();
+	FrustumComponent& camFr = cam.getComponent<FrustumComponent>();
 	m_drawer->prepareDraw(cmdb);
-	m_drawer->setViewProjectionMatrix(cam.getViewProjectionMatrix());
+	m_drawer->setViewProjectionMatrix(camFr.getViewProjectionMatrix());
 	m_drawer->setModelMatrix(Mat4::getIdentity());
 	//drawer->drawGrid();
 

+ 2 - 4
src/renderer/Dp.cpp

@@ -16,10 +16,8 @@ Error Dp::init(const ConfigSet& config)
 		getAlignedRoundDown(16, m_r->getHeight() / 3));
 
 	Error err = m_r->createRenderTarget(
-		m_smallDepthSize.x(), 
-		m_smallDepthSize.y(),
-		GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT,
-		GL_UNSIGNED_INT, 1, m_smallDepthRt);
+		m_smallDepthSize.x(), m_smallDepthSize.y(),
+		GL_DEPTH_COMPONENT24, 1, m_smallDepthRt);
 	if(err) return err;
 
 	GlDevice& gl = getGlDevice();

+ 3 - 2
src/renderer/Ez.cpp

@@ -29,13 +29,14 @@ Error Ez::run(GlCommandBufferHandle& cmdBuff)
 	Error err = ErrorCode::NONE;
 	SceneGraph& scene = m_r->getSceneGraph();
 	Camera& cam = scene.getActiveCamera();
+	FrustumComponent& camFr = cam.getComponent<FrustumComponent>();
 
 	m_r->getSceneDrawer().prepareDraw(
 		RenderingStage::MATERIAL, Pass::DEPTH, cmdBuff);
 
 	U count = m_maxObjectsToDraw;
-	auto it = cam.getVisibilityTestResults().getRenderablesBegin();
-	auto end = cam.getVisibilityTestResults().getRenderablesEnd();
+	auto it = camFr.getVisibilityTestResults().getRenderablesBegin();
+	auto end = camFr.getVisibilityTestResults().getRenderablesEnd();
 	for(; it != end; ++it)
 	{
 		err = m_r->getSceneDrawer().render(cam, *it);

+ 1 - 2
src/renderer/Hdr.cpp

@@ -18,8 +18,7 @@ Error Hdr::initFb(GlFramebufferHandle& fb, GlTextureHandle& rt)
 {
 	Error err = ErrorCode::NONE;
 
-	err = m_r->createRenderTarget(m_width, m_height, GL_RGB8, GL_RGB, 
-		GL_UNSIGNED_BYTE, 1, rt);
+	err = m_r->createRenderTarget(m_width, m_height, GL_RGB8, 1, rt);
 	if(err) return err;
 
 	// Set to bilinear because the blurring techniques take advantage of that

+ 57 - 44
src/renderer/Is.cpp

@@ -94,9 +94,15 @@ public:
 	/// Bin lights on CPU path
 	Bool m_binLights = true;
 
+	// Cached values
+	const FrustumComponent* m_camFrustum = nullptr;
+	const MoveComponent* m_camMove = nullptr;
+
 	Error operator()(U32 threadId, PtrSize threadsCount)
 	{
 		U ligthsCount = m_lightsEnd - m_lightsBegin;
+		m_camFrustum = &m_is->m_cam->getComponent<FrustumComponent>();
+		m_camMove = &m_is->m_cam->getComponent<MoveComponent>();
 
 		// Count job bounds
 		PtrSize start, end;
@@ -106,28 +112,28 @@ public:
 		for(U64 i = start; i < end; i++)
 		{
 			SceneNode* snode = (*(m_lightsBegin + i)).m_node;
-			Light* light = staticCastPtr<Light*>(snode);
+			MoveComponent& move = snode->getComponent<MoveComponent>();
+			LightComponent& light = snode->getComponent<LightComponent>();
+			SpatialComponent& sp = snode->getComponent<SpatialComponent>();
+			FrustumComponent* fr = snode->tryGetComponent<FrustumComponent>();
 
-			switch(light->getLightType())
+			switch(light.getLightType())
 			{
-			case Light::Type::POINT:
+			case LightComponent::LightType::POINT:
 				{
-					PointLight& l = 
-						*staticCastPtr<PointLight*>(light);
-					I pos = doLight(l);
+					I pos = doPointLight(light, move);
 					if(m_binLights && pos != -1)
 					{
-						binLight(l, pos);
+						binPointLight(sp, pos);
 					}
 				}
 				break;
-			case Light::Type::SPOT:
+			case LightComponent::LightType::SPOT:
 				{
-					SpotLight& l = *staticCastPtr<SpotLight*>(light);
-					I pos = doLight(l);
+					I pos = doSpotLight(light, move, fr);
 					if(m_binLights && pos != -1)
 					{
-						binLight(l, pos);
+						binSpotLight(light, sp, pos);
 					}
 				}
 				break;
@@ -141,7 +147,7 @@ public:
 	}
 
 	/// Copy CPU light to GPU buffer
-	I doLight(const PointLight& light)
+	I doPointLight(const LightComponent& light, const MoveComponent& move)
 	{
 		// Get GPU light
 		I i = m_pointLightsCount->fetch_add(1);
@@ -155,8 +161,8 @@ public:
 		const Camera* cam = m_is->m_cam;
 		ANKI_ASSERT(cam);
 	
-		Vec4 pos = 
-			cam->getViewMatrix() * light.getWorldTransform().getOrigin().xyz1();
+		Vec4 pos = m_camFrustum->getViewMatrix() 
+			* move.getWorldTransform().getOrigin().xyz1();
 
 		slight.m_posRadius = Vec4(pos.xyz(), -1.0 / light.getRadius());
 		slight.m_diffuseColorShadowmapId = light.getDiffuseColor();
@@ -166,9 +172,9 @@ public:
 	}
 
 	/// Copy CPU spot light to GPU buffer
-	I doLight(const SpotLight& light)
+	I doSpotLight(const LightComponent& light, const MoveComponent& move,
+		const FrustumComponent* fr)
 	{
-		const Camera* cam = m_is->m_cam;
 		Bool isTexLight = light.getShadowEnabled();
 		I i;
 		shader::SpotLight* baseslight = nullptr;
@@ -193,9 +199,10 @@ public:
 				0.0, 0.0, 0.5, 0.5, 
 				0.0, 0.0, 0.0, 1.0);
 			// bias * proj_l * view_l * world_c
-			slight.m_texProjectionMat = biasMat4 * light.getProjectionMatrix() 
-				* Mat4::combineTransformations(light.getViewMatrix(),
-				Mat4(cam->getWorldTransform()));
+			slight.m_texProjectionMat = 
+				biasMat4 
+				* fr->getViewProjectionMatrix() 
+				* Mat4(m_camMove->getWorldTransform());
 
 			// Transpose because of driver bug
 			slight.m_texProjectionMat.transpose();
@@ -219,7 +226,8 @@ public:
 
 		// Pos & dist
 		Vec4 pos = 
-			cam->getViewMatrix() * light.getWorldTransform().getOrigin().xyz1();
+			m_camFrustum->getViewMatrix() 
+			* move.getWorldTransform().getOrigin().xyz1();
 		baseslight->m_posRadius = Vec4(pos.xyz(), -1.0 / light.getDistance());
 
 		// Diff color and shadowmap ID now
@@ -230,8 +238,8 @@ public:
 		baseslight->m_specularColorTexId = light.getSpecularColor();
 
 		// Light dir
-		Vec3 lightDir = -light.getWorldTransform().getRotation().getZAxis();
-		lightDir = cam->getViewMatrix().getRotationPart() * lightDir;
+		Vec3 lightDir = -move.getWorldTransform().getRotation().getZAxis();
+		lightDir = m_camFrustum->getViewMatrix().getRotationPart() * lightDir;
 		baseslight->m_lightDir = Vec4(lightDir, 0.0);
 		
 		// Angles
@@ -242,14 +250,15 @@ public:
 			1.0);
 
 		// extend points
-		const PerspectiveFrustum& frustum = light.getFrustum();
+		const PerspectiveFrustum& frustum = 
+			static_cast<const PerspectiveFrustum&>(fr->getFrustum());
 
 		for(U i = 0; i < 4; i++)
 		{
-			Vec4 extendPoint = light.getWorldTransform().getOrigin() 
+			Vec4 extendPoint = move.getWorldTransform().getOrigin() 
 				+ frustum.getLineSegments()[i].getDirection();
 
-			extendPoint = cam->getViewMatrix() * extendPoint.xyz1();
+			extendPoint = m_camFrustum->getViewMatrix() * extendPoint.xyz1();
 			baseslight->m_extendPoints[i] = extendPoint;
 		}
 
@@ -257,11 +266,11 @@ public:
 	}
 
 	// Bin point light
-	void binLight(PointLight& light, U pos)
+	void binPointLight(SpatialComponent& sp, U pos)
 	{
 		// Do the tests
 		Tiler::Bitset bitset;
-		m_tiler->test(light.getSpatialCollisionShape(), true, &bitset);
+		m_tiler->test(sp.getSpatialCollisionShape(), true, &bitset);
 
 		// Bin to the correct tiles
 		PtrSize tilesCount = 
@@ -287,11 +296,14 @@ public:
 	}
 
 	// Bin spot light
-	void binLight(SpotLight& light, U pos)
+	void binSpotLight(
+		const LightComponent& light, 
+		const SpatialComponent& sp, 
+		U pos)
 	{
 		// Do the tests
 		Tiler::Bitset bitset;
-		m_tiler->test(light.getSpatialCollisionShape(), true, &bitset);
+		m_tiler->test(sp.getSpatialCollisionShape(), true, &bitset);
 
 		// Bin to the correct tiles
 		PtrSize tilesCount = 
@@ -483,8 +495,8 @@ Error Is::initInternal(const ConfigSet& config)
 	// Create framebuffer
 	//
 
-	err = m_r->createRenderTarget(m_r->getWidth(), m_r->getHeight(), GL_RGB8,
-			GL_RGB, GL_UNSIGNED_BYTE, 1, m_rt);
+	err = m_r->createRenderTarget(
+		m_r->getWidth(), m_r->getHeight(), GL_RGB8, 1, m_rt);
 	if(err) return err;
 
 	err = m_fb.create(cmdBuff, {{m_rt, GL_COLOR_ATTACHMENT0}});
@@ -534,7 +546,8 @@ Error Is::lightPass(GlCommandBufferHandle& cmdBuff)
 	Error err = ErrorCode::NONE;
 	Threadpool& threadPool = m_r->_getThreadpool();
 	m_cam = &m_r->getSceneGraph().getActiveCamera();
-	VisibilityTestResults& vi = m_cam->getVisibilityTestResults();
+	FrustumComponent& fr = m_cam->getComponent<FrustumComponent>();
+	VisibilityTestResults& vi = fr.getVisibilityTestResults();
 
 	//
 	// Quickly get the lights
@@ -542,25 +555,24 @@ Error Is::lightPass(GlCommandBufferHandle& cmdBuff)
 	U visiblePointLightsCount = 0;
 	U visibleSpotLightsCount = 0;
 	U visibleSpotTexLightsCount = 0;
-	Array<Light*, Sm::MAX_SHADOW_CASTERS> shadowCasters;
+	Array<SceneNode*, Sm::MAX_SHADOW_CASTERS> shadowCasters;
 
 	auto it = vi.getLightsBegin();
 	auto lend = vi.getLightsEnd();
 	for(; it != lend; ++it)
 	{
-		Light* light = staticCastPtr<Light*>((*it).m_node);
-		switch(light->getLightType())
+		SceneNode* node = (*it).m_node;
+		LightComponent& light = node->getComponent<LightComponent>();
+		switch(light.getLightType())
 		{
-		case Light::Type::POINT:
+		case LightComponent::LightType::POINT:
 			++visiblePointLightsCount;
 			break;
-		case Light::Type::SPOT:
-			{
-				SpotLight* slight = staticCastPtr<SpotLight*>(light);
-				
-				if(slight->getShadowEnabled())
+		case LightComponent::LightType::SPOT:
+			{				
+				if(light.getShadowEnabled())
 				{
-					shadowCasters[visibleSpotTexLightsCount++] = slight;
+					shadowCasters[visibleSpotTexLightsCount++] = node;
 				}
 				else
 				{
@@ -838,8 +850,9 @@ Error Is::updateCommonBlock(GlCommandBufferHandle& cmdBuff)
 	Vec3 groundLightDir;
 	if(m_groundLightEnabled)
 	{
-		blk.m_groundLightDir = 
-			Vec4(-m_cam->getViewMatrix().getColumn(1).xyz(), 1.0);
+		const Mat4& viewMat = 
+			m_cam->getComponent<FrustumComponent>().getViewMatrix();
+		blk.m_groundLightDir = Vec4(-viewMat.getColumn(1).xyz(), 1.0);
 	}
 
 	m_commonBuff.write(cmdBuff, cbuff, 0, 0, cbuff.getSize());

+ 7 - 6
src/renderer/Lf.cpp

@@ -183,8 +183,7 @@ Error Lf::initInternal(const ConfigSet& config)
 
 	// Create the render target
 	err = m_r->createRenderTarget(m_r->getPps().getHdr()._getWidth(), 
-		m_r->getPps().getHdr()._getHeight(), 
-		GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE, 1, m_rt);
+		m_r->getPps().getHdr()._getHeight(), GL_RGB8, 1, m_rt);
 	if(err) return err;
 
 	err = m_fb.create(cmdBuff, {{m_rt, GL_COLOR_ATTACHMENT0}});
@@ -212,7 +211,8 @@ Error Lf::runOcclusionTests(GlCommandBufferHandle& cmdb)
 	// Retrieve some things
 	SceneGraph& scene = m_r->getSceneGraph();
 	Camera& cam = scene.getActiveCamera();
-	VisibilityTestResults& vi = cam.getVisibilityTestResults();
+	FrustumComponent& camFr = cam.getComponent<FrustumComponent>();
+	VisibilityTestResults& vi = camFr.getVisibilityTestResults();
 
 	U totalCount = min<U>(vi.getLensFlaresCount(), m_maxFlares);
 	if(totalCount > 0)
@@ -229,7 +229,7 @@ Error Lf::runOcclusionTests(GlCommandBufferHandle& cmdb)
 		if(err) return err;
 		Mat4* mvpWrite = static_cast<Mat4*>(mvpCBuff.getBaseAddress());
 		ANKI_ASSERT(mvpWrite);
-		*mvpWrite = cam.getViewProjectionMatrix().getTransposed();
+		*mvpWrite = camFr.getViewProjectionMatrix().getTransposed();
 		m_mvpBuff.write(cmdb, mvpCBuff, 0, 0, sizeof(Mat4));
 		m_mvpBuff.bindShaderBuffer(cmdb, 0, sizeof(Mat4), 0);
 
@@ -305,7 +305,8 @@ Error Lf::run(GlCommandBufferHandle& cmdBuff)
 	// Retrieve some things
 	SceneGraph& scene = m_r->getSceneGraph();
 	Camera& cam = scene.getActiveCamera();
-	VisibilityTestResults& vi = cam.getVisibilityTestResults();
+	FrustumComponent& camFr = cam.getComponent<FrustumComponent>();
+	VisibilityTestResults& vi = camFr.getVisibilityTestResults();
 
 	U totalCount = min<U>(vi.getLensFlaresCount(), m_maxFlares);
 	if(totalCount > 0)
@@ -343,7 +344,7 @@ Error Lf::run(GlCommandBufferHandle& cmdBuff)
 
 			// Compute position
 			Vec4 lfPos = Vec4(lf.getWorldPosition().xyz(), 1.0);
-			Vec4 posClip = cam.getViewProjectionMatrix() * lfPos;
+			Vec4 posClip = camFr.getViewProjectionMatrix() * lfPos;
 
 			if(posClip.x() > posClip.w() || posClip.x() < -posClip.w()
 				|| posClip.y() > posClip.w() || posClip.y() < -posClip.w())

+ 5 - 5
src/renderer/Ms.cpp

@@ -26,16 +26,15 @@ Error Ms::createRt(U32 index, U32 samples)
 	Plane& plane = m_planes[index];
 
 	err = m_r->createRenderTarget(m_r->getWidth(), m_r->getHeight(),
-		GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT,
-		GL_UNSIGNED_INT, samples, plane.m_depthRt);
+		GL_DEPTH_COMPONENT24, samples, plane.m_depthRt);
 	if(err) return err;
 
 	err = m_r->createRenderTarget(m_r->getWidth(), m_r->getHeight(), GL_RGBA8,
-			GL_RGBA, GL_UNSIGNED_BYTE, samples, plane.m_rt0);
+			samples, plane.m_rt0);
 	if(err) return err;
 
 	err = m_r->createRenderTarget(m_r->getWidth(), m_r->getHeight(), GL_RGBA8,
-		GL_RGBA, GL_UNSIGNED_BYTE, samples, plane.m_rt1);
+		samples, plane.m_rt1);
 	if(err) return err;
 
 	GlDevice& gl = getGlDevice();
@@ -129,7 +128,8 @@ Error Ms::run(GlCommandBufferHandle& cmdb)
 	Camera& cam = m_r->getSceneGraph().getActiveCamera();
 	
 	VisibilityTestResults& vi =
-		m_r->getSceneGraph().getActiveCamera().getVisibilityTestResults();
+		m_r->getSceneGraph().getActiveCamera().
+		getComponent<FrustumComponent>().getVisibilityTestResults();
 
 	auto it = vi.getRenderablesBegin();
 	auto end = vi.getRenderablesEnd();

+ 2 - 2
src/renderer/Pps.cpp

@@ -53,8 +53,8 @@ Error Pps::initInternal(const ConfigSet& config)
 	err = cmdBuff.create(&getGlDevice());
 	if(err) return err;
 
-	err = m_r->createRenderTarget(m_r->getWidth(), m_r->getHeight(), GL_RGB8, 
-		GL_RGB, GL_UNSIGNED_BYTE, 1, m_rt);
+	err = m_r->createRenderTarget(
+		m_r->getWidth(), m_r->getHeight(), GL_RGB8, 1, m_rt);
 	if(err) return err;
 
 	err = m_fb.create(cmdBuff, {{m_rt, GL_COLOR_ATTACHMENT0}});

+ 4 - 3
src/renderer/Renderer.cpp

@@ -152,14 +152,15 @@ Error Renderer::render(SceneGraph& scene,
 
 	// Calc a few vars
 	//
-	Timestamp camUpdateTimestamp = cam.FrustumComponent::getTimestamp();
+	const FrustumComponent& fr = cam.getComponent<FrustumComponent>();
+	Timestamp camUpdateTimestamp = fr.getTimestamp();
 	if(m_projectionParamsUpdateTimestamp 
 			< m_scene->getActiveCameraChangeTimestamp()
 		|| m_projectionParamsUpdateTimestamp < camUpdateTimestamp
 		|| m_projectionParamsUpdateTimestamp == 1)
 	{
 		ANKI_ASSERT(cam.getCameraType() == Camera::Type::PERSPECTIVE);
-		computeProjectionParams(cam.getProjectionMatrix());
+		computeProjectionParams(fr.getProjectionMatrix());
 		m_projectionParamsUpdateTimestamp = getGlobTimestamp();
 	}
 
@@ -271,7 +272,7 @@ void Renderer::computeProjectionParams(const Mat4& m)
 
 //==============================================================================
 Error Renderer::createRenderTarget(U32 w, U32 h, GLenum internalFormat, 
-	GLenum format, GLenum type, U32 samples, GlTextureHandle& rt)
+	U32 samples, GlTextureHandle& rt)
 {
 	Error err = ErrorCode::NONE;
 

+ 1 - 2
src/renderer/RenderingPass.cpp

@@ -69,8 +69,7 @@ Error BlurringRenderingPass::initBlurring(
 	{
 		Direction& dir = m_dirs[i];
 
-		err = r.createRenderTarget(width, height, GL_RGB8, GL_RGB, 
-			GL_UNSIGNED_BYTE, 1, dir.m_rt);
+		err = r.createRenderTarget(width, height, GL_RGB8, 1, dir.m_rt);
 		if(err) return err;
 
 		// Set to bilinear because the blurring techniques take advantage of 

+ 6 - 5
src/renderer/Sm.cpp

@@ -111,7 +111,7 @@ void Sm::finishDraw(GlCommandBufferHandle& cmdBuff)
 }
 
 //==============================================================================
-Error Sm::run(Light* shadowCasters[], U32 shadowCastersCount, 
+Error Sm::run(SceneNode* shadowCasters[], U32 shadowCastersCount, 
 	GlCommandBufferHandle& cmdBuff)
 {
 	ANKI_ASSERT(m_enabled);
@@ -136,7 +136,7 @@ Error Sm::run(Light* shadowCasters[], U32 shadowCastersCount,
 }
 
 //==============================================================================
-Sm::Shadowmap& Sm::bestCandidate(Light& light)
+Sm::Shadowmap& Sm::bestCandidate(SceneNode& light)
 {
 	// Allready there
 	for(Shadowmap& sm : m_sms)
@@ -175,7 +175,7 @@ Sm::Shadowmap& Sm::bestCandidate(Light& light)
 
 //==============================================================================
 Error Sm::doLight(
-	Light& light, GlCommandBufferHandle& cmdBuff, Sm::Shadowmap*& sm)
+	SceneNode& light, GlCommandBufferHandle& cmdBuff, Sm::Shadowmap*& sm)
 {
 	Error err = ErrorCode::NONE;
 
@@ -183,11 +183,12 @@ Error Sm::doLight(
 
 	FrustumComponent& fr = light.getComponent<FrustumComponent>();
 	VisibilityTestResults& vi = fr.getVisibilityTestResults();
+	LightComponent& lcomp = light.getComponent<LightComponent>();
 
 	//
 	// Find last update
 	//
-	U32 lastUpdate = light.MoveComponent::getTimestamp();
+	U32 lastUpdate = light.getComponent<MoveComponent>().getTimestamp();
 	lastUpdate = std::max(lastUpdate, fr.getTimestamp());
 
 	auto it = vi.getRenderablesBegin();
@@ -222,7 +223,7 @@ Error Sm::doLight(
 	}
 
 	sm->m_timestamp = getGlobTimestamp();
-	light.setShadowMapIndex(sm - &m_sms[0]);
+	lcomp.setShadowMapIndex(sm - &m_sms[0]);
 
 	//
 	// Render

+ 5 - 4
src/renderer/Ssao.cpp

@@ -71,8 +71,7 @@ Error Ssao::createFb(GlFramebufferHandle& fb, GlTextureHandle& rt)
 {
 	Error err = ErrorCode::NONE;
 
-	err = m_r->createRenderTarget(m_width, m_height, GL_R8, GL_RED, 
-		GL_UNSIGNED_BYTE, 1, rt);
+	err = m_r->createRenderTarget(m_width, m_height, GL_R8, 1, rt);
 	if(err) return err;
 
 	// Set to bilinear because the blurring techniques take advantage of that
@@ -283,9 +282,11 @@ Error Ssao::run(GlCommandBufferHandle& cmdb)
 	cmdb.bindTextures(0, tarr.begin(), tarr.getSize());
 
 	// Write common block
+	const FrustumComponent& camFr = cam.getComponent<FrustumComponent>();
+
 	if(m_commonUboUpdateTimestamp 
 			< m_r->getProjectionParametersUpdateTimestamp()
-		|| m_commonUboUpdateTimestamp < cam.FrustumComponent::getTimestamp()
+		|| m_commonUboUpdateTimestamp < camFr.getTimestamp()
 		|| m_commonUboUpdateTimestamp == 1)
 	{
 		GlClientBufferHandle tmpBuff;
@@ -298,7 +299,7 @@ Error Ssao::run(GlCommandBufferHandle& cmdb)
 
 		blk.m_projectionParams = m_r->getProjectionParameters();
 
-		blk.m_projectionMatrix = cam.getProjectionMatrix().getTransposed();
+		blk.m_projectionMatrix = camFr.getProjectionMatrix().getTransposed();
 
 		m_uniformsBuff.write(cmdb, tmpBuff, 0, 0, tmpBuff.getSize());
 		m_commonUboUpdateTimestamp = getGlobTimestamp();

+ 1 - 2
src/renderer/Sslr.cpp

@@ -74,8 +74,7 @@ Error Sslr::init(const ConfigSet& config)
 	{
 		Direction& dir = m_dirs[(U)DirectionEnum::VERTICAL];
 
-		err = m_r->createRenderTarget(m_width, m_height, GL_RGB8, GL_RGB, 
-			GL_UNSIGNED_BYTE, 1, dir.m_rt);
+		err = m_r->createRenderTarget(m_width, m_height, GL_RGB8, 1, dir.m_rt);
 		if(err) return err;
 
 		// Set to bilinear because the blurring techniques take advantage of 

+ 14 - 9
src/renderer/Tiler.cpp

@@ -28,6 +28,7 @@ public:
 	Tiler* m_tiler = nullptr;
 	PerspectiveCamera* m_cam = nullptr;
 	Bool m_frustumChanged;
+	const PerspectiveFrustum* m_frustum = nullptr; ///< Cached value
 #if ANKI_TILER_ENABLE_GPU
 	const PixelArray* m_pixels = nullptr;
 #endif
@@ -39,15 +40,19 @@ public:
 #endif
 
 		PtrSize start, end;
-		Transform trf = Transform(m_cam->getWorldTransform());
+		const MoveComponent& move = m_cam->getComponent<MoveComponent>();
+		const FrustumComponent& fr = m_cam->getComponent<FrustumComponent>();
+		m_frustum = &static_cast<const PerspectiveFrustum&>(fr.getFrustum());
+
+		Transform trf = Transform(move.getWorldTransform());
 
 		if(m_frustumChanged)
 		{
 			// Re-calculate the planes in local space
 
-			const F32 fx = m_cam->getFovX();
-			const F32 fy = m_cam->getFovY();
-			const F32 n = m_cam->getNear();
+			const F32 fx = m_frustum->getFovX();
+			const F32 fy = m_frustum->getFovY();
+			const F32 n = m_frustum->getNear();
 
 			// Calculate l6 and o6 used to rotate the planes
 			F32 l = 2.0 * n * tan(fx / 2.0);
@@ -161,7 +166,7 @@ public:
 	void calcPlaneI(U i, const F32 o6)
 	{
 		Vec4 a, b;
-		const F32 n = m_cam->getNear();
+		const F32 n = m_frustum->getNear();
 		Plane& plane = m_tiler->m_planesY[i];
 		CHECK_PLANE_PTR(&plane);
 
@@ -180,7 +185,7 @@ public:
 	void calcPlaneJ(U j, const F32 l6)
 	{
 		Vec4 a, b;
-		const F32 n = m_cam->getNear();
+		const F32 n = m_frustum->getNear();
 		Plane& plane = m_tiler->m_planesX[j];
 		CHECK_PLANE_PTR(&plane);
 
@@ -255,8 +260,7 @@ Error Tiler::initInternal()
 
 	// Create FB
 	err = m_r->createRenderTarget(
-		m_r->getTilesCount().x(), m_r->getTilesCount().y(),
-		GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, 1, m_rt);
+		m_r->getTilesCount().x(), m_r->getTilesCount().y(), GL_RG32UI, 1, m_rt);
 	if(err) return err;
 
 	GlCommandBufferHandle cmdBuff;
@@ -341,7 +345,8 @@ void Tiler::updateTiles(Camera& cam)
 	// Issue parallel jobs
 	//
 	Array<UpdatePlanesPerspectiveCameraTask, Threadpool::MAX_THREADS> jobs;
-	U32 camTimestamp = cam.FrustumComponent::getTimestamp();
+	const FrustumComponent& camFr = cam.getComponent<FrustumComponent>();
+	U32 camTimestamp = camFr.getTimestamp();
 
 	// Do a job that transforms only the planes when:
 	// - it is the same camera as before and

+ 96 - 38
src/scene/Camera.cpp

@@ -7,39 +7,90 @@
 
 namespace anki {
 
+//==============================================================================
+// FeedbackComponent                                                           =
+//==============================================================================
+
+/// Feedback component.
+class FeedbackComponent: public SceneComponent
+{
+public:
+	FeedbackComponent(Camera* node)
+	:	SceneComponent(SceneComponent::Type::NONE, node)
+	{}
+
+	ANKI_USE_RESULT Error update(
+		SceneNode& node, F32, F32, Bool& updated)
+	{
+		updated = false;
+
+		MoveComponent& move = node.getComponent<MoveComponent>();
+		if(move.getTimestamp() == getGlobTimestamp())
+		{
+			Camera& cam = static_cast<Camera&>(node);
+			cam.onMoveComponentUpdate(move);
+		}
+
+		FrustumComponent& fr = node.getComponent<FrustumComponent>();
+		if(fr.getTimestamp() == getGlobTimestamp())
+		{
+			Camera& cam = static_cast<Camera&>(node);
+			cam.onFrustumComponentUpdate(fr);
+		}
+
+		return ErrorCode::NONE;
+	}
+};
+
 //==============================================================================
 // Camera                                                                      =
 //==============================================================================
 
 //==============================================================================
-Camera::Camera(SceneGraph* scene, Type type, Frustum* frustum) 
-:	SceneNode(scene), 
-	MoveComponent(this),
-	FrustumComponent(this, frustum),
-	SpatialComponent(this),
+Camera::Camera(SceneGraph* scene, Type type) 
+:	SceneNode(scene),
 	m_type(type)
 {}
 
 //==============================================================================
-Error Camera::create(const CString& name) 
+Error Camera::create(const CString& name, Frustum* frustum) 
 {
 	Error err = SceneNode::create(name);
+	if(err) return err;
 
-	// Init components
-	if(!err)
-	{
-		err = addComponent(static_cast<MoveComponent*>(this));
-	}
+	SceneComponent* comp;
 
-	if(!err)
-	{
-		err = addComponent(static_cast<FrustumComponent*>(this));
-	}
+	// Move component
+	comp = getSceneAllocator().newInstance<MoveComponent>(this);
+	if(comp == nullptr) return ErrorCode::OUT_OF_MEMORY;
 
-	if(!err)
-	{
-		err = addComponent(static_cast<SpatialComponent*>(this));
-	}
+	err = addComponent(comp);
+	if(err) return err;
+	comp->setAutomaticCleanup(true);
+
+	// Frustum component
+	comp = getSceneAllocator().newInstance<FrustumComponent>(this, frustum);
+	if(comp == nullptr) return ErrorCode::OUT_OF_MEMORY;
+
+	err = addComponent(comp);
+	if(err) return err;
+	comp->setAutomaticCleanup(true);
+
+	// Feedback component
+	comp = getSceneAllocator().newInstance<FeedbackComponent>(this);
+	if(comp == nullptr) return ErrorCode::OUT_OF_MEMORY;
+
+	err = addComponent(comp);
+	if(err) return err;
+	comp->setAutomaticCleanup(true);
+
+	// Spatial component
+	comp = getSceneAllocator().newInstance<SpatialComponent>(this, frustum);
+	if(comp == nullptr) return ErrorCode::OUT_OF_MEMORY;
+
+	err = addComponent(comp);
+	if(err) return err;
+	comp->setAutomaticCleanup(true);
 
 	return err;
 }
@@ -51,7 +102,7 @@ Camera::~Camera()
 //==============================================================================
 void Camera::lookAtPoint(const Vec3& point)
 {
-	MoveComponent& move = *this;
+	MoveComponent& move = getComponent<MoveComponent>();
 
 	Vec4 j = Vec4(0.0, 1.0, 0.0, 0.0);
 	Vec4 vdir = 
@@ -65,33 +116,25 @@ void Camera::lookAtPoint(const Vec3& point)
 }
 
 //==============================================================================
-void Camera::frustumUpdate()
+void Camera::onFrustumComponentUpdate(FrustumComponent& fr)
 {
-	// Frustum (it's marked for update)
-	FrustumComponent& fr = *this;
-	fr.setProjectionMatrix(fr.getFrustum().calculateProjectionMatrix());
-	fr.setViewProjectionMatrix(fr.getProjectionMatrix() * fr.getViewMatrix());
-
 	// Spatial
-	SpatialComponent& sp = *this;
+	SpatialComponent& sp = getComponent<SpatialComponent>();
 	sp.markForUpdate();
 }
 
 //==============================================================================
-Error Camera::onMoveComponentUpdate(SceneNode&, F32, F32)
+void Camera::onMoveComponentUpdate(MoveComponent& move)
 {
 	// Frustum
-	FrustumComponent& fr = *this;
-
-	fr.setViewMatrix(Mat4(getWorldTransform().getInverse()));
-	fr.setViewProjectionMatrix(fr.getProjectionMatrix() * fr.getViewMatrix());
-	fr.getFrustum().resetTransform(getWorldTransform());
+	FrustumComponent& fr = getComponent<FrustumComponent>();
+	fr.markTransformForUpdate();
+	fr.getFrustum().resetTransform(move.getWorldTransform());
 
 	// Spatial
-	SpatialComponent& sp = *this;
+	SpatialComponent& sp = getComponent<SpatialComponent>();
 	sp.markForUpdate();
-
-	return ErrorCode::NONE;
+	sp.setSpatialOrigin(move.getWorldTransform().getOrigin());
 }
 
 //==============================================================================
@@ -100,16 +143,31 @@ Error Camera::onMoveComponentUpdate(SceneNode&, F32, F32)
 
 //==============================================================================
 PerspectiveCamera::PerspectiveCamera(SceneGraph* scene)
-:	Camera(scene, Type::PERSPECTIVE, &m_frustum)
+:	Camera(scene, Type::PERSPECTIVE)
+{}
+
+//==============================================================================
+PerspectiveCamera::~PerspectiveCamera()
 {}
 
+//==============================================================================
+void PerspectiveCamera::setAll(F32 fovX, F32 fovY, F32 near, F32 far)
+{
+	m_frustum.setAll(fovX, fovY, near, far);
+	getComponent<FrustumComponent>().markShapeForUpdate();
+}
+
 //==============================================================================
 // OrthographicCamera                                                          =
 //==============================================================================
 
 //==============================================================================
 OrthographicCamera::OrthographicCamera(SceneGraph* scene)
-:	Camera(scene, Type::ORTHOGRAPHIC, &m_frustum)
+:	Camera(scene, Type::ORTHOGRAPHIC)
+{}
+
+//==============================================================================
+OrthographicCamera::~OrthographicCamera()
 {}
 
 } // end namespace anki

+ 26 - 0
src/scene/FrustumComponent.cpp

@@ -18,4 +18,30 @@ void FrustumComponent::setVisibilityTestResults(VisibilityTestResults* visible)
 	m_stats.m_lightsCount = visible->getLightsCount();
 }
 
+//==============================================================================
+Error FrustumComponent::update(SceneNode& node, F32, F32, Bool& updated)
+{
+	updated = false;
+
+	if(m_flags & SHAPE_MARKED_FOR_UPDATE)
+	{
+		updated = true;
+		m_pm = m_frustum->calculateProjectionMatrix();
+	}
+
+	if(m_flags & TRANSFORM_MARKED_FOR_UPDATE)
+	{
+		updated = true;
+		m_vm = Mat4(m_frustum->getTransform().getInverse());
+	}
+
+	if(updated)
+	{
+		m_vpm = m_pm * m_vm;
+		m_flags = 0;
+	}
+
+	return ErrorCode::NONE;
+}
+
 } // end namespace anki

+ 150 - 82
src/scene/Light.cpp

@@ -5,6 +5,9 @@
 
 #include "anki/scene/Light.h"
 #include "anki/scene/LensFlareComponent.h"
+#include "anki/scene/MoveComponent.h"
+#include "anki/scene/SpatialComponent.h"
+#include "anki/scene/FrustumComponent.h"
 
 namespace anki {
 
@@ -13,110 +16,164 @@ namespace anki {
 //==============================================================================
 
 //==============================================================================
-LightComponent::LightComponent(Light* node)
-:	SceneComponent(Type::LIGHT, node)
-{}
+LightComponent::LightComponent(SceneNode* node, LightType type)
+:	SceneComponent(Type::LIGHT, node),
+	m_type(type)
+{
+	setInnerAngle(toRad(45.0));
+	setOuterAngle(toRad(30.0));
+	m_radius = 1.0;
+}
+
+//==============================================================================
+Error LightComponent::update(SceneNode&, F32, F32, Bool& updated)
+{
+	if(m_dirty)
+	{
+		updated = true;
+		m_dirty = false;
+	}
+
+	return ErrorCode::NONE;
+}
+
+//==============================================================================
+// LightFeedbackComponent                                                      =
+//==============================================================================
+
+/// Feedback component.
+class LightFeedbackComponent: public SceneComponent
+{
+public:
+	LightFeedbackComponent(SceneNode* node)
+	:	SceneComponent(SceneComponent::Type::NONE, node)
+	{}
+
+	Error update(SceneNode& node, F32, F32, Bool& updated) override
+	{
+		updated = false;
+		Light& lnode = static_cast<Light&>(node);
+
+		MoveComponent& move = node.getComponent<MoveComponent>();
+		if(move.getTimestamp() == getGlobTimestamp())
+		{
+			// Move updated
+			lnode.onMoveUpdate(move);
+		}
+
+		LightComponent& light = node.getComponent<LightComponent>();
+		if(light.getTimestamp() == getGlobTimestamp())
+		{
+			// Shape updated
+			lnode.onShapeUpdate(light);
+		}
+
+		return ErrorCode::NONE;
+	}
+};
 
 //==============================================================================
 // Light                                                                       =
 //==============================================================================
 
 //==============================================================================
-Light::Light(SceneGraph* scene, Type t)
-:	SceneNode(scene),
-	LightComponent(this),
-	MoveComponent(this),
-	SpatialComponent(this),
-	m_type(t)
+Light::Light(SceneGraph* scene)
+:	SceneNode(scene)
 {}
 
 //==============================================================================
-Error Light::create(const CString& name)
+Error Light::create(const CString& name, 
+	LightComponent::LightType type,
+	CollisionShape* shape)
 {
 	Error err = SceneNode::create(name);
-	
-	if(!err)
-	{
-		err = addComponent(static_cast<MoveComponent*>(this));
-	}
+	if(err) return err;
 
-	if(!err)
-	{
-		err = addComponent(static_cast<SpatialComponent*>(this));
-	}
+	SceneComponent* comp;
 
-	if(!err)
-	{
-		err = addComponent(static_cast<LightComponent*>(this));
-	}
+	// Move component
+	comp = getSceneAllocator().newInstance<MoveComponent>(this);
+	if(comp == nullptr) return ErrorCode::OUT_OF_MEMORY; 
+	comp->setAutomaticCleanup(true);
+
+	err = addComponent(comp);
+	if(err) return err;
+
+	// Light component
+	comp = getSceneAllocator().newInstance<LightComponent>(this, type);
+	if(comp == nullptr) return ErrorCode::OUT_OF_MEMORY; 
+	comp->setAutomaticCleanup(true);
+
+	err = addComponent(comp);
+	if(err) return err;
+
+	// Feedback component
+	comp = getSceneAllocator().newInstance<LightFeedbackComponent>(this);
+	if(comp == nullptr) return ErrorCode::OUT_OF_MEMORY; 
+	comp->setAutomaticCleanup(true);
+
+	err = addComponent(comp);
+	if(err) return err;
+
+	// Spatial component
+	comp = getSceneAllocator().newInstance<SpatialComponent>(this, shape);
+	if(comp == nullptr) return ErrorCode::OUT_OF_MEMORY; 
+	comp->setAutomaticCleanup(true);
+
+	err = addComponent(comp);
+	if(err) return err;
 
 	return err;
 }
 
 //==============================================================================
 Light::~Light()
-{
-	LensFlareComponent* flareComp = tryGetComponent<LensFlareComponent>();
-	if(flareComp)
-	{
-		getSceneAllocator().deleteInstance(flareComp);
-	}
-}
+{}
 
 //==============================================================================
-void Light::frustumUpdate()
+void Light::onMoveUpdateCommon(MoveComponent& move)
 {
 	// Update the frustums
 	Error err = iterateComponentsOfType<FrustumComponent>(
 		[&](FrustumComponent& fr) -> Error
 	{
-		fr.setProjectionMatrix(fr.getFrustum().calculateProjectionMatrix());
-		fr.setViewProjectionMatrix(
-			fr.getProjectionMatrix() * fr.getViewMatrix());
+		fr.markTransformForUpdate();
+		fr.getFrustum().resetTransform(move.getWorldTransform());
 
 		return ErrorCode::NONE;
 	});
 
 	(void)err;
 
-	// Mark the spatial for update
+	// Update the spatial
 	SpatialComponent& sp = getComponent<SpatialComponent>();
 	sp.markForUpdate();
+	sp.setSpatialOrigin(move.getWorldTransform().getOrigin());
+
+	// Update the lens flare
+	LensFlareComponent* lf = tryGetComponent<LensFlareComponent>();
+	if(lf)
+	{
+		lf->setWorldPosition(move.getWorldTransform().getOrigin());
+	}
 }
 
 //==============================================================================
-void Light::onMoveComponentUpdateCommon()
+void Light::onShapeUpdateCommon(LightComponent& light)
 {
-	MoveComponent& move = *this;
-
 	// Update the frustums
 	Error err = iterateComponentsOfType<FrustumComponent>(
 		[&](FrustumComponent& fr) -> Error
 	{
-		fr.setProjectionMatrix(fr.getFrustum().calculateProjectionMatrix());
-		fr.setViewMatrix(Mat4(move.getWorldTransform().getInverse()));
-		fr.setViewProjectionMatrix(
-			fr.getProjectionMatrix() * fr.getViewMatrix());
-
-		fr.getFrustum().resetTransform(move.getWorldTransform());
-
-		fr.markForUpdate();
-
+		fr.markShapeForUpdate();
 		return ErrorCode::NONE;
 	});
 
 	(void)err;
 
-	// Update the spatial
+	// Mark the spatial for update
 	SpatialComponent& sp = getComponent<SpatialComponent>();
 	sp.markForUpdate();
-
-	// Update the lens flare
-	LensFlareComponent* lf = tryGetComponent<LensFlareComponent>();
-	if(lf)
-	{
-		lf->setWorldPosition(move.getWorldTransform().getOrigin());
-	}
 }
 
 //==============================================================================
@@ -139,7 +196,12 @@ Error Light::loadLensFlare(const CString& filename)
 
 	if(!err)
 	{
-		err = addComponent(flareComp);	
+		err = addComponent(flareComp);
+	}
+
+	if(!err)
+	{
+		flareComp->setAutomaticCleanup(true);
 	}
 
 	// Clean up on any error
@@ -157,26 +219,33 @@ Error Light::loadLensFlare(const CString& filename)
 
 //==============================================================================
 PointLight::PointLight(SceneGraph* scene)
-:	Light(scene, Light::Type::POINT)
+:	Light(scene)
 {}
 
 //==============================================================================
 Error PointLight::create(const CString& name)
 {
-	return Light::create(name);
+	return Light::create(name, LightComponent::LightType::POINT, &m_sphereW);
 }
 
 //==============================================================================
-Error PointLight::onMoveComponentUpdate(SceneNode&, F32, F32)
+void PointLight::onMoveUpdate(MoveComponent& move)
 {
-	m_sphereW.setCenter(getWorldTransform().getOrigin());
-	onMoveComponentUpdateCommon();
-	return ErrorCode::NONE;
+	onMoveUpdateCommon(move);
+	m_sphereW.setCenter(move.getWorldTransform().getOrigin());
+}
+
+//==============================================================================
+void PointLight::onShapeUpdate(LightComponent& light)
+{
+	onShapeUpdateCommon(light);
+	m_sphereW.setRadius(light.getRadius());
 }
 
 //==============================================================================
 Error PointLight::frameUpdate(F32 prevUpdateTime, F32 crntTime)
 {
+#if 0
 	if(getShadowEnabled() && m_shadowData == nullptr)
 	{
 		m_shadowData = getSceneAllocator().newInstance<ShadowData>(this);
@@ -204,6 +273,7 @@ Error PointLight::frameUpdate(F32 prevUpdateTime, F32 crntTime)
 		trfs[4].setRotation(Mat3x4(Mat3(Axisang(ang, axis))));
 		trfs[5].setRotation(Mat3x4(Mat3(Axisang(-ang, axis))));
 	}
+#endif
 
 	return ErrorCode::NONE;
 }
@@ -214,42 +284,40 @@ Error PointLight::frameUpdate(F32 prevUpdateTime, F32 crntTime)
 
 //==============================================================================
 SpotLight::SpotLight(SceneGraph* scene)
-:	Light(scene, Light::Type::SPOT),
-	FrustumComponent(this, &m_frustum)
+:	Light(scene)
 {}
 
 //==============================================================================
 Error SpotLight::create(const CString& name)
 {
-	Error err = Light::create(name);
-
-	if(!err)
-	{
-		err = addComponent(static_cast<FrustumComponent*>(this));
-	}
+	Error err = Light::create(
+		name, LightComponent::LightType::SPOT, &m_frustum);
+	if(err) return err;
 
-	if(!err)
-	{
-		const F32 ang = toRad(45.0);
-		const F32 dist = 1.0;
-		m_frustum.setAll(ang, ang, 0.1, dist);
-	}
+	FrustumComponent* fr = 
+		getSceneAllocator().newInstance<FrustumComponent>(this, &m_frustum);
+	if(fr == nullptr) return ErrorCode::OUT_OF_MEMORY;
+	
+	err = addComponent(fr);
+	if(err) return err;
 
 	return err;
 }
 
 //==============================================================================
-Error SpotLight::onMoveComponentUpdate(SceneNode&, F32, F32)
+void SpotLight::onMoveUpdate(MoveComponent& move)
 {
-	m_frustum.resetTransform(getWorldTransform());
-	onMoveComponentUpdateCommon();
-	return ErrorCode::NONE;
+	onMoveUpdateCommon(move);
+	m_frustum.resetTransform(move.getWorldTransform());
 }
 
 //==============================================================================
-Error SpotLight::loadTexture(const CString& filename)
+void SpotLight::onShapeUpdate(LightComponent& light)
 {
-	return m_tex.load(filename, &getResourceManager());
+	onShapeUpdateCommon(light);
+	m_frustum.setFovX(light.getOuterAngle());
+	m_frustum.setFovY(light.getOuterAngle());
+	m_frustum.setFar(light.getDistance());
 }
 
 } // end namespace anki

+ 143 - 88
src/scene/ModelNode.cpp

@@ -14,48 +14,87 @@
 
 namespace anki {
 
+//==============================================================================
+// ModelPatchRenderComponent                                                   =
+//==============================================================================
+
+/// Render component implementation.
+class ModelPatchRenderComponent: public RenderComponent
+{
+public:
+	ModelPatchNode* m_node;
+
+	ModelPatchRenderComponent(ModelPatchNode* node)
+	:	RenderComponent(node),
+		m_node(node)
+	{}
+
+	ANKI_USE_RESULT Error buildRendering(RenderingBuildData& data) override
+	{
+		return m_node->buildRendering(data);
+	}
+
+	const Material& getMaterial() override
+	{
+		return m_node->m_modelPatch->getMaterial();
+	}
+
+	void getRenderWorldTransform(U index, Transform& trf) override
+	{
+		m_node->getRenderWorldTransform(index, trf);
+	}
+
+	Bool getHasWorldTransforms() override
+	{
+		return true;
+	}
+};
+
 //==============================================================================
 // ModelPatchNode                                                              =
 //==============================================================================
 
 //==============================================================================
 ModelPatchNode::ModelPatchNode(SceneGraph* scene)
-:	SceneNode(scene),
-	RenderComponent(this),
-	SpatialComponent(this)
+:	SceneNode(scene)
 {}
 
+//==============================================================================
+ModelPatchNode::~ModelPatchNode()
+{
+	m_spatials.destroy(getSceneAllocator());
+}
+
 //==============================================================================
 Error ModelPatchNode::create(const CString& name, 
 	const ModelPatchBase* modelPatch)
 {
 	ANKI_ASSERT(modelPatch);
 	Error err = SceneNode::create(name);
+	if(err) return err;
 
 	m_modelPatch = modelPatch;
 
-	if(!err)
-	{
-		err = addComponent(static_cast<RenderComponent*>(this));
-	}
+	// Spatial component
+	SceneComponent* comp = getSceneAllocator().newInstance<SpatialComponent>(
+		this, &m_obb);
+	if(comp == nullptr) return ErrorCode::OUT_OF_MEMORY;
 
-	if(!err)
-	{
-		err = addComponent(static_cast<SpatialComponent*>(this));
-	}
+	err = addComponent(comp, true);
+	if(err) return err;
 
-	if(!err)
-	{
-		err = RenderComponent::create();
-	}
+	// Render component
+	RenderComponent* rcomp = 
+		getSceneAllocator().newInstance<ModelPatchRenderComponent>(this);
+	comp = rcomp;
+	if(comp == nullptr) return ErrorCode::OUT_OF_MEMORY;
 
-	return err;
-}
+	err = addComponent(comp, true);
+	if(err) return err;
 
-//==============================================================================
-ModelPatchNode::~ModelPatchNode()
-{
-	m_spatials.destroy(getSceneAllocator());
+	err = rcomp->create();
+
+	return err;
 }
 
 //==============================================================================
@@ -121,7 +160,6 @@ void ModelPatchNode::getRenderWorldTransform(U index, Transform& trf)
 		ModelNode* mnode = staticCastPtr<ModelNode*>(parent);
 
 		--index;
-		ANKI_ASSERT(index < mnode->m_transforms.getSize());
 		trf = mnode->m_transforms[index];
 	}
 }
@@ -184,12 +222,41 @@ Error ModelPatchNode::updateInstanceSpatials(
 				inst.getWorldTransform());
 
 			sp.markForUpdate();
+			sp.setSpatialOrigin(inst.getWorldTransform().getOrigin());
 		}
 	}
 
 	return err;
 }
 
+//==============================================================================
+// ModelNodeFeedbackComponent                                                  =
+//==============================================================================
+
+/// Feedback component.
+class ModelNodeFeedbackComponent: public SceneComponent
+{
+public:
+	ModelNodeFeedbackComponent(SceneNode* node)
+	:	SceneComponent(SceneComponent::Type::NONE, node)
+	{}
+
+	ANKI_USE_RESULT Error update(
+		SceneNode& node, F32, F32, Bool& updated)
+	{
+		updated = false;
+
+		MoveComponent& move = node.getComponent<MoveComponent>();
+		if(move.getTimestamp() == getGlobTimestamp())
+		{
+			ModelNode& mnode = static_cast<ModelNode&>(node);
+			mnode.onMoveComponentUpdate(move);
+		}
+
+		return ErrorCode::NONE;
+	}
+};
+
 //==============================================================================
 // ModelNode                                                                   =
 //==============================================================================
@@ -197,7 +264,6 @@ Error ModelPatchNode::updateInstanceSpatials(
 //==============================================================================
 ModelNode::ModelNode(SceneGraph* scene)
 : 	SceneNode(scene),
-	MoveComponent(this),
 	m_transformsTimestamp(0)
 {}
 
@@ -207,11 +273,6 @@ ModelNode::~ModelNode()
 	m_modelPatches.destroy(getSceneAllocator());
 	m_transforms.destroy(getSceneAllocator());
 
-	if(m_bodyComp)
-	{
-		getSceneAllocator().deleteInstance(m_bodyComp);
-	}
-
 	if(m_body)
 	{
 		getSceneAllocator().deleteInstance(m_body);
@@ -224,41 +285,46 @@ Error ModelNode::create(const CString& name, const CString& modelFname)
 	Error err = ErrorCode::NONE;
 
 	err = SceneNode::create(name);
+	if(err) return err;
 
-	if(!err)
-	{
-		err = addComponent(static_cast<MoveComponent*>(this));
-	}
+	SceneComponent* comp;
 
-	if(!err)
-	{
-		err = m_model.load(modelFname, &getResourceManager());
-	}
+	err = m_model.load(modelFname, &getResourceManager());
+	if(err) return err;
 
-	if(!err)
+	err = m_modelPatches.create(
+		getSceneAllocator(), m_model->getModelPatches().getSize(), nullptr);
+	if(err) return err;
+
+	U count = 0;
+	auto it = m_model->getModelPatches().getBegin();
+	auto end = m_model->getModelPatches().getEnd();
+	for(; it != end; it++)
 	{
-		err = m_modelPatches.create(
-			getSceneAllocator(), m_model->getModelPatches().getSize());
+		ModelPatchNode* mpn;
+		err = getSceneGraph().newSceneNode(CString(), mpn, *it);
+		if(err) return err;
+
+		m_modelPatches[count++] = mpn;
+		err = addChild(mpn);
+		if(err) return err;
 	}
 
-	if(!err)
-	{
-		U count = 0;
-		auto it = m_model->getModelPatches().getBegin();
-		auto end = m_model->getModelPatches().getEnd();
-		for(; !err && it != end; it++)
-		{
-			ModelPatchNode* mpn;
-			err = getSceneGraph().newSceneNode(CString(), mpn, *it);
+	// Move component
+	comp = getSceneAllocator().newInstance<MoveComponent>(this);
+	if(comp == nullptr) return ErrorCode::OUT_OF_MEMORY;
 
-			if(!err)
-			{
-				m_modelPatches[count++] = mpn;
+	err = addComponent(comp);
+	if(err) return err;
+	comp->setAutomaticCleanup(true);
 
-				err = addChild(mpn);
-			}
-		}
-	}
+	// Feedback component
+	comp = getSceneAllocator().newInstance<ModelNodeFeedbackComponent>(this);
+	if(comp == nullptr) return ErrorCode::OUT_OF_MEMORY;
+
+	err = addComponent(comp);
+	if(err) return err;
+	comp->setAutomaticCleanup(true);
 
 	// Load rigid body
 	const PhysicsCollisionShape* shape = m_model->getPhysicsCollisionShape();
@@ -270,23 +336,16 @@ Error ModelNode::create(const CString& name, const CString& modelFname)
 
 		m_body = 
 			getSceneGraph()._getPhysicsWorld().newBody<PhysicsBody>(init);
-		if(m_body == nullptr)
-		{
-			return ErrorCode::OUT_OF_MEMORY;
-		}
+		if(m_body == nullptr) return ErrorCode::OUT_OF_MEMORY;
 
-		m_bodyComp = 
+		BodyComponent* bodyComp = 
 			getSceneAllocator().newInstance<BodyComponent>(this, m_body);
-		if(m_bodyComp == nullptr)
-		{
-			return ErrorCode::OUT_OF_MEMORY;
-		}
+		if(bodyComp == nullptr) return ErrorCode::OUT_OF_MEMORY;
 
-		err = addComponent(m_bodyComp);
-		if(err)
-		{
-			return err;
-		}
+		err = addComponent(bodyComp);
+		if(err) return err;
+
+		bodyComp->setAutomaticCleanup(true);
 	}
 
 	return err;
@@ -304,10 +363,7 @@ Error ModelNode::frameUpdate(F32, F32)
 	Timestamp instancesTimestamp = 0;
 
 	err = instanceMoves.create(64);
-	if(err)
-	{
-		return err;
-	}
+	if(err)	return err;
 
 	err = visitChildren([&](SceneNode& sn) -> Error
 	{
@@ -318,7 +374,7 @@ Error ModelNode::frameUpdate(F32, F32)
 			instanceMoves[instanceMovesCount++] = &move;
 
 			instancesTimestamp = 
-				std::max(instancesTimestamp, move.getTimestamp());
+				max(instancesTimestamp, move.getTimestamp());
 		}
 
 		return ErrorCode::NONE;
@@ -333,9 +389,10 @@ Error ModelNode::frameUpdate(F32, F32)
 		{
 			fullUpdate = true;
 			err = m_transforms.resize(getSceneAllocator(), instanceMovesCount);
+			if(err) return err;
 		}
 
-		if(!err && (fullUpdate || m_transformsTimestamp < instancesTimestamp))
+		if(fullUpdate || m_transformsTimestamp < instancesTimestamp)
 		{
 			m_transformsTimestamp = instancesTimestamp;
 
@@ -346,15 +403,13 @@ Error ModelNode::frameUpdate(F32, F32)
 		}
 
 		// Update children
-		if(!err)
+		auto it = m_modelPatches.getBegin();
+		auto end = m_modelPatches.getEnd();
+		for(; it != end; ++it)
 		{
-			auto it = m_modelPatches.getBegin();
-			auto end = m_modelPatches.getEnd();
-			for(; it != end && !err; ++it)
-			{
-				err = (*it)->updateInstanceSpatials(
-					&instanceMoves[0], instanceMovesCount);
-			}
+			err = (*it)->updateInstanceSpatials(
+				&instanceMoves[0], instanceMovesCount);
+			return err;
 		}
 	}
 
@@ -362,18 +417,18 @@ Error ModelNode::frameUpdate(F32, F32)
 }
 
 //==============================================================================
-Error ModelNode::onMoveComponentUpdate(SceneNode&, F32, F32)
+void ModelNode::onMoveComponentUpdate(MoveComponent& move)
 {
 	// Inform the children about the moves
 	for(ModelPatchNode* child : m_modelPatches)
 	{
 		child->m_obb = child->m_modelPatch->getBoundingShape().getTransformed(
-			getWorldTransform());
+			move.getWorldTransform());
 
-		child->SpatialComponent::markForUpdate();
+		SpatialComponent& sp = child->getComponent<SpatialComponent>();
+		sp.markForUpdate();
+		sp.setSpatialOrigin(move.getWorldTransform().getOrigin());
 	}
-
-	return ErrorCode::NONE;
 }
 
 } // end namespace anki

+ 136 - 90
src/scene/ParticleEmitter.cpp

@@ -52,7 +52,7 @@ static Vec3 getRandom(const Vec3& initial, const Vec3& deviation)
 //==============================================================================
 
 //==============================================================================
-void ParticleBase::revive(const ParticleEmitter& pe,
+void ParticleBase::revive(const ParticleEmitter& pe, const Transform& trf,
 	F32 /*prevUpdateTime*/, F32 crntTime)
 {
 	ANKI_ASSERT(isDead());
@@ -87,10 +87,10 @@ void ParticleSimple::simulate(const ParticleEmitter& pe,
 }
 
 //==============================================================================
-void ParticleSimple::revive(const ParticleEmitter& pe,
+void ParticleSimple::revive(const ParticleEmitter& pe, const Transform& trf,
 	F32 prevUpdateTime, F32 crntTime)
 {
-	ParticleBase::revive(pe, prevUpdateTime, crntTime);
+	ParticleBase::revive(pe, trf, prevUpdateTime, crntTime);
 	m_velocity = Vec4(0.0);
 
 	const ParticleEmitterProperties& props = pe;
@@ -102,7 +102,7 @@ void ParticleSimple::revive(const ParticleEmitter& pe,
 	m_position = getRandom(props.m_particle.m_startingPos,
 		props.m_particle.m_startingPosDeviation).xyz0();
 
-	m_position += pe.getWorldTransform().getOrigin();
+	m_position += trf.getOrigin();
 }
 
 //==============================================================================
@@ -195,16 +195,76 @@ void Particle::revive(const ParticleEmitter& pe,
 }
 #endif
 
+//==============================================================================
+// ParticleEmitterRenderComponent                                              =
+//==============================================================================
+
+/// The derived render component for particle emitters.
+class ParticleEmitterRenderComponent: public RenderComponent
+{
+public:
+	ParticleEmitter* m_node;
+
+	ParticleEmitterRenderComponent(ParticleEmitter* node)
+	:	RenderComponent(node),
+		m_node(node)
+	{}
+
+	ANKI_USE_RESULT Error buildRendering(RenderingBuildData& data) override
+	{
+		return m_node->buildRendering(data);
+	}
+
+	const Material& getMaterial() override
+	{
+		return m_node->m_particleEmitterResource->getMaterial();
+	}
+
+	void getRenderWorldTransform(U index, Transform& trf) override
+	{
+		m_node->getRenderWorldTransform(index, trf);
+	}
+
+	Bool getHasWorldTransforms() override
+	{
+		return true;
+	}
+};
+
+//==============================================================================
+// MoveFeedbackComponent                                                       =
+//==============================================================================
+
+/// Feedback component
+class MoveFeedbackComponent: public SceneComponent
+{
+public:
+	MoveFeedbackComponent(ParticleEmitter* node)
+	:	SceneComponent(SceneComponent::Type::NONE, node)
+	{}
+
+	ANKI_USE_RESULT Error update(
+		SceneNode& node, F32, F32, Bool& updated) override
+	{
+		updated = false;
+
+		MoveComponent& move = node.getComponent<MoveComponent>();
+		if(move.getTimestamp() == getGlobTimestamp())
+		{
+			static_cast<ParticleEmitter&>(node).onMoveComponentUpdate(move);
+		}
+
+		return ErrorCode::NONE;
+	}
+};
+
 //==============================================================================
 // ParticleEmitter                                                             =
 //==============================================================================
 
 //==============================================================================
 ParticleEmitter::ParticleEmitter(SceneGraph* scene)
-:	SceneNode(scene),
-	SpatialComponent(this),
-	MoveComponent(this),
-	RenderComponent(this)
+:	SceneNode(scene)
 {}
 
 //==============================================================================
@@ -228,80 +288,78 @@ Error ParticleEmitter::create(
 	const CString& name, const CString& filename)
 {
 	Error err = SceneNode::create(name);
+	SceneComponent* comp;
 
-	if(!err)
-	{
-		err = addComponent(static_cast<MoveComponent*>(this));
-	}
+	// Load resource
+	err = m_particleEmitterResource.load(filename, &getResourceManager());
+	if(err) return err;
 
-	if(!err)
-	{
-		err = addComponent(static_cast<SpatialComponent*>(this));
-	}
+	// Move component
+	comp = getSceneAllocator().newInstance<MoveComponent>(this);
+	if(comp == nullptr) return ErrorCode::OUT_OF_MEMORY;
 
-	if(!err)
-	{
-		err = addComponent(static_cast<RenderComponent*>(this));
-	}
+	err = addComponent(comp);
+	if(err) return err;
+	comp->setAutomaticCleanup(true);
 
-	if(!err)
-	{
-		m_obb.setCenter(Vec4(0.0));
-		m_obb.setExtend(Vec4(1.0, 1.0, 1.0, 0.0));
-		m_obb.setRotation(Mat3x4::getIdentity());
+	// Spatial component
+	comp = getSceneAllocator().newInstance<SpatialComponent>(this, &m_obb);
+	if(comp == nullptr) return ErrorCode::OUT_OF_MEMORY;
 
-		// Load resource
-		err = m_particleEmitterResource.load(filename, &getResourceManager());
-	}
+	err = addComponent(comp);
+	if(err) return err;
+	comp->setAutomaticCleanup(true);
+
+	// Render component
+	ParticleEmitterRenderComponent* rcomp = 
+		getSceneAllocator().newInstance<ParticleEmitterRenderComponent>(this);
+	if(rcomp == nullptr) return ErrorCode::OUT_OF_MEMORY;
+
+	err = rcomp->create();
+	if(err) return err;
+
+	err = addComponent(comp);
+	if(err) return err;
+	comp->setAutomaticCleanup(true);
+
+	// Other
+	m_obb.setCenter(Vec4(0.0));
+	m_obb.setExtend(Vec4(1.0, 1.0, 1.0, 0.0));
+	m_obb.setRotation(Mat3x4::getIdentity());
 
 	// copy the resource to me
-	if(!err)
-	{
-		ParticleEmitterProperties& me = *this;
-		const ParticleEmitterProperties& other =
-			m_particleEmitterResource->getProperties();
-		me = other;
+	ParticleEmitterProperties& me = *this;
+	const ParticleEmitterProperties& other =
+		m_particleEmitterResource->getProperties();
+	me = other;
 
-		if(m_usePhysicsEngine)
-		{
-			err = createParticlesSimulation(&getSceneGraph());
-			m_simulationType = SimulationType::PHYSICS_ENGINE;
-		}
-		else
-		{
-			err = createParticlesSimpleSimulation();
-			m_simulationType = SimulationType::SIMPLE;
-		}
+	if(m_usePhysicsEngine)
+	{
+		err = createParticlesSimulation(&getSceneGraph());
+		m_simulationType = SimulationType::PHYSICS_ENGINE;
 	}
-
-	if(!err)
+	else
 	{
-		err = RenderComponent::create();
+		err = createParticlesSimpleSimulation();
+		m_simulationType = SimulationType::SIMPLE;
 	}
 
+	if(err) return err;
+
 	// Create the vertex buffer and object
-	//
 	GlCommandBufferHandle cmd;
-	if(!err)
-	{
-		GlDevice& gl = getSceneGraph()._getGlDevice();
-		err = cmd.create(&gl);
-	}
-
-	if(!err)
-	{
-		PtrSize buffSize = m_maxNumOfParticles * VERT_SIZE * 3;
-		err = m_vertBuff.create(cmd, GL_ARRAY_BUFFER, buffSize, 
-			GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
-	}
+	GlDevice& gl = getSceneGraph()._getGlDevice();
+	err = cmd.create(&gl);
+	if(err) return err;
 
-	if(!err)
-	{
-		// TODO Optimize that to avoid serialization
-		cmd.finish();
+	PtrSize buffSize = m_maxNumOfParticles * VERT_SIZE * 3;
+	err = m_vertBuff.create(cmd, GL_ARRAY_BUFFER, buffSize, 
+		GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
+	if(err) return err;
 
-		m_vertBuffMapping = (U8*)m_vertBuff.getPersistentMappingAddress();
-	}
+	cmd.finish(); /// TODO Optimize serialization
+	m_vertBuffMapping = 
+		static_cast<U8*>(m_vertBuff.getPersistentMappingAddress());
 
 	return err;
 }
@@ -352,19 +410,10 @@ Error ParticleEmitter::buildRendering(RenderingBuildData& data)
 }
 
 //==============================================================================
-const Material& ParticleEmitter::getMaterial()
-{
-	return m_particleEmitterResource->getMaterial();
-}
-
-//==============================================================================
-Error ParticleEmitter::onMoveComponentUpdate(
-	SceneNode& node, F32 prevTime, F32 crntTime)
+void ParticleEmitter::onMoveComponentUpdate(MoveComponent& move)
 {
 	m_identityRotation =
-		getWorldTransform().getRotation() == Mat3x4::getIdentity();
-
-	return ErrorCode::NONE;
+		move.getWorldTransform().getRotation() == Mat3x4::getIdentity();
 }
 
 //==============================================================================
@@ -515,13 +564,16 @@ Error ParticleEmitter::frameUpdate(F32 prevUpdateTime, F32 crntTime)
 	{
 		m_obb = Obb(Vec4(0.0), Mat3x4::getIdentity(), Vec4(0.001));
 	}
-	SpatialComponent::markForUpdate();
+	
+	getComponent<SpatialComponent>().markForUpdate();
 
 	//
 	// Emit new particles
 	//
 	if(m_timeLeftForNextEmission <= 0.0)
 	{
+		MoveComponent& move = getComponent<MoveComponent>();
+
 		U particlesCount = 0; // How many particles I am allowed to emmit
 		for(ParticleBase* pp : m_particles)
 		{
@@ -532,7 +584,7 @@ Error ParticleEmitter::frameUpdate(F32 prevUpdateTime, F32 crntTime)
 				continue;
 			}
 
-			p.revive(*this, prevUpdateTime, crntTime);
+			p.revive(*this, move.getWorldTransform(), prevUpdateTime, crntTime);
 
 			// do the rest
 			++particlesCount;
@@ -558,6 +610,7 @@ Error ParticleEmitter::doInstancingCalcs()
 {
 	Error err = ErrorCode::NONE;
 
+#if 0
 	//
 	// Gather the move components of the instances
 	//
@@ -567,10 +620,7 @@ Error ParticleEmitter::doInstancingCalcs()
 	Timestamp instancesTimestamp = 0;
 
 	err = instanceMoves.create(64);
-	if(err)
-	{
-		return err;
-	}
+	if(err)	return err;
 
 	err = SceneNode::visitChildren([&](SceneNode& sn) -> Error
 	{	
@@ -684,16 +734,11 @@ Error ParticleEmitter::doInstancingCalcs()
 			ANKI_ASSERT(count == m_transforms.getSize());
 		}
 	} // end if instancing
+#endif
 
 	return err;
 }
 
-//==============================================================================
-Bool ParticleEmitter::getHasWorldTransforms()
-{
-	return true;
-}
-
 //==============================================================================
 void ParticleEmitter::getRenderWorldTransform(U index, Transform& trf)
 {
@@ -712,7 +757,8 @@ void ParticleEmitter::getRenderWorldTransform(U index, Transform& trf)
 
 		// The particle positions are already in word space. Move them back to
 		// local space
-		Transform invTrf = getWorldTransform().getInverse();
+		const MoveComponent& move = getComponent<MoveComponent>();
+		Transform invTrf = move.getWorldTransform().getInverse();
 		trf = m_transforms[index].combineTransformations(invTrf);
 	}
 }

+ 11 - 1
src/scene/SceneNode.cpp

@@ -79,11 +79,19 @@ U32 SceneNode::getLastUpdateFrame() const
 }
 
 //==============================================================================
-Error SceneNode::addComponent(SceneComponent* comp)
+Error SceneNode::addComponent(SceneComponent* comp, Bool transferOwnership)
 {
 	ANKI_ASSERT(comp);
 	Error err = ErrorCode::NONE;
 
+#if ANKI_ASSERTIONS
+	err = iterateComponents([&](const SceneComponent& bcomp) -> Error
+	{
+		ANKI_ASSERT(comp != &bcomp);
+		return ErrorCode::NONE;
+	});
+#endif
+
 	if(m_components.getSize() < m_componentsCount + 1u)
 	{
 		// Not enough room
@@ -95,6 +103,8 @@ Error SceneNode::addComponent(SceneComponent* comp)
 	if(!err)
 	{
 		m_components[m_componentsCount++] = comp;
+
+		comp->setAutomaticCleanup(transferOwnership);
 	}
 
 	return err;

+ 9 - 6
src/scene/SpatialComponent.cpp

@@ -9,10 +9,15 @@
 namespace anki {
 
 //==============================================================================
-SpatialComponent::SpatialComponent(SceneNode* node, Flag flags)
-:	SceneComponent(Type::SPATIAL, node), 
-	Bitset<Flag>(flags)
+SpatialComponent::SpatialComponent(
+	SceneNode* node, 
+	const CollisionShape* shape, 
+	Flag flags)
+:	SceneComponent(Type::SPATIAL, node),
+	Bitset<Flag>(flags),
+	m_shape(shape)
 {
+	ANKI_ASSERT(shape);
 	markForUpdate();
 }
 
@@ -23,12 +28,10 @@ SpatialComponent::~SpatialComponent()
 //==============================================================================
 Error SpatialComponent::update(SceneNode&, F32, F32, Bool& updated)
 {
-	updated = false;
-
 	updated = bitsEnabled(Flag::MARKED_FOR_UPDATE);
 	if(updated)
 	{
-		getSpatialCollisionShape().computeAabb(m_aabb);
+		m_shape->computeAabb(m_aabb);
 		disableBits(Flag::MARKED_FOR_UPDATE);
 	}
 

+ 45 - 69
src/scene/StaticGeometryNode.cpp

@@ -9,15 +9,30 @@
 namespace anki {
 
 //==============================================================================
-// StaticGeometrySpatial                                                       =
+// StaticGeometryRenderComponent                                               =
 //==============================================================================
 
-//==============================================================================
-StaticGeometrySpatial::StaticGeometrySpatial(
-	SceneNode* node, const Obb* obb)
-:	SpatialComponent(node), 
-	m_obb(obb)
-{}
+/// The implementation of static geometry node renderable component.
+class StaticGeometryRenderComponent: public RenderComponent
+{
+public:
+	StaticGeometryPatchNode* m_node;
+
+	StaticGeometryRenderComponent(StaticGeometryPatchNode* node)
+	:	RenderComponent(node),
+		m_node(node)
+	{}
+
+	Error buildRendering(RenderingBuildData& data) override
+	{
+		return m_node->buildRendering(data);
+	}
+
+	const Material& getMaterial()
+	{
+		return m_node->m_modelPatch->getMaterial();
+	}
+};
 
 //==============================================================================
 // StaticGeometryPatchNode                                                     =
@@ -25,9 +40,7 @@ StaticGeometrySpatial::StaticGeometrySpatial(
 
 //==============================================================================
 StaticGeometryPatchNode::StaticGeometryPatchNode(SceneGraph* scene)
-:	SceneNode(scene),
-	SpatialComponent(this),
-	RenderComponent(this)
+:	SceneNode(scene)
 {}
 
 //==============================================================================
@@ -40,76 +53,40 @@ Error StaticGeometryPatchNode::create(
 	m_modelPatch = modelPatch;
 
 	err = SceneNode::create(name);
-	
-	if(!err)
-	{
-		err = addComponent(static_cast<SpatialComponent*>(this));
-	}
+	if(err) return err;
 
-	if(!err)
+	// Create spatial components
+	for(U i = 1; i < m_modelPatch->getSubMeshesCount() && !err; i++)
 	{
-		err = addComponent(static_cast<RenderComponent*>(this));
-	}
+		SpatialComponent* spatial =
+			getSceneAllocator().newInstance<SpatialComponent>(
+			this, &m_modelPatch->getBoundingShapeSub(i));
 
-	if(!err)
-	{	
-		err = RenderComponent::create();
-	}
+		if(spatial == nullptr) return ErrorCode::OUT_OF_MEMORY;
 
-	if(!err)
-	{
-		// Check if multimesh
-		if(m_modelPatch->getSubMeshesCount() > 1)
-		{
-			// If multimesh create additional spatial components
-
-			m_obb = &m_modelPatch->getBoundingShapeSub(0);
 
-			for(U i = 1; i < m_modelPatch->getSubMeshesCount() && !err; i++)
-			{
-				StaticGeometrySpatial* spatial =
-					getSceneAllocator().newInstance<StaticGeometrySpatial>(
-					this, &m_modelPatch->getBoundingShapeSub(i));
-
-				if(spatial)
-				{
-					err = addComponent(static_cast<SpatialComponent*>(spatial));
-				}
-				else
-				{
-					err = ErrorCode::OUT_OF_MEMORY;
-				}
-			}
-		}
-		else
-		{
-			// If not multimesh then set the current spatial component
+		err = addComponent(spatial);
+		if(err) return err;
 
-			m_obb = &modelPatch->getBoundingShape();
-		}
+		spatial->setSpatialOrigin(
+			m_modelPatch->getBoundingShapeSub(i).getCenter());
+		spatial->setAutomaticCleanup(true);
 	}
 
+	// Create render component
+	RenderComponent* rcomp = 
+		getSceneAllocator().newInstance<StaticGeometryRenderComponent>(this);
+	if(rcomp == nullptr) return ErrorCode::OUT_OF_MEMORY;
+	
+	err = addComponent(rcomp);
+	if(err) return err;
+
 	return err;
 }
 
 //==============================================================================
 StaticGeometryPatchNode::~StaticGeometryPatchNode()
-{
-	U i = 0;
-	Error err = iterateComponentsOfType<SpatialComponent>([&](
-		SpatialComponent& spatial)
-	{
-		if(i != 0)
-		{
-			getSceneAllocator().deleteInstance(&spatial);
-		}
-		++i;
-
-		return ErrorCode::NONE;
-	});
-
-	(void)err;
-}
+{}
 
 //==============================================================================
 Error StaticGeometryPatchNode::buildRendering(RenderingBuildData& data)
@@ -163,8 +140,7 @@ Error StaticGeometryPatchNode::buildRendering(RenderingBuildData& data)
 //==============================================================================
 StaticGeometryNode::StaticGeometryNode(SceneGraph* scene)
 :	SceneNode(scene)
-{
-}
+{}
 
 //==============================================================================
 Error StaticGeometryNode::create(const CString& name, const CString& filename)

+ 1 - 3
src/scene/Visibility.cpp

@@ -182,11 +182,9 @@ Error VisibilityTestTask::test(SceneNode& testedNode, Bool testingLight,
 			LightComponent* l = node.tryGetComponent<LightComponent>();
 			if(!err && l)
 			{
-				Light* light = staticCastPtr<Light*>(&node);
-
 				err = visible->moveBackLight(m_alloc, visibleNode);
 
-				if(!err && light->getShadowEnabled() && fr)
+				if(!err && l->getShadowEnabled() && fr)
 				{
 					err = test(node, true, 0, 0);
 				}

+ 33 - 32
testapp/Main.cpp

@@ -45,11 +45,16 @@ Error init()
 	Error err = ErrorCode::NONE;
 	ANKI_LOGI("Other init...");
 
+	SpotLight* spot;
+	PointLight* point;
+	MoveComponent* move;
+	LightComponent* lightc;
+
 	SceneGraph& scene = app->getSceneGraph();
 	MainRenderer& renderer = app->getMainRenderer();
 	ResourceManager& resources = app->getResourceManager();
 
-	scene.setAmbientColor(Vec4(0.1, 0.05, 0.05, 0.0) * 0.0);
+	scene.setAmbientColor(Vec4(0.1, 0.05, 0.05, 0.0) * 1.0);
 
 	if(getenv("PROFILE"))
 	{
@@ -63,7 +68,8 @@ Error init()
 	cam->setAll(
 		renderer.getAspectRatio() * toRad(ang),
 		toRad(ang), 0.5, 500.0);
-	cam->setLocalTransform(Transform(Vec4(17.0, 5.2, 0.0, 0),
+	cam->getComponent<MoveComponent>().
+		setLocalTransform(Transform(Vec4(17.0, 5.2, 0.0, 0),
 		Mat3x4(Euler(toRad(-10.0), toRad(90.0), toRad(0.0))),
 		1.0));
 	scene.setActiveCamera(cam);
@@ -95,18 +101,21 @@ Error init()
 	}
 #endif
 
-#if 1
-	SpotLight* spot;
+#if 0
 	err = scene.newSceneNode<SpotLight>("spot0", spot);
 	if(err) return err;
-	spot->setOuterAngle(toRad(45.0));
-	spot->setInnerAngle(toRad(15.0));
-	spot->setLocalTransform(Transform(Vec4(8.27936, 5.86285, 1.85526, 0.0),
+
+	lightc = spot->tryGetComponent<LightComponent>();
+	lightc->setOuterAngle(toRad(45.0));
+	lightc->setInnerAngle(toRad(15.0));
+	lightc->setDiffuseColor(Vec4(1.0));
+	lightc->setSpecularColor(Vec4(1.2));
+	lightc->setDistance(30.0);
+	lightc->setShadowEnabled(true);
+
+	move = spot->tryGetComponent<MoveComponent>();
+	move->setLocalTransform(Transform(Vec4(8.27936, 5.86285, 1.85526, 0.0),
 		Mat3x4(Quat(-0.125117, 0.620465, 0.154831, 0.758544)), 1.0));
-	spot->setDiffuseColor(Vec4(1.0));
-	spot->setSpecularColor(Vec4(1.2));
-	spot->setDistance(30.0);
-	spot->setShadowEnabled(true);
 
 #endif
 
@@ -210,12 +219,12 @@ Error init()
 	}
 #endif
 
-#if 0
+#if 1
 	// horse
 	err = scene.newSceneNode<ModelNode>("horse", horse, 
 		"models/horse/horse.ankimdl");
 	if(err) return err;
-	horse->setLocalTransform(
+	horse->getComponent<MoveComponent>().setLocalTransform(
 		Transform(Vec4(-2, 0, 0, 0.0), Mat3x4::getIdentity(), 0.7));
 
 	//horse = scene.newSceneNode<ModelNode>("crate", "models/crate0/crate0.ankimdl");
@@ -230,29 +239,21 @@ Error init()
 		0.7));*/
 #endif
 
-	if(1)
+	if(0)
 	{
-		PointLight* point;
 		err = scene.newSceneNode<PointLight>("plight0", point);
-		point->setLocalOrigin(Vec4(0.0, 1.4, 0.6, 0.0));
-		point->setRadius(30.0);
-		point->setDiffuseColor(Vec4(0.6));
-		point->setSpecularColor(Vec4(0.6, 0.6, 0.3, 1.0));
 		if(err) return err;
-	}
 
-	if(0)
-	{
-		PointLight* point;
-		err = scene.newSceneNode<PointLight>("plight1", point);
-		point->setLocalOrigin(Vec4(0.0, 1.1, -15.3, 0.0));
-		point->setRadius(30.0);
-		point->setDiffuseColor(Vec4(1.0));
-		point->setSpecularColor(Vec4(1.0, 0.0, 1.0, 0.0));
-		if(err) return err;
+		lightc = point->tryGetComponent<LightComponent>();
+		lightc->setRadius(30.0);
+		lightc->setDiffuseColor(Vec4(0.6));
+		lightc->setSpecularColor(Vec4(0.6, 0.6, 0.3, 1.0));
+
+		move = point->tryGetComponent<MoveComponent>();
+		move->setLocalOrigin(Vec4(0.0, 1.4, 0.6, 0.0));
 	}
 
-#if 1
+#if 0
 	{
 		ScriptResourcePointer script;
 
@@ -368,7 +369,7 @@ Error mainLoopExtra(App& app, void*, Bool& quit)
 
 	if(in.getKey(KeyCode::_1))
 	{
-		mover = &scene.getActiveCamera();
+		mover = scene.getActiveCamera().tryGetComponent<MoveComponent>();
 	}
 	if(in.getKey(KeyCode::_2))
 	{
@@ -553,7 +554,7 @@ Error initSubsystems(int argc, char* argv[])
 	config.set("tilesXCount", 16);
 	config.set("tilesYCount", 16);
 
-	config.set("fullscreenDesktopResolution", true);
+	config.set("fullscreenDesktopResolution", false);
 	config.set("debugContext", false);
 
 	app = new App;