stk-code_catmod/src/graphics/CBatchingMesh.cpp
hikerstk 8efe0fff20 Performance improvement: the b3d exporter does not sort/combine meshes by texture, so this
is (for now) done by a special Batching Mesh.


git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@5518 178a84e3-b1eb-0310-8ba1-8eac791a3b58
2010-06-16 00:18:51 +00:00

509 lines
13 KiB
C++

#include "graphics/CBatchingMesh.hpp"
namespace irr
{
namespace scene
{
CBatchingMesh::CBatchingMesh()
: Box(core::vector3df(0,0,0)), IsDirty(false), IsFinal(false)
{
}
CBatchingMesh::~CBatchingMesh()
{
u32 i;
for (i=0; i < DestBuffers.size(); ++i)
DestBuffers[i].Buffer->drop();
for (i=0; i < SourceBuffers.size(); ++i)
SourceBuffers[i]->drop();
}
bool CBatchingMesh::isDirty(s32 id)
{
if ((u32)id > DestBuffers.size())
return IsDirty;
else
return DestBuffers[id].IsDirty;
}
//! refreshes the internal buffers from source
void CBatchingMesh::update()
{
// allocate the index and vertex arrays
u32 i;
for (i=0; i<DestBuffers.size(); ++i)
{
if (DestBuffers[i].IndexCount != DestBuffers[i].Buffer->getIndexCount() ||
DestBuffers[i].VertexCount != DestBuffers[i].Buffer->getVertexCount())
{
DestBuffers[i].IsDirty = true;
switch (DestBuffers[i].VertexType)
{
case video::EVT_STANDARD:
{
SMeshBuffer* mb = (SMeshBuffer*)DestBuffers[i].Buffer;
mb->Vertices.set_used(DestBuffers[i].VertexCount);
mb->Indices.set_used(DestBuffers[i].IndexCount);
break;
}
case video::EVT_2TCOORDS:
{
SMeshBufferLightMap* mb = (SMeshBufferLightMap*)DestBuffers[i].Buffer;
mb->Vertices.set_used(DestBuffers[i].VertexCount);
mb->Indices.set_used(DestBuffers[i].IndexCount);
break;
}
case video::EVT_TANGENTS:
{
SMeshBufferTangents* mb = (SMeshBufferTangents*)DestBuffers[i].Buffer;
mb->Vertices.set_used(DestBuffers[i].VertexCount);
mb->Indices.set_used(DestBuffers[i].IndexCount);
break;
}
default: // shouldn't ever happen
continue;
}
}
}
// refresh dirty buffers from source
for (i=0; i<BufferReferences.size(); ++i)
{
if (DestBuffers[BufferReferences[i].DestReference].IsDirty)
{
updateDestFromSourceBuffer(i);
}
}
// calculate bounding boxes
for (i=0; i< DestBuffers.size(); ++i)
{
if (DestBuffers[i].IsDirty)
{
recalculateDestBufferBoundingBox(i);
// reset dirty state too
DestBuffers[i].IsDirty = false;
}
}
IsDirty = false;
recalculateBoundingBox();
}
//! adds a mesh to the buffers with the given offset
/** \Returns Returns an array of ID numbers */
core::array<s32> CBatchingMesh::addMesh(IMesh* mesh, core::vector3df pos, core::vector3df rot, core::vector3df scale)
{
core::matrix4 m;
m.setRotationDegrees(rot);
m.setTranslation(pos);
core::matrix4 scalem;
scalem.setScale(scale);
m *= scalem;
return addMesh(mesh, m);
}
//! adds a mesh with the given transformation
core::array<s32> CBatchingMesh::addMesh(IMesh* mesh, const core::matrix4 &transform)
{
core::array<s32> bufferNos;
if (!mesh)
return bufferNos;
u32 i;
for (i=0; i<mesh->getMeshBufferCount(); ++i)
bufferNos.push_back(addMeshBuffer(mesh->getMeshBuffer(i), transform));
return bufferNos;
}
//! adds a mesh buffer with the given transformation
/** \Return Returns the ID of this mesh buffer */
s32 CBatchingMesh::addMeshBuffer(IMeshBuffer* buffer, core::vector3df pos, core::vector3df rot, core::vector3df scale)
{
core::matrix4 m;
m.setRotationDegrees(rot);
m.setTranslation(pos);
core::matrix4 scalem;
scalem.setScale(scale);
m *= scalem;
return addMeshBuffer(buffer, m);
}
//! adds a mesh with the given transformation
/** \Return Returns the ID of this mesh buffer */
s32 CBatchingMesh::addMeshBuffer(IMeshBuffer* buffer, const core::matrix4 &transform)
{
if (!buffer || IsFinal)
return -1;
u32 i;
video::SMaterial m = buffer->getMaterial();
// find material
bool found=false;
video::E_VERTEX_TYPE vt = buffer->getVertexType();
for (i=0; i<MaterialReferences.size(); ++i)
{
if (MaterialReferences[i].VertexType == vt &&
MaterialReferences[i].Material == m)
{
// will there be too many vertices in the buffer?
u32 newTotalI = buffer->getIndexCount() + DestBuffers[ MaterialReferences[i].BufferIndex ].IndexCount;
u32 newTotalV = buffer->getVertexCount() + DestBuffers[ MaterialReferences[i].BufferIndex ].VertexCount;
if ( newTotalI < 65536*3 && newTotalV < 65536)
{
found = true;
DestBuffers[ MaterialReferences[i].BufferIndex ].IndexCount = newTotalI;
DestBuffers[ MaterialReferences[i].BufferIndex ].VertexCount = newTotalV;
break;
}
}
}
if (!found)
{
// we need a new destination buffer and material reference
IMeshBuffer *mb=0;
SMaterialReference r;
r.Material = m;
r.VertexType = vt;
r.BufferIndex = DestBuffers.size();
switch (vt)
{
case video::EVT_STANDARD:
mb = (IMeshBuffer*)new SMeshBuffer();
mb->getMaterial() = m;
break;
case video::EVT_2TCOORDS:
mb = (IMeshBuffer*)new SMeshBufferLightMap();
mb->getMaterial() = m;
break;
case video::EVT_TANGENTS:
mb = (IMeshBuffer*)new SMeshBufferTangents();
mb->getMaterial() = m;
break;
default: // unknown vertex type
return -1;
}
i = MaterialReferences.size();
MaterialReferences.push_back(r);
SDestBufferReference db;
db.Buffer = mb;
db.IndexCount = buffer->getIndexCount();
db.VertexCount = buffer->getVertexCount();
db.IsDirty = true;
db.VertexType = vt;
DestBuffers.push_back(db);
}
// now we add the mesh reference
SBufferReference r;
r.DestReference = i;
r.SourceBuffer = buffer;
r.Transform = transform;
r.IndexCount = buffer->getIndexCount();
r.VertexCount = buffer->getVertexCount();
r.FirstIndex = DestBuffers[ MaterialReferences[i].BufferIndex ].IndexCount - r.IndexCount;
r.FirstVertex = DestBuffers[ MaterialReferences[i].BufferIndex ].VertexCount - r.VertexCount;
r.Initialized = false;
BufferReferences.push_back(r);
addSourceBuffer(buffer);
IsDirty = true;
return BufferReferences.size()-1;
}
//! updates bouding box from internal buffers
void CBatchingMesh::recalculateBoundingBox()
{
if (DestBuffers.size() == 0)
Box.reset(0,0,0);
else
{
Box.reset(DestBuffers[0].Buffer->getBoundingBox().MinEdge);
u32 i;
for (i=0; i < DestBuffers.size(); ++i)
Box.addInternalBox(DestBuffers[i].Buffer->getBoundingBox());
}
}
/* Standard IMesh functions */
//! Returns the amount of mesh buffers.
/** \return Returns the amount of mesh buffers (IMeshBuffer) in this mesh. */
u32 CBatchingMesh::getMeshBufferCount() const
{
return DestBuffers.size();
}
//! Returns pointer to a mesh buffer.
/** \param nr: Zero based index of the mesh buffer. The maximum value is
getMeshBufferCount() - 1;
\return Returns the pointer to the mesh buffer or
NULL if there is no such mesh buffer. */
IMeshBuffer* CBatchingMesh::getMeshBuffer(u32 nr) const
{
if (nr < DestBuffers.size())
return DestBuffers[nr].Buffer;
else
return 0;
}
//! Returns pointer to a mesh buffer which fits a material
IMeshBuffer* CBatchingMesh::getMeshBuffer( const video::SMaterial &material) const
{
return 0;
}
//! Returns an axis aligned bounding box of the mesh.
/** \return A bounding box of this mesh is returned. */
const core::aabbox3d<f32>& CBatchingMesh::getBoundingBox() const
{
return Box;
}
//! set user axis aligned bounding box
void CBatchingMesh::setBoundingBox( const core::aabbox3df& box)
{
Box = box;
}
//! Sets a flag of all contained materials to a new value.
/** \param flag: Flag to set in all materials.
\param newvalue: New value to set in all materials. */
void CBatchingMesh::setMaterialFlag(video::E_MATERIAL_FLAG flag, bool newvalue)
{
for (u32 i=0; i<DestBuffers.size(); ++i)
DestBuffers[i].Buffer->getMaterial().setFlag(flag, newvalue);
}
//! drops all buffers and clears internal states
void CBatchingMesh::clear()
{
u32 i;
for (i=0; i < DestBuffers.size(); ++i)
DestBuffers[i].Buffer->drop();
for (i=0; i < SourceBuffers.size(); ++i)
SourceBuffers[i]->drop();
BufferReferences.clear();
MaterialReferences.clear();
DestBuffers.clear();
SourceBuffers.clear();
IsDirty = false;
IsFinal = false;
}
//! first updates the mesh, then drops all source buffers.
/** once this mesh has been finalized, it cannot be changed again! */
void CBatchingMesh::finalize()
{
update();
for (u32 i=0; i < SourceBuffers.size(); ++i)
SourceBuffers[i]->drop();
SourceBuffers.clear();
IsFinal = true;
}
//! Moves a mesh
core::array<bool> CBatchingMesh::moveMesh(const core::array<s32>& bufferIDs, const core::matrix4 &newMatrix)
{
core::array<bool> result;
result.reallocate(bufferIDs.size());
for (u32 i=0; i<bufferIDs.size(); ++i)
result.push_back(moveMeshBuffer(bufferIDs[i], newMatrix));
return result;
}
//! Moves a mesh buffer
bool CBatchingMesh::moveMeshBuffer(const s32 id, const core::matrix4 &newMatrix)
{
if ((u32)id > BufferReferences.size() || IsFinal )
return false;
BufferReferences[id].Transform = newMatrix;
// is the source buffer dirty?
if (!DestBuffers[BufferReferences[id].DestReference].IsDirty)
{
// transform each vertex and normal
updateDestFromSourceBuffer(id);
recalculateDestBufferBoundingBox(BufferReferences[id].DestReference);
}
return true;
}
//! returns the source buffer, if available
IMeshBuffer* CBatchingMesh::getSourceBuffer(s32 id)
{
if ((u32)id > BufferReferences.size() || IsFinal)
return 0;
else
return BufferReferences[id].SourceBuffer;
}
//! returns the matrix of the source buffer
core::matrix4 CBatchingMesh::getSourceBufferMatrix(s32 id)
{
core::matrix4 ret;
if ((u32)id > BufferReferences.size() || IsFinal)
ret.makeIdentity();
else
ret = BufferReferences[id].Transform;
return ret;
}
//! returns the number of source buffers
u32 CBatchingMesh::getSourceBufferCount() const
{
return BufferReferences.size();
}
// private functions
void CBatchingMesh::recalculateDestBufferBoundingBox(u32 i)
{
switch (DestBuffers[i].VertexType)
{
case video::EVT_STANDARD:
((SMeshBuffer*)DestBuffers[i].Buffer)->recalculateBoundingBox();
break;
case video::EVT_2TCOORDS:
((SMeshBufferLightMap*)DestBuffers[i].Buffer)->recalculateBoundingBox();
break;
case video::EVT_TANGENTS:
((SMeshBufferTangents*)DestBuffers[i].Buffer)->recalculateBoundingBox();
break;
}
}
void CBatchingMesh::updateDestFromSourceBuffer(u32 i)
{
u16* ind = BufferReferences[i].SourceBuffer->getIndices();
void*ver = BufferReferences[i].SourceBuffer->getVertices();
core::matrix4 m = BufferReferences[i].Transform;
u32 fi = BufferReferences[i].FirstIndex;
u32 fv = BufferReferences[i].FirstVertex;
u32 ic = BufferReferences[i].IndexCount;
u32 vc = BufferReferences[i].VertexCount;
u32 x;
video::E_VERTEX_TYPE vt = DestBuffers[BufferReferences[i].DestReference].VertexType;
switch (vt)
{
case video::EVT_STANDARD:
{
SMeshBuffer* dest = (SMeshBuffer*) DestBuffers[BufferReferences[i].DestReference].Buffer;
for (x=fi; x < fi+ic; ++x)
dest->Indices[x] = ind[x-fi]+fv;
video::S3DVertex* vertices= (video::S3DVertex*) ver;
for (x=fv; x < fv+vc; ++x)
{
dest->Vertices[x] = vertices[x-fv];
m.transformVect(dest->Vertices[x].Pos);
m.rotateVect(dest->Vertices[x].Normal);
}
break;
}
case video::EVT_2TCOORDS:
{
SMeshBufferLightMap* dest = (SMeshBufferLightMap*) DestBuffers[BufferReferences[i].DestReference].Buffer;
for (x=fi; x < fi+ic; ++x)
dest->Indices[x] = ind[x-fi]+fv;
video::S3DVertex2TCoords* vertices= (video::S3DVertex2TCoords*) ver;
for (x=fv; x < fv+vc; ++x)
{
dest->Vertices[x] = vertices[x-fv];
m.transformVect(dest->Vertices[x].Pos);
m.rotateVect(dest->Vertices[x].Normal);
}
break;
}
case video::EVT_TANGENTS:
{
SMeshBufferTangents* dest = (SMeshBufferTangents*) DestBuffers[BufferReferences[i].DestReference].Buffer;
for (x=fi; x < fi+ic; ++x)
dest->Indices[x] = ind[x-fi]+fv;
video::S3DVertexTangents* vertices= (video::S3DVertexTangents*) ver;
for (x=fv; x < fv+vc; ++x)
{
dest->Vertices[x] = vertices[x-fv];
m.transformVect(dest->Vertices[x].Pos);
m.rotateVect(dest->Vertices[x].Normal); // are tangents/binormals in face space?
}
break;
}
default:
break;
}
}
void CBatchingMesh::addSourceBuffer(IMeshBuffer *source)
{
bool found = false;
for (u32 i=0; i<SourceBuffers.size(); ++i)
{
if (SourceBuffers[i] == source)
{
found = true;
break;
}
}
if (!found)
{
source->grab();
SourceBuffers.push_back(source);
}
}
void CBatchingMesh::setHardwareMappingHint(E_HARDWARE_MAPPING mapping, E_BUFFER_TYPE type)
{
for (u32 i=0; i < DestBuffers.size(); ++i)
DestBuffers[i].Buffer->setHardwareMappingHint(mapping, type);
}
void CBatchingMesh::setDirty(E_BUFFER_TYPE type)
{
for (u32 i=0; i < DestBuffers.size(); ++i)
DestBuffers[i].Buffer->setDirty(type);
}
} // namespace scene
} // namespace irr