mirror of
https://github.com/minetest/irrlicht.git
synced 2024-12-28 11:40:27 +01:00
8372a70f21
CTriangleSelector constructor with IAnimatedMeshSceneNode parameter didn't initialize MeshBuffer. Not used internally, so no big problem, but it got passed back to users in some cases Replaced SCollisionTriangleRange, by CTriangleSelector::SBufferTriangleRange. Was a bad idea to use a struct which is for something else just because some member were the same (my fault, sorry). Also started unifying the case of using ranges vs not using them. Can maybe even get rid of the non-ranges case in future. Or unify the duplicated code for getting triangles at least. git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6579 dfc29bdd-3216-0410-991c-e03cc46cb475
328 lines
9.2 KiB
C++
328 lines
9.2 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 "COctreeTriangleSelector.h"
|
|
#include "ISceneNode.h"
|
|
|
|
#include "os.h"
|
|
|
|
namespace irr
|
|
{
|
|
namespace scene
|
|
{
|
|
|
|
//! constructor
|
|
COctreeTriangleSelector::COctreeTriangleSelector(const IMesh* mesh,
|
|
ISceneNode* node, s32 minimalPolysPerNode)
|
|
: CTriangleSelector(mesh, node, false)
|
|
, Root(0), NodeCount(0)
|
|
, MinimalPolysPerNode(minimalPolysPerNode)
|
|
{
|
|
#ifdef _DEBUG
|
|
setDebugName("COctreeTriangleSelector");
|
|
#endif
|
|
|
|
if (!Triangles.empty())
|
|
{
|
|
const u32 start = os::Timer::getRealTime();
|
|
|
|
// create the triangle octree
|
|
Root = new SOctreeNode();
|
|
Root->Triangles = Triangles;
|
|
constructOctree(Root);
|
|
|
|
c8 tmp[256];
|
|
sprintf(tmp, "Needed %ums to create OctreeTriangleSelector.(%d nodes, %u polys)",
|
|
os::Timer::getRealTime() - start, NodeCount, Triangles.size());
|
|
os::Printer::log(tmp, ELL_INFORMATION);
|
|
}
|
|
}
|
|
|
|
COctreeTriangleSelector::COctreeTriangleSelector(const IMeshBuffer* meshBuffer, irr::u32 materialIndex, ISceneNode* node, s32 minimalPolysPerNode)
|
|
: CTriangleSelector(meshBuffer, materialIndex, node)
|
|
, Root(0), NodeCount(0)
|
|
, MinimalPolysPerNode(minimalPolysPerNode)
|
|
{
|
|
#ifdef _DEBUG
|
|
setDebugName("COctreeTriangleSelector");
|
|
#endif
|
|
|
|
if (!Triangles.empty())
|
|
{
|
|
const u32 start = os::Timer::getRealTime();
|
|
|
|
// create the triangle octree
|
|
Root = new SOctreeNode();
|
|
Root->Triangles = Triangles;
|
|
constructOctree(Root);
|
|
|
|
c8 tmp[256];
|
|
sprintf(tmp, "Needed %ums to create OctreeTriangleSelector.(%d nodes, %u polys)",
|
|
os::Timer::getRealTime() - start, NodeCount, Triangles.size());
|
|
os::Printer::log(tmp, ELL_INFORMATION);
|
|
}
|
|
}
|
|
|
|
//! destructor
|
|
COctreeTriangleSelector::~COctreeTriangleSelector()
|
|
{
|
|
delete Root;
|
|
}
|
|
|
|
|
|
void COctreeTriangleSelector::constructOctree(SOctreeNode* node)
|
|
{
|
|
++NodeCount;
|
|
|
|
node->Box.reset(node->Triangles[0].pointA);
|
|
|
|
// get bounding box
|
|
const u32 cnt = node->Triangles.size();
|
|
for (u32 i=0; i<cnt; ++i)
|
|
{
|
|
node->Box.addInternalPoint(node->Triangles[i].pointA);
|
|
node->Box.addInternalPoint(node->Triangles[i].pointB);
|
|
node->Box.addInternalPoint(node->Triangles[i].pointC);
|
|
}
|
|
|
|
// calculate children
|
|
|
|
if (!node->Box.isEmpty() && (s32)node->Triangles.size() > MinimalPolysPerNode)
|
|
{
|
|
const core::vector3df& middle = node->Box.getCenter();
|
|
core::vector3df edges[8];
|
|
node->Box.getEdges(edges);
|
|
|
|
core::aabbox3d<f32> box;
|
|
core::array<core::triangle3df> keepTriangles(node->Triangles.size()); // reserving enough memory, so we don't get re-allocations per child
|
|
|
|
for (s32 ch=0; ch<8; ++ch)
|
|
{
|
|
box.reset(middle);
|
|
box.addInternalPoint(edges[ch]);
|
|
node->Child[ch] = new SOctreeNode();
|
|
|
|
for (s32 i=0; i<(s32)node->Triangles.size(); ++i)
|
|
{
|
|
if (node->Triangles[i].isTotalInsideBox(box))
|
|
{
|
|
node->Child[ch]->Triangles.push_back(node->Triangles[i]);
|
|
//node->Triangles.erase(i);
|
|
//--i;
|
|
}
|
|
else
|
|
{
|
|
keepTriangles.push_back(node->Triangles[i]);
|
|
}
|
|
}
|
|
memcpy(node->Triangles.pointer(), keepTriangles.pointer(),
|
|
sizeof(core::triangle3df)*keepTriangles.size());
|
|
|
|
node->Triangles.set_used(keepTriangles.size());
|
|
keepTriangles.set_used(0);
|
|
}
|
|
keepTriangles.clear(); // release memory early, for large meshes it can matter.
|
|
node->Triangles.reallocate(node->Triangles.size(), true); // shrink memory to minimum necessary
|
|
|
|
// Note: We use an extra loop to construct child-nodes instead of doing
|
|
// that in above loop to avoid memory fragmentation which happens if
|
|
// the code has to switch between allocating memory for this node and
|
|
// the child nodes (thanks @Squarefox for noting this).
|
|
for (s32 ch=0; ch<8; ++ch)
|
|
{
|
|
if (node->Child[ch]->Triangles.empty())
|
|
{
|
|
delete node->Child[ch];
|
|
node->Child[ch] = 0;
|
|
}
|
|
else
|
|
constructOctree(node->Child[ch]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//! Gets all triangles which lie within a specific bounding box.
|
|
void COctreeTriangleSelector::getTriangles(core::triangle3df* triangles,
|
|
s32 arraySize, s32& outTriangleCount,
|
|
const core::aabbox3d<f32>& box,
|
|
const core::matrix4* transform, bool useNodeTransform,
|
|
irr::core::array<SCollisionTriangleRange>* outTriangleInfo) const
|
|
{
|
|
core::matrix4 mat(core::matrix4::EM4CONST_NOTHING);
|
|
core::aabbox3d<f32> invbox = box;
|
|
|
|
if (SceneNode && useNodeTransform)
|
|
{
|
|
if ( SceneNode->getAbsoluteTransformation().getInverse(mat) )
|
|
mat.transformBoxEx(invbox);
|
|
else
|
|
// TODO: case not handled well, we can only return all triangles
|
|
return CTriangleSelector::getTriangles(triangles, arraySize, outTriangleCount, transform, useNodeTransform, outTriangleInfo);
|
|
}
|
|
|
|
if (transform)
|
|
mat = *transform;
|
|
else
|
|
mat.makeIdentity();
|
|
|
|
if (SceneNode && useNodeTransform)
|
|
mat *= SceneNode->getAbsoluteTransformation();
|
|
|
|
s32 trianglesWritten = 0;
|
|
|
|
if (Root)
|
|
getTrianglesFromOctree(Root, trianglesWritten,
|
|
arraySize, invbox, &mat, triangles);
|
|
|
|
if ( outTriangleInfo )
|
|
{
|
|
SCollisionTriangleRange triRange;
|
|
triRange.RangeSize = trianglesWritten;
|
|
triRange.Selector = this;
|
|
triRange.SceneNode = SceneNode;
|
|
triRange.MeshBuffer = SingleBufferRange.MeshBuffer;
|
|
triRange.MaterialIndex = SingleBufferRange.MaterialIndex;
|
|
outTriangleInfo->push_back(triRange);
|
|
}
|
|
|
|
outTriangleCount = trianglesWritten;
|
|
}
|
|
|
|
|
|
void COctreeTriangleSelector::getTrianglesFromOctree(
|
|
SOctreeNode* node, s32& trianglesWritten,
|
|
s32 maximumSize, const core::aabbox3d<f32>& box,
|
|
const core::matrix4* mat, core::triangle3df* triangles) const
|
|
{
|
|
if (!box.intersectsWithBox(node->Box))
|
|
return;
|
|
|
|
const u32 cnt = node->Triangles.size();
|
|
|
|
for (u32 i=0; i<cnt; ++i)
|
|
{
|
|
const core::triangle3df& srcTri = node->Triangles[i];
|
|
// This isn't an accurate test, but it's fast, and the
|
|
// API contract doesn't guarantee complete accuracy.
|
|
if (srcTri.isTotalOutsideBox(box))
|
|
continue;
|
|
|
|
core::triangle3df& dstTri = triangles[trianglesWritten];
|
|
mat->transformVect(dstTri.pointA, srcTri.pointA );
|
|
mat->transformVect(dstTri.pointB, srcTri.pointB );
|
|
mat->transformVect(dstTri.pointC, srcTri.pointC );
|
|
++trianglesWritten;
|
|
|
|
// Halt when the out array is full.
|
|
if (trianglesWritten == maximumSize)
|
|
return;
|
|
}
|
|
|
|
for (u32 i=0; i<8; ++i)
|
|
if (node->Child[i])
|
|
getTrianglesFromOctree(node->Child[i], trianglesWritten,
|
|
maximumSize, box, mat, triangles);
|
|
}
|
|
|
|
|
|
//! Gets all triangles which have or may have contact with a 3d line.
|
|
// new version: from user Piraaate
|
|
void COctreeTriangleSelector::getTriangles(core::triangle3df* triangles, s32 arraySize,
|
|
s32& outTriangleCount, const core::line3d<f32>& line,
|
|
const core::matrix4* transform, bool useNodeTransform,
|
|
irr::core::array<SCollisionTriangleRange>* outTriangleInfo) const
|
|
{
|
|
#if 0
|
|
core::aabbox3d<f32> box(line.start);
|
|
box.addInternalPoint(line.end);
|
|
|
|
// TODO: Could be optimized for line a little bit more.
|
|
COctreeTriangleSelector::getTriangles(triangles, arraySize, outTriangleCount,
|
|
box, transform);
|
|
#else
|
|
|
|
core::matrix4 mat ( core::matrix4::EM4CONST_NOTHING );
|
|
|
|
core::vector3df vectStartInv ( line.start ), vectEndInv ( line.end );
|
|
if (SceneNode && useNodeTransform)
|
|
{
|
|
mat = SceneNode->getAbsoluteTransformation();
|
|
mat.makeInverse();
|
|
mat.transformVect(vectStartInv, line.start);
|
|
mat.transformVect(vectEndInv, line.end);
|
|
}
|
|
core::line3d<f32> invline(vectStartInv, vectEndInv);
|
|
|
|
mat.makeIdentity();
|
|
|
|
if (transform)
|
|
mat = (*transform);
|
|
|
|
if (SceneNode && useNodeTransform)
|
|
mat *= SceneNode->getAbsoluteTransformation();
|
|
|
|
s32 trianglesWritten = 0;
|
|
|
|
if (Root)
|
|
getTrianglesFromOctree(Root, trianglesWritten, arraySize, invline, &mat, triangles);
|
|
|
|
if ( outTriangleInfo )
|
|
{
|
|
SCollisionTriangleRange triRange;
|
|
triRange.RangeSize = trianglesWritten;
|
|
triRange.Selector = this;
|
|
triRange.SceneNode = SceneNode;
|
|
triRange.MeshBuffer = SingleBufferRange.MeshBuffer;
|
|
triRange.MaterialIndex = SingleBufferRange.MaterialIndex;
|
|
outTriangleInfo->push_back(triRange);
|
|
}
|
|
|
|
outTriangleCount = trianglesWritten;
|
|
#endif
|
|
}
|
|
|
|
void COctreeTriangleSelector::getTrianglesFromOctree(SOctreeNode* node,
|
|
s32& trianglesWritten, s32 maximumSize, const core::line3d<f32>& line,
|
|
const core::matrix4* transform, core::triangle3df* triangles) const
|
|
{
|
|
if (!node->Box.intersectsWithLine(line))
|
|
return;
|
|
|
|
s32 cnt = node->Triangles.size();
|
|
if (cnt + trianglesWritten > maximumSize)
|
|
cnt -= cnt + trianglesWritten - maximumSize;
|
|
|
|
s32 i;
|
|
|
|
if ( transform->isIdentity() )
|
|
{
|
|
for (i=0; i<cnt; ++i)
|
|
{
|
|
triangles[trianglesWritten] = node->Triangles[i];
|
|
++trianglesWritten;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0; i<cnt; ++i)
|
|
{
|
|
triangles[trianglesWritten] = node->Triangles[i];
|
|
transform->transformVect(triangles[trianglesWritten].pointA);
|
|
transform->transformVect(triangles[trianglesWritten].pointB);
|
|
transform->transformVect(triangles[trianglesWritten].pointC);
|
|
++trianglesWritten;
|
|
}
|
|
}
|
|
|
|
for (i=0; i<8; ++i)
|
|
if (node->Child[i])
|
|
getTrianglesFromOctree(node->Child[i], trianglesWritten,
|
|
maximumSize, line, transform, triangles);
|
|
}
|
|
|
|
|
|
} // end namespace scene
|
|
} // end namespace irr
|