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:
hikerstk 2010-06-16 00:18:51 +00:00
parent 1f878f5de6
commit 8efe0fff20
8 changed files with 768 additions and 21 deletions

View File

@ -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 \

View 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

View 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

View File

@ -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
// ----------------------------------------------------------------------------

View File

@ -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);

View File

@ -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"
>

View File

@ -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")

View File

@ -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,