Browse Source

Add two new components

Panagiotis Christopoulos Charitos 4 months ago
parent
commit
fef9b02612

+ 2 - 1
AnKi/Resource/MeshResource.h

@@ -74,7 +74,8 @@ public:
 		vertexCount = m_lods[lod].m_vertexCount;
 		vertexCount = m_lods[lod].m_vertexCount;
 	}
 	}
 
 
-	void getMeshletBufferInfo(U32 lod, PtrSize& meshletBoundingVolumesUgbOffset, PtrSize& meshletGeometryDescriptorsUgbOffset, U32& meshletCount)
+	void getMeshletBufferInfo(U32 lod, PtrSize& meshletBoundingVolumesUgbOffset, PtrSize& meshletGeometryDescriptorsUgbOffset,
+							  U32& meshletCount) const
 	{
 	{
 		meshletBoundingVolumesUgbOffset = m_lods[lod].m_meshletBoundingVolumes.getOffset();
 		meshletBoundingVolumesUgbOffset = m_lods[lod].m_meshletBoundingVolumes.getOffset();
 		meshletGeometryDescriptorsUgbOffset = m_lods[lod].m_meshletGeometryDescriptors.getOffset();
 		meshletGeometryDescriptorsUgbOffset = m_lods[lod].m_meshletGeometryDescriptors.getOffset();

+ 2 - 0
AnKi/Scene.h

@@ -25,6 +25,8 @@
 #include <AnKi/Scene/Components/SkyboxComponent.h>
 #include <AnKi/Scene/Components/SkyboxComponent.h>
 #include <AnKi/Scene/Components/TriggerComponent.h>
 #include <AnKi/Scene/Components/TriggerComponent.h>
 #include <AnKi/Scene/Components/UiComponent.h>
 #include <AnKi/Scene/Components/UiComponent.h>
+#include <AnKi/Scene/Components/MeshComponent.h>
+#include <AnKi/Scene/Components/MaterialComponent.h>
 
 
 #include <AnKi/Scene/Events/EventManager.h>
 #include <AnKi/Scene/Events/EventManager.h>
 #include <AnKi/Scene/Events/AnimationEvent.h>
 #include <AnKi/Scene/Events/AnimationEvent.h>

+ 342 - 0
AnKi/Scene/Components/MaterialComponent.cpp

@@ -0,0 +1,342 @@
+// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <AnKi/Scene/Components/MaterialComponent.h>
+#include <AnKi/Scene/Components/SkinComponent.h>
+#include <AnKi/Scene/Components/MeshComponent.h>
+#include <AnKi/Resource/MeshResource.h>
+#include <AnKi/Resource/MaterialResource.h>
+#include <AnKi/Resource/ResourceManager.h>
+#include <AnKi/Core/App.h>
+#include <AnKi/Shaders/Include/GpuSceneFunctions.h>
+
+namespace anki {
+
+MaterialComponent::MaterialComponent(SceneNode* node)
+	: SceneComponent(node, kClassType)
+{
+	m_gpuSceneTransforms.allocate();
+	m_gpuSceneRenderable.allocate();
+	m_gpuSceneMeshLods.allocate();
+}
+
+MaterialComponent::~MaterialComponent()
+{
+	m_gpuSceneTransforms.free();
+	m_gpuSceneRenderable.free();
+	m_gpuSceneMeshLods.free();
+}
+
+void MaterialComponent::setMaterialFilename(CString fname)
+{
+	MaterialResourcePtr newRsrc;
+	const Error err = ResourceManager::getSingleton().loadResource(fname, newRsrc);
+	if(err)
+	{
+		ANKI_SCENE_LOGE("Failed to load resource: %s", fname.cstr());
+		return;
+	}
+
+	m_resource = newRsrc;
+	m_resourceDirty = true;
+}
+
+void MaterialComponent::setSubmeshIndex(U32 submeshIdx)
+{
+	if(m_submeshIdx != submeshIdx)
+	{
+		m_submeshIdx = submeshIdx;
+		m_submeshIdxDirty = true;
+	}
+}
+
+void MaterialComponent::onOtherComponentRemovedOrAdded(SceneComponent* other, Bool added)
+{
+	ANKI_ASSERT(other);
+
+	if(other->getType() != SceneComponentType::kSkin && other->getType() != SceneComponentType::kMesh)
+	{
+		return;
+	}
+
+	const Bool alreadyHasSkinComponent = m_skinComponent != nullptr;
+	if(added && !alreadyHasSkinComponent)
+	{
+		m_skinComponent = static_cast<SkinComponent*>(other);
+		m_skinDirty = true;
+	}
+	else if(!added && other == m_skinComponent)
+	{
+		m_skinComponent = nullptr;
+		m_skinDirty = true;
+	}
+
+	const Bool alreadyHasMeshComponent = m_meshComponent != nullptr;
+	if(added && !alreadyHasMeshComponent)
+	{
+		m_meshComponent = static_cast<MeshComponent*>(other);
+		m_meshComponentDirty = true;
+	}
+	else if(!added && other == m_meshComponent)
+	{
+		m_meshComponent = nullptr;
+		m_meshComponentDirty = true;
+	}
+}
+
+void MaterialComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
+{
+	const Bool mtlUpdated = m_resourceDirty;
+	const Bool meshUpdated = m_meshComponentDirty || (m_meshComponent && m_meshComponent->updatedThisFrame());
+	const Bool moved = info.m_node->movedThisFrame() || m_firstTimeUpdate;
+	const Bool movedLastFrame = m_movedLastFrame || m_firstTimeUpdate;
+	const Bool skinUpdated = m_skinDirty;
+	const Bool submeshUpdated = m_submeshIdxDirty;
+	const Bool hasSkin = m_skinComponent && m_skinComponent->isEnabled();
+	const Bool isValid = m_resource.isCreated() && m_meshComponent && m_meshComponent->isEnabled();
+	m_resourceDirty = false;
+	m_firstTimeUpdate = false;
+	m_meshComponentDirty = false;
+	m_movedLastFrame = moved;
+	m_skinDirty = false;
+	m_submeshIdxDirty = false;
+
+	updated = mtlUpdated || meshUpdated || moved || skinUpdated || submeshUpdated;
+
+	if(!isValid) [[unlikely]]
+	{
+		m_gpuSceneRenderableAabbGBuffer.free();
+		m_gpuSceneRenderableAabbDepth.free();
+		m_gpuSceneRenderableAabbForward.free();
+		m_gpuSceneRenderableAabbRt.free();
+
+		for(RenderingTechnique t : EnumIterable<RenderingTechnique>())
+		{
+			RenderStateBucketContainer::getSingleton().removeUser(m_renderStateBucketIndices[t]);
+		}
+
+		return;
+	}
+
+	// From now on the component is considered valid
+
+	const MaterialResource& mtl = *m_resource;
+	const MeshResource& mesh = m_meshComponent->getMeshResource();
+	const U32 submeshIdx = min(mesh.getSubMeshCount() - 1, m_submeshIdx);
+
+	// Upload transforms
+	if(moved || movedLastFrame) [[unlikely]]
+	{
+		Array<Mat3x4, 2> trfs;
+		trfs[0] = Mat3x4(info.m_node->getWorldTransform());
+		trfs[1] = Mat3x4(info.m_node->getPreviousWorldTransform());
+		m_gpuSceneTransforms.uploadToGpuScene(trfs);
+	}
+
+	// Update mesh lods
+	const Bool meshLodsNeedUpdate = meshUpdated || submeshUpdated;
+	if(meshLodsNeedUpdate) [[unlikely]]
+	{
+		Array<GpuSceneMeshLod, kMaxLodCount> meshLods;
+
+		for(U32 l = 0; l < mesh.getLodCount(); ++l)
+		{
+			GpuSceneMeshLod& meshLod = meshLods[l];
+			meshLod = {};
+			meshLod.m_positionScale = mesh.getPositionsScale();
+			meshLod.m_positionTranslation = mesh.getPositionsTranslation();
+
+			U32 firstIndex, indexCount, firstMeshlet, meshletCount;
+			Aabb aabb;
+			mesh.getSubMeshInfo(l, submeshIdx, firstIndex, indexCount, firstMeshlet, meshletCount, aabb);
+
+			U32 totalIndexCount;
+			IndexType indexType;
+			PtrSize indexUgbOffset;
+			mesh.getIndexBufferInfo(l, indexUgbOffset, totalIndexCount, indexType);
+			indexUgbOffset += firstIndex * getIndexSize(indexType);
+
+			for(VertexStreamId stream = VertexStreamId::kMeshRelatedFirst; stream < VertexStreamId::kMeshRelatedCount; ++stream)
+			{
+				if(mesh.isVertexStreamPresent(stream))
+				{
+					U32 vertCount;
+					PtrSize offset;
+					mesh.getVertexBufferInfo(l, stream, offset, vertCount);
+					const PtrSize elementSize = getFormatInfo(kMeshRelatedVertexStreamFormats[stream]).m_texelSize;
+					ANKI_ASSERT(offset % elementSize == 0);
+					meshLod.m_vertexOffsets[U32(stream)] = U32(offset / elementSize);
+				}
+			}
+
+			meshLod.m_indexCount = indexCount;
+
+			ANKI_ASSERT(indexUgbOffset % getIndexSize(indexType) == 0);
+			meshLod.m_firstIndex = U32(indexUgbOffset / getIndexSize(indexType)) + firstIndex;
+
+			meshLod.m_renderableIndex = m_gpuSceneRenderable.getIndex();
+
+			if(GrManager::getSingleton().getDeviceCapabilities().m_meshShaders || g_meshletRenderingCVar)
+			{
+				U32 dummy;
+				PtrSize meshletBoundingVolumesUgbOffset, meshletGometryDescriptorsUgbOffset;
+				mesh.getMeshletBufferInfo(l, meshletBoundingVolumesUgbOffset, meshletGometryDescriptorsUgbOffset, dummy);
+
+				meshLod.m_firstMeshletBoundingVolume = firstMeshlet + U32(meshletBoundingVolumesUgbOffset / sizeof(MeshletBoundingVolume));
+				meshLod.m_firstMeshletGeometryDescriptor = firstMeshlet + U32(meshletGometryDescriptorsUgbOffset / sizeof(MeshletGeometryDescriptor));
+				meshLod.m_meshletCount = meshletCount;
+			}
+
+			meshLod.m_lod = l;
+
+			if(!!(mtl.getRenderingTechniques() & RenderingTechniqueBit::kAllRt))
+			{
+				const U64 address = mesh.getBottomLevelAccelerationStructure(l, submeshIdx)->getGpuAddress();
+				memcpy(&meshLod.m_blasAddress, &address, sizeof(meshLod.m_blasAddress));
+
+				meshLod.m_tlasInstanceMask = 0xFFFFFFFF;
+			}
+		}
+
+		m_gpuSceneMeshLods.uploadToGpuScene(meshLods);
+	}
+
+	// Update the constants
+	const Bool constantsNeedUpdate = mtlUpdated;
+	if(mtlUpdated) [[unlikely]]
+	{
+		ConstWeakArray<U8> preallocatedConsts = mtl.getPrefilledLocalConstants();
+
+		if(!m_gpuSceneConstants.isValid() || m_gpuSceneConstants.getAllocatedSize() != preallocatedConsts.getSizeInBytes())
+		{
+			GpuSceneBuffer::getSingleton().deferredFree(m_gpuSceneConstants);
+			m_gpuSceneConstants = GpuSceneBuffer::getSingleton().allocate(preallocatedConsts.getSizeInBytes(), 4);
+		}
+
+		GpuSceneMicroPatcher::getSingleton().newCopy(*info.m_framePool, m_gpuSceneConstants.getOffset(), m_gpuSceneConstants.getAllocatedSize(),
+													 preallocatedConsts.getBegin());
+	}
+
+	// Update renderable
+	if(constantsNeedUpdate || skinUpdated) [[unlikely]]
+	{
+		GpuSceneRenderable gpuRenderable = {};
+		gpuRenderable.m_worldTransformsIndex = m_gpuSceneTransforms.getIndex() * 2;
+		gpuRenderable.m_constantsOffset = m_gpuSceneConstants.getOffset();
+		gpuRenderable.m_meshLodsIndex = m_gpuSceneMeshLods.getIndex();
+		gpuRenderable.m_boneTransformsOffset = (hasSkin) ? m_skinComponent->getBoneTransformsGpuSceneOffset() : 0;
+		gpuRenderable.m_particleEmitterIndex = kMaxU32;
+		if(!!(mtl.getRenderingTechniques() & RenderingTechniqueBit::kRtShadow))
+		{
+			const RenderingKey key(RenderingTechnique::kRtShadow, 0, false, false, false);
+			const MaterialVariant& variant = mtl.getOrCreateVariant(key);
+			gpuRenderable.m_rtShadowsShaderHandleIndex = variant.getRtShaderGroupHandleIndex();
+		}
+		if(!!(mtl.getRenderingTechniques() & RenderingTechniqueBit::kRtMaterialFetch))
+		{
+			const RenderingKey key(RenderingTechnique::kRtMaterialFetch, 0, false, false, false);
+			const MaterialVariant& variant = mtl.getOrCreateVariant(key);
+			gpuRenderable.m_rtMaterialFetchShaderHandleIndex = variant.getRtShaderGroupHandleIndex();
+		}
+		gpuRenderable.m_uuid = SceneGraph::getSingleton().getNewUuid();
+
+		m_gpuSceneRenderable.uploadToGpuScene(gpuRenderable);
+	}
+
+	// Scene bounds update
+	const Bool aabbUpdated = moved || meshUpdated || submeshUpdated || hasSkin;
+	Aabb aabbWorld;
+	if(aabbUpdated && isValid) [[unlikely]]
+	{
+		U32 firstIndex, indexCount, firstMeshlet, meshletCount;
+		Aabb aabbLocal;
+		mesh.getSubMeshInfo(0, submeshIdx, firstIndex, indexCount, firstMeshlet, meshletCount, aabbLocal);
+
+		if(m_skinComponent)
+		{
+			aabbLocal = m_skinComponent->getBoneBoundingVolumeLocalSpace().getCompoundShape(aabbLocal);
+		}
+
+		aabbWorld = aabbLocal.getTransformed(info.m_node->getWorldTransform());
+		SceneGraph::getSingleton().updateSceneBounds(aabbWorld.getMin().xyz(), aabbWorld.getMax().xyz());
+	}
+
+	// Update the buckets
+	const Bool bucketsNeedUpdate = mtlUpdated || submeshUpdated || moved != movedLastFrame;
+	if(bucketsNeedUpdate) [[unlikely]]
+	{
+		for(RenderingTechnique t : EnumIterable<RenderingTechnique>())
+		{
+			RenderStateBucketContainer::getSingleton().removeUser(m_renderStateBucketIndices[t]);
+
+			if(!(RenderingTechniqueBit(1 << t) & mtl.getRenderingTechniques()))
+			{
+				continue;
+			}
+
+			// Fill the state
+			RenderingKey key;
+			key.setLod(0); // Materials don't care
+			key.setRenderingTechnique(t);
+			key.setSkinned(hasSkin);
+			key.setVelocity(moved);
+			key.setMeshletRendering(GrManager::getSingleton().getDeviceCapabilities().m_meshShaders || g_meshletRenderingCVar);
+
+			const MaterialVariant& mvariant = mtl.getOrCreateVariant(key);
+
+			RenderStateInfo state;
+			state.m_primitiveTopology = PrimitiveTopology::kTriangles;
+			state.m_indexedDrawcall = true;
+			state.m_program = mvariant.getShaderProgram();
+
+			U32 firstIndex, indexCount, firstMeshlet, meshletCount;
+			Aabb aabb;
+			mesh.getSubMeshInfo(0, submeshIdx, firstIndex, indexCount, firstMeshlet, meshletCount, aabb);
+			const Bool wantsMesletCount = key.getMeshletRendering() && !(RenderingTechniqueBit(1 << t) & RenderingTechniqueBit::kAllRt);
+
+			m_renderStateBucketIndices[t] = RenderStateBucketContainer::getSingleton().addUser(state, t, (wantsMesletCount) ? meshletCount : 0);
+		}
+	}
+
+	// Upload the AABBs to the GPU scene
+	const Bool gpuSceneAabbsNeedUpdate = aabbUpdated || bucketsNeedUpdate;
+	if(gpuSceneAabbsNeedUpdate) [[unlikely]]
+	{
+		// Do raster techniques
+		for(RenderingTechnique t :
+			EnumBitsIterable<RenderingTechnique, RenderingTechniqueBit>(mtl.getRenderingTechniques() & ~RenderingTechniqueBit::kAllRt))
+		{
+			const GpuSceneRenderableBoundingVolume gpuVolume = initGpuSceneRenderableBoundingVolume(
+				aabbWorld.getMin().xyz(), aabbWorld.getMax().xyz(), m_gpuSceneRenderable.getIndex(), m_renderStateBucketIndices[t].get());
+
+			switch(t)
+			{
+			case RenderingTechnique::kGBuffer:
+				m_gpuSceneRenderableAabbGBuffer.uploadToGpuScene(gpuVolume);
+				break;
+			case RenderingTechnique::kDepth:
+				m_gpuSceneRenderableAabbDepth.uploadToGpuScene(gpuVolume);
+				break;
+			case RenderingTechnique::kForward:
+				m_gpuSceneRenderableAabbForward.uploadToGpuScene(gpuVolume);
+				break;
+			default:
+				ANKI_ASSERT(0);
+			}
+		}
+
+		// Do RT techniques
+		if(!!(mtl.getRenderingTechniques() & RenderingTechniqueBit::kAllRt))
+		{
+			const U32 bucket = 0;
+			const GpuSceneRenderableBoundingVolume gpuVolume =
+				initGpuSceneRenderableBoundingVolume(aabbWorld.getMin().xyz(), aabbWorld.getMax().xyz(), m_gpuSceneRenderable.getIndex(), bucket);
+
+			m_gpuSceneRenderableAabbRt.uploadToGpuScene(gpuVolume);
+		}
+	}
+}
+
+} // end namespace anki

+ 65 - 0
AnKi/Scene/Components/MaterialComponent.h

@@ -0,0 +1,65 @@
+// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <AnKi/Scene/Components/SceneComponent.h>
+#include <AnKi/Scene/GpuSceneArray.h>
+#include <AnKi/Scene/RenderStateBucket.h>
+#include <AnKi/Resource/Forward.h>
+
+namespace anki {
+
+/// @addtogroup scene
+/// @{
+
+/// Holds geometry information.
+class MaterialComponent final : public SceneComponent
+{
+	ANKI_SCENE_COMPONENT(MaterialComponent)
+
+public:
+	MaterialComponent(SceneNode* node);
+
+	~MaterialComponent();
+
+	void setMaterialFilename(CString fname);
+
+	void setSubmeshIndex(U32 submeshIdx);
+
+private:
+	GpuSceneArrays::MeshLod::Allocation m_gpuSceneMeshLods;
+	GpuSceneArrays::Renderable::Allocation m_gpuSceneRenderable;
+	GpuSceneArrays::RenderableBoundingVolumeGBuffer::Allocation m_gpuSceneRenderableAabbGBuffer;
+	GpuSceneArrays::RenderableBoundingVolumeDepth::Allocation m_gpuSceneRenderableAabbDepth;
+	GpuSceneArrays::RenderableBoundingVolumeForward::Allocation m_gpuSceneRenderableAabbForward;
+	GpuSceneArrays::RenderableBoundingVolumeRt::Allocation m_gpuSceneRenderableAabbRt;
+	GpuSceneArrays::Transform::Allocation m_gpuSceneTransforms;
+	GpuSceneBufferAllocation m_gpuSceneConstants;
+
+	Array<RenderStateBucketIndex, U32(RenderingTechnique::kCount)> m_renderStateBucketIndices;
+
+	MaterialResourcePtr m_resource;
+
+	SkinComponent* m_skinComponent = nullptr;
+	MeshComponent* m_meshComponent = nullptr;
+
+	U32 m_submeshIdx = 0;
+
+	Bool m_resourceDirty : 1 = true;
+	Bool m_skinDirty : 1 = true;
+	Bool m_meshComponentDirty : 1 = true;
+	Bool m_firstTimeUpdate : 1 = true; ///< Extra flag in case the component is added in a node that hasn't been moved.
+	Bool m_movedLastFrame : 1 = true;
+	Bool m_submeshIdxDirty : 1 = true;
+	Bool m_castsShadow : 1 = false;
+
+	void update(SceneComponentUpdateInfo& info, Bool& updated) override;
+
+	void onOtherComponentRemovedOrAdded(SceneComponent* other, Bool added) override;
+};
+/// @}
+
+} // end namespace anki

+ 41 - 0
AnKi/Scene/Components/MeshComponent.cpp

@@ -0,0 +1,41 @@
+// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <AnKi/Scene/Components/MeshComponent.h>
+#include <AnKi/Resource/ResourceManager.h>
+#include <AnKi/Resource/MeshResource.h>
+
+namespace anki {
+
+MeshComponent::MeshComponent(SceneNode* node)
+	: SceneComponent(node, kClassType)
+{
+}
+
+MeshComponent::~MeshComponent()
+{
+}
+
+void MeshComponent::setMeshFilename(CString fname)
+{
+	MeshResourcePtr newRsrc;
+	const Error err = ResourceManager::getSingleton().loadResource(fname, newRsrc);
+	if(err)
+	{
+		ANKI_SCENE_LOGE("Failed to load resource: %s", fname.cstr());
+		return;
+	}
+
+	m_resource = newRsrc;
+	m_resourceDirty = true;
+}
+
+void MeshComponent::update([[maybe_unused]] SceneComponentUpdateInfo& info, Bool& updated)
+{
+	updated = m_resourceDirty;
+	m_resourceDirty = false;
+}
+
+} // end namespace anki

+ 47 - 0
AnKi/Scene/Components/MeshComponent.h

@@ -0,0 +1,47 @@
+// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <AnKi/Scene/Components/SceneComponent.h>
+#include <AnKi/Resource/Forward.h>
+
+namespace anki {
+
+/// @addtogroup scene
+/// @{
+
+/// Holds geometry information.
+class MeshComponent final : public SceneComponent
+{
+	ANKI_SCENE_COMPONENT(MeshComponent)
+
+public:
+	MeshComponent(SceneNode* node);
+
+	~MeshComponent();
+
+	void update(SceneComponentUpdateInfo& info, Bool& updated) override;
+
+	void setMeshFilename(CString fname);
+
+	Bool isEnabled() const
+	{
+		return m_resource.isCreated();
+	}
+
+	const MeshResource& getMeshResource() const
+	{
+		return *m_resource;
+	}
+
+private:
+	MeshResourcePtr m_resource;
+
+	Bool m_resourceDirty = true;
+};
+/// @}
+
+} // end namespace anki

+ 6 - 0
AnKi/Scene/Components/SceneComponent.h

@@ -9,6 +9,7 @@
 #include <AnKi/Util/Functions.h>
 #include <AnKi/Util/Functions.h>
 #include <AnKi/Util/BitMask.h>
 #include <AnKi/Util/BitMask.h>
 #include <AnKi/Util/Enum.h>
 #include <AnKi/Util/Enum.h>
+#include <AnKi/Core/Common.h>
 
 
 namespace anki {
 namespace anki {
 
 
@@ -127,6 +128,11 @@ public:
 		return m_updateOrderWeights[type];
 		return m_updateOrderWeights[type];
 	}
 	}
 
 
+	Bool updatedThisFrame() const
+	{
+		return m_timestamp == GlobalFrameIndex::getSingleton().m_value;
+	}
+
 protected:
 protected:
 	U32 regenerateUuid();
 	U32 regenerateUuid();
 
 

+ 5 - 0
AnKi/Scene/Components/SceneComponentClasses.def.h

@@ -26,6 +26,11 @@ ANKI_SCENE_COMPONENT_SEPARATOR
 ANKI_DEFINE_SCENE_COMPONENT(Trigger, 40.0f)
 ANKI_DEFINE_SCENE_COMPONENT(Trigger, 40.0f)
 ANKI_SCENE_COMPONENT_SEPARATOR
 ANKI_SCENE_COMPONENT_SEPARATOR
 
 
+ANKI_DEFINE_SCENE_COMPONENT(Mesh, 50.0f)
+ANKI_SCENE_COMPONENT_SEPARATOR
+
+ANKI_DEFINE_SCENE_COMPONENT(Material, 100.0f)
+ANKI_SCENE_COMPONENT_SEPARATOR
 ANKI_DEFINE_SCENE_COMPONENT(Model, 100.0f)
 ANKI_DEFINE_SCENE_COMPONENT(Model, 100.0f)
 ANKI_SCENE_COMPONENT_SEPARATOR
 ANKI_SCENE_COMPONENT_SEPARATOR
 ANKI_DEFINE_SCENE_COMPONENT(ParticleEmitter, 100.0f)
 ANKI_DEFINE_SCENE_COMPONENT(ParticleEmitter, 100.0f)

+ 2 - 0
AnKi/Scene/SceneGraph.cpp

@@ -33,6 +33,8 @@
 #include <AnKi/Scene/Components/SkyboxComponent.h>
 #include <AnKi/Scene/Components/SkyboxComponent.h>
 #include <AnKi/Scene/Components/TriggerComponent.h>
 #include <AnKi/Scene/Components/TriggerComponent.h>
 #include <AnKi/Scene/Components/UiComponent.h>
 #include <AnKi/Scene/Components/UiComponent.h>
+#include <AnKi/Scene/Components/MeshComponent.h>
+#include <AnKi/Scene/Components/MaterialComponent.h>
 
 
 namespace anki {
 namespace anki {
 
 

+ 2 - 0
AnKi/Scene/SceneNode.cpp

@@ -26,6 +26,8 @@
 #include <AnKi/Scene/Components/SkyboxComponent.h>
 #include <AnKi/Scene/Components/SkyboxComponent.h>
 #include <AnKi/Scene/Components/TriggerComponent.h>
 #include <AnKi/Scene/Components/TriggerComponent.h>
 #include <AnKi/Scene/Components/UiComponent.h>
 #include <AnKi/Scene/Components/UiComponent.h>
+#include <AnKi/Scene/Components/MeshComponent.h>
+#include <AnKi/Scene/Components/MaterialComponent.h>
 
 
 namespace anki {
 namespace anki {
 
 

+ 199 - 24
AnKi/Script/Scene.cpp

@@ -40,7 +40,7 @@ static EventManager* getEventManager(lua_State* l)
 
 
 using WeakArraySceneNodePtr = WeakArray<SceneNode*>;
 using WeakArraySceneNodePtr = WeakArray<SceneNode*>;
 
 
-LuaUserDataTypeInfo luaUserDataTypeInfoLightComponentType = {-422043100225580406, "LightComponentType", 0, nullptr, nullptr};
+LuaUserDataTypeInfo luaUserDataTypeInfoLightComponentType = {1017868390018106569, "LightComponentType", 0, nullptr, nullptr};
 
 
 template<>
 template<>
 const LuaUserDataTypeInfo& LuaUserData::getDataTypeInfoFor<LightComponentType>()
 const LuaUserDataTypeInfo& LuaUserData::getDataTypeInfoFor<LightComponentType>()
@@ -75,7 +75,7 @@ static inline void wrapLightComponentType(lua_State* l)
 	lua_settop(l, 0);
 	lua_settop(l, 0);
 }
 }
 
 
-LuaUserDataTypeInfo luaUserDataTypeInfoBodyComponentCollisionShapeType = {3195632967110394111, "BodyComponentCollisionShapeType", 0, nullptr,
+LuaUserDataTypeInfo luaUserDataTypeInfoBodyComponentCollisionShapeType = {-4469018935477875302, "BodyComponentCollisionShapeType", 0, nullptr,
 																		  nullptr};
 																		  nullptr};
 
 
 template<>
 template<>
@@ -120,7 +120,7 @@ static inline void wrapBodyComponentCollisionShapeType(lua_State* l)
 }
 }
 
 
 LuaUserDataTypeInfo luaUserDataTypeInfoWeakArraySceneNodePtr = {
 LuaUserDataTypeInfo luaUserDataTypeInfoWeakArraySceneNodePtr = {
-	-7373253839206137444, "WeakArraySceneNodePtr", LuaUserData::computeSizeForGarbageCollected<WeakArraySceneNodePtr>(), nullptr, nullptr};
+	5474400433201232564, "WeakArraySceneNodePtr", LuaUserData::computeSizeForGarbageCollected<WeakArraySceneNodePtr>(), nullptr, nullptr};
 
 
 template<>
 template<>
 const LuaUserDataTypeInfo& LuaUserData::getDataTypeInfoFor<WeakArraySceneNodePtr>()
 const LuaUserDataTypeInfo& LuaUserData::getDataTypeInfoFor<WeakArraySceneNodePtr>()
@@ -238,7 +238,7 @@ static inline void wrapWeakArraySceneNodePtr(lua_State* l)
 	lua_settop(l, 0);
 	lua_settop(l, 0);
 }
 }
 
 
-LuaUserDataTypeInfo luaUserDataTypeInfoLightComponent = {-9073978231010587683, "LightComponent",
+LuaUserDataTypeInfo luaUserDataTypeInfoLightComponent = {833662240382024818, "LightComponent",
 														 LuaUserData::computeSizeForGarbageCollected<LightComponent>(), nullptr, nullptr};
 														 LuaUserData::computeSizeForGarbageCollected<LightComponent>(), nullptr, nullptr};
 
 
 template<>
 template<>
@@ -849,7 +849,7 @@ static inline void wrapLightComponent(lua_State* l)
 	lua_settop(l, 0);
 	lua_settop(l, 0);
 }
 }
 
 
-LuaUserDataTypeInfo luaUserDataTypeInfoDecalComponent = {-2335717325099644994, "DecalComponent",
+LuaUserDataTypeInfo luaUserDataTypeInfoDecalComponent = {-2747121038409584545, "DecalComponent",
 														 LuaUserData::computeSizeForGarbageCollected<DecalComponent>(), nullptr, nullptr};
 														 LuaUserData::computeSizeForGarbageCollected<DecalComponent>(), nullptr, nullptr};
 
 
 template<>
 template<>
@@ -971,7 +971,7 @@ static inline void wrapDecalComponent(lua_State* l)
 	lua_settop(l, 0);
 	lua_settop(l, 0);
 }
 }
 
 
-LuaUserDataTypeInfo luaUserDataTypeInfoLensFlareComponent = {-8264422868601051319, "LensFlareComponent",
+LuaUserDataTypeInfo luaUserDataTypeInfoLensFlareComponent = {4022752674248547993, "LensFlareComponent",
 															 LuaUserData::computeSizeForGarbageCollected<LensFlareComponent>(), nullptr, nullptr};
 															 LuaUserData::computeSizeForGarbageCollected<LensFlareComponent>(), nullptr, nullptr};
 
 
 template<>
 template<>
@@ -1134,7 +1134,7 @@ static inline void wrapLensFlareComponent(lua_State* l)
 	lua_settop(l, 0);
 	lua_settop(l, 0);
 }
 }
 
 
-LuaUserDataTypeInfo luaUserDataTypeInfoBodyComponent = {-3002233952553439959, "BodyComponent",
+LuaUserDataTypeInfo luaUserDataTypeInfoBodyComponent = {2720560564281158059, "BodyComponent",
 														LuaUserData::computeSizeForGarbageCollected<BodyComponent>(), nullptr, nullptr};
 														LuaUserData::computeSizeForGarbageCollected<BodyComponent>(), nullptr, nullptr};
 
 
 template<>
 template<>
@@ -1444,7 +1444,7 @@ static inline void wrapBodyComponent(lua_State* l)
 	lua_settop(l, 0);
 	lua_settop(l, 0);
 }
 }
 
 
-LuaUserDataTypeInfo luaUserDataTypeInfoTriggerComponent = {7726514997328849856, "TriggerComponent",
+LuaUserDataTypeInfo luaUserDataTypeInfoTriggerComponent = {8183277099337493224, "TriggerComponent",
 														   LuaUserData::computeSizeForGarbageCollected<TriggerComponent>(), nullptr, nullptr};
 														   LuaUserData::computeSizeForGarbageCollected<TriggerComponent>(), nullptr, nullptr};
 
 
 template<>
 template<>
@@ -1558,7 +1558,7 @@ static inline void wrapTriggerComponent(lua_State* l)
 	lua_settop(l, 0);
 	lua_settop(l, 0);
 }
 }
 
 
-LuaUserDataTypeInfo luaUserDataTypeInfoFogDensityComponent = {-6644828763714815062, "FogDensityComponent",
+LuaUserDataTypeInfo luaUserDataTypeInfoFogDensityComponent = {2337586273051444619, "FogDensityComponent",
 															  LuaUserData::computeSizeForGarbageCollected<FogDensityComponent>(), nullptr, nullptr};
 															  LuaUserData::computeSizeForGarbageCollected<FogDensityComponent>(), nullptr, nullptr};
 
 
 template<>
 template<>
@@ -1664,7 +1664,7 @@ static inline void wrapFogDensityComponent(lua_State* l)
 	lua_settop(l, 0);
 	lua_settop(l, 0);
 }
 }
 
 
-LuaUserDataTypeInfo luaUserDataTypeInfoCameraComponent = {2204986172338020118, "CameraComponent",
+LuaUserDataTypeInfo luaUserDataTypeInfoCameraComponent = {-5107745728978735734, "CameraComponent",
 														  LuaUserData::computeSizeForGarbageCollected<CameraComponent>(), nullptr, nullptr};
 														  LuaUserData::computeSizeForGarbageCollected<CameraComponent>(), nullptr, nullptr};
 
 
 template<>
 template<>
@@ -1746,7 +1746,7 @@ static inline void wrapCameraComponent(lua_State* l)
 }
 }
 
 
 LuaUserDataTypeInfo luaUserDataTypeInfoGlobalIlluminationProbeComponent = {
 LuaUserDataTypeInfo luaUserDataTypeInfoGlobalIlluminationProbeComponent = {
-	1351895094142149124, "GlobalIlluminationProbeComponent", LuaUserData::computeSizeForGarbageCollected<GlobalIlluminationProbeComponent>(), nullptr,
+	8103061505586344331, "GlobalIlluminationProbeComponent", LuaUserData::computeSizeForGarbageCollected<GlobalIlluminationProbeComponent>(), nullptr,
 	nullptr};
 	nullptr};
 
 
 template<>
 template<>
@@ -1943,7 +1943,7 @@ static inline void wrapGlobalIlluminationProbeComponent(lua_State* l)
 }
 }
 
 
 LuaUserDataTypeInfo luaUserDataTypeInfoReflectionProbeComponent = {
 LuaUserDataTypeInfo luaUserDataTypeInfoReflectionProbeComponent = {
-	5326043231317868834, "ReflectionProbeComponent", LuaUserData::computeSizeForGarbageCollected<ReflectionProbeComponent>(), nullptr, nullptr};
+	-2939452214358016744, "ReflectionProbeComponent", LuaUserData::computeSizeForGarbageCollected<ReflectionProbeComponent>(), nullptr, nullptr};
 
 
 template<>
 template<>
 const LuaUserDataTypeInfo& LuaUserData::getDataTypeInfoFor<ReflectionProbeComponent>()
 const LuaUserDataTypeInfo& LuaUserData::getDataTypeInfoFor<ReflectionProbeComponent>()
@@ -1959,7 +1959,7 @@ static inline void wrapReflectionProbeComponent(lua_State* l)
 }
 }
 
 
 LuaUserDataTypeInfo luaUserDataTypeInfoParticleEmitterComponent = {
 LuaUserDataTypeInfo luaUserDataTypeInfoParticleEmitterComponent = {
-	3473927078792861447, "ParticleEmitterComponent", LuaUserData::computeSizeForGarbageCollected<ParticleEmitterComponent>(), nullptr, nullptr};
+	386004265828847962, "ParticleEmitterComponent", LuaUserData::computeSizeForGarbageCollected<ParticleEmitterComponent>(), nullptr, nullptr};
 
 
 template<>
 template<>
 const LuaUserDataTypeInfo& LuaUserData::getDataTypeInfoFor<ParticleEmitterComponent>()
 const LuaUserDataTypeInfo& LuaUserData::getDataTypeInfoFor<ParticleEmitterComponent>()
@@ -2021,7 +2021,7 @@ static inline void wrapParticleEmitterComponent(lua_State* l)
 	lua_settop(l, 0);
 	lua_settop(l, 0);
 }
 }
 
 
-LuaUserDataTypeInfo luaUserDataTypeInfoModelComponent = {-4163551330392490848, "ModelComponent",
+LuaUserDataTypeInfo luaUserDataTypeInfoModelComponent = {2696577720630677131, "ModelComponent",
 														 LuaUserData::computeSizeForGarbageCollected<ModelComponent>(), nullptr, nullptr};
 														 LuaUserData::computeSizeForGarbageCollected<ModelComponent>(), nullptr, nullptr};
 
 
 template<>
 template<>
@@ -2084,7 +2084,180 @@ static inline void wrapModelComponent(lua_State* l)
 	lua_settop(l, 0);
 	lua_settop(l, 0);
 }
 }
 
 
-LuaUserDataTypeInfo luaUserDataTypeInfoSkinComponent = {7633077660147082214, "SkinComponent",
+LuaUserDataTypeInfo luaUserDataTypeInfoMeshComponent = {-8442755837115802242, "MeshComponent",
+														LuaUserData::computeSizeForGarbageCollected<MeshComponent>(), nullptr, nullptr};
+
+template<>
+const LuaUserDataTypeInfo& LuaUserData::getDataTypeInfoFor<MeshComponent>()
+{
+	return luaUserDataTypeInfoMeshComponent;
+}
+
+/// Pre-wrap method MeshComponent::setMeshFilename.
+static inline int pwrapMeshComponentsetMeshFilename(lua_State* l)
+{
+	[[maybe_unused]] LuaUserData* ud;
+	[[maybe_unused]] void* voidp;
+	[[maybe_unused]] PtrSize size;
+
+	if(LuaBinder::checkArgsCount(l, 2)) [[unlikely]]
+	{
+		return -1;
+	}
+
+	// Get "this" as "self"
+	if(LuaBinder::checkUserData(l, 1, luaUserDataTypeInfoMeshComponent, ud))
+	{
+		return -1;
+	}
+
+	MeshComponent* self = ud->getData<MeshComponent>();
+
+	// Pop arguments
+	const char* arg0;
+	if(LuaBinder::checkString(l, 2, arg0)) [[unlikely]]
+	{
+		return -1;
+	}
+
+	// Call the method
+	self->setMeshFilename(arg0);
+
+	return 0;
+}
+
+/// Wrap method MeshComponent::setMeshFilename.
+static int wrapMeshComponentsetMeshFilename(lua_State* l)
+{
+	int res = pwrapMeshComponentsetMeshFilename(l);
+	if(res >= 0)
+	{
+		return res;
+	}
+
+	lua_error(l);
+	return 0;
+}
+
+/// Wrap class MeshComponent.
+static inline void wrapMeshComponent(lua_State* l)
+{
+	LuaBinder::createClass(l, &luaUserDataTypeInfoMeshComponent);
+	LuaBinder::pushLuaCFuncMethod(l, "setMeshFilename", wrapMeshComponentsetMeshFilename);
+	lua_settop(l, 0);
+}
+
+LuaUserDataTypeInfo luaUserDataTypeInfoMaterialComponent = {-5580106782512361834, "MaterialComponent",
+															LuaUserData::computeSizeForGarbageCollected<MaterialComponent>(), nullptr, nullptr};
+
+template<>
+const LuaUserDataTypeInfo& LuaUserData::getDataTypeInfoFor<MaterialComponent>()
+{
+	return luaUserDataTypeInfoMaterialComponent;
+}
+
+/// Pre-wrap method MaterialComponent::setMaterialFilename.
+static inline int pwrapMaterialComponentsetMaterialFilename(lua_State* l)
+{
+	[[maybe_unused]] LuaUserData* ud;
+	[[maybe_unused]] void* voidp;
+	[[maybe_unused]] PtrSize size;
+
+	if(LuaBinder::checkArgsCount(l, 2)) [[unlikely]]
+	{
+		return -1;
+	}
+
+	// Get "this" as "self"
+	if(LuaBinder::checkUserData(l, 1, luaUserDataTypeInfoMaterialComponent, ud))
+	{
+		return -1;
+	}
+
+	MaterialComponent* self = ud->getData<MaterialComponent>();
+
+	// Pop arguments
+	const char* arg0;
+	if(LuaBinder::checkString(l, 2, arg0)) [[unlikely]]
+	{
+		return -1;
+	}
+
+	// Call the method
+	self->setMaterialFilename(arg0);
+
+	return 0;
+}
+
+/// Wrap method MaterialComponent::setMaterialFilename.
+static int wrapMaterialComponentsetMaterialFilename(lua_State* l)
+{
+	int res = pwrapMaterialComponentsetMaterialFilename(l);
+	if(res >= 0)
+	{
+		return res;
+	}
+
+	lua_error(l);
+	return 0;
+}
+
+/// Pre-wrap method MaterialComponent::setSubmeshIndex.
+static inline int pwrapMaterialComponentsetSubmeshIndex(lua_State* l)
+{
+	[[maybe_unused]] LuaUserData* ud;
+	[[maybe_unused]] void* voidp;
+	[[maybe_unused]] PtrSize size;
+
+	if(LuaBinder::checkArgsCount(l, 2)) [[unlikely]]
+	{
+		return -1;
+	}
+
+	// Get "this" as "self"
+	if(LuaBinder::checkUserData(l, 1, luaUserDataTypeInfoMaterialComponent, ud))
+	{
+		return -1;
+	}
+
+	MaterialComponent* self = ud->getData<MaterialComponent>();
+
+	// Pop arguments
+	U32 arg0;
+	if(LuaBinder::checkNumber(l, 2, arg0)) [[unlikely]]
+	{
+		return -1;
+	}
+
+	// Call the method
+	self->setSubmeshIndex(arg0);
+
+	return 0;
+}
+
+/// Wrap method MaterialComponent::setSubmeshIndex.
+static int wrapMaterialComponentsetSubmeshIndex(lua_State* l)
+{
+	int res = pwrapMaterialComponentsetSubmeshIndex(l);
+	if(res >= 0)
+	{
+		return res;
+	}
+
+	lua_error(l);
+	return 0;
+}
+
+/// Wrap class MaterialComponent.
+static inline void wrapMaterialComponent(lua_State* l)
+{
+	LuaBinder::createClass(l, &luaUserDataTypeInfoMaterialComponent);
+	LuaBinder::pushLuaCFuncMethod(l, "setMaterialFilename", wrapMaterialComponentsetMaterialFilename);
+	LuaBinder::pushLuaCFuncMethod(l, "setSubmeshIndex", wrapMaterialComponentsetSubmeshIndex);
+	lua_settop(l, 0);
+}
+
+LuaUserDataTypeInfo luaUserDataTypeInfoSkinComponent = {8982112231014763956, "SkinComponent",
 														LuaUserData::computeSizeForGarbageCollected<SkinComponent>(), nullptr, nullptr};
 														LuaUserData::computeSizeForGarbageCollected<SkinComponent>(), nullptr, nullptr};
 
 
 template<>
 template<>
@@ -2147,7 +2320,7 @@ static inline void wrapSkinComponent(lua_State* l)
 	lua_settop(l, 0);
 	lua_settop(l, 0);
 }
 }
 
 
-LuaUserDataTypeInfo luaUserDataTypeInfoSkyboxComponent = {5640611622526745985, "SkyboxComponent",
+LuaUserDataTypeInfo luaUserDataTypeInfoSkyboxComponent = {7444083320643039653, "SkyboxComponent",
 														  LuaUserData::computeSizeForGarbageCollected<SkyboxComponent>(), nullptr, nullptr};
 														  LuaUserData::computeSizeForGarbageCollected<SkyboxComponent>(), nullptr, nullptr};
 
 
 template<>
 template<>
@@ -2638,7 +2811,7 @@ static inline void wrapSkyboxComponent(lua_State* l)
 	lua_settop(l, 0);
 	lua_settop(l, 0);
 }
 }
 
 
-LuaUserDataTypeInfo luaUserDataTypeInfoSceneNode = {8031015106478875500, "SceneNode", LuaUserData::computeSizeForGarbageCollected<SceneNode>(),
+LuaUserDataTypeInfo luaUserDataTypeInfoSceneNode = {-742916811498860873, "SceneNode", LuaUserData::computeSizeForGarbageCollected<SceneNode>(),
 													nullptr, nullptr};
 													nullptr, nullptr};
 
 
 template<>
 template<>
@@ -4481,7 +4654,7 @@ static inline void wrapSceneNode(lua_State* l)
 	lua_settop(l, 0);
 	lua_settop(l, 0);
 }
 }
 
 
-LuaUserDataTypeInfo luaUserDataTypeInfoSceneGraph = {-9169673447479816112, "SceneGraph", LuaUserData::computeSizeForGarbageCollected<SceneGraph>(),
+LuaUserDataTypeInfo luaUserDataTypeInfoSceneGraph = {-2594793568578495671, "SceneGraph", LuaUserData::computeSizeForGarbageCollected<SceneGraph>(),
 													 nullptr, nullptr};
 													 nullptr, nullptr};
 
 
 template<>
 template<>
@@ -4667,7 +4840,7 @@ static inline void wrapSceneGraph(lua_State* l)
 	lua_settop(l, 0);
 	lua_settop(l, 0);
 }
 }
 
 
-LuaUserDataTypeInfo luaUserDataTypeInfoEvent = {-4553391550055383207, "Event", LuaUserData::computeSizeForGarbageCollected<Event>(), nullptr,
+LuaUserDataTypeInfo luaUserDataTypeInfoEvent = {-4509585165280068945, "Event", LuaUserData::computeSizeForGarbageCollected<Event>(), nullptr,
 												nullptr};
 												nullptr};
 
 
 template<>
 template<>
@@ -4732,7 +4905,7 @@ static inline void wrapEvent(lua_State* l)
 	lua_settop(l, 0);
 	lua_settop(l, 0);
 }
 }
 
 
-LuaUserDataTypeInfo luaUserDataTypeInfoLightEvent = {-7196089091128304157, "LightEvent", LuaUserData::computeSizeForGarbageCollected<LightEvent>(),
+LuaUserDataTypeInfo luaUserDataTypeInfoLightEvent = {-5941448867113593269, "LightEvent", LuaUserData::computeSizeForGarbageCollected<LightEvent>(),
 													 nullptr, nullptr};
 													 nullptr, nullptr};
 
 
 template<>
 template<>
@@ -4851,7 +5024,7 @@ static inline void wrapLightEvent(lua_State* l)
 	lua_settop(l, 0);
 	lua_settop(l, 0);
 }
 }
 
 
-LuaUserDataTypeInfo luaUserDataTypeInfoScriptEvent = {3704782575420779455, "ScriptEvent", LuaUserData::computeSizeForGarbageCollected<ScriptEvent>(),
+LuaUserDataTypeInfo luaUserDataTypeInfoScriptEvent = {6583137492092883558, "ScriptEvent", LuaUserData::computeSizeForGarbageCollected<ScriptEvent>(),
 													  nullptr, nullptr};
 													  nullptr, nullptr};
 
 
 template<>
 template<>
@@ -4867,7 +5040,7 @@ static inline void wrapScriptEvent(lua_State* l)
 	lua_settop(l, 0);
 	lua_settop(l, 0);
 }
 }
 
 
-LuaUserDataTypeInfo luaUserDataTypeInfoJitterMoveEvent = {-4564135298097450632, "JitterMoveEvent",
+LuaUserDataTypeInfo luaUserDataTypeInfoJitterMoveEvent = {2406885170982830049, "JitterMoveEvent",
 														  LuaUserData::computeSizeForGarbageCollected<JitterMoveEvent>(), nullptr, nullptr};
 														  LuaUserData::computeSizeForGarbageCollected<JitterMoveEvent>(), nullptr, nullptr};
 
 
 template<>
 template<>
@@ -4942,7 +5115,7 @@ static inline void wrapJitterMoveEvent(lua_State* l)
 	lua_settop(l, 0);
 	lua_settop(l, 0);
 }
 }
 
 
-LuaUserDataTypeInfo luaUserDataTypeInfoAnimationEvent = {9125743954504628346, "AnimationEvent",
+LuaUserDataTypeInfo luaUserDataTypeInfoAnimationEvent = {-2264930335835658590, "AnimationEvent",
 														 LuaUserData::computeSizeForGarbageCollected<AnimationEvent>(), nullptr, nullptr};
 														 LuaUserData::computeSizeForGarbageCollected<AnimationEvent>(), nullptr, nullptr};
 
 
 template<>
 template<>
@@ -4958,7 +5131,7 @@ static inline void wrapAnimationEvent(lua_State* l)
 	lua_settop(l, 0);
 	lua_settop(l, 0);
 }
 }
 
 
-LuaUserDataTypeInfo luaUserDataTypeInfoEventManager = {5839939442792142754, "EventManager",
+LuaUserDataTypeInfo luaUserDataTypeInfoEventManager = {-8213202443626292885, "EventManager",
 													   LuaUserData::computeSizeForGarbageCollected<EventManager>(), nullptr, nullptr};
 													   LuaUserData::computeSizeForGarbageCollected<EventManager>(), nullptr, nullptr};
 
 
 template<>
 template<>
@@ -5374,6 +5547,8 @@ void wrapModuleScene(lua_State* l)
 	wrapReflectionProbeComponent(l);
 	wrapReflectionProbeComponent(l);
 	wrapParticleEmitterComponent(l);
 	wrapParticleEmitterComponent(l);
 	wrapModelComponent(l);
 	wrapModelComponent(l);
+	wrapMeshComponent(l);
+	wrapMaterialComponent(l);
 	wrapSkinComponent(l);
 	wrapSkinComponent(l);
 	wrapSkyboxComponent(l);
 	wrapSkyboxComponent(l);
 	wrapSceneNode(l);
 	wrapSceneNode(l);

+ 25 - 0
AnKi/Script/Scene.xml

@@ -284,6 +284,31 @@ using WeakArraySceneNodePtr = WeakArray<SceneNode*>;
 			</methods>
 			</methods>
 		</class>
 		</class>
 
 
+		<class name="MeshComponent">
+			<methods>
+				<method name="setMeshFilename">
+					<args>
+						<arg>CString</arg>
+					</args>
+				</method>
+			</methods>
+		</class>
+
+		<class name="MaterialComponent">
+			<methods>
+				<method name="setMaterialFilename">
+					<args>
+						<arg>CString</arg>
+					</args>
+				</method>
+				<method name="setSubmeshIndex">
+					<args>
+						<arg>U32</arg>
+					</args>
+				</method>
+			</methods>
+		</class>
+
 		<class name="SkinComponent">
 		<class name="SkinComponent">
 			<methods>
 			<methods>
 				<method name="loadSkeletonResource">
 				<method name="loadSkeletonResource">