Browse Source

Add triggers in the scene. Not quite working yet

Panagiotis Christopoulos Charitos 7 years ago
parent
commit
5a7acb930d

+ 18 - 0
samples/physics_playground/Main.cpp

@@ -109,6 +109,14 @@ Error MyApp::sampleExtraInit()
 		}
 	}
 
+	// Trigger
+	{
+		TriggerNode* node;
+		ANKI_CHECK(getSceneGraph().newSceneNode("trigger", node, 2.0f));
+
+		node->getComponent<MoveComponent>().setLocalOrigin(Vec4(1.0f, 1.0f, 0.0f, 0.0f));
+	}
+
 	return Error::NONE;
 }
 
@@ -203,6 +211,16 @@ end
 		getPhysicsWorld().rayCast(ray);
 	}
 
+	{
+		SceneNode& node = getSceneGraph().findSceneNode("trigger");
+		TriggerComponent& comp = node.getComponent<TriggerComponent>();
+
+		for(U i = 0; i < comp.getContactSceneNodes().getSize(); ++i)
+		{
+			ANKI_LOGI("Touching %s", comp.getContactSceneNodes()[i]->getName().cstr());
+		}
+	}
+
 	return Error::NONE;
 }
 

+ 2 - 0
src/anki/Scene.h

@@ -21,6 +21,7 @@
 #include <anki/scene/DecalNode.h>
 #include <anki/scene/Octree.h>
 #include <anki/scene/PhysicsDebugNode.h>
+#include <anki/scene/TriggerNode.h>
 
 #include <anki/scene/components/MoveComponent.h>
 #include <anki/scene/components/RenderComponent.h>
@@ -37,6 +38,7 @@
 #include <anki/scene/components/ReflectionProxyComponent.h>
 #include <anki/scene/components/FrustumComponent.h>
 #include <anki/scene/components/JointComponent.h>
+#include <anki/scene/components/TriggerComponent.h>
 
 #include <anki/scene/events/EventManager.h>
 #include <anki/scene/events/Event.h>

+ 1 - 1
src/anki/core/App.cpp

@@ -652,7 +652,7 @@ void App::injectStatsUiElement(DynamicArrayAuto<UiQueueElement>& newUiElementArr
 			static_cast<StatsUi*>(userData)->build(canvas);
 		};
 
-		rqueue.m_uis = newUiElementArr;
+		rqueue.m_uis = WeakArray<UiQueueElement>(newUiElementArr);
 	}
 }
 

+ 9 - 31
src/anki/physics/PhysicsBody.cpp

@@ -10,22 +10,6 @@
 namespace anki
 {
 
-class PhysicsBody::MotionState : public btMotionState
-{
-public:
-	PhysicsBody* m_body = nullptr;
-
-	void getWorldTransform(btTransform& worldTrans) const override
-	{
-		worldTrans = toBt(m_body->m_trf);
-	}
-
-	void setWorldTransform(const btTransform& worldTrans) override
-	{
-		m_body->m_trf = toAnki(worldTrans);
-	}
-};
-
 PhysicsBody::PhysicsBody(PhysicsWorld* world, const PhysicsBodyInitInfo& init)
 	: PhysicsFilteredObject(CLASS_TYPE, world)
 {
@@ -34,8 +18,7 @@ PhysicsBody::PhysicsBody(PhysicsWorld* world, const PhysicsBodyInitInfo& init)
 	m_mass = init.m_mass;
 
 	// Create motion state
-	m_motionState = getAllocator().newInstance<MotionState>();
-	m_motionState->m_body = this;
+	m_motionState.m_body = this;
 
 	// Compute inertia
 	btCollisionShape* shape = m_shape->getBtShape(dynamic);
@@ -46,12 +29,13 @@ PhysicsBody::PhysicsBody(PhysicsWorld* world, const PhysicsBodyInitInfo& init)
 	}
 
 	// Create body
-	btRigidBody::btRigidBodyConstructionInfo cInfo(init.m_mass, m_motionState, shape, localInertia);
+	btRigidBody& body = *getBtBody();
+	btRigidBody::btRigidBodyConstructionInfo cInfo(init.m_mass, &m_motionState, shape, localInertia);
 	cInfo.m_friction = init.m_friction;
-	m_body = getAllocator().newInstance<btRigidBody>(cInfo);
+	::new(&body) btRigidBody(cInfo);
 
 	// User pointer
-	m_body->setUserPointer(static_cast<PhysicsObject*>(this));
+	body.setUserPointer(static_cast<PhysicsObject*>(this));
 
 	// Other
 	setMaterialGroup((dynamic) ? PhysicsMaterialBit::DYNAMIC_GEOMETRY : PhysicsMaterialBit::STATIC_GEOMETRY);
@@ -60,19 +44,13 @@ PhysicsBody::PhysicsBody(PhysicsWorld* world, const PhysicsBodyInitInfo& init)
 
 	// Add to world
 	auto lock = getWorld().lockBtWorld();
-	getWorld().getBtWorld()->addRigidBody(m_body);
+	getWorld().getBtWorld()->addRigidBody(&body);
 }
 
 PhysicsBody::~PhysicsBody()
 {
-	if(m_body)
-	{
-		auto lock = getWorld().lockBtWorld();
-		getWorld().getBtWorld()->removeRigidBody(m_body);
-	}
-
-	getAllocator().deleteInstance(m_body);
-	getAllocator().deleteInstance(m_motionState);
+	auto lock = getWorld().lockBtWorld();
+	getWorld().getBtWorld()->removeRigidBody(getBtBody());
 }
 
 void PhysicsBody::setMass(F32 mass)
@@ -81,7 +59,7 @@ void PhysicsBody::setMass(F32 mass)
 	ANKI_ASSERT(mass > 0.0f);
 	btVector3 inertia;
 	m_shape->getBtShape(true)->calculateLocalInertia(mass, inertia);
-	m_body->setMassProps(mass, inertia);
+	getBtBody()->setMassProps(mass, inertia);
 	m_mass = mass;
 }
 

+ 30 - 9
src/anki/physics/PhysicsBody.h

@@ -39,12 +39,12 @@ public:
 	void setTransform(const Transform& trf)
 	{
 		m_trf = trf;
-		m_body->setWorldTransform(toBt(trf));
+		getBtBody()->setWorldTransform(toBt(trf));
 	}
 
 	void applyForce(const Vec3& force, const Vec3& relPos)
 	{
-		m_body->applyForce(toBt(force), toBt(relPos));
+		getBtBody()->applyForce(toBt(force), toBt(relPos));
 	}
 
 	void setMass(F32 mass);
@@ -55,20 +55,41 @@ public:
 	}
 
 anki_internal:
-	btRigidBody* getBtBody() const
+	const btRigidBody* getBtBody() const
 	{
-		ANKI_ASSERT(m_body);
-		return m_body;
+		return reinterpret_cast<const btRigidBody*>(&m_bodyMem[0]);
+	}
+
+	btRigidBody* getBtBody()
+	{
+		return reinterpret_cast<btRigidBody*>(&m_bodyMem[0]);
 	}
 
 private:
-	class MotionState;
+	class MotionState : public btMotionState
+	{
+	public:
+		PhysicsBody* m_body = nullptr;
 
-	PhysicsCollisionShapePtr m_shape;
-	MotionState* m_motionState = nullptr;
-	btRigidBody* m_body = nullptr;
+		void getWorldTransform(btTransform& worldTrans) const override
+		{
+			worldTrans = toBt(m_body->m_trf);
+		}
+
+		void setWorldTransform(const btTransform& worldTrans) override
+		{
+			m_body->m_trf = toAnki(worldTrans);
+		}
+	};
+
+	/// Store the data of the btRigidBody in place to avoid additional allocations.
+	alignas(alignof(btRigidBody)) Array<U8, sizeof(btRigidBody)> m_bodyMem;
 
 	Transform m_trf = Transform::getIdentity();
+	MotionState m_motionState;
+
+	PhysicsCollisionShapePtr m_shape;
+
 	F32 m_mass = 1.0f;
 
 	PhysicsBody(PhysicsWorld* world, const PhysicsBodyInitInfo& init);

+ 6 - 0
src/anki/physics/PhysicsJoint.h

@@ -31,6 +31,12 @@ public:
 		return !m_joint->isEnabled();
 	}
 
+	/// Break the joint.
+	void brake()
+	{
+		m_joint->setEnabled(false);
+	}
+
 protected:
 	btTypedConstraint* m_joint = nullptr;
 	PhysicsBodyPtr m_bodyA;

+ 2 - 5
src/anki/physics/PhysicsTrigger.cpp

@@ -41,7 +41,7 @@ PhysicsTrigger::~PhysicsTrigger()
 
 void PhysicsTrigger::processContacts()
 {
-	if(m_contactFunctor == nullptr)
+	if(m_contactCallback == nullptr)
 	{
 		return;
 	}
@@ -52,8 +52,6 @@ void PhysicsTrigger::processContacts()
 		return;
 	}
 
-	PhysicsTriggerPtr thisPtr(this);
-
 	// Process contacts
 	for(U i = 0; i < pairCount; ++i)
 	{
@@ -81,8 +79,7 @@ void PhysicsTrigger::processContacts()
 			otherObj = fobj0;
 		}
 
-		PhysicsFilteredObjectPtr ptr(otherObj);
-		m_contactFunctor->processContact(thisPtr, ptr, ConstWeakArray<PhysicsTriggerContact>());
+		m_contactCallback->processContact(*this, *otherObj, ConstWeakArray<PhysicsTriggerContact>());
 	}
 }
 

+ 8 - 4
src/anki/physics/PhysicsTrigger.h

@@ -27,8 +27,12 @@ public:
 class PhysicsTriggerProcessContactCallback
 {
 public:
+	virtual ~PhysicsTriggerProcessContactCallback()
+	{
+	}
+
 	virtual void processContact(
-		PhysicsTriggerPtr& trigger, PhysicsFilteredObjectPtr& obj, ConstWeakArray<PhysicsTriggerContact> contacts) = 0;
+		PhysicsTrigger& trigger, PhysicsFilteredObject& obj, ConstWeakArray<PhysicsTriggerContact> contacts) = 0;
 };
 
 /// A trigger that uses a PhysicsShape and its purpose is to collect collision events.
@@ -44,9 +48,9 @@ public:
 		m_ghostShape->setWorldTransform(toBt(trf));
 	}
 
-	void setContactProcess(PhysicsTriggerProcessContactCallback* functor)
+	void setContactProcessCallback(PhysicsTriggerProcessContactCallback* cb)
 	{
-		m_contactFunctor = functor;
+		m_contactCallback = cb;
 	}
 
 private:
@@ -55,7 +59,7 @@ private:
 	PhysicsCollisionShapePtr m_shape;
 	btPairCachingGhostObject* m_ghostShape = nullptr;
 
-	PhysicsTriggerProcessContactCallback* m_contactFunctor = nullptr;
+	PhysicsTriggerProcessContactCallback* m_contactCallback = nullptr;
 
 	PhysicsTrigger(PhysicsWorld* world, PhysicsCollisionShapePtr shape);
 

+ 4 - 4
src/anki/scene/BodyNode.cpp

@@ -61,16 +61,16 @@ Error BodyNode::init(const CString& resourceFname)
 	m_body->setUserData(this);
 
 	// Joint component
-	newComponent<JointComponent>(this);
+	newComponent<JointComponent>();
 
 	// Body component
-	newComponent<BodyComponent>(this, m_body);
+	newComponent<BodyComponent>(m_body);
 
 	// Feedback component
-	newComponent<BodyFeedbackComponent>(this);
+	newComponent<BodyFeedbackComponent>();
 
 	// Move component
-	newComponent<MoveComponent>(this, MoveComponentFlag::IGNORE_PARENT_TRANSFORM);
+	newComponent<MoveComponent>(MoveComponentFlag::IGNORE_PARENT_TRANSFORM);
 
 	return Error::NONE;
 }

+ 7 - 7
src/anki/scene/CameraNode.cpp

@@ -14,7 +14,7 @@ namespace anki
 class CameraMoveFeedbackComponent : public SceneComponent
 {
 public:
-	CameraMoveFeedbackComponent(CameraNode* node)
+	CameraMoveFeedbackComponent(SceneNode* node)
 		: SceneComponent(SceneComponentType::NONE, node)
 	{
 	}
@@ -38,7 +38,7 @@ public:
 class CameraFrustumFeedbackComponent : public SceneComponent
 {
 public:
-	CameraFrustumFeedbackComponent(CameraNode* node)
+	CameraFrustumFeedbackComponent(SceneNode* node)
 		: SceneComponent(SceneComponentType::NONE, node)
 	{
 	}
@@ -67,13 +67,13 @@ CameraNode::CameraNode(SceneGraph* scene, Type type, CString name)
 Error CameraNode::init(Frustum* frustum)
 {
 	// Move component
-	newComponent<MoveComponent>(this);
+	newComponent<MoveComponent>();
 
 	// Feedback component
-	newComponent<CameraMoveFeedbackComponent>(this);
+	newComponent<CameraMoveFeedbackComponent>();
 
 	// Frustum component
-	FrustumComponent* frc = newComponent<FrustumComponent>(this, frustum);
+	FrustumComponent* frc = newComponent<FrustumComponent>(frustum);
 	frc->setEnabledVisibilityTests(
 		FrustumComponentVisibilityTestFlag::RENDER_COMPONENTS | FrustumComponentVisibilityTestFlag::LIGHT_COMPONENTS
 		| FrustumComponentVisibilityTestFlag::LENS_FLARE_COMPONENTS
@@ -82,10 +82,10 @@ Error CameraNode::init(Frustum* frustum)
 		| FrustumComponentVisibilityTestFlag::EARLY_Z);
 
 	// Feedback component #2
-	newComponent<CameraFrustumFeedbackComponent>(this);
+	newComponent<CameraFrustumFeedbackComponent>();
 
 	// Spatial component
-	newComponent<SpatialComponent>(this, frustum);
+	newComponent<SpatialComponent>(frustum);
 
 	return Error::NONE;
 }

+ 5 - 5
src/anki/scene/DecalNode.cpp

@@ -65,11 +65,11 @@ DecalNode::~DecalNode()
 
 Error DecalNode::init()
 {
-	newComponent<MoveComponent>(this);
-	newComponent<DecalMoveFeedbackComponent>(this);
-	DecalComponent* decalc = newComponent<DecalComponent>(this);
-	newComponent<DecalShapeFeedbackComponent>(this);
-	newComponent<SpatialComponent>(this, &decalc->getBoundingVolume());
+	newComponent<MoveComponent>();
+	newComponent<DecalMoveFeedbackComponent>();
+	DecalComponent* decalc = newComponent<DecalComponent>();
+	newComponent<DecalShapeFeedbackComponent>();
+	newComponent<SpatialComponent>(&decalc->getBoundingVolume());
 
 	return Error::NONE;
 }

+ 8 - 8
src/anki/scene/LightNode.cpp

@@ -74,19 +74,19 @@ LightNode::~LightNode()
 Error LightNode::init(LightComponentType type, CollisionShape* shape)
 {
 	// Move component
-	newComponent<MoveComponent>(this);
+	newComponent<MoveComponent>();
 
 	// Feedback component
-	newComponent<MovedFeedbackComponent>(this);
+	newComponent<MovedFeedbackComponent>();
 
 	// Light component
-	newComponent<LightComponent>(this, type);
+	newComponent<LightComponent>(type);
 
 	// Feedback component
-	newComponent<LightChangedFeedbackComponent>(this);
+	newComponent<LightChangedFeedbackComponent>();
 
 	// Spatial component
-	newComponent<SpatialComponent>(this, shape);
+	newComponent<SpatialComponent>(shape);
 
 	return Error::NONE;
 }
@@ -149,7 +149,7 @@ Error LightNode::loadLensFlare(const CString& filename)
 {
 	ANKI_ASSERT(tryGetComponent<LensFlareComponent>() == nullptr);
 
-	LensFlareComponent* flareComp = newComponent<LensFlareComponent>(this);
+	LensFlareComponent* flareComp = newComponent<LensFlareComponent>();
 
 	Error err = flareComp->init(filename);
 	if(err)
@@ -244,7 +244,7 @@ Error PointLightNode::frameUpdate(Second prevUpdateTime, Second crntTime)
 			trf.setOrigin(origin);
 			m_shadowData[i].m_frustum.resetTransform(trf);
 
-			newComponent<FrustumComponent>(this, &m_shadowData[i].m_frustum);
+			newComponent<FrustumComponent>(&m_shadowData[i].m_frustum);
 		}
 	}
 
@@ -262,7 +262,7 @@ Error SpotLightNode::init()
 {
 	ANKI_CHECK(LightNode::init(LightComponentType::SPOT, &m_frustum));
 
-	FrustumComponent* fr = newComponent<FrustumComponent>(this, &m_frustum);
+	FrustumComponent* fr = newComponent<FrustumComponent>(&m_frustum);
 	fr->setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::NONE);
 
 	return Error::NONE;

+ 13 - 13
src/anki/scene/ModelNode.cpp

@@ -25,8 +25,8 @@ public:
 		return static_cast<const ModelPatchNode&>(getSceneNode());
 	}
 
-	MRenderComponent(ModelPatchNode* node)
-		: MaterialRenderComponent(node, node->m_modelPatch->getMaterial())
+	MRenderComponent(SceneNode* node)
+		: MaterialRenderComponent(node, static_cast<ModelPatchNode*>(node)->m_modelPatch->getMaterial())
 	{
 	}
 
@@ -52,10 +52,10 @@ Error ModelPatchNode::init(const ModelPatch* modelPatch, U idx, const ModelNode&
 	m_modelPatch = modelPatch;
 
 	// Spatial component
-	newComponent<SpatialComponent>(this, &m_obb);
+	newComponent<SpatialComponent>(&m_obb);
 
 	// Render component
-	newComponent<MRenderComponent>(this);
+	newComponent<MRenderComponent>();
 
 	// Merge key
 	Array<U64, 2> toHash;
@@ -153,8 +153,8 @@ public:
 class ModelNode::MRenderComponent : public MaterialRenderComponent
 {
 public:
-	MRenderComponent(ModelNode* node)
-		: MaterialRenderComponent(node, node->m_model->getModelPatches()[0]->getMaterial())
+	MRenderComponent(SceneNode* node)
+		: MaterialRenderComponent(node, static_cast<ModelNode*>(node)->m_model->getModelPatches()[0]->getMaterial())
 	{
 	}
 
@@ -198,10 +198,10 @@ Error ModelNode::init(const CString& modelFname)
 		}
 
 		// Move component
-		newComponent<MoveComponent>(this);
+		newComponent<MoveComponent>();
 
 		// Feedback component
-		newComponent<MoveFeedbackComponent>(this);
+		newComponent<MoveFeedbackComponent>();
 	}
 	else
 	{
@@ -211,12 +211,12 @@ Error ModelNode::init(const CString& modelFname)
 
 		if(m_model->getSkeleton().isCreated())
 		{
-			newComponent<SkinComponent>(this, m_model->getSkeleton());
+			newComponent<SkinComponent>(m_model->getSkeleton());
 		}
-		newComponent<MoveComponent>(this);
-		newComponent<MoveFeedbackComponent>(this);
-		newComponent<SpatialComponent>(this, &m_obb);
-		newComponent<MRenderComponent>(this);
+		newComponent<MoveComponent>();
+		newComponent<MoveFeedbackComponent>();
+		newComponent<SpatialComponent>(&m_obb);
+		newComponent<MRenderComponent>();
 	}
 
 	ANKI_CHECK(getResourceManager().loadResource("shaders/SceneDebug.glslp", m_dbgProg));

+ 3 - 3
src/anki/scene/OccluderNode.cpp

@@ -62,9 +62,9 @@ Error OccluderNode::init(const CString& meshFname)
 	}
 
 	// Create the components
-	newComponent<MoveComponent>(this);
-	newComponent<OccluderMoveFeedbackComponent>(this);
-	newComponent<OccluderComponent>(this);
+	newComponent<MoveComponent>();
+	newComponent<OccluderMoveFeedbackComponent>();
+	newComponent<OccluderComponent>();
 
 	return Error::NONE;
 }

+ 8 - 7
src/anki/scene/ParticleEmitterNode.cpp

@@ -172,8 +172,9 @@ public:
 		return static_cast<const ParticleEmitterNode&>(getSceneNode());
 	}
 
-	ParticleEmitterRenderComponent(ParticleEmitterNode* node)
-		: MaterialRenderComponent(node, node->m_particleEmitterResource->getMaterial())
+	ParticleEmitterRenderComponent(SceneNode* node)
+		: MaterialRenderComponent(
+			  node, static_cast<ParticleEmitterNode*>(node)->m_particleEmitterResource->getMaterial())
 	{
 	}
 
@@ -187,7 +188,7 @@ public:
 class MoveFeedbackComponent : public SceneComponent
 {
 public:
-	MoveFeedbackComponent(ParticleEmitterNode* node)
+	MoveFeedbackComponent(SceneNode* node)
 		: SceneComponent(SceneComponentType::NONE, node)
 	{
 	}
@@ -231,16 +232,16 @@ Error ParticleEmitterNode::init(const CString& filename)
 	ANKI_CHECK(getResourceManager().loadResource(filename, m_particleEmitterResource));
 
 	// Move component
-	newComponent<MoveComponent>(this);
+	newComponent<MoveComponent>();
 
 	// Move component feedback
-	newComponent<MoveFeedbackComponent>(this);
+	newComponent<MoveFeedbackComponent>();
 
 	// Spatial component
-	newComponent<SpatialComponent>(this, &m_obb);
+	newComponent<SpatialComponent>(&m_obb);
 
 	// Render component
-	newComponent<ParticleEmitterRenderComponent>(this);
+	newComponent<ParticleEmitterRenderComponent>();
 
 	// Other
 	m_obb.setCenter(Vec4(0.0));

+ 2 - 2
src/anki/scene/PhysicsDebugNode.cpp

@@ -66,10 +66,10 @@ PhysicsDebugNode::~PhysicsDebugNode()
 
 Error PhysicsDebugNode::init()
 {
-	MyRenderComponent* rcomp = newComponent<MyRenderComponent>(this);
+	MyRenderComponent* rcomp = newComponent<MyRenderComponent>();
 	ANKI_CHECK(rcomp->init());
 
-	ObbSpatialComponent* scomp = newComponent<ObbSpatialComponent>(this);
+	ObbSpatialComponent* scomp = newComponent<ObbSpatialComponent>();
 	Vec3 center = (getSceneGraph().getSceneMax() + getSceneGraph().getSceneMin()) / 2.0f;
 	scomp->m_obb.setCenter(center.xyz0());
 	scomp->m_obb.setExtend((getSceneGraph().getSceneMax() - center).xyz0());

+ 4 - 4
src/anki/scene/PlayerNode.cpp

@@ -128,16 +128,16 @@ Error PlayerNode::init(const Vec4& position)
 	m_player->setUserData(this);
 
 	// Player controller component
-	newComponent<PlayerControllerComponent>(this, m_player);
+	newComponent<PlayerControllerComponent>(m_player);
 
 	// Feedback component
-	newComponent<PlayerNodeFeedbackComponent>(this);
+	newComponent<PlayerNodeFeedbackComponent>();
 
 	// Move component
-	newComponent<MoveComponent>(this);
+	newComponent<MoveComponent>();
 
 	// Feedback component #2
-	newComponent<PlayerNodeFeedbackComponent2>(this);
+	newComponent<PlayerNodeFeedbackComponent2>();
 
 	return Error::NONE;
 }

+ 5 - 5
src/anki/scene/ReflectionProbeNode.cpp

@@ -49,10 +49,10 @@ ReflectionProbeNode::~ReflectionProbeNode()
 Error ReflectionProbeNode::init(const Vec4& aabbMinLSpace, const Vec4& aabbMaxLSpace)
 {
 	// Move component first
-	newComponent<MoveComponent>(this);
+	newComponent<MoveComponent>();
 
 	// Feedback component
-	newComponent<ReflectionProbeMoveFeedbackComponent>(this);
+	newComponent<ReflectionProbeMoveFeedbackComponent>();
 
 	// The frustum components
 	const F32 ang = toRad(90.0f);
@@ -81,7 +81,7 @@ Error ReflectionProbeNode::init(const Vec4& aabbMinLSpace, const Vec4& aabbMaxLS
 		m_cubeSides[i].m_frustum.setAll(ang, ang, zNear, EFFECTIVE_DISTANCE);
 		m_cubeSides[i].m_frustum.resetTransform(m_cubeSides[i].m_localTrf);
 
-		FrustumComponent* frc = newComponent<FrustumComponent>(this, &m_cubeSides[i].m_frustum);
+		FrustumComponent* frc = newComponent<FrustumComponent>(&m_cubeSides[i].m_frustum);
 
 		frc->setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::NONE);
 	}
@@ -91,10 +91,10 @@ Error ReflectionProbeNode::init(const Vec4& aabbMinLSpace, const Vec4& aabbMaxLS
 	m_aabbMaxLSpace = aabbMaxLSpace.xyz();
 	m_spatialAabb.setMin(aabbMinLSpace);
 	m_spatialAabb.setMax(aabbMaxLSpace);
-	newComponent<SpatialComponent>(this, &m_spatialAabb);
+	newComponent<SpatialComponent>(&m_spatialAabb);
 
 	// Reflection probe comp
-	ReflectionProbeComponent* reflc = newComponent<ReflectionProbeComponent>(this);
+	ReflectionProbeComponent* reflc = newComponent<ReflectionProbeComponent>();
 	reflc->setPosition(Vec4(0.0f));
 	reflc->setBoundingBox(aabbMinLSpace, aabbMaxLSpace);
 

+ 1 - 1
src/anki/scene/SceneNode.h

@@ -222,7 +222,7 @@ protected:
 	template<typename TComponent, typename... TArgs>
 	TComponent* newComponent(TArgs&&... args)
 	{
-		TComponent* comp = getSceneAllocator().newInstance<TComponent>(std::forward<TArgs>(args)...);
+		TComponent* comp = getSceneAllocator().newInstance<TComponent>(this, std::forward<TArgs>(args)...);
 		m_components.emplaceBack(getSceneAllocator(), comp);
 		return comp;
 	}

+ 3 - 3
src/anki/scene/SectorNode.cpp

@@ -59,16 +59,16 @@ PortalSectorBase::~PortalSectorBase()
 Error PortalSectorBase::init(const CString& meshFname, Bool isSector)
 {
 	// Create move component
-	newComponent<MoveComponent>(this);
+	newComponent<MoveComponent>();
 
 	// Create portal or sector component
 	if(isSector)
 	{
-		newComponent<SectorComponent>(this);
+		newComponent<SectorComponent>();
 	}
 	else
 	{
-		newComponent<PortalComponent>(this);
+		newComponent<PortalComponent>();
 	}
 
 	// Load mesh

+ 1 - 0
src/anki/scene/StaticCollisionNode.cpp

@@ -34,6 +34,7 @@ Error StaticCollisionNode::init(const CString& resourceFname, const Transform& t
 	init.m_transform = transform;
 
 	m_body = getSceneGraph().getPhysicsWorld().newInstance<PhysicsBody>(init);
+	m_body->setUserData(this);
 
 	return Error::NONE;
 }

+ 4 - 7
src/anki/scene/StaticGeometryNode.cpp

@@ -15,11 +15,8 @@ namespace anki
 class StaticGeometryRenderComponent : public MaterialRenderComponent
 {
 public:
-	StaticGeometryPatchNode* m_node;
-
-	StaticGeometryRenderComponent(StaticGeometryPatchNode* node)
-		: MaterialRenderComponent(node, node->m_modelPatch->getMaterial())
-		, m_node(node)
+	StaticGeometryRenderComponent(SceneNode* node)
+		: MaterialRenderComponent(node, static_cast<StaticGeometryPatchNode*>(node)->m_modelPatch->getMaterial())
 	{
 	}
 
@@ -47,13 +44,13 @@ Error StaticGeometryPatchNode::init(const ModelPatch* modelPatch)
 	// Create spatial components
 	for(U i = 1; i < m_modelPatch->getSubMeshCount(); i++)
 	{
-		SpatialComponent* spatial = newComponent<SpatialComponent>(this, &m_modelPatch->getBoundingShapeSub(i));
+		SpatialComponent* spatial = newComponent<SpatialComponent>(&m_modelPatch->getBoundingShapeSub(i));
 
 		spatial->setSpatialOrigin(m_modelPatch->getBoundingShapeSub(i).getCenter());
 	}
 
 	// Create render component
-	newComponent<StaticGeometryRenderComponent>(this);
+	newComponent<StaticGeometryRenderComponent>();
 
 	return Error::NONE;
 }

+ 62 - 0
src/anki/scene/TriggerNode.cpp

@@ -0,0 +1,62 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/scene/TriggerNode.h>
+#include <anki/scene/SceneGraph.h>
+#include <anki/scene/components/MoveComponent.h>
+#include <anki/scene/components/TriggerComponent.h>
+#include <anki/physics/PhysicsWorld.h>
+#include <anki/physics/PhysicsCollisionShape.h>
+#include <anki/physics/PhysicsTrigger.h>
+
+namespace anki
+{
+
+class TriggerNode::MoveFeedbackComponent : public SceneComponent
+{
+public:
+	MoveFeedbackComponent(SceneNode* node)
+		: SceneComponent(SceneComponentType::NONE, node)
+	{
+	}
+
+	ANKI_USE_RESULT Error update(Second, Second, Bool& updated) final
+	{
+		updated = false;
+
+		const MoveComponent& move = m_node->getComponent<MoveComponent>();
+		if(move.getTimestamp() == m_node->getGlobalTimestamp())
+		{
+			TriggerNode* node = static_cast<TriggerNode*>(m_node);
+			node->m_trigger->setTransform(move.getWorldTransform());
+		}
+
+		return Error::NONE;
+	}
+};
+
+TriggerNode::TriggerNode(SceneGraph* scene, CString name)
+	: SceneNode(scene, name)
+{
+}
+
+TriggerNode::~TriggerNode()
+{
+}
+
+Error TriggerNode::init(F32 sphereRadius)
+{
+	m_shape = getSceneGraph().getPhysicsWorld().newInstance<PhysicsSphere>(sphereRadius);
+	m_trigger = getSceneGraph().getPhysicsWorld().newInstance<PhysicsTrigger>(m_shape);
+	m_trigger->setUserData(this);
+
+	newComponent<MoveComponent>();
+	newComponent<MoveFeedbackComponent>();
+	newComponent<TriggerComponent>(m_trigger);
+
+	return Error::NONE;
+}
+
+} // end namespace anki

+ 37 - 0
src/anki/scene/TriggerNode.h

@@ -0,0 +1,37 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/scene/SceneNode.h>
+#include <anki/physics/Forward.h>
+
+namespace anki
+{
+
+/// @addtogroup scene
+/// @{
+
+/// Trigger node.
+class TriggerNode : public SceneNode
+{
+public:
+	TriggerNode(SceneGraph* scene, CString name);
+
+	~TriggerNode();
+
+	ANKI_USE_RESULT Error init(const CString& collisionShapeFilename);
+
+	ANKI_USE_RESULT Error init(F32 sphereRadius);
+
+private:
+	class MoveFeedbackComponent;
+
+	PhysicsCollisionShapePtr m_shape;
+	PhysicsTriggerPtr m_trigger;
+};
+/// @}
+
+} // end namespace anki

+ 1 - 0
src/anki/scene/components/SceneComponent.h

@@ -36,6 +36,7 @@ enum class SceneComponentType : U16
 	SKIN,
 	SCRIPT,
 	JOINT,
+	TRIGGER,
 	PLAYER_CONTROLLER,
 
 	COUNT,

+ 60 - 0
src/anki/scene/components/TriggerComponent.cpp

@@ -0,0 +1,60 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/scene/components/TriggerComponent.h>
+
+namespace anki
+{
+
+class TriggerComponent::MyPhysicsTriggerProcessContactCallback : public PhysicsTriggerProcessContactCallback
+{
+public:
+	TriggerComponent* m_comp = nullptr;
+
+	void processContact(
+		PhysicsTrigger& trigger, PhysicsFilteredObject& obj, ConstWeakArray<PhysicsTriggerContact> contacts) final
+	{
+		void* ptr = obj.getUserData();
+		ANKI_ASSERT(ptr);
+
+		SceneNode* node = static_cast<SceneNode*>(ptr);
+		m_comp->processContact(*node);
+	}
+};
+
+TriggerComponent::TriggerComponent(SceneNode* node, PhysicsTriggerPtr trigger)
+	: SceneComponent(CLASS_TYPE, node)
+	, m_trigger(trigger)
+{
+	m_contactCb = getAllocator().newInstance<MyPhysicsTriggerProcessContactCallback>();
+	m_contactCb->m_comp = this;
+	m_trigger->setContactProcessCallback(m_contactCb);
+}
+
+TriggerComponent::~TriggerComponent()
+{
+	getAllocator().deleteInstance(m_contactCb);
+	m_contactNodes.destroy(getAllocator());
+}
+
+Error TriggerComponent::update(Second, Second, Bool& updated)
+{
+	updated = false;
+	return Error::NONE;
+}
+
+void TriggerComponent::processContact(SceneNode& node)
+{
+	// Clear the list if it's a new frame
+	if(m_contactNodesArrayTimestamp != getGlobalTimestamp())
+	{
+		m_contactNodesArrayTimestamp = getGlobalTimestamp();
+		m_contactNodes.destroy(getAllocator());
+	}
+
+	m_contactNodes.emplaceBack(getAllocator(), &node);
+}
+
+} // end namespace anki

+ 47 - 0
src/anki/scene/components/TriggerComponent.h

@@ -0,0 +1,47 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/scene/components/SceneComponent.h>
+#include <anki/physics/PhysicsTrigger.h>
+
+namespace anki
+{
+
+/// @addtogroup scene
+/// @{
+
+/// Trigger component.
+class TriggerComponent : public SceneComponent
+{
+public:
+	static const SceneComponentType CLASS_TYPE = SceneComponentType::TRIGGER;
+
+	TriggerComponent(SceneNode* node, PhysicsTriggerPtr trigger);
+
+	~TriggerComponent();
+
+	ANKI_USE_RESULT Error update(Second, Second, Bool& updated) override;
+
+	/// Process a contact.
+	virtual void processContact(SceneNode& node);
+
+	WeakArray<SceneNode*> getContactSceneNodes()
+	{
+		return WeakArray<SceneNode*>(m_contactNodes);
+	}
+
+private:
+	class MyPhysicsTriggerProcessContactCallback;
+
+	PhysicsTriggerPtr m_trigger;
+	DynamicArray<SceneNode*> m_contactNodes;
+	Timestamp m_contactNodesArrayTimestamp = 0;
+	MyPhysicsTriggerProcessContactCallback* m_contactCb = nullptr;
+};
+/// @}
+
+} // end namespace anki

+ 364 - 109
src/anki/script/Scene.cpp

@@ -61,6 +61,129 @@ static EventManager* getEventManager(lua_State* l)
 
 using WeakArraySceneNodePtr = WeakArray<SceneNode*>;
 
+static const char* classnameWeakArraySceneNodePtr = "WeakArraySceneNodePtr";
+
+template<>
+I64 LuaBinder::getWrappedTypeSignature<WeakArraySceneNodePtr>()
+{
+	return 4158963409681942864;
+}
+
+template<>
+const char* LuaBinder::getWrappedTypeName<WeakArraySceneNodePtr>()
+{
+	return classnameWeakArraySceneNodePtr;
+}
+
+/// Pre-wrap method WeakArraySceneNodePtr::getSize.
+static inline int pwrapWeakArraySceneNodePtrgetSize(lua_State* l)
+{
+	LuaUserData* ud;
+	(void)ud;
+	void* voidp;
+	(void)voidp;
+	PtrSize size;
+	(void)size;
+
+	LuaBinder::checkArgsCount(l, 1);
+
+	// Get "this" as "self"
+	if(LuaBinder::checkUserData(l, 1, classnameWeakArraySceneNodePtr, 4158963409681942864, ud))
+	{
+		return -1;
+	}
+
+	WeakArraySceneNodePtr* self = ud->getData<WeakArraySceneNodePtr>();
+
+	// Call the method
+	PtrSize ret = self->getSize();
+
+	// Push return value
+	lua_pushnumber(l, ret);
+
+	return 1;
+}
+
+/// Wrap method WeakArraySceneNodePtr::getSize.
+static int wrapWeakArraySceneNodePtrgetSize(lua_State* l)
+{
+	int res = pwrapWeakArraySceneNodePtrgetSize(l);
+	if(res >= 0)
+	{
+		return res;
+	}
+
+	lua_error(l);
+	return 0;
+}
+
+/// Pre-wrap method WeakArraySceneNodePtr::getAt.
+static inline int pwrapWeakArraySceneNodePtrgetAt(lua_State* l)
+{
+	LuaUserData* ud;
+	(void)ud;
+	void* voidp;
+	(void)voidp;
+	PtrSize size;
+	(void)size;
+
+	LuaBinder::checkArgsCount(l, 2);
+
+	// Get "this" as "self"
+	if(LuaBinder::checkUserData(l, 1, classnameWeakArraySceneNodePtr, 4158963409681942864, ud))
+	{
+		return -1;
+	}
+
+	WeakArraySceneNodePtr* self = ud->getData<WeakArraySceneNodePtr>();
+
+	// Pop arguments
+	U arg0;
+	if(LuaBinder::checkNumber(l, 2, arg0))
+	{
+		return -1;
+	}
+
+	// Call the method
+	SceneNode* ret = (*self)[arg0];
+
+	// Push return value
+	if(ANKI_UNLIKELY(ret == nullptr))
+	{
+		lua_pushstring(l, "Glue code returned nullptr");
+		return -1;
+	}
+
+	voidp = lua_newuserdata(l, sizeof(LuaUserData));
+	ud = static_cast<LuaUserData*>(voidp);
+	luaL_setmetatable(l, "SceneNode");
+	ud->initPointed(-2220074417980276571, const_cast<SceneNode*>(ret));
+
+	return 1;
+}
+
+/// Wrap method WeakArraySceneNodePtr::getAt.
+static int wrapWeakArraySceneNodePtrgetAt(lua_State* l)
+{
+	int res = pwrapWeakArraySceneNodePtrgetAt(l);
+	if(res >= 0)
+	{
+		return res;
+	}
+
+	lua_error(l);
+	return 0;
+}
+
+/// Wrap class WeakArraySceneNodePtr.
+static inline void wrapWeakArraySceneNodePtr(lua_State* l)
+{
+	LuaBinder::createClass(l, classnameWeakArraySceneNodePtr);
+	LuaBinder::pushLuaCFuncMethod(l, "getSize", wrapWeakArraySceneNodePtrgetSize);
+	LuaBinder::pushLuaCFuncMethod(l, "getAt", wrapWeakArraySceneNodePtrgetAt);
+	lua_settop(l, 0);
+}
+
 static const char* classnameMoveComponent = "MoveComponent";
 
 template<>
@@ -1358,6 +1481,75 @@ static inline void wrapLensFlareComponent(lua_State* l)
 	lua_settop(l, 0);
 }
 
+static const char* classnameTriggerComponent = "TriggerComponent";
+
+template<>
+I64 LuaBinder::getWrappedTypeSignature<TriggerComponent>()
+{
+	return 7180780522076545145;
+}
+
+template<>
+const char* LuaBinder::getWrappedTypeName<TriggerComponent>()
+{
+	return classnameTriggerComponent;
+}
+
+/// Pre-wrap method TriggerComponent::getContactSceneNodes.
+static inline int pwrapTriggerComponentgetContactSceneNodes(lua_State* l)
+{
+	LuaUserData* ud;
+	(void)ud;
+	void* voidp;
+	(void)voidp;
+	PtrSize size;
+	(void)size;
+
+	LuaBinder::checkArgsCount(l, 1);
+
+	// Get "this" as "self"
+	if(LuaBinder::checkUserData(l, 1, classnameTriggerComponent, 7180780522076545145, ud))
+	{
+		return -1;
+	}
+
+	TriggerComponent* self = ud->getData<TriggerComponent>();
+
+	// Call the method
+	WeakArraySceneNodePtr ret = self->getContactSceneNodes();
+
+	// Push return value
+	size = LuaUserData::computeSizeForGarbageCollected<WeakArraySceneNodePtr>();
+	voidp = lua_newuserdata(l, size);
+	luaL_setmetatable(l, "WeakArraySceneNodePtr");
+	ud = static_cast<LuaUserData*>(voidp);
+	ud->initGarbageCollected(4158963409681942864);
+	::new(ud->getData<WeakArraySceneNodePtr>()) WeakArraySceneNodePtr(std::move(ret));
+
+	return 1;
+}
+
+/// Wrap method TriggerComponent::getContactSceneNodes.
+static int wrapTriggerComponentgetContactSceneNodes(lua_State* l)
+{
+	int res = pwrapTriggerComponentgetContactSceneNodes(l);
+	if(res >= 0)
+	{
+		return res;
+	}
+
+	lua_error(l);
+	return 0;
+}
+
+/// Wrap class TriggerComponent.
+static inline void wrapTriggerComponent(lua_State* l)
+{
+	LuaBinder::createClass(l, classnameTriggerComponent);
+	LuaBinder::pushLuaCFuncMethod(l, "getContactSceneNodes", wrapTriggerComponentgetContactSceneNodes);
+	lua_settop(l, 0);
+}
+
 static const char* classnameSceneNode = "SceneNode";
 
 template<>
@@ -1705,6 +1897,57 @@ static int wrapSceneNodegetDecalComponent(lua_State* l)
 	return 0;
 }
 
+/// Pre-wrap method SceneNode::tryGetComponent<TriggerComponent>.
+static inline int pwrapSceneNodegetTriggerComponent(lua_State* l)
+{
+	LuaUserData* ud;
+	(void)ud;
+	void* voidp;
+	(void)voidp;
+	PtrSize size;
+	(void)size;
+
+	LuaBinder::checkArgsCount(l, 1);
+
+	// Get "this" as "self"
+	if(LuaBinder::checkUserData(l, 1, classnameSceneNode, -2220074417980276571, ud))
+	{
+		return -1;
+	}
+
+	SceneNode* self = ud->getData<SceneNode>();
+
+	// Call the method
+	TriggerComponent* ret = self->tryGetComponent<TriggerComponent>();
+
+	// Push return value
+	if(ANKI_UNLIKELY(ret == nullptr))
+	{
+		lua_pushstring(l, "Glue code returned nullptr");
+		return -1;
+	}
+
+	voidp = lua_newuserdata(l, sizeof(LuaUserData));
+	ud = static_cast<LuaUserData*>(voidp);
+	luaL_setmetatable(l, "TriggerComponent");
+	ud->initPointed(7180780522076545145, const_cast<TriggerComponent*>(ret));
+
+	return 1;
+}
+
+/// Wrap method SceneNode::tryGetComponent<TriggerComponent>.
+static int wrapSceneNodegetTriggerComponent(lua_State* l)
+{
+	int res = pwrapSceneNodegetTriggerComponent(l);
+	if(res >= 0)
+	{
+		return res;
+	}
+
+	lua_error(l);
+	return 0;
+}
+
 /// Wrap class SceneNode.
 static inline void wrapSceneNode(lua_State* l)
 {
@@ -1716,6 +1959,7 @@ static inline void wrapSceneNode(lua_State* l)
 	LuaBinder::pushLuaCFuncMethod(l, "getLightComponent", wrapSceneNodegetLightComponent);
 	LuaBinder::pushLuaCFuncMethod(l, "getLensFlareComponent", wrapSceneNodegetLensFlareComponent);
 	LuaBinder::pushLuaCFuncMethod(l, "getDecalComponent", wrapSceneNodegetDecalComponent);
+	LuaBinder::pushLuaCFuncMethod(l, "getTriggerComponent", wrapSceneNodegetTriggerComponent);
 	lua_settop(l, 0);
 }
 
@@ -2644,6 +2888,73 @@ static inline void wrapDecalNode(lua_State* l)
 	lua_settop(l, 0);
 }
 
+static const char* classnameTriggerNode = "TriggerNode";
+
+template<>
+I64 LuaBinder::getWrappedTypeSignature<TriggerNode>()
+{
+	return -3029786875306006141;
+}
+
+template<>
+const char* LuaBinder::getWrappedTypeName<TriggerNode>()
+{
+	return classnameTriggerNode;
+}
+
+/// Pre-wrap method TriggerNode::getSceneNodeBase.
+static inline int pwrapTriggerNodegetSceneNodeBase(lua_State* l)
+{
+	LuaUserData* ud;
+	(void)ud;
+	void* voidp;
+	(void)voidp;
+	PtrSize size;
+	(void)size;
+
+	LuaBinder::checkArgsCount(l, 1);
+
+	// Get "this" as "self"
+	if(LuaBinder::checkUserData(l, 1, classnameTriggerNode, -3029786875306006141, ud))
+	{
+		return -1;
+	}
+
+	TriggerNode* self = ud->getData<TriggerNode>();
+
+	// Call the method
+	SceneNode& ret = *self;
+
+	// Push return value
+	voidp = lua_newuserdata(l, sizeof(LuaUserData));
+	ud = static_cast<LuaUserData*>(voidp);
+	luaL_setmetatable(l, "SceneNode");
+	ud->initPointed(-2220074417980276571, const_cast<SceneNode*>(&ret));
+
+	return 1;
+}
+
+/// Wrap method TriggerNode::getSceneNodeBase.
+static int wrapTriggerNodegetSceneNodeBase(lua_State* l)
+{
+	int res = pwrapTriggerNodegetSceneNodeBase(l);
+	if(res >= 0)
+	{
+		return res;
+	}
+
+	lua_error(l);
+	return 0;
+}
+
+/// Wrap class TriggerNode.
+static inline void wrapTriggerNode(lua_State* l)
+{
+	LuaBinder::createClass(l, classnameTriggerNode);
+	LuaBinder::pushLuaCFuncMethod(l, "getSceneNodeBase", wrapTriggerNodegetSceneNodeBase);
+	lua_settop(l, 0);
+}
+
 static const char* classnameSceneGraph = "SceneGraph";
 
 template<>
@@ -3420,8 +3731,8 @@ static int wrapSceneGraphnewDecalNode(lua_State* l)
 	return 0;
 }
 
-/// Pre-wrap method SceneGraph::setActiveCameraNode.
-static inline int pwrapSceneGraphsetActiveCameraNode(lua_State* l)
+/// Pre-wrap method SceneGraph::newTriggerNode.
+static inline int pwrapSceneGraphnewTriggerNode(lua_State* l)
 {
 	LuaUserData* ud;
 	(void)ud;
@@ -3430,7 +3741,7 @@ static inline int pwrapSceneGraphsetActiveCameraNode(lua_State* l)
 	PtrSize size;
 	(void)size;
 
-	LuaBinder::checkArgsCount(l, 2);
+	LuaBinder::checkArgsCount(l, 3);
 
 	// Get "this" as "self"
 	if(LuaBinder::checkUserData(l, 1, classnameSceneGraph, -7754439619132389154, ud))
@@ -3441,100 +3752,40 @@ static inline int pwrapSceneGraphsetActiveCameraNode(lua_State* l)
 	SceneGraph* self = ud->getData<SceneGraph>();
 
 	// Pop arguments
-	if(LuaBinder::checkUserData(l, 2, "SceneNode", -2220074417980276571, ud))
+	const char* arg0;
+	if(LuaBinder::checkString(l, 2, arg0))
 	{
 		return -1;
 	}
 
-	SceneNode* iarg0 = ud->getData<SceneNode>();
-	SceneNode* arg0(iarg0);
-
-	// Call the method
-	self->setActiveCameraNode(arg0);
-
-	return 0;
-}
-
-/// Wrap method SceneGraph::setActiveCameraNode.
-static int wrapSceneGraphsetActiveCameraNode(lua_State* l)
-{
-	int res = pwrapSceneGraphsetActiveCameraNode(l);
-	if(res >= 0)
+	F32 arg1;
+	if(LuaBinder::checkNumber(l, 3, arg1))
 	{
-		return res;
+		return -1;
 	}
 
-	lua_error(l);
-	return 0;
-}
-
-/// Wrap class SceneGraph.
-static inline void wrapSceneGraph(lua_State* l)
-{
-	LuaBinder::createClass(l, classnameSceneGraph);
-	LuaBinder::pushLuaCFuncMethod(l, "newPerspectiveCameraNode", wrapSceneGraphnewPerspectiveCameraNode);
-	LuaBinder::pushLuaCFuncMethod(l, "newModelNode", wrapSceneGraphnewModelNode);
-	LuaBinder::pushLuaCFuncMethod(l, "newPointLightNode", wrapSceneGraphnewPointLightNode);
-	LuaBinder::pushLuaCFuncMethod(l, "newSpotLightNode", wrapSceneGraphnewSpotLightNode);
-	LuaBinder::pushLuaCFuncMethod(l, "newStaticCollisionNode", wrapSceneGraphnewStaticCollisionNode);
-	LuaBinder::pushLuaCFuncMethod(l, "newPortalNode", wrapSceneGraphnewPortalNode);
-	LuaBinder::pushLuaCFuncMethod(l, "newSectorNode", wrapSceneGraphnewSectorNode);
-	LuaBinder::pushLuaCFuncMethod(l, "newParticleEmitterNode", wrapSceneGraphnewParticleEmitterNode);
-	LuaBinder::pushLuaCFuncMethod(l, "newReflectionProbeNode", wrapSceneGraphnewReflectionProbeNode);
-	LuaBinder::pushLuaCFuncMethod(l, "newReflectionProxyNode", wrapSceneGraphnewReflectionProxyNode);
-	LuaBinder::pushLuaCFuncMethod(l, "newOccluderNode", wrapSceneGraphnewOccluderNode);
-	LuaBinder::pushLuaCFuncMethod(l, "newDecalNode", wrapSceneGraphnewDecalNode);
-	LuaBinder::pushLuaCFuncMethod(l, "setActiveCameraNode", wrapSceneGraphsetActiveCameraNode);
-	lua_settop(l, 0);
-}
-
-static const char* classnameWeakArraySceneNodePtr = "WeakArraySceneNodePtr";
-
-template<>
-I64 LuaBinder::getWrappedTypeSignature<WeakArraySceneNodePtr>()
-{
-	return 4158963409681942864;
-}
-
-template<>
-const char* LuaBinder::getWrappedTypeName<WeakArraySceneNodePtr>()
-{
-	return classnameWeakArraySceneNodePtr;
-}
-
-/// Pre-wrap method WeakArraySceneNodePtr::getSize.
-static inline int pwrapWeakArraySceneNodePtrgetSize(lua_State* l)
-{
-	LuaUserData* ud;
-	(void)ud;
-	void* voidp;
-	(void)voidp;
-	PtrSize size;
-	(void)size;
-
-	LuaBinder::checkArgsCount(l, 1);
+	// Call the method
+	TriggerNode* ret = newSceneNode<TriggerNode>(self, arg0, arg1);
 
-	// Get "this" as "self"
-	if(LuaBinder::checkUserData(l, 1, classnameWeakArraySceneNodePtr, 4158963409681942864, ud))
+	// Push return value
+	if(ANKI_UNLIKELY(ret == nullptr))
 	{
+		lua_pushstring(l, "Glue code returned nullptr");
 		return -1;
 	}
 
-	WeakArraySceneNodePtr* self = ud->getData<WeakArraySceneNodePtr>();
-
-	// Call the method
-	PtrSize ret = self->getSize();
-
-	// Push return value
-	lua_pushnumber(l, ret);
+	voidp = lua_newuserdata(l, sizeof(LuaUserData));
+	ud = static_cast<LuaUserData*>(voidp);
+	luaL_setmetatable(l, "TriggerNode");
+	ud->initPointed(-3029786875306006141, const_cast<TriggerNode*>(ret));
 
 	return 1;
 }
 
-/// Wrap method WeakArraySceneNodePtr::getSize.
-static int wrapWeakArraySceneNodePtrgetSize(lua_State* l)
+/// Wrap method SceneGraph::newTriggerNode.
+static int wrapSceneGraphnewTriggerNode(lua_State* l)
 {
-	int res = pwrapWeakArraySceneNodePtrgetSize(l);
+	int res = pwrapSceneGraphnewTriggerNode(l);
 	if(res >= 0)
 	{
 		return res;
@@ -3544,8 +3795,8 @@ static int wrapWeakArraySceneNodePtrgetSize(lua_State* l)
 	return 0;
 }
 
-/// Pre-wrap method WeakArraySceneNodePtr::getAt.
-static inline int pwrapWeakArraySceneNodePtrgetAt(lua_State* l)
+/// Pre-wrap method SceneGraph::setActiveCameraNode.
+static inline int pwrapSceneGraphsetActiveCameraNode(lua_State* l)
 {
 	LuaUserData* ud;
 	(void)ud;
@@ -3557,42 +3808,32 @@ static inline int pwrapWeakArraySceneNodePtrgetAt(lua_State* l)
 	LuaBinder::checkArgsCount(l, 2);
 
 	// Get "this" as "self"
-	if(LuaBinder::checkUserData(l, 1, classnameWeakArraySceneNodePtr, 4158963409681942864, ud))
+	if(LuaBinder::checkUserData(l, 1, classnameSceneGraph, -7754439619132389154, ud))
 	{
 		return -1;
 	}
 
-	WeakArraySceneNodePtr* self = ud->getData<WeakArraySceneNodePtr>();
+	SceneGraph* self = ud->getData<SceneGraph>();
 
 	// Pop arguments
-	U arg0;
-	if(LuaBinder::checkNumber(l, 2, arg0))
+	if(LuaBinder::checkUserData(l, 2, "SceneNode", -2220074417980276571, ud))
 	{
 		return -1;
 	}
 
-	// Call the method
-	SceneNode* ret = (*self)[arg0];
-
-	// Push return value
-	if(ANKI_UNLIKELY(ret == nullptr))
-	{
-		lua_pushstring(l, "Glue code returned nullptr");
-		return -1;
-	}
+	SceneNode* iarg0 = ud->getData<SceneNode>();
+	SceneNode* arg0(iarg0);
 
-	voidp = lua_newuserdata(l, sizeof(LuaUserData));
-	ud = static_cast<LuaUserData*>(voidp);
-	luaL_setmetatable(l, "SceneNode");
-	ud->initPointed(-2220074417980276571, const_cast<SceneNode*>(ret));
+	// Call the method
+	self->setActiveCameraNode(arg0);
 
-	return 1;
+	return 0;
 }
 
-/// Wrap method WeakArraySceneNodePtr::getAt.
-static int wrapWeakArraySceneNodePtrgetAt(lua_State* l)
+/// Wrap method SceneGraph::setActiveCameraNode.
+static int wrapSceneGraphsetActiveCameraNode(lua_State* l)
 {
-	int res = pwrapWeakArraySceneNodePtrgetAt(l);
+	int res = pwrapSceneGraphsetActiveCameraNode(l);
 	if(res >= 0)
 	{
 		return res;
@@ -3602,12 +3843,24 @@ static int wrapWeakArraySceneNodePtrgetAt(lua_State* l)
 	return 0;
 }
 
-/// Wrap class WeakArraySceneNodePtr.
-static inline void wrapWeakArraySceneNodePtr(lua_State* l)
+/// Wrap class SceneGraph.
+static inline void wrapSceneGraph(lua_State* l)
 {
-	LuaBinder::createClass(l, classnameWeakArraySceneNodePtr);
-	LuaBinder::pushLuaCFuncMethod(l, "getSize", wrapWeakArraySceneNodePtrgetSize);
-	LuaBinder::pushLuaCFuncMethod(l, "getAt", wrapWeakArraySceneNodePtrgetAt);
+	LuaBinder::createClass(l, classnameSceneGraph);
+	LuaBinder::pushLuaCFuncMethod(l, "newPerspectiveCameraNode", wrapSceneGraphnewPerspectiveCameraNode);
+	LuaBinder::pushLuaCFuncMethod(l, "newModelNode", wrapSceneGraphnewModelNode);
+	LuaBinder::pushLuaCFuncMethod(l, "newPointLightNode", wrapSceneGraphnewPointLightNode);
+	LuaBinder::pushLuaCFuncMethod(l, "newSpotLightNode", wrapSceneGraphnewSpotLightNode);
+	LuaBinder::pushLuaCFuncMethod(l, "newStaticCollisionNode", wrapSceneGraphnewStaticCollisionNode);
+	LuaBinder::pushLuaCFuncMethod(l, "newPortalNode", wrapSceneGraphnewPortalNode);
+	LuaBinder::pushLuaCFuncMethod(l, "newSectorNode", wrapSceneGraphnewSectorNode);
+	LuaBinder::pushLuaCFuncMethod(l, "newParticleEmitterNode", wrapSceneGraphnewParticleEmitterNode);
+	LuaBinder::pushLuaCFuncMethod(l, "newReflectionProbeNode", wrapSceneGraphnewReflectionProbeNode);
+	LuaBinder::pushLuaCFuncMethod(l, "newReflectionProxyNode", wrapSceneGraphnewReflectionProxyNode);
+	LuaBinder::pushLuaCFuncMethod(l, "newOccluderNode", wrapSceneGraphnewOccluderNode);
+	LuaBinder::pushLuaCFuncMethod(l, "newDecalNode", wrapSceneGraphnewDecalNode);
+	LuaBinder::pushLuaCFuncMethod(l, "newTriggerNode", wrapSceneGraphnewTriggerNode);
+	LuaBinder::pushLuaCFuncMethod(l, "setActiveCameraNode", wrapSceneGraphsetActiveCameraNode);
 	lua_settop(l, 0);
 }
 
@@ -3986,10 +4239,12 @@ static int wrapgetEventManager(lua_State* l)
 /// Wrap the module.
 void wrapModuleScene(lua_State* l)
 {
+	wrapWeakArraySceneNodePtr(l);
 	wrapMoveComponent(l);
 	wrapLightComponent(l);
 	wrapDecalComponent(l);
 	wrapLensFlareComponent(l);
+	wrapTriggerComponent(l);
 	wrapSceneNode(l);
 	wrapModelNode(l);
 	wrapPerspectiveCameraNode(l);
@@ -4003,8 +4258,8 @@ void wrapModuleScene(lua_State* l)
 	wrapReflectionProxyNode(l);
 	wrapOccluderNode(l);
 	wrapDecalNode(l);
+	wrapTriggerNode(l);
 	wrapSceneGraph(l);
-	wrapWeakArraySceneNodePtr(l);
 	wrapEvent(l);
 	wrapLightEvent(l);
 	wrapEventManager(l);

+ 43 - 12
src/anki/script/Scene.xml

@@ -64,6 +64,21 @@ using WeakArraySceneNodePtr = WeakArray<SceneNode*>;
 ]]></head>
 
 	<classes>
+		<!-- Other -->
+		<class name="WeakArraySceneNodePtr">
+			<methods>
+				<method name="getSize">
+					<return>PtrSize</return>
+				</method>
+				<method name="getAt">
+					<overrideCall><![CDATA[SceneNode* ret = (*self)[arg0];]]></overrideCall>
+					<args><arg>U</arg></args>
+					<return>SceneNode*</return>
+				</method>
+			</methods>
+		</class>
+
+		<!-- Components -->
 		<class name="MoveComponent">
 			<methods>
 				<method name="setLocalOrigin">
@@ -193,6 +208,15 @@ using WeakArraySceneNodePtr = WeakArray<SceneNode*>;
 				</method>
 			</methods>
 		</class>
+		<class name="TriggerComponent">
+			<methods>
+				<method name="getContactSceneNodes">
+					<return>WeakArraySceneNodePtr</return>
+				</method>
+			</methods>
+		</class>
+
+		<!-- Nodes -->
 		<class name="SceneNode">
 			<methods>
 				<method name="getName">
@@ -216,6 +240,9 @@ using WeakArraySceneNodePtr = WeakArray<SceneNode*>;
 				<method name="tryGetComponent&lt;DecalComponent&gt;" alias="getDecalComponent">
 					<return>DecalComponent*</return>
 				</method>
+				<method name="tryGetComponent&lt;TriggerComponent&gt;" alias="getTriggerComponent">
+					<return>TriggerComponent*</return>
+				</method>
 			</methods>
 		</class>
 		<class name="ModelNode">
@@ -328,6 +355,14 @@ using WeakArraySceneNodePtr = WeakArray<SceneNode*>;
 				</method>
 			</methods>
 		</class>
+		<class name="TriggerNode">
+			<methods>
+				<method name="getSceneNodeBase">
+					<overrideCall>SceneNode&amp; ret = *self;</overrideCall>
+					<return>SceneNode&amp;</return>
+				</method>
+			</methods>
+		</class>
 		<class name="SceneGraph">
 			<methods>
 				<method name="newPerspectiveCameraNode">
@@ -424,6 +459,14 @@ using WeakArraySceneNodePtr = WeakArray<SceneNode*>;
 					</args>
 					<return>DecalNode*</return>
 				</method>
+				<method name="newTriggerNode">
+					<overrideCall><![CDATA[TriggerNode* ret = newSceneNode<TriggerNode>(self, arg0, arg1);]]></overrideCall>
+					<args>
+						<arg>const CString&amp;</arg>
+						<arg>F32</arg>
+					</args>
+					<return>TriggerNode*</return>
+				</method>
 				<method name="setActiveCameraNode">
 					<args>
 						<arg>SceneNode*</arg>
@@ -433,18 +476,6 @@ using WeakArraySceneNodePtr = WeakArray<SceneNode*>;
 		</class>
 
 		<!-- Events -->
-		<class name="WeakArraySceneNodePtr">
-			<methods>
-				<method name="getSize">
-					<return>PtrSize</return>
-				</method>
-				<method name="getAt">
-					<overrideCall><![CDATA[SceneNode* ret = (*self)[arg0];]]></overrideCall>
-					<args><arg>U</arg></args>
-					<return>SceneNode*</return>
-				</method>
-			</methods>
-		</class>
 		<class name="Event">
 			<methods>
 				<method name="getAssociatedSceneNodes">

+ 1 - 1
src/anki/util/WeakArray.h

@@ -46,7 +46,7 @@ public:
 	{
 	}
 
-	WeakArray(DynamicArray<T>& arr)
+	explicit WeakArray(DynamicArray<T>& arr)
 		: WeakArray()
 	{
 		if(arr.getSize())