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
This commit is contained in:
parent
1f878f5de6
commit
8efe0fff20
@ -47,6 +47,8 @@ supertuxkart_SOURCES = \
|
||||
config/user_config.hpp \
|
||||
config/device_config.cpp \
|
||||
config/device_config.hpp \
|
||||
graphics/CBatchingMesh.cpp \
|
||||
graphics/CBatchingMesh.hpp \
|
||||
graphics/camera.cpp \
|
||||
graphics/camera.hpp \
|
||||
graphics/explosion.cpp \
|
||||
|
508
src/graphics/CBatchingMesh.cpp
Normal file
508
src/graphics/CBatchingMesh.cpp
Normal file
@ -0,0 +1,508 @@
|
||||
|
||||
#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
|
||||
|
174
src/graphics/CBatchingMesh.hpp
Normal file
174
src/graphics/CBatchingMesh.hpp
Normal file
@ -0,0 +1,174 @@
|
||||
// A mesh used for batching many other meshes together, to reduce the number
|
||||
// of draw calls. Simply add meshes into this one, with given transformations
|
||||
// or positions, and then call update
|
||||
|
||||
// TODO: Adapt the VBO interface and integrate setDirty with the current VBO updates.
|
||||
|
||||
#include "IMesh.h"
|
||||
#include "SMeshBuffer.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace scene
|
||||
{
|
||||
|
||||
class CBatchingMesh : public IMesh
|
||||
{
|
||||
public:
|
||||
CBatchingMesh();
|
||||
|
||||
virtual ~CBatchingMesh();
|
||||
|
||||
//! returns true if new buffers have been added without updating the internal buffers
|
||||
bool isDirty(s32 id=-1);
|
||||
|
||||
//! refreshes the internal buffers from source
|
||||
void update();
|
||||
|
||||
//! drops all buffers and clears internal states
|
||||
void clear();
|
||||
|
||||
//! first updates the mesh, then drops all source buffers.
|
||||
/** once this mesh has been finalized, it cannot be changed again! */
|
||||
void finalize();
|
||||
|
||||
//! adds a mesh to the buffers with the given offset
|
||||
/** \Return: Returns an array of ID numbers */
|
||||
core::array<s32> addMesh(IMesh* mesh,
|
||||
core::vector3df pos = core::vector3df(0,0,0),
|
||||
core::vector3df rot = core::vector3df(0,0,0),
|
||||
core::vector3df scale = core::vector3df(1,1,1));
|
||||
|
||||
//! adds a mesh with the given transformation
|
||||
/** \Return: Returns an array of ID numbers */
|
||||
core::array<s32> addMesh(IMesh* mesh, const core::matrix4 &transform);
|
||||
|
||||
//! adds a mesh buffer with the given transformation
|
||||
/** \Return: Returns the ID of this mesh buffer */
|
||||
s32 addMeshBuffer(IMeshBuffer* buffer,
|
||||
core::vector3df pos = core::vector3df(0,0,0),
|
||||
core::vector3df rot = core::vector3df(0,0,0),
|
||||
core::vector3df scale = core::vector3df(1,1,1));
|
||||
|
||||
//! adds a mesh with the given transformation
|
||||
/** \Return Returns the ID of this mesh buffer */
|
||||
s32 addMeshBuffer(IMeshBuffer* buffer, const core::matrix4 &transform);
|
||||
|
||||
//! updates bouding box from internal buffers
|
||||
void recalculateBoundingBox();
|
||||
|
||||
//! Moves a mesh,
|
||||
/** mesh buffers in clean destination buffers will be moved immediately,
|
||||
ones in dirty buffers will be left until the next update */
|
||||
core::array<bool> moveMesh(const core::array<s32>& bufferIDs, const core::matrix4 &newMatrix);
|
||||
|
||||
//! Moves a mesh buffer
|
||||
/** if the destination buffer is clean it will be moved immediately,
|
||||
if a member of a dirty buffer, it will be left until the next update */
|
||||
bool moveMeshBuffer(const s32 id, const core::matrix4 &newMatrix);
|
||||
|
||||
//! returns the source buffer, if available
|
||||
IMeshBuffer* getSourceBuffer(s32 id);
|
||||
|
||||
//! returns the matrix of the source buffer
|
||||
core::matrix4 getSourceBufferMatrix(s32 id);
|
||||
|
||||
//! returns the number of source buffers
|
||||
u32 getSourceBufferCount() const;
|
||||
|
||||
/* Standard IMesh functions */
|
||||
|
||||
//! Returns the amount of mesh buffers.
|
||||
/** \return Returns the amount of mesh buffers (IMeshBuffer) in this mesh. */
|
||||
virtual u32 getMeshBufferCount() const;
|
||||
|
||||
//! 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. */
|
||||
virtual IMeshBuffer* getMeshBuffer(u32 nr) const;
|
||||
|
||||
//! Returns pointer to a mesh buffer which fits a material
|
||||
/** \param material: material to search for
|
||||
\return Returns the pointer to the mesh buffer or
|
||||
NULL if there is no such mesh buffer. */
|
||||
virtual IMeshBuffer* getMeshBuffer( const video::SMaterial &material) const;
|
||||
|
||||
//! Returns an axis aligned bounding box of the mesh.
|
||||
/** \return A bounding box of this mesh is returned. */
|
||||
virtual const core::aabbox3d<f32>& getBoundingBox() const;
|
||||
|
||||
//! set user axis aligned bounding box
|
||||
virtual void setBoundingBox( const core::aabbox3df& 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. */
|
||||
virtual void setMaterialFlag(video::E_MATERIAL_FLAG flag, bool newvalue);
|
||||
|
||||
virtual void setHardwareMappingHint(E_HARDWARE_MAPPING mapping, E_BUFFER_TYPE type);
|
||||
|
||||
virtual void setDirty(E_BUFFER_TYPE type);
|
||||
|
||||
private:
|
||||
|
||||
// add a buffer to the source buffers array if it doesn't already exist
|
||||
void addSourceBuffer(IMeshBuffer* source);
|
||||
|
||||
// updates the vertices in dest buffer from the source one
|
||||
void updateDestFromSourceBuffer(u32 id);
|
||||
|
||||
// recalculates the bounding box for the given dest buffer
|
||||
void recalculateDestBufferBoundingBox(u32 i);
|
||||
|
||||
struct SBufferReference
|
||||
{
|
||||
SBufferReference()
|
||||
: SourceBuffer(0), DestReference(0), FirstVertex(0), VertexCount(0),
|
||||
FirstIndex(0), IndexCount(0), Initialized(false) { }
|
||||
|
||||
IMeshBuffer* SourceBuffer;
|
||||
u32 DestReference;
|
||||
u32 FirstVertex, VertexCount, FirstIndex, IndexCount;
|
||||
core::matrix4 Transform;
|
||||
bool Initialized;
|
||||
};
|
||||
|
||||
struct SMaterialReference
|
||||
{
|
||||
video::SMaterial Material;
|
||||
video::E_VERTEX_TYPE VertexType;
|
||||
u32 BufferIndex;
|
||||
};
|
||||
|
||||
struct SDestBufferReference
|
||||
{
|
||||
IMeshBuffer* Buffer;
|
||||
video::E_VERTEX_TYPE VertexType;
|
||||
u32 VertexCount;
|
||||
u32 IndexCount;
|
||||
bool IsDirty;
|
||||
};
|
||||
|
||||
//! Source mesh buffers, these are locked
|
||||
core::array<IMeshBuffer*> SourceBuffers;
|
||||
|
||||
core::array<SBufferReference> BufferReferences;
|
||||
core::array<SMaterialReference> MaterialReferences;
|
||||
core::array<SDestBufferReference> DestBuffers;
|
||||
|
||||
//! bounding containing all destination buffers
|
||||
core::aabbox3d<f32> Box;
|
||||
|
||||
//! does it require an update?
|
||||
bool IsDirty;
|
||||
|
||||
//! can it be changed?
|
||||
bool IsFinal;
|
||||
};
|
||||
|
||||
} // namespace scene
|
||||
} // namespace irr
|
||||
|
||||
// #endif
|
@ -368,7 +368,7 @@ void IrrDriver::cancelResChange()
|
||||
void IrrDriver::printRenderStats()
|
||||
{
|
||||
io::IAttributes * attr = m_scene_manager->getParameters();
|
||||
printf("[%ls], FPS:%03d Tri:%.03fm Cull %d/%d nodes (%d,%d,%d)\n",
|
||||
printf("[%ls], FPS:%3d Tri:%.03fm Cull %d/%d nodes (%d,%d,%d)\n",
|
||||
m_video_driver->getName(),
|
||||
m_video_driver->getFPS (),
|
||||
(f32) m_video_driver->getPrimitiveCountDrawn( 0 ) * ( 1.f / 1000000.f ),
|
||||
@ -451,7 +451,7 @@ scene::ISceneNode* IrrDriver::addWaterNode(scene::IMesh *mesh,
|
||||
/** Adds a mesh that will be optimised using an oct tree.
|
||||
* \param mesh Mesh to add.
|
||||
*/
|
||||
scene::ISceneNode *IrrDriver::addOctTree(scene::IMesh *mesh)
|
||||
scene::IMeshSceneNode *IrrDriver::addOctTree(scene::IMesh *mesh)
|
||||
{
|
||||
return m_scene_manager->addOctreeSceneNode(mesh);
|
||||
} // addOctTree
|
||||
@ -860,8 +860,12 @@ void IrrDriver::update(float dt)
|
||||
} // just to mark the begin/end scene block
|
||||
m_device->getVideoDriver()->endScene();
|
||||
// Enable this next print statement to get render information printed
|
||||
// E.g. number of triangles rendered, culled etc.
|
||||
//printRenderStats();
|
||||
// E.g. number of triangles rendered, culled etc. The stats is only
|
||||
// printed while the race is running and not while the in-game menu
|
||||
// is shown. This way the output can be studied by just opening the
|
||||
// menu.
|
||||
//if(World::getWorld() && World::getWorld()->isRacePhase())
|
||||
// printRenderStats();
|
||||
} // update
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -110,7 +110,7 @@ public:
|
||||
scene::IMesh *createTexturedQuadMesh(const video::SMaterial *material, const double w, const double h);
|
||||
scene::ISceneNode *addWaterNode(scene::IMesh *mesh, float wave_height,
|
||||
float wave_speed, float wave_length);
|
||||
scene::ISceneNode *addOctTree(scene::IMesh *mesh);
|
||||
scene::IMeshSceneNode*addOctTree(scene::IMesh *mesh);
|
||||
scene::IMeshSceneNode*addMesh(scene::IMesh *mesh);
|
||||
scene::ISceneNode *addBillboard(const core::dimension2d< f32 > size, video::ITexture *texture, scene::ISceneNode* parent=NULL);
|
||||
|
||||
|
@ -537,6 +537,10 @@
|
||||
RelativePath="..\..\graphics\camera.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\graphics\CBatchingMesh.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\graphics\explosion.cpp"
|
||||
>
|
||||
@ -1379,6 +1383,10 @@
|
||||
RelativePath="..\..\graphics\camera.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\graphics\CBatchingMesh.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\graphics\explosion.hpp"
|
||||
>
|
||||
|
@ -31,6 +31,7 @@ using namespace irr;
|
||||
#include "audio/music_manager.hpp"
|
||||
#include "config/stk_config.hpp"
|
||||
#include "config/user_config.hpp"
|
||||
#include "graphics/CBatchingMesh.hpp"
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "graphics/material_manager.hpp"
|
||||
#include "graphics/mesh_tools.hpp"
|
||||
@ -129,6 +130,9 @@ void Track::cleanup()
|
||||
}
|
||||
m_all_nodes.clear();
|
||||
|
||||
// The meshes stored in the scene nodes are dropped now.
|
||||
// But to really remove all track meshes from memory
|
||||
// they have to be removed from the cache.
|
||||
for(unsigned int i=0; i<m_all_meshes.size(); i++)
|
||||
{
|
||||
irr_driver->removeMesh(m_all_meshes[i]);
|
||||
@ -388,9 +392,9 @@ void Track::createPhysicsModel(unsigned int main_track_count)
|
||||
}
|
||||
|
||||
m_track_mesh->removeBody();
|
||||
for(unsigned int i=main_track_count; i<m_all_meshes.size(); i++)
|
||||
for(unsigned int i=main_track_count; i<m_all_nodes.size(); i++)
|
||||
{
|
||||
convertTrackToBullet(m_all_meshes[i], m_all_nodes[i]);
|
||||
convertTrackToBullet(m_all_nodes[i]);
|
||||
}
|
||||
m_track_mesh->createBody();
|
||||
m_non_collision_mesh->createBody(btCollisionObject::CF_NO_CONTACT_RESPONSE);
|
||||
@ -402,11 +406,31 @@ void Track::createPhysicsModel(unsigned int main_track_count)
|
||||
* \param mesh The mesh to convert.
|
||||
* \param node The scene node.
|
||||
*/
|
||||
void Track::convertTrackToBullet(const scene::IMesh *mesh,
|
||||
const scene::ISceneNode *node)
|
||||
void Track::convertTrackToBullet(const scene::ISceneNode *node)
|
||||
{
|
||||
const core::vector3df &pos = node->getPosition();
|
||||
const core::vector3df &hpr = node->getRotation();
|
||||
scene::IMesh *mesh;
|
||||
switch(node->getType())
|
||||
{
|
||||
case scene::ESNT_MESH :
|
||||
case scene::ESNT_WATER_SURFACE :
|
||||
case scene::ESNT_OCTREE :
|
||||
mesh = ((scene::IMeshSceneNode*)node)->getMesh();
|
||||
break;
|
||||
case scene::ESNT_ANIMATED_MESH :
|
||||
mesh = ((scene::IAnimatedMeshSceneNode*)node)->getMesh();
|
||||
break;
|
||||
case scene::ESNT_SKY_BOX :
|
||||
case scene::ESNT_SKY_DOME:
|
||||
// Don't add sky box/dome to the physics model.
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
printf("Unknown scene node type.\n");
|
||||
return;
|
||||
} // switch node->getType()
|
||||
|
||||
core::matrix4 mat;
|
||||
mat.setRotationDegrees(hpr);
|
||||
mat.setTranslation(pos);
|
||||
@ -451,6 +475,7 @@ void Track::convertTrackToBullet(const scene::IMesh *mesh,
|
||||
} // for i<getMeshBufferCount
|
||||
|
||||
} // convertTrackToBullet
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Loads the main track model (i.e. all other objects contained in the
|
||||
* scene might use raycast on this track model to determine the actual
|
||||
@ -462,18 +487,36 @@ bool Track::loadMainTrack(const XMLNode &root)
|
||||
std::string model_name;
|
||||
track_node->get("model", &model_name);
|
||||
std::string full_path = m_root+"/"+model_name;
|
||||
scene::IMesh *mesh = irr_driver->getAnimatedMesh(full_path);
|
||||
// scene::IMesh *mesh = irr_driver->getAnimatedMesh(full_path);
|
||||
scene::IMesh *mesh = irr_driver->getMesh(full_path);
|
||||
if(!mesh)
|
||||
{
|
||||
fprintf(stderr, "Warning: Main track model '%s' in '%s' not found, aborting.\n",
|
||||
track_node->getName().c_str(), model_name.c_str());
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
m_all_meshes.push_back(mesh);
|
||||
//scene::ISceneNode *scene_node = irr_driver->addMesh(mesh);
|
||||
scene::ISceneNode *scene_node = irr_driver->addOctTree(mesh);
|
||||
mesh->setHardwareMappingHint(scene::EHM_STATIC);
|
||||
|
||||
// The mesh as returned does not have all mesh buffers with the same
|
||||
// texture combined. This can result in a _HUGE_ overhead. E.g. instead
|
||||
// of 46 different mesh buffers over 500 (for some tracks even >1000)
|
||||
// were created. This means less effect from hardware support, less
|
||||
// vertices per opengl operation, more overhead on CPU, ...
|
||||
// So till we have a better b3d exporter which can combine the different
|
||||
// meshes which use the same texture when exporting, the meshes are
|
||||
// combined using CBatchingMesh.
|
||||
scene::CBatchingMesh *merged_mesh = new scene::CBatchingMesh();
|
||||
merged_mesh->addMesh(mesh);
|
||||
merged_mesh->finalize();
|
||||
// FIXME: LEAK: What happens to this mesh? If it is dropped here,
|
||||
// something breaks in the Batching mesh (and the conversion to
|
||||
// bullet crashes), but atm this mesh is not freed.
|
||||
//mesh->drop();
|
||||
|
||||
m_all_meshes.push_back(merged_mesh);
|
||||
//scene::ISceneNode *scene_node = irr_driver->addMesh(merged_mesh);
|
||||
scene::IMeshSceneNode *scene_node = irr_driver->addOctTree(merged_mesh);
|
||||
//merged_mesh->setHardwareMappingHint(scene::EHM_STATIC);
|
||||
|
||||
core::vector3df xyz(0,0,0);
|
||||
track_node->getXYZ(&xyz);
|
||||
core::vector3df hpr(0,0,0);
|
||||
@ -483,7 +526,7 @@ bool Track::loadMainTrack(const XMLNode &root)
|
||||
handleAnimatedTextures(scene_node, *track_node);
|
||||
m_all_nodes.push_back(scene_node);
|
||||
|
||||
MeshTools::minMax3D(mesh, &m_aabb_min, &m_aabb_max);
|
||||
MeshTools::minMax3D(merged_mesh, &m_aabb_min, &m_aabb_max);
|
||||
World::getWorld()->getPhysics()->init(m_aabb_min, m_aabb_max);
|
||||
|
||||
for(unsigned int i=0; i<track_node->getNumNodes(); i++)
|
||||
@ -522,9 +565,9 @@ bool Track::loadMainTrack(const XMLNode &root)
|
||||
} // for i
|
||||
|
||||
// This will (at this stage) only convert the main track model.
|
||||
for(unsigned int i=0; i<m_all_meshes.size(); i++)
|
||||
for(unsigned int i=0; i<m_all_nodes.size(); i++)
|
||||
{
|
||||
convertTrackToBullet(m_all_meshes[i], m_all_nodes[i]);
|
||||
convertTrackToBullet(m_all_nodes[i]);
|
||||
}
|
||||
if (m_track_mesh == NULL)
|
||||
{
|
||||
@ -640,6 +683,7 @@ void Track::createWater(const XMLNode &node)
|
||||
node.getName().c_str(), model_name.c_str());
|
||||
return;
|
||||
}
|
||||
mesh->grab();
|
||||
m_all_meshes.push_back(mesh);
|
||||
core::vector3df xyz(0,0,0);
|
||||
node.getXYZ(&xyz);
|
||||
@ -697,7 +741,7 @@ void Track::loadTrackModel(World* parent, unsigned int mode_id)
|
||||
throw std::runtime_error(msg.str());
|
||||
}
|
||||
loadMainTrack(*root);
|
||||
unsigned int main_track_count = m_all_meshes.size();
|
||||
unsigned int main_track_count = m_all_nodes.size();
|
||||
|
||||
for(unsigned int i=0; i<root->getNumNodes(); i++)
|
||||
{
|
||||
@ -871,7 +915,15 @@ void Track::adjustForFog(scene::ISceneNode *node)
|
||||
{
|
||||
node->setMaterialFlag(video::EMF_FOG_ENABLE, m_use_fog);
|
||||
} // adjustForFog
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Handles a sky-dome or sky-box. It takes the xml node with the
|
||||
* corresponding data for the sky and stores the corresponding data in
|
||||
* the corresponding data structures.
|
||||
* \param xml_node XML node with the sky data.
|
||||
* \param filename Name of the file which is read, only used to print
|
||||
* meaningful error messages.
|
||||
*/
|
||||
void Track::handleSky(const XMLNode &xml_node, const std::string &filename)
|
||||
{
|
||||
if(xml_node.getName()=="sky-dome")
|
||||
|
@ -185,8 +185,7 @@ private:
|
||||
void itemCommand(const Vec3 &xyz, Item::ItemType item_type,
|
||||
bool drop);
|
||||
void loadQuadGraph(unsigned int mode_id);
|
||||
void convertTrackToBullet(const scene::IMesh *mesh,
|
||||
const scene::ISceneNode*node);
|
||||
void convertTrackToBullet(const scene::ISceneNode *node);
|
||||
bool loadMainTrack(const XMLNode &node);
|
||||
void createWater(const XMLNode &node);
|
||||
void getMusicInformation(std::vector<std::string>& filenames,
|
||||
|
Loading…
x
Reference in New Issue
Block a user