mirror of
https://github.com/minetest/irrlicht.git
synced 2024-11-14 11:43:48 +01:00
473ab1ea58
Fix bug that AnimatedMeshSceneNode ignored ReadOnlyMaterials flag when checking materials for transparent render passes. Make IVideoDriver::getMaterialRenderer const. Fix bugs in COctreeSceneNode, CMeshSceneNode and CAnimatedMeshSceneNode where check for transparency in OnRegisterSceneNode() and in render() where no longer identical (those got added after Irrlicht 1.8). Some notes for future: - Maybe we should have a getRenderPass instead of just needsTransparentRenderPass, but this way the code didn't need so much changes and behaves (aside from fixes) pretty much as before. - Still wondering if the default implementation in CNullDriver::needsTransparentRenderPass should always return false when SMaterial.ZWriteEnable is set to EZW_ON. This might be nicer with another material flag. Thought then we might want a material enum to choose the renderpass and that's more work. And we get some recursion as needsTransparentRenderPass might want to check result of getWriteZBuffer which calls needsTransparentRenderPass, so we might need a second function or an additional flag there. But return false when SMaterial.ZWriteEnable == EZW_ON could still be done as EZW_ON is a new flag so existing behavior shouldn't break. I just don't know right now if having an extra render pass for transparent nodes might still make sense even when zbuffer is not written or if that's really the only reason to do that. Any feedback anyone? git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6033 dfc29bdd-3216-0410-991c-e03cc46cb475
678 lines
19 KiB
C++
678 lines
19 KiB
C++
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
|
// This file is part of the "Irrlicht Engine".
|
|
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
|
|
|
#include "IrrCompileConfig.h"
|
|
#ifdef _IRR_COMPILE_WITH_OCTREE_SCENENODE_
|
|
|
|
#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"
|
|
#ifdef _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_
|
|
#include "CShadowVolumeSceneNode.h"
|
|
#else
|
|
#include "IShadowVolumeSceneNode.h"
|
|
#endif // _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_
|
|
#include "EProfileIDs.h"
|
|
#include "IProfiler.h"
|
|
|
|
namespace irr
|
|
{
|
|
namespace scene
|
|
{
|
|
|
|
|
|
//! constructor
|
|
COctreeSceneNode::COctreeSceneNode(ISceneNode* parent, ISceneManager* mgr,
|
|
s32 id, s32 minimalPolysPerNode)
|
|
: IOctreeSceneNode(parent, mgr, id), StdOctree(0), LightMapOctree(0),
|
|
TangentsOctree(0), VertexType((video::E_VERTEX_TYPE)-1),
|
|
MinimalPolysPerNode(minimalPolysPerNode), Mesh(0), Shadow(0),
|
|
UseVBOs(EOV_NO_VBO), PolygonChecks(EOPC_BOX)
|
|
{
|
|
#ifdef _DEBUG
|
|
setDebugName("COctreeSceneNode");
|
|
#endif
|
|
|
|
IRR_PROFILE(
|
|
static bool initProfile = false;
|
|
if (!initProfile )
|
|
{
|
|
initProfile = true;
|
|
getProfiler().add(EPID_OC_RENDER, L"render octnode", L"Irrlicht scene");
|
|
getProfiler().add(EPID_OC_CALCPOLYS, L"calc octnode", L"Irrlicht scene");
|
|
}
|
|
)
|
|
}
|
|
|
|
|
|
//! destructor
|
|
COctreeSceneNode::~COctreeSceneNode()
|
|
{
|
|
if (Shadow)
|
|
Shadow->drop();
|
|
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)
|
|
{
|
|
if (driver->needsTransparentRenderPass(Materials[i]))
|
|
++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();
|
|
}
|
|
}
|
|
|
|
template <class VT>
|
|
void renderMeshBuffer(video::IVideoDriver* driver, EOCTREENODE_VBO useVBO, typename Octree<VT>::SMeshChunk& meshChunk, const typename Octree<VT>::SIndexData& indexData)
|
|
{
|
|
switch ( useVBO )
|
|
{
|
|
case EOV_NO_VBO:
|
|
driver->drawIndexedTriangleList(
|
|
&meshChunk.Vertices[0],
|
|
meshChunk.Vertices.size(),
|
|
indexData.Indices, indexData.CurrentSize / 3);
|
|
break;
|
|
case EOV_USE_VBO:
|
|
driver->drawMeshBuffer ( &meshChunk );
|
|
break;
|
|
case EOV_USE_VBO_WITH_VISIBITLY:
|
|
{
|
|
u16* oldPointer = meshChunk.Indices.pointer();
|
|
const u32 oldSize = meshChunk.Indices.size();
|
|
meshChunk.Indices.set_free_when_destroyed(false);
|
|
meshChunk.Indices.set_pointer(indexData.Indices, indexData.CurrentSize, false, false);
|
|
meshChunk.setDirty(scene::EBT_INDEX);
|
|
driver->drawMeshBuffer ( &meshChunk );
|
|
meshChunk.Indices.set_pointer(oldPointer, oldSize);
|
|
meshChunk.setDirty(scene::EBT_INDEX);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//! renders the node.
|
|
void COctreeSceneNode::render()
|
|
{
|
|
IRR_PROFILE(CProfileScope psRender(EPID_OC_RENDER);)
|
|
video::IVideoDriver* driver = SceneManager->getVideoDriver();
|
|
|
|
if (!driver)
|
|
return;
|
|
|
|
ICameraSceneNode* camera = SceneManager->getActiveCamera();
|
|
if (!camera)
|
|
return;
|
|
|
|
const bool isTransparentPass =
|
|
SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT;
|
|
++PassCount;
|
|
|
|
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
|
|
|
|
if (Shadow)
|
|
Shadow->updateShadowVolumes();
|
|
|
|
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:
|
|
{
|
|
IRR_PROFILE(getProfiler().start(EPID_OC_CALCPOLYS));
|
|
switch ( PolygonChecks )
|
|
{
|
|
case EOPC_BOX:
|
|
StdOctree->calculatePolys(box);
|
|
break;
|
|
case EOPC_FRUSTUM:
|
|
StdOctree->calculatePolys(frust);
|
|
break;
|
|
}
|
|
IRR_PROFILE(getProfiler().stop(EPID_OC_CALCPOLYS));
|
|
|
|
const Octree<video::S3DVertex>::SIndexData* d = StdOctree->getIndexData();
|
|
|
|
for (u32 i=0; i<Materials.size(); ++i)
|
|
{
|
|
if ( 0 == d[i].CurrentSize )
|
|
continue;
|
|
|
|
const bool transparent = driver->needsTransparentRenderPass(Materials[i]);
|
|
|
|
// only render transparent buffer if this is the transparent render pass
|
|
// and solid only in solid pass
|
|
if (transparent == isTransparentPass)
|
|
{
|
|
driver->setMaterial(Materials[i]);
|
|
renderMeshBuffer<video::S3DVertex>(driver, UseVBOs, StdMeshes[i], d[i]);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case video::EVT_2TCOORDS:
|
|
{
|
|
IRR_PROFILE(getProfiler().start(EPID_OC_CALCPOLYS));
|
|
switch ( PolygonChecks )
|
|
{
|
|
case EOPC_BOX:
|
|
LightMapOctree->calculatePolys(box);
|
|
break;
|
|
case EOPC_FRUSTUM:
|
|
LightMapOctree->calculatePolys(frust);
|
|
break;
|
|
}
|
|
IRR_PROFILE(getProfiler().stop(EPID_OC_CALCPOLYS));
|
|
|
|
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]);
|
|
|
|
renderMeshBuffer<video::S3DVertex2TCoords>(driver, UseVBOs, LightMapMeshes[i], d[i]);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case video::EVT_TANGENTS:
|
|
{
|
|
IRR_PROFILE(getProfiler().start(EPID_OC_CALCPOLYS));
|
|
switch ( PolygonChecks )
|
|
{
|
|
case EOPC_BOX:
|
|
TangentsOctree->calculatePolys(box);
|
|
break;
|
|
case EOPC_FRUSTUM:
|
|
TangentsOctree->calculatePolys(frust);
|
|
break;
|
|
}
|
|
IRR_PROFILE(getProfiler().stop(EPID_OC_CALCPOLYS));
|
|
|
|
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]);
|
|
renderMeshBuffer<video::S3DVertexTangents>(driver, UseVBOs, TangentsMeshes[i], d[i]);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
// for debug purposes only
|
|
if (DebugDataVisible && !Materials.empty() && PassCount==1)
|
|
{
|
|
core::array< const core::aabbox3d<f32>* > boxes;
|
|
video::SMaterial m;
|
|
m.Lighting = false;
|
|
driver->setMaterial(m);
|
|
if ( DebugDataVisible & scene::EDS_BBOX_BUFFERS )
|
|
{
|
|
switch (VertexType)
|
|
{
|
|
case video::EVT_STANDARD:
|
|
StdOctree->getBoundingBoxes(box, boxes);
|
|
break;
|
|
case video::EVT_2TCOORDS:
|
|
LightMapOctree->getBoundingBoxes(box, boxes);
|
|
break;
|
|
case video::EVT_TANGENTS:
|
|
TangentsOctree->getBoundingBoxes(box, boxes);
|
|
break;
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//! Removes a child from this scene node.
|
|
//! Implemented here, to be able to remove the shadow properly, if there is one,
|
|
//! or to remove attached childs.
|
|
bool COctreeSceneNode::removeChild(ISceneNode* child)
|
|
{
|
|
if (child && Shadow == child)
|
|
{
|
|
Shadow->drop();
|
|
Shadow = 0;
|
|
}
|
|
|
|
return ISceneNode::removeChild(child);
|
|
}
|
|
|
|
void COctreeSceneNode::setUseVBO(EOCTREENODE_VBO useVBO)
|
|
{
|
|
UseVBOs = useVBO;
|
|
if ( Mesh )
|
|
createTree(Mesh);
|
|
}
|
|
|
|
EOCTREENODE_VBO COctreeSceneNode::getUseVBO() const
|
|
{
|
|
return UseVBOs;
|
|
}
|
|
|
|
void COctreeSceneNode::setPolygonChecks(EOCTREE_POLYGON_CHECKS checks)
|
|
{
|
|
PolygonChecks = checks;
|
|
}
|
|
|
|
EOCTREE_POLYGON_CHECKS COctreeSceneNode::getPolygonChecks() const
|
|
{
|
|
return PolygonChecks;
|
|
}
|
|
|
|
//! Creates shadow volume scene node as child of this node
|
|
//! and returns a pointer to it.
|
|
IShadowVolumeSceneNode* COctreeSceneNode::addShadowVolumeSceneNode(
|
|
const IMesh* shadowMesh, s32 id, bool zfailmethod, f32 infinity)
|
|
{
|
|
#ifdef _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_
|
|
if (!SceneManager->getVideoDriver()->queryFeature(video::EVDF_STENCIL_BUFFER))
|
|
return 0;
|
|
|
|
if (!shadowMesh)
|
|
shadowMesh = Mesh; // if null is given, use the mesh of node
|
|
|
|
if (Shadow)
|
|
Shadow->drop();
|
|
|
|
Shadow = new CShadowVolumeSceneNode(shadowMesh, this, SceneManager, id, zfailmethod, infinity);
|
|
return Shadow;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
//! returns the axis aligned bounding box of this node
|
|
const core::aabbox3d<f32>& COctreeSceneNode::getBoundingBox() const
|
|
{
|
|
return Box;
|
|
}
|
|
|
|
|
|
//! creates the tree
|
|
/* This method has a lot of duplication and overhead. Moreover, the tangents mesh conversion does not really work. I think we need a a proper mesh implementation for octrees, which handle all vertex types internally. Converting all structures to just one vertex type is always problematic.
|
|
Thanks to Auria for fixing major parts of this method. */
|
|
bool COctreeSceneNode::createTree(IMesh* mesh)
|
|
{
|
|
if (!mesh)
|
|
return false;
|
|
|
|
MeshName = SceneManager->getMeshCache()->getMeshName(mesh);
|
|
|
|
mesh->grab();
|
|
deleteTree();
|
|
|
|
Mesh = mesh;
|
|
|
|
const u32 beginTime = os::Timer::getRealTime();
|
|
|
|
u32 nodeCount = 0;
|
|
u32 polyCount = 0;
|
|
u32 i;
|
|
|
|
Box = mesh->getBoundingBox();
|
|
|
|
if (mesh->getMeshBufferCount())
|
|
{
|
|
// check for "largest" buffer types
|
|
// Also dropping buffers/materials for empty buffer
|
|
// (which looks like a horrible idea. If a user wanted that material without mesh he should still get it...
|
|
// but not going to change that now. Only documenting it after figuring out what happens here.
|
|
// It works at least as Materials are reset in deleteTree).
|
|
VertexType = video::EVT_STANDARD;
|
|
u32 meshReserve = 0;
|
|
for (i=0; i<mesh->getMeshBufferCount(); ++i)
|
|
{
|
|
const IMeshBuffer* b = mesh->getMeshBuffer(i);
|
|
if (b->getVertexCount() && b->getIndexCount())
|
|
{
|
|
++meshReserve;
|
|
if (b->getVertexType() == video::EVT_2TCOORDS)
|
|
VertexType = video::EVT_2TCOORDS;
|
|
else if (b->getVertexType() == video::EVT_TANGENTS)
|
|
VertexType = video::EVT_TANGENTS;
|
|
}
|
|
}
|
|
Materials.reallocate(Materials.size()+meshReserve);
|
|
|
|
switch(VertexType)
|
|
{
|
|
case video::EVT_STANDARD:
|
|
{
|
|
StdMeshes.reallocate(StdMeshes.size() + meshReserve);
|
|
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());
|
|
switch (b->getVertexType())
|
|
{
|
|
case video::EVT_STANDARD:
|
|
for (v=0; v<b->getVertexCount(); ++v)
|
|
nchunk.Vertices.push_back(((video::S3DVertex*)b->getVertices())[v]);
|
|
break;
|
|
case video::EVT_2TCOORDS:
|
|
for (v=0; v<b->getVertexCount(); ++v)
|
|
nchunk.Vertices.push_back(((video::S3DVertex2TCoords*)b->getVertices())[v]);
|
|
break;
|
|
case video::EVT_TANGENTS:
|
|
for (v=0; v<b->getVertexCount(); ++v)
|
|
nchunk.Vertices.push_back(((video::S3DVertexTangents*)b->getVertices())[v]);
|
|
break;
|
|
}
|
|
|
|
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:
|
|
{
|
|
LightMapMeshes.reallocate(LightMapMeshes.size() + meshReserve);
|
|
|
|
for ( i=0; i < mesh->getMeshBufferCount(); ++i)
|
|
{
|
|
IMeshBuffer* 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 (UseVBOs == EOV_USE_VBO_WITH_VISIBITLY)
|
|
{
|
|
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());
|
|
switch (b->getVertexType())
|
|
{
|
|
case video::EVT_STANDARD:
|
|
for (v=0; v<b->getVertexCount(); ++v)
|
|
nchunk.Vertices.push_back(((video::S3DVertex*)b->getVertices())[v]);
|
|
break;
|
|
case video::EVT_2TCOORDS:
|
|
for (v=0; v<b->getVertexCount(); ++v)
|
|
nchunk.Vertices.push_back(((video::S3DVertex2TCoords*)b->getVertices())[v]);
|
|
break;
|
|
case video::EVT_TANGENTS:
|
|
for (v=0; v<b->getVertexCount(); ++v)
|
|
nchunk.Vertices.push_back(((video::S3DVertexTangents*)b->getVertices())[v]);
|
|
break;
|
|
}
|
|
|
|
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:
|
|
{
|
|
TangentsMeshes.reallocate(TangentsMeshes.size() + meshReserve);
|
|
|
|
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());
|
|
switch (b->getVertexType())
|
|
{
|
|
case video::EVT_STANDARD:
|
|
for (v=0; v<b->getVertexCount(); ++v)
|
|
{
|
|
const video::S3DVertex& tmpV = ((video::S3DVertex*)b->getVertices())[v];
|
|
nchunk.Vertices.push_back(video::S3DVertexTangents(tmpV.Pos, tmpV.Color, tmpV.TCoords));
|
|
}
|
|
break;
|
|
case video::EVT_2TCOORDS:
|
|
for (v=0; v<b->getVertexCount(); ++v)
|
|
{
|
|
const video::S3DVertex2TCoords& tmpV = ((video::S3DVertex2TCoords*)b->getVertices())[v];
|
|
nchunk.Vertices.push_back(video::S3DVertexTangents(tmpV.Pos, tmpV.Color, tmpV.TCoords));
|
|
}
|
|
break;
|
|
case video::EVT_TANGENTS:
|
|
for (v=0; v<b->getVertexCount(); ++v)
|
|
nchunk.Vertices.push_back(((video::S3DVertexTangents*)b->getVertices())[v]);
|
|
break;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
const 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.
|
|
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
|
|
|
|
#endif // _IRR_COMPILE_WITH_OCTREE_SCENENODE_
|