Skip to content

Commit 391ee25

Browse files
committed
Merge pull request #1253 from DelinWorks/instancing-support
1 parent b71ab7a commit 391ee25

22 files changed

+725
-55
lines changed

core/3d/Mesh.cpp

Lines changed: 116 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,61 @@ void Mesh::resetLightUniformValues()
9090
_spotLightUniformRangeInverseValues.assign(maxSpotLight, 0.0f);
9191
}
9292

93+
94+
95+
void Mesh::enableInstancing(bool instance, int count)
96+
{
97+
_instancing = instance;
98+
_instanceCount = count;
99+
}
100+
101+
void Mesh::setInstanceCount(int count) {
102+
AXASSERT(_instancing, "Instancing should be enabled on this mesh.");
103+
104+
_instanceCount = count;
105+
}
106+
107+
void Mesh::addInstanceChild(Node* child)
108+
{
109+
AX_SAFE_RETAIN(child);
110+
_instances.push_back(child);
111+
_instanceTransformDirty = true;
112+
113+
if (_instances.size() > _instanceCount)
114+
{
115+
_instanceCount *= 2;
116+
_instanceTransformBufferDirty = true;
117+
}
118+
}
119+
120+
void Mesh::shrinkToFitInstances()
121+
{
122+
if (_instanceCount > _instances.size())
123+
{
124+
_instanceCount = _instances.size();
125+
_instanceTransformBufferDirty = true;
126+
}
127+
}
128+
129+
void Mesh::rebuildInstances()
130+
{
131+
_instanceTransformDirty = true;
132+
}
133+
134+
void Mesh::setDynamicInstancing(bool dynamic)
135+
{
136+
_dynamicInstancing = dynamic;
137+
}
138+
93139
Mesh::Mesh()
94140
: _skin(nullptr)
95141
, _visible(true)
142+
, _instancing(false)
143+
, _instanceTransformBuffer(nullptr)
144+
, _instanceTransformBufferDirty(false)
145+
, _instanceCount(0)
146+
, _dynamicInstancing(false)
147+
, _instanceMatrixCache(nullptr)
96148
, meshIndexFormat(CustomCommand::IndexFormat::U_SHORT)
97149
, _meshIndexData(nullptr)
98150
, _blend(BlendFunc::ALPHA_NON_PREMULTIPLIED)
@@ -103,12 +155,16 @@ Mesh::Mesh()
103155
Mesh::~Mesh()
104156
{
105157
for (auto&& tex : _textures)
106-
{
107158
AX_SAFE_RELEASE(tex.second);
108-
}
159+
160+
for (auto&& ins : _instances)
161+
AX_SAFE_RELEASE(ins);
162+
109163
AX_SAFE_RELEASE(_skin);
110164
AX_SAFE_RELEASE(_meshIndexData);
111165
AX_SAFE_RELEASE(_material);
166+
AX_SAFE_RELEASE(_instanceTransformBuffer);
167+
AX_SAFE_DELETE_ARRAY(_instanceMatrixCache);
112168
}
113169

114170
backend::Buffer* Mesh::getVertexBuffer() const
@@ -343,7 +399,7 @@ void Mesh::setMaterial(Material* material)
343399
auto& attributes = program->getActiveAttributes();
344400
auto meshVertexData = _meshIndexData->getMeshVertexData();
345401
auto attributeCount = meshVertexData->getMeshVertexAttribCount();
346-
AXASSERT(attributes.size() <= attributeCount, "missing attribute data");
402+
//AXASSERT(attributes.size() <= attributeCount, "missing attribute data");
347403
}
348404
#endif
349405
// TODO
@@ -387,6 +443,56 @@ void Mesh::draw(Renderer* renderer,
387443
if (isTransparent)
388444
flags |= Node::FLAGS_RENDER_AS_3D;
389445

446+
if (_instancing && _instanceCount > 0)
447+
{
448+
if (!_instanceTransformBuffer || _instanceTransformBufferDirty)
449+
{
450+
AX_SAFE_RELEASE(_instanceTransformBuffer);
451+
452+
_instanceTransformBuffer = backend::Device::getInstance()->newBuffer(
453+
_instanceCount * 64, backend::BufferType::VERTEX, backend::BufferUsage::DYNAMIC);
454+
455+
_instanceMatrixCache = new float[_instanceCount * 16];
456+
for (int i = 0; i < _instanceCount; i++)
457+
{
458+
_instanceMatrixCache[i * 16 + 0] = 1.0f;
459+
_instanceMatrixCache[i * 16 + 1] = 0.0f;
460+
_instanceMatrixCache[i * 16 + 2] = 0.0f;
461+
_instanceMatrixCache[i * 16 + 3] = 0.0f;
462+
_instanceMatrixCache[i * 16 + 4] = 0.0f;
463+
_instanceMatrixCache[i * 16 + 5] = 1.0f;
464+
_instanceMatrixCache[i * 16 + 6] = 0.0f;
465+
_instanceMatrixCache[i * 16 + 7] = 0.0f;
466+
_instanceMatrixCache[i * 16 + 8] = 0.0f;
467+
_instanceMatrixCache[i * 16 + 9] = 0.0f;
468+
_instanceMatrixCache[i * 16 + 10] = 1.0f;
469+
_instanceMatrixCache[i * 16 + 11] = 0.0f;
470+
_instanceMatrixCache[i * 16 + 12] = 0.0f;
471+
_instanceMatrixCache[i * 16 + 13] = 0.0f;
472+
_instanceMatrixCache[i * 16 + 14] = 0.0f;
473+
_instanceMatrixCache[i * 16 + 15] = 1.0f;
474+
}
475+
476+
// Fill the buffer with identity matrix.
477+
_instanceTransformBuffer->updateData(_instanceMatrixCache, _instanceCount * 64);
478+
479+
_instanceTransformBufferDirty = false;
480+
}
481+
482+
if (_instanceTransformDirty || _dynamicInstancing)
483+
{
484+
_instanceTransformDirty = false;
485+
486+
int memOffset = 0;
487+
for (auto& _ : _instances)
488+
{
489+
auto& mat = _->getNodeToParentTransform();
490+
std::copy(mat.m, mat.m + 16, _instanceMatrixCache + 16 * memOffset++);
491+
}
492+
_instanceTransformBuffer->updateSubData(_instanceMatrixCache, 0, _instanceCount * 64);
493+
}
494+
}
495+
390496
// TODO
391497
// _meshCommand.init(globalZ,
392498
// _material,
@@ -428,6 +534,13 @@ void Mesh::draw(Renderer* renderer,
428534
command.setTransparent(isTransparent);
429535
command.set3D(!_material->isForce2DQueue());
430536
command.setWireframe(wireframe);
537+
if (_instancing && _instances.size() > 0)
538+
{
539+
command.setDrawType(CustomCommand::DrawType::ELEMENT_INSTANCE);
540+
command.setInstanceBuffer(_instanceTransformBuffer, _instances.size());
541+
}
542+
else if (_instancing)
543+
return;
431544
}
432545

433546
_meshIndexData->setPrimitiveType(_material->_drawPrimitive);

core/3d/Mesh.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "math/Math.h"
3636
#include "renderer/MeshCommand.h"
3737
#include "renderer/CustomCommand.h"
38+
#include "renderer/backend/Backend.h"
3839

3940
NS_AX_BEGIN
4041

@@ -242,6 +243,36 @@ class AX_DLL Mesh : public Ref
242243

243244
std::string getTextureFileName() { return _texFile; }
244245

246+
void setInstanceCount(int count = 0);
247+
248+
/** Enables instancing for this Mesh Renderer, keep in mind that
249+
a special vertex shader has to be used, make sure that your shader
250+
has a mat4 attribute set on the location of total vertex attributes +1
251+
*/
252+
void enableInstancing(bool instance, int count = 0);
253+
254+
/** Set this to true and instancing objects within this mesh renderer
255+
will be recalculated each frame, use it when you plan to move objects,
256+
Otherwise, transforms will be built once for better performance.
257+
* to update transforms on demand use `rebuildInstances()` */
258+
void setDynamicInstancing(bool dynamic);
259+
260+
/** Adds a child to use it's transformations for instancing.
261+
* The child is in the space of this Node, keep in mind that
262+
the node isn't added to the scene graph, it is instead retained
263+
and it's parent is set to this node, updates and actions will not run.
264+
* the reason for this is performance.
265+
*
266+
* @param child, The child to use for instancing.
267+
*/
268+
void addInstanceChild(Node* child);
269+
270+
/** shrinks the instance transform buffer after many steps of expansion to increase performance. */
271+
void shrinkToFitInstances();
272+
273+
/** rebuilds the instance transform buffer next frame. */
274+
void rebuildInstances();
275+
245276
Mesh();
246277
virtual ~Mesh();
247278

@@ -254,6 +285,15 @@ class AX_DLL Mesh : public Ref
254285
MeshSkin* _skin; // skin
255286
bool _visible; // is the submesh visible
256287

288+
bool _instancing;
289+
backend::Buffer* _instanceTransformBuffer;
290+
bool _instanceTransformDirty;
291+
bool _instanceTransformBufferDirty;
292+
int _instanceCount;
293+
std::vector<Node*> _instances;
294+
float* _instanceMatrixCache;
295+
bool _dynamicInstancing;
296+
257297
CustomCommand::IndexFormat meshIndexFormat;
258298

259299
std::string _name;

core/3d/MeshMaterial.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ MeshMaterialCache* MeshMaterialCache::_cacheInstance = nullptr;
4040

4141
std::unordered_map<std::string, MeshMaterial*> MeshMaterial::_materials;
4242
MeshMaterial* MeshMaterial::_unLitMaterial = nullptr;
43+
MeshMaterial* MeshMaterial::_unLitInstanceMaterial = nullptr;
4344
MeshMaterial* MeshMaterial::_unLitNoTexMaterial = nullptr;
4445
MeshMaterial* MeshMaterial::_vertexLitMaterial = nullptr;
4546
MeshMaterial* MeshMaterial::_diffuseMaterial = nullptr;
@@ -55,6 +56,7 @@ MeshMaterial* MeshMaterial::_quadTextureMaterial = nullptr;
5556
MeshMaterial* MeshMaterial::_quadColorMaterial = nullptr;
5657

5758
backend::ProgramState* MeshMaterial::_unLitMaterialProgState = nullptr;
59+
backend::ProgramState* MeshMaterial::_unLitInstanceMaterialProgState = nullptr;
5860
backend::ProgramState* MeshMaterial::_unLitNoTexMaterialProgState = nullptr;
5961
backend::ProgramState* MeshMaterial::_vertexLitMaterialProgState = nullptr;
6062
backend::ProgramState* MeshMaterial::_diffuseMaterialProgState = nullptr;
@@ -103,6 +105,14 @@ void MeshMaterial::createBuiltInMaterial()
103105
_unLitMaterial->_type = MeshMaterial::MaterialType::UNLIT;
104106
}
105107

108+
program = backend::Program::getBuiltinProgram(backend::ProgramType::POSITION_TEXTURE_3D_INSTANCE);
109+
_unLitInstanceMaterialProgState = new backend::ProgramState(program);
110+
_unLitInstanceMaterial = new MeshMaterial();
111+
if (_unLitInstanceMaterial && _unLitInstanceMaterial->initWithProgramState(_unLitInstanceMaterialProgState))
112+
{
113+
_unLitInstanceMaterial->_type = MeshMaterial::MaterialType::UNLIT_INSTANCE;
114+
}
115+
106116
program = backend::Program::getBuiltinProgram(backend::ProgramType::POSITION_3D);
107117
_unLitNoTexMaterialProgState = new backend::ProgramState(program);
108118
_unLitNoTexMaterial = new MeshMaterial();
@@ -230,6 +240,10 @@ MeshMaterial* MeshMaterial::createBuiltInMaterial(MaterialType type, bool skinne
230240
material = skinned ? _unLitMaterialSkin : _unLitMaterial;
231241
break;
232242

243+
case MeshMaterial::MaterialType::UNLIT_INSTANCE:
244+
material = skinned ? /* TODO: implement instanced hardware skinning */ nullptr : _unLitInstanceMaterial;
245+
break;
246+
233247
case MeshMaterial::MaterialType::UNLIT_NOTEX:
234248
material = _unLitNoTexMaterial;
235249
break;

core/3d/MeshMaterial.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class AX_DLL MeshMaterial : public Material
5858
{
5959
// Built in materials
6060
UNLIT, // unlit material
61+
UNLIT_INSTANCE, // unlit instance material
6162
UNLIT_NOTEX, // unlit material (without texture)
6263
VERTEX_LIT, // vertex lit
6364
DIFFUSE, // diffuse (pixel lighting)
@@ -70,6 +71,18 @@ class AX_DLL MeshMaterial : public Material
7071
CUSTOM, // Create from a material file
7172
};
7273

74+
/**
75+
* Instanced Material types, there are mainly two types of materials. Built-in materials and Custom materials.
76+
*/
77+
enum class InstanceMaterialType
78+
{
79+
NO_INSTANCING, // disabled instancing
80+
UNLIT_INSTANCE, // unlit instance material
81+
82+
// Custom material
83+
CUSTOM, // Create from a material file
84+
};
85+
7386
/**
7487
* Get material type
7588
* @return Material type
@@ -124,6 +137,7 @@ class AX_DLL MeshMaterial : public Material
124137
MaterialType _type;
125138
static std::unordered_map<std::string, MeshMaterial*> _materials; // cached material
126139
static MeshMaterial* _unLitMaterial;
140+
static MeshMaterial* _unLitInstanceMaterial;
127141
static MeshMaterial* _unLitNoTexMaterial;
128142
static MeshMaterial* _vertexLitMaterial;
129143
static MeshMaterial* _diffuseMaterial;
@@ -139,6 +153,7 @@ class AX_DLL MeshMaterial : public Material
139153
static MeshMaterial* _quadColorMaterial;
140154

141155
static backend::ProgramState* _unLitMaterialProgState;
156+
static backend::ProgramState* _unLitInstanceMaterialProgState;
142157
static backend::ProgramState* _unLitNoTexMaterialProgState;
143158
static backend::ProgramState* _vertexLitMaterialProgState;
144159
static backend::ProgramState* _diffuseMaterialProgState;

core/3d/MeshRenderer.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,63 @@ void MeshRenderer::setModelTexture(std::string_view modelPath, std::string_view
692692
}
693693
}
694694

695+
void MeshRenderer::enableInstancing(MeshMaterial::InstanceMaterialType instanceMat, int count)
696+
{
697+
switch (instanceMat)
698+
{
699+
case MeshMaterial::InstanceMaterialType::UNLIT_INSTANCE:
700+
{
701+
auto mat = MeshMaterial::createBuiltInMaterial(MeshMaterial::MaterialType::UNLIT_INSTANCE, false);
702+
enableInstancing(mat, count);
703+
}
704+
}
705+
}
706+
707+
void MeshRenderer::enableInstancing(MeshMaterial* instanceMat, int count)
708+
{
709+
for (auto&& mesh : _meshes)
710+
{
711+
mesh->enableInstancing(true, MAX(1, count));
712+
mesh->setMaterial(instanceMat);
713+
}
714+
}
715+
716+
void MeshRenderer::disableInstancing()
717+
{
718+
for (auto&& mesh : _meshes)
719+
mesh->enableInstancing(false, 0);
720+
}
721+
722+
void MeshRenderer::setDynamicInstancing(bool dynamic)
723+
{
724+
for (auto&& mesh : _meshes)
725+
mesh->setDynamicInstancing(dynamic);
726+
}
727+
728+
void MeshRenderer::addInstanceChild(Node* child, bool active)
729+
{
730+
for (auto&& mesh : _meshes)
731+
{
732+
mesh->addInstanceChild(child);
733+
if (active)
734+
addChild(child);
735+
else
736+
child->setParent(this);
737+
}
738+
}
739+
740+
void MeshRenderer::shrinkToFitInstances()
741+
{
742+
for (auto&& mesh : _meshes)
743+
mesh->shrinkToFitInstances();
744+
}
745+
746+
void MeshRenderer::rebuildInstances()
747+
{
748+
for (auto&& mesh : _meshes)
749+
mesh->rebuildInstances();
750+
}
751+
695752
void MeshRenderer::setTexture(std::string_view texFile)
696753
{
697754
auto tex = _director->getTextureCache()->addImage(texFile);

0 commit comments

Comments
 (0)