irrlicht/source/Irrlicht/COctreeSceneNode.cpp

544 lines
15 KiB
C++

// Copyright (C) 2002-2009 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
#include "COctreeSceneNode.h"
#include "Octree.h"
#include "ISceneManager.h"
#include "IVideoDriver.h"
#include "ICameraSceneNode.h"
#include "IMeshCache.h"
#include "IAnimatedMesh.h"
#include "IMaterialRenderer.h"
#include "os.h"
namespace irr
{
namespace scene
{
//! constructor
COctreeSceneNode::COctreeSceneNode(ISceneNode* parent, ISceneManager* mgr,
s32 id, s32 minimalPolysPerNode)
: IMeshSceneNode(parent, mgr, id), StdOctree(0), LightMapOctree(0),
TangentsOctree(0), VertexType((video::E_VERTEX_TYPE)-1),
MinimalPolysPerNode(minimalPolysPerNode), Mesh(0),
UseVBOs(OCTREE_USE_HARDWARE), UseVisibilityAndVBOs(OCTREE_USE_VISIBILITY),
BoxBased(OCTREE_BOX_BASED)
{
#ifdef _DEBUG
setDebugName("COctreeSceneNode");
#endif
}
//! destructor
COctreeSceneNode::~COctreeSceneNode()
{
deleteTree();
}
void COctreeSceneNode::OnRegisterSceneNode()
{
if (IsVisible)
{
// because this node supports rendering of mixed mode meshes consisting of
// transparent and solid material at the same time, we need to go through all
// materials, check of what type they are and register this node for the right
// render pass according to that.
video::IVideoDriver* driver = SceneManager->getVideoDriver();
PassCount = 0;
u32 transparentCount = 0;
u32 solidCount = 0;
// count transparent and solid materials in this scene node
for (u32 i=0; i<Materials.size(); ++i)
{
const video::IMaterialRenderer* const rnd =
driver->getMaterialRenderer(Materials[i].MaterialType);
if (rnd && rnd->isTransparent())
++transparentCount;
else
++solidCount;
if (solidCount && transparentCount)
break;
}
// register according to material types counted
if (solidCount)
SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
if (transparentCount)
SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
ISceneNode::OnRegisterSceneNode();
}
}
//! renders the node.
void COctreeSceneNode::render()
{
video::IVideoDriver* driver = SceneManager->getVideoDriver();
if (VertexType == -1 || !driver)
return;
ICameraSceneNode* camera = SceneManager->getActiveCamera();
if (!camera)
return;
bool isTransparentPass =
SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT;
++PassCount;
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
SViewFrustum frust = *camera->getViewFrustum();
//transform the frustum to the current absolute transformation
if ( !AbsoluteTransformation.isIdentity() )
{
core::matrix4 invTrans(AbsoluteTransformation, core::matrix4::EM4CONST_INVERSE);
frust.transform(invTrans);
}
const core::aabbox3d<float> &box = frust.getBoundingBox();
switch (VertexType)
{
case video::EVT_STANDARD:
{
if (BoxBased)
StdOctree->calculatePolys(box);
else
StdOctree->calculatePolys(frust);
const Octree<video::S3DVertex>::SIndexData* d = StdOctree->getIndexData();
for (u32 i=0; i<Materials.size(); ++i)
{
if ( 0 == d[i].CurrentSize )
continue;
const video::IMaterialRenderer* const rnd = driver->getMaterialRenderer(Materials[i].MaterialType);
const bool transparent = (rnd && rnd->isTransparent());
// only render transparent buffer if this is the transparent render pass
// and solid only in solid pass
if (transparent == isTransparentPass)
{
driver->setMaterial(Materials[i]);
driver->drawIndexedTriangleList(
&StdMeshes[i].Vertices[0], StdMeshes[i].Vertices.size(),
d[i].Indices, d[i].CurrentSize / 3);
}
}
// for debug purposes only
if (DebugDataVisible && !Materials.empty() && PassCount==1)
{
const core::aabbox3df& box = frust.getBoundingBox();
core::array< const core::aabbox3d<f32>* > boxes;
video::SMaterial m;
m.Lighting = false;
driver->setMaterial(m);
if ( DebugDataVisible & scene::EDS_BBOX_BUFFERS )
{
StdOctree->getBoundingBoxes(box, boxes);
for (u32 b=0; b!=boxes.size(); ++b)
driver->draw3DBox(*boxes[b]);
}
if ( DebugDataVisible & scene::EDS_BBOX )
driver->draw3DBox(Box,video::SColor(0,255,0,0));
}
}
break;
case video::EVT_2TCOORDS:
{
if (BoxBased)
LightMapOctree->calculatePolys(box);
else
LightMapOctree->calculatePolys(frust);
const Octree<video::S3DVertex2TCoords>::SIndexData* d = LightMapOctree->getIndexData();
for (u32 i=0; i<Materials.size(); ++i)
{
if ( 0 == d[i].CurrentSize )
continue;
const video::IMaterialRenderer* const rnd = driver->getMaterialRenderer(Materials[i].MaterialType);
const bool transparent = (rnd && rnd->isTransparent());
// only render transparent buffer if this is the transparent render pass
// and solid only in solid pass
if (transparent == isTransparentPass)
{
driver->setMaterial(Materials[i]);
if (UseVBOs)
{
if (UseVisibilityAndVBOs)
{
u16* oldPointer = LightMapMeshes[i].Indices.pointer();
const u32 oldSize = LightMapMeshes[i].Indices.size();
LightMapMeshes[i].Indices.set_free_when_destroyed(false);
LightMapMeshes[i].Indices.set_pointer(d[i].Indices, d[i].CurrentSize, false, false);
LightMapMeshes[i].setDirty(scene::EBT_INDEX);
driver->drawMeshBuffer ( &LightMapMeshes[i] );
LightMapMeshes[i].Indices.set_pointer(oldPointer, oldSize);
LightMapMeshes[i].setDirty(scene::EBT_INDEX);
}
else
driver->drawMeshBuffer ( &LightMapMeshes[i] );
}
else
driver->drawIndexedTriangleList(
&LightMapMeshes[i].Vertices[0],
LightMapMeshes[i].Vertices.size(),
d[i].Indices, d[i].CurrentSize / 3);
}
}
// for debug purposes only
if (DebugDataVisible && !Materials.empty() && PassCount==1)
{
const core::aabbox3d<float> &box = frust.getBoundingBox();
core::array< const core::aabbox3d<f32>* > boxes;
video::SMaterial m;
m.Lighting = false;
driver->setMaterial(m);
if ( DebugDataVisible & scene::EDS_BBOX_BUFFERS )
{
LightMapOctree->getBoundingBoxes(box, boxes);
for (u32 b=0; b<boxes.size(); ++b)
driver->draw3DBox(*boxes[b]);
}
if ( DebugDataVisible & scene::EDS_BBOX )
driver->draw3DBox(Box,video::SColor(0,255,0,0));
}
}
break;
case video::EVT_TANGENTS:
{
if (BoxBased)
TangentsOctree->calculatePolys(box);
else
TangentsOctree->calculatePolys(frust);
const Octree<video::S3DVertexTangents>::SIndexData* d = TangentsOctree->getIndexData();
for (u32 i=0; i<Materials.size(); ++i)
{
if ( 0 == d[i].CurrentSize )
continue;
const video::IMaterialRenderer* const rnd = driver->getMaterialRenderer(Materials[i].MaterialType);
const bool transparent = (rnd && rnd->isTransparent());
// only render transparent buffer if this is the transparent render pass
// and solid only in solid pass
if (transparent == isTransparentPass)
{
driver->setMaterial(Materials[i]);
driver->drawIndexedTriangleList(
&TangentsMeshes[i].Vertices[0], TangentsMeshes[i].Vertices.size(),
d[i].Indices, d[i].CurrentSize / 3);
}
}
// for debug purposes only
if (DebugDataVisible && !Materials.empty() && PassCount==1)
{
const core::aabbox3d<float> &box = frust.getBoundingBox();
core::array< const core::aabbox3d<f32>* > boxes;
video::SMaterial m;
m.Lighting = false;
driver->setMaterial(m);
if ( DebugDataVisible & scene::EDS_BBOX_BUFFERS )
{
TangentsOctree->getBoundingBoxes(box, boxes);
for (u32 b=0; b<boxes.size(); ++b)
driver->draw3DBox(*boxes[b]);
}
if ( DebugDataVisible & scene::EDS_BBOX )
driver->draw3DBox(Box,video::SColor(0,255,0,0));
}
}
break;
}
}
//! returns the axis aligned bounding box of this node
const core::aabbox3d<f32>& COctreeSceneNode::getBoundingBox() const
{
return Box;
}
//! creates the tree
bool COctreeSceneNode::createTree(IMesh* mesh)
{
if (!mesh)
return false;
MeshName = SceneManager->getMeshCache()->getMeshName(mesh);
mesh->grab();
deleteTree();
Mesh = mesh;
u32 beginTime = os::Timer::getRealTime();
u32 nodeCount = 0;
u32 polyCount = 0;
u32 i;
Box = mesh->getBoundingBox();
if (mesh->getMeshBufferCount())
{
VertexType = mesh->getMeshBuffer(0)->getVertexType();
switch(VertexType)
{
case video::EVT_STANDARD:
{
for (i=0; i<mesh->getMeshBufferCount(); ++i)
{
IMeshBuffer* b = mesh->getMeshBuffer(i);
if (b->getVertexCount() && b->getIndexCount())
{
Materials.push_back(b->getMaterial());
StdMeshes.push_back(Octree<video::S3DVertex>::SMeshChunk());
Octree<video::S3DVertex>::SMeshChunk &nchunk = StdMeshes.getLast();
nchunk.MaterialId = Materials.size() - 1;
u32 v;
nchunk.Vertices.reallocate(b->getVertexCount());
for (v=0; v<b->getVertexCount(); ++v)
nchunk.Vertices.push_back(((video::S3DVertex*)b->getVertices())[v]);
polyCount += b->getIndexCount();
nchunk.Indices.reallocate(b->getIndexCount());
for (v=0; v<b->getIndexCount(); ++v)
nchunk.Indices.push_back(b->getIndices()[v]);
}
}
StdOctree = new Octree<video::S3DVertex>(StdMeshes, MinimalPolysPerNode);
nodeCount = StdOctree->getNodeCount();
}
break;
case video::EVT_2TCOORDS:
{
IMeshBuffer* b;
u32 meshReserve = 0;
for ( i=0; i < mesh->getMeshBufferCount(); ++i)
{
b = mesh->getMeshBuffer(i);
if (b->getVertexCount() && b->getIndexCount())
{
meshReserve += 1;
}
}
LightMapMeshes.reallocate ( LightMapMeshes.size() + meshReserve );
for ( i=0; i < mesh->getMeshBufferCount(); ++i)
{
b = mesh->getMeshBuffer(i);
if (b->getVertexCount() && b->getIndexCount())
{
Materials.push_back(b->getMaterial());
LightMapMeshes.push_back(Octree<video::S3DVertex2TCoords>::SMeshChunk());
Octree<video::S3DVertex2TCoords>::SMeshChunk& nchunk = LightMapMeshes.getLast();
nchunk.MaterialId = Materials.size() - 1;
if (UseVisibilityAndVBOs)
{
nchunk.setHardwareMappingHint(scene::EHM_STATIC, scene::EBT_VERTEX);
nchunk.setHardwareMappingHint(scene::EHM_DYNAMIC, scene::EBT_INDEX);
}
else
nchunk.setHardwareMappingHint(scene::EHM_STATIC);
u32 v;
nchunk.Vertices.reallocate(b->getVertexCount());
for (v=0; v<b->getVertexCount(); ++v)
nchunk.Vertices.push_back(((video::S3DVertex2TCoords*)b->getVertices())[v]);
polyCount += b->getIndexCount();
nchunk.Indices.reallocate(b->getIndexCount());
for (v=0; v<b->getIndexCount(); ++v)
nchunk.Indices.push_back(b->getIndices()[v]);
}
}
LightMapOctree = new Octree<video::S3DVertex2TCoords>(LightMapMeshes, MinimalPolysPerNode);
nodeCount = LightMapOctree->getNodeCount();
}
break;
case video::EVT_TANGENTS:
{
for (u32 i=0; i<mesh->getMeshBufferCount(); ++i)
{
IMeshBuffer* b = mesh->getMeshBuffer(i);
if (b->getVertexCount() && b->getIndexCount())
{
Materials.push_back(b->getMaterial());
TangentsMeshes.push_back(Octree<video::S3DVertexTangents>::SMeshChunk());
Octree<video::S3DVertexTangents>::SMeshChunk& nchunk = TangentsMeshes.getLast();
nchunk.MaterialId = Materials.size() - 1;
u32 v;
nchunk.Vertices.reallocate(b->getVertexCount());
for (v=0; v<b->getVertexCount(); ++v)
nchunk.Vertices.push_back(((video::S3DVertexTangents*)b->getVertices())[v]);
polyCount += b->getIndexCount();
nchunk.Indices.reallocate(b->getIndexCount());
for (v=0; v<b->getIndexCount(); ++v)
nchunk.Indices.push_back(b->getIndices()[v]);
}
}
TangentsOctree = new Octree<video::S3DVertexTangents>(TangentsMeshes, MinimalPolysPerNode);
nodeCount = TangentsOctree->getNodeCount();
}
break;
}
}
u32 endTime = os::Timer::getRealTime();
c8 tmp[255];
sprintf(tmp, "Needed %ums to create Octree SceneNode.(%u nodes, %u polys)",
endTime - beginTime, nodeCount, polyCount/3);
os::Printer::log(tmp, ELL_INFORMATION);
return true;
}
//! returns the material based on the zero based index i. To get the amount
//! of materials used by this scene node, use getMaterialCount().
//! This function is needed for inserting the node into the scene hirachy on a
//! optimal position for minimizing renderstate changes, but can also be used
//! to directly modify the material of a scene node.
video::SMaterial& COctreeSceneNode::getMaterial(u32 i)
{
if ( i >= Materials.size() )
return ISceneNode::getMaterial(i);
return Materials[i];
}
//! returns amount of materials used by this scene node.
u32 COctreeSceneNode::getMaterialCount() const
{
return Materials.size();
}
//! Writes attributes of the scene node.
void COctreeSceneNode::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const
{
ISceneNode::serializeAttributes(out, options);
out->addInt("MinimalPolysPerNode", MinimalPolysPerNode);
out->addString("Mesh", MeshName.c_str());
}
//! Reads attributes of the scene node.
void COctreeSceneNode::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options)
{
const s32 oldMinimal = MinimalPolysPerNode;
MinimalPolysPerNode = in->getAttributeAsInt("MinimalPolysPerNode");
io::path newMeshStr = in->getAttributeAsString("Mesh");
IMesh* newMesh = 0;
if (newMeshStr == "")
newMeshStr = MeshName;
IAnimatedMesh* newAnimatedMesh = SceneManager->getMesh(newMeshStr.c_str());
if (newAnimatedMesh)
newMesh = newAnimatedMesh->getMesh(0);
if (newMesh && ((MeshName != newMeshStr) || (MinimalPolysPerNode != oldMinimal)))
{
// recalculate tree
createTree(newMesh);
}
ISceneNode::deserializeAttributes(in, options);
}
void COctreeSceneNode::deleteTree()
{
delete StdOctree;
StdOctree = 0;
StdMeshes.clear();
delete LightMapOctree;
LightMapOctree = 0;
LightMapMeshes.clear();
delete TangentsOctree;
TangentsOctree = 0;
TangentsMeshes.clear();
Materials.clear();
if(Mesh)
Mesh->drop();
}
void COctreeSceneNode::setMesh(IMesh* mesh)
{
createTree(mesh);
}
IMesh* COctreeSceneNode::getMesh(void)
{
return Mesh;
}
void COctreeSceneNode::setReadOnlyMaterials(bool readonly)
{
// Do nothing
}
bool COctreeSceneNode::isReadOnlyMaterials() const
{
return false;
}
} // end namespace scene
} // end namespace irr