// Copyright (C) 2002-2008 Nikolaus Gebhardt // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h #ifndef __C_OCT_TREE_H_INCLUDED__ #define __C_OCT_TREE_H_INCLUDED__ #include "SViewFrustum.h" #include "S3DVertex.h" #include "aabbox3d.h" #include "irrArray.h" #include "irrString.h" namespace irr { //! template octtree. T must be a vertex type which has a member //! called .Pos, which is a core::vertex3df position. template class OctTree { public: u32 nodeCount; struct SMeshChunk { core::array Vertices; core::array Indices; s32 MaterialId; }; struct SIndexChunk { core::array Indices; s32 MaterialId; }; struct SIndexData { u16* Indices; s32 CurrentSize; s32 MaxSize; }; //! constructor OctTree(const core::array& meshes, s32 minimalPolysPerNode=128) { nodeCount = 0; IndexDataCount = meshes.size(); IndexData = new SIndexData[IndexDataCount]; // construct array of all indices core::array* indexChunks = new core::array; SIndexChunk ic; for (u32 i=0; ipush_back(ic); SIndexChunk& tic = (*indexChunks)[i]; for (u32 t=0; t& box) { for (u32 i=0; igetPolys(box, IndexData, 0); } //! returns all ids of polygons partially or fully enclosed //! by a view frustum. void calculatePolys(const scene::SViewFrustum& frustum) { for (u32 i=0; igetPolys(frustum, IndexData, 0); } SIndexData* getIndexData() { return IndexData; } u32 getIndexDataCount() { return IndexDataCount; } // for debug purposes only, renders the bounding boxes of the tree void renderBoundingBoxes(const core::aabbox3d& box, core::array< core::aabbox3d >&outBoxes) { Root->renderBoundingBoxes(box, outBoxes); } //! destructor ~OctTree() { for (u32 i=0; i& allmeshdata, core::array* indices, s32 minimalPolysPerNode) : IndexData(0), Depth(currentdepth+1) { ++nodeCount; u32 i; // new ISO for scoping problem with different compilers for (i=0; i<8; ++i) Children[i] = 0; if (indices->empty()) { delete indices; return; } bool found = false; // find first point for bounding box for (i=0; isize(); ++i) { if (!(*indices)[i].Indices.empty()) { Box.reset(allmeshdata[i].Vertices[(*indices)[i].Indices[0]].Pos); found = true; break; } } if (!found) { delete indices; return; } s32 totalPrimitives = 0; // now lets calculate our bounding box for (i=0; isize(); ++i) { totalPrimitives += (*indices)[i].Indices.size(); for (u32 j=0; j<(*indices)[i].Indices.size(); ++j) Box.addInternalPoint(allmeshdata[i].Vertices[(*indices)[i].Indices[j]].Pos); } core::vector3df middle = Box.getCenter(); core::vector3df edges[8]; Box.getEdges(edges); // calculate all children core::aabbox3d box; core::array keepIndices; if (totalPrimitives > minimalPolysPerNode && !Box.isEmpty()) for (s32 ch=0; ch<8; ++ch) { box.reset(middle); box.addInternalPoint(edges[ch]); // create indices for child core::array* cindexChunks = new core::array; bool added = false; for (i=0; ipush_back(ic); SIndexChunk& tic = (*cindexChunks)[i]; for (u32 t=0; t<(*indices)[i].Indices.size(); t+=3) { if (box.isPointInside(allmeshdata[i].Vertices[(*indices)[i].Indices[t]].Pos) && box.isPointInside(allmeshdata[i].Vertices[(*indices)[i].Indices[t+1]].Pos) && box.isPointInside(allmeshdata[i].Vertices[(*indices)[i].Indices[t+2]].Pos)) { tic.Indices.push_back((*indices)[i].Indices[t]); tic.Indices.push_back((*indices)[i].Indices[t+1]); tic.Indices.push_back((*indices)[i].Indices[t+2]); added = true; } else { keepIndices.push_back((*indices)[i].Indices[t]); keepIndices.push_back((*indices)[i].Indices[t+1]); keepIndices.push_back((*indices)[i].Indices[t+2]); } } memcpy( (*indices)[i].Indices.pointer(), keepIndices.pointer(), keepIndices.size()*sizeof(u16)); (*indices)[i].Indices.set_used(keepIndices.size()); keepIndices.set_used(0); } if (added) Children[ch] = new OctTreeNode(nodeCount, Depth, allmeshdata, cindexChunks, minimalPolysPerNode); else delete cindexChunks; } // end for all possible children IndexData = indices; } // destructor ~OctTreeNode() { delete IndexData; for (u32 i=0; i<8; ++i) delete Children[i]; } // returns all ids of polygons partially or full enclosed // by this bounding box. void getPolys(const core::aabbox3d& box, SIndexData* idxdata, u32 parentTest ) const { // if not full inside if ( parentTest != 2 ) { // partially inside ? parentTest = (u32) Box.intersectsWithBox(box); if ( 0 == parentTest ) return; // fully inside ? parentTest+= Box.isFullInside(box); } //if (Box.intersectsWithBox(box)) { u32 cnt = IndexData->size(); u32 i; // new ISO for scoping problem in some compilers for (i=0; igetPolys(box, idxdata,parentTest); } } // returns all ids of polygons partially or full enclosed // by the view frustum. void getPolys(const scene::SViewFrustum& frustum, SIndexData* idxdata,u32 parentTest) const { s32 i; // new ISO for scoping problem in some compilers // not fully inside //if ( parentTest != 2 ) { core::vector3df edges[8]; Box.getEdges(edges); for (i=0; isize(); for (i=0; igetPolys(frustum, idxdata,parentTest); } void renderBoundingBoxes(const core::aabbox3d& box, core::array< core::aabbox3d >&outBoxes) { if (Box.intersectsWithBox(box)) { outBoxes.push_back(Box); for (u32 i=0; i<8; ++i) if (Children[i]) Children[i]->renderBoundingBoxes(box, outBoxes); } } private: core::aabbox3df Box; core::array* IndexData; OctTreeNode* Children[8]; u32 Depth; }; OctTreeNode* Root; SIndexData* IndexData; u32 IndexDataCount; }; } // end namespace #endif