mirror of
https://github.com/minetest/irrlicht.git
synced 2024-11-19 00:30:30 +01:00
66d189ce63
So far it only used the inverse of the node transformation to calculate the box used to check Which gave wrong results as soon as one tried to pass an additional matrix transformation Wasn't ever used internally or by examples in Irrlicht, so I guess no one ever noticed (also in some cases this still worked accidentally). git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6577 dfc29bdd-3216-0410-991c-e03cc46cb475
509 lines
14 KiB
C++
509 lines
14 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 "CTriangleSelector.h"
|
|
#include "ISceneNode.h"
|
|
#include "IMeshBuffer.h"
|
|
#include "IAnimatedMeshSceneNode.h"
|
|
#include "SSkinMeshBuffer.h"
|
|
|
|
namespace irr
|
|
{
|
|
namespace scene
|
|
{
|
|
|
|
//! constructor
|
|
CTriangleSelector::CTriangleSelector(ISceneNode* node)
|
|
: SceneNode(node), MeshBuffer(0), MaterialIndex(0), AnimatedNode(0), LastMeshFrame(0)
|
|
{
|
|
#ifdef _DEBUG
|
|
setDebugName("CTriangleSelector");
|
|
#endif
|
|
|
|
BoundingBox.reset(0.f, 0.f, 0.f);
|
|
}
|
|
|
|
|
|
//! constructor
|
|
CTriangleSelector::CTriangleSelector(const core::aabbox3d<f32>& box, ISceneNode* node)
|
|
: SceneNode(node), MeshBuffer(0), MaterialIndex(0), AnimatedNode(0), LastMeshFrame(0)
|
|
{
|
|
#ifdef _DEBUG
|
|
setDebugName("CTriangleSelector");
|
|
#endif
|
|
|
|
BoundingBox=box;
|
|
// TODO
|
|
}
|
|
|
|
|
|
//! constructor
|
|
CTriangleSelector::CTriangleSelector(const IMesh* mesh, ISceneNode* node, bool separateMeshbuffers)
|
|
: SceneNode(node), MeshBuffer(0), MaterialIndex(0), AnimatedNode(0), LastMeshFrame(0)
|
|
{
|
|
#ifdef _DEBUG
|
|
setDebugName("CTriangleSelector");
|
|
#endif
|
|
|
|
createFromMesh(mesh, separateMeshbuffers);
|
|
}
|
|
|
|
CTriangleSelector::CTriangleSelector(const IMeshBuffer* meshBuffer, irr::u32 materialIndex, ISceneNode* node)
|
|
: SceneNode(node), MeshBuffer(meshBuffer), MaterialIndex(materialIndex), AnimatedNode(0), LastMeshFrame(0)
|
|
{
|
|
#ifdef _DEBUG
|
|
setDebugName("CTriangleSelector");
|
|
#endif
|
|
|
|
createFromMeshBuffer(meshBuffer);
|
|
}
|
|
|
|
CTriangleSelector::CTriangleSelector(IAnimatedMeshSceneNode* node, bool separateMeshbuffers)
|
|
: SceneNode(node), AnimatedNode(node), LastMeshFrame(0)
|
|
{
|
|
#ifdef _DEBUG
|
|
setDebugName("CTriangleSelector");
|
|
#endif
|
|
|
|
if (!AnimatedNode)
|
|
return;
|
|
|
|
IAnimatedMesh* animatedMesh = AnimatedNode->getMesh();
|
|
if (!animatedMesh)
|
|
return;
|
|
|
|
LastMeshFrame = (u32)AnimatedNode->getFrameNr();
|
|
IMesh* mesh = animatedMesh->getMesh(LastMeshFrame);
|
|
|
|
if (mesh)
|
|
createFromMesh(mesh, separateMeshbuffers);
|
|
}
|
|
|
|
|
|
void CTriangleSelector::createFromMesh(const IMesh* mesh, bool createBufferRanges)
|
|
{
|
|
BufferRanges.clear();
|
|
Triangles.clear();
|
|
|
|
const u32 cnt = mesh->getMeshBufferCount();
|
|
u32 totalFaceCount = 0;
|
|
for (u32 j=0; j<cnt; ++j)
|
|
{
|
|
SCollisionTriangleRange range;
|
|
range.MeshBuffer = mesh->getMeshBuffer(j);
|
|
range.MaterialIndex = j;
|
|
range.RangeSize = range.MeshBuffer->getIndexCount() / 3;
|
|
|
|
if ( createBufferRanges )
|
|
{
|
|
range.RangeStart = totalFaceCount;
|
|
|
|
BufferRanges.push_back(range);
|
|
}
|
|
|
|
totalFaceCount += range.RangeSize;
|
|
}
|
|
Triangles.set_used(totalFaceCount);
|
|
|
|
updateFromMesh(mesh);
|
|
}
|
|
|
|
void CTriangleSelector::createFromMeshBuffer(const IMeshBuffer* meshBuffer)
|
|
{
|
|
BufferRanges.clear();
|
|
Triangles.clear();
|
|
|
|
if ( meshBuffer )
|
|
{
|
|
Triangles.set_used(meshBuffer->getIndexCount() / 3);
|
|
}
|
|
|
|
updateFromMeshBuffer(meshBuffer);
|
|
}
|
|
|
|
template <typename TIndex>
|
|
static void updateTriangles(u32& triangleCount, core::array<core::triangle3df>& triangles, u32 idxCnt, const TIndex* indices, const u8* vertices, u32 vertexPitch, const core::matrix4* bufferTransform)
|
|
{
|
|
if ( bufferTransform )
|
|
{
|
|
for (u32 index = 2; index < idxCnt; index += 3)
|
|
{
|
|
core::triangle3df& tri = triangles[triangleCount++];
|
|
bufferTransform->transformVect( tri.pointA, (*reinterpret_cast<const video::S3DVertex*>(&vertices[indices[index - 2]*vertexPitch])).Pos );
|
|
bufferTransform->transformVect( tri.pointB, (*reinterpret_cast<const video::S3DVertex*>(&vertices[indices[index - 1]*vertexPitch])).Pos );
|
|
bufferTransform->transformVect( tri.pointC, (*reinterpret_cast<const video::S3DVertex*>(&vertices[indices[index - 0]*vertexPitch])).Pos );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (u32 index = 2; index < idxCnt; index += 3)
|
|
{
|
|
core::triangle3df& tri = triangles[triangleCount++];
|
|
tri.pointA = (*reinterpret_cast<const video::S3DVertex*>(&vertices[indices[index - 2]*vertexPitch])).Pos;
|
|
tri.pointB = (*reinterpret_cast<const video::S3DVertex*>(&vertices[indices[index - 1]*vertexPitch])).Pos;
|
|
tri.pointC = (*reinterpret_cast<const video::S3DVertex*>(&vertices[indices[index - 0]*vertexPitch])).Pos;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTriangleSelector::updateFromMesh(const IMesh* mesh) const
|
|
{
|
|
if (!mesh)
|
|
return;
|
|
|
|
bool skinnnedMesh = mesh->getMeshType() == EAMT_SKINNED;
|
|
u32 meshBuffers = mesh->getMeshBufferCount();
|
|
u32 triangleCount = 0;
|
|
|
|
for (u32 i = 0; i < meshBuffers; ++i)
|
|
{
|
|
IMeshBuffer* buf = mesh->getMeshBuffer(i);
|
|
u32 idxCnt = buf->getIndexCount();
|
|
u32 vertexPitch = getVertexPitchFromType(buf->getVertexType());
|
|
u8* vertices = (u8*)buf->getVertices();
|
|
|
|
const core::matrix4* bufferTransform = 0;
|
|
if ( skinnnedMesh )
|
|
{
|
|
bufferTransform = &(((scene::SSkinMeshBuffer*)buf)->Transformation);
|
|
if ( bufferTransform->isIdentity() )
|
|
bufferTransform = 0;
|
|
}
|
|
|
|
switch ( buf->getIndexType() )
|
|
{
|
|
case video::EIT_16BIT:
|
|
{
|
|
const u16* indices = buf->getIndices();
|
|
updateTriangles(triangleCount, Triangles, idxCnt, indices, vertices, vertexPitch, bufferTransform);
|
|
}
|
|
break;
|
|
case video::EIT_32BIT:
|
|
{
|
|
const u32* indices = (u32*)buf->getIndices();
|
|
updateTriangles(triangleCount, Triangles, idxCnt, indices, vertices, vertexPitch, bufferTransform);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Update bounding box
|
|
updateBoundingBox();
|
|
}
|
|
|
|
void CTriangleSelector::updateFromMeshBuffer(const IMeshBuffer* meshBuffer) const
|
|
{
|
|
if ( !meshBuffer )
|
|
return;
|
|
|
|
u32 idxCnt = meshBuffer->getIndexCount();
|
|
u32 vertexPitch = getVertexPitchFromType(meshBuffer->getVertexType());
|
|
u8* vertices = (u8*)meshBuffer->getVertices();
|
|
u32 triangleCount = 0;
|
|
switch ( meshBuffer->getIndexType() )
|
|
{
|
|
case video::EIT_16BIT:
|
|
{
|
|
const u16* indices = meshBuffer->getIndices();
|
|
updateTriangles(triangleCount, Triangles, idxCnt, indices, vertices, vertexPitch, 0);
|
|
}
|
|
break;
|
|
case video::EIT_32BIT:
|
|
{
|
|
const u32* indices = (u32*)meshBuffer->getIndices();
|
|
updateTriangles(triangleCount, Triangles, idxCnt, indices, vertices, vertexPitch, 0);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CTriangleSelector::updateBoundingBox() const
|
|
{
|
|
if ( !Triangles.empty() )
|
|
{
|
|
BoundingBox.reset( Triangles[0].pointA );
|
|
for (u32 i=0; i < Triangles.size(); ++i)
|
|
{
|
|
const core::triangle3df& tri = Triangles[i];
|
|
BoundingBox.addInternalPoint(tri.pointA);
|
|
BoundingBox.addInternalPoint(tri.pointB);
|
|
BoundingBox.addInternalPoint(tri.pointC);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BoundingBox.reset(0.f, 0.f, 0.f);
|
|
}
|
|
}
|
|
|
|
void CTriangleSelector::update(void) const
|
|
{
|
|
if (!AnimatedNode)
|
|
return; //< harmless no-op
|
|
|
|
const u32 currentFrame = (u32)AnimatedNode->getFrameNr();
|
|
if (currentFrame == LastMeshFrame)
|
|
return; //< Nothing to do
|
|
|
|
LastMeshFrame = currentFrame;
|
|
IAnimatedMesh * animatedMesh = AnimatedNode->getMesh();
|
|
|
|
if (animatedMesh)
|
|
{
|
|
IMesh * mesh = animatedMesh->getMesh(LastMeshFrame);
|
|
|
|
if (mesh)
|
|
updateFromMesh(mesh);
|
|
}
|
|
}
|
|
|
|
|
|
//! Gets all triangles.
|
|
void CTriangleSelector::getTriangles(core::triangle3df* triangles,
|
|
s32 arraySize, s32& outTriangleCount,
|
|
const core::matrix4* transform, bool useNodeTransform,
|
|
irr::core::array<SCollisionTriangleRange>* outTriangleInfo) const
|
|
{
|
|
// Update my triangles if necessary
|
|
update();
|
|
|
|
u32 cnt = Triangles.size();
|
|
if (cnt > (u32)arraySize)
|
|
cnt = (u32)arraySize;
|
|
|
|
core::matrix4 mat;
|
|
if (transform)
|
|
mat = *transform;
|
|
if (SceneNode&&useNodeTransform)
|
|
mat *= SceneNode->getAbsoluteTransformation();
|
|
|
|
for (u32 i=0; i<cnt; ++i)
|
|
{
|
|
mat.transformVect( triangles[i].pointA, Triangles[i].pointA );
|
|
mat.transformVect( triangles[i].pointB, Triangles[i].pointB );
|
|
mat.transformVect( triangles[i].pointC, Triangles[i].pointC );
|
|
}
|
|
|
|
if ( outTriangleInfo )
|
|
{
|
|
if ( BufferRanges.empty() )
|
|
{
|
|
SCollisionTriangleRange triRange;
|
|
|
|
triRange.RangeStart = 0;
|
|
triRange.RangeSize = cnt;
|
|
triRange.Selector = this;
|
|
triRange.SceneNode = SceneNode;
|
|
triRange.MeshBuffer = MeshBuffer;
|
|
triRange.MaterialIndex = MaterialIndex;
|
|
outTriangleInfo->push_back(triRange);
|
|
}
|
|
else
|
|
{
|
|
irr::u32 rangeIndex = 0;
|
|
for (u32 i=0; i<cnt; )
|
|
{
|
|
while ( i >= (BufferRanges[rangeIndex].RangeStart + BufferRanges[rangeIndex].RangeSize) )
|
|
++rangeIndex;
|
|
|
|
SCollisionTriangleRange triRange;
|
|
|
|
triRange.MaterialIndex = BufferRanges[rangeIndex].MaterialIndex;
|
|
triRange.MeshBuffer = BufferRanges[rangeIndex].MeshBuffer;
|
|
triRange.RangeStart = BufferRanges[rangeIndex].RangeStart;
|
|
triRange.RangeSize = core::min_( cnt-BufferRanges[rangeIndex].RangeStart, BufferRanges[rangeIndex].RangeSize);
|
|
triRange.Selector = this;
|
|
triRange.SceneNode = SceneNode;
|
|
outTriangleInfo->push_back(triRange);
|
|
|
|
i += triRange.RangeSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
outTriangleCount = cnt;
|
|
}
|
|
|
|
|
|
//! Gets all triangles which lie within a specific bounding box.
|
|
void CTriangleSelector::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
|
|
{
|
|
// Update my triangles if necessary
|
|
update();
|
|
|
|
core::matrix4 mat(core::matrix4::EM4CONST_NOTHING);
|
|
if (transform)
|
|
mat = *transform;
|
|
else
|
|
mat.makeIdentity();
|
|
if (SceneNode && useNodeTransform)
|
|
mat *= SceneNode->getAbsoluteTransformation();
|
|
|
|
core::aabbox3df tBox(box);
|
|
core::matrix4 invMat(core::matrix4::EM4CONST_NOTHING);
|
|
if ( mat.getInverse(invMat) )
|
|
{
|
|
invMat.transformBoxEx(tBox);
|
|
}
|
|
else
|
|
{
|
|
// TODO: else is not yet handled optimally.
|
|
// If a node has an axis scaled to 0 we return all triangles without any check
|
|
return getTriangles(triangles, arraySize, outTriangleCount,
|
|
transform, useNodeTransform, outTriangleInfo );
|
|
}
|
|
|
|
outTriangleCount = 0;
|
|
|
|
if (!tBox.intersectsWithBox(BoundingBox))
|
|
return;
|
|
|
|
s32 triangleCount = 0;
|
|
const u32 cnt = Triangles.size();
|
|
|
|
if ( outTriangleInfo && !BufferRanges.empty() )
|
|
{
|
|
irr::u32 activeRange = 0;
|
|
SCollisionTriangleRange triRange;
|
|
triRange.Selector = this;
|
|
triRange.SceneNode = SceneNode;
|
|
triRange.RangeStart = triangleCount;
|
|
triRange.MeshBuffer = BufferRanges[activeRange].MeshBuffer;
|
|
triRange.MaterialIndex = BufferRanges[activeRange].MaterialIndex;
|
|
|
|
for (u32 i=0; i<cnt; ++i)
|
|
{
|
|
// This isn't an accurate test, but it's fast, and the
|
|
// API contract doesn't guarantee complete accuracy.
|
|
if (Triangles[i].isTotalOutsideBox(tBox))
|
|
continue;
|
|
|
|
if ( i >= BufferRanges[activeRange].RangeStart + BufferRanges[activeRange].RangeSize )
|
|
{
|
|
triRange.RangeSize = triangleCount-triRange.RangeStart;
|
|
if ( triRange.RangeSize > 0 )
|
|
outTriangleInfo->push_back(triRange);
|
|
|
|
++activeRange;
|
|
triRange.RangeStart = triangleCount;
|
|
triRange.MeshBuffer = BufferRanges[activeRange].MeshBuffer;
|
|
triRange.MaterialIndex = BufferRanges[activeRange].MaterialIndex;
|
|
}
|
|
|
|
triangles[triangleCount] = Triangles[i];
|
|
mat.transformVect(triangles[triangleCount].pointA);
|
|
mat.transformVect(triangles[triangleCount].pointB);
|
|
mat.transformVect(triangles[triangleCount].pointC);
|
|
|
|
++triangleCount;
|
|
|
|
if (triangleCount == arraySize)
|
|
break;
|
|
}
|
|
triRange.RangeSize = triangleCount-triRange.RangeStart;
|
|
if ( triRange.RangeSize > 0 )
|
|
outTriangleInfo->push_back(triRange);
|
|
}
|
|
else
|
|
{
|
|
for (u32 i=0; i<cnt; ++i)
|
|
{
|
|
// This isn't an accurate test, but it's fast, and the
|
|
// API contract doesn't guarantee complete accuracy.
|
|
if (Triangles[i].isTotalOutsideBox(tBox))
|
|
continue;
|
|
|
|
triangles[triangleCount] = Triangles[i];
|
|
mat.transformVect(triangles[triangleCount].pointA);
|
|
mat.transformVect(triangles[triangleCount].pointB);
|
|
mat.transformVect(triangles[triangleCount].pointC);
|
|
|
|
++triangleCount;
|
|
|
|
if (triangleCount == arraySize)
|
|
break;
|
|
}
|
|
|
|
if ( outTriangleInfo )
|
|
{
|
|
SCollisionTriangleRange triRange;
|
|
triRange.RangeSize = triangleCount;
|
|
triRange.Selector = this;
|
|
triRange.SceneNode = SceneNode;
|
|
triRange.MeshBuffer = MeshBuffer;
|
|
triRange.MaterialIndex = MaterialIndex;
|
|
outTriangleInfo->push_back(triRange);
|
|
}
|
|
}
|
|
|
|
outTriangleCount = triangleCount;
|
|
}
|
|
|
|
|
|
//! Gets all triangles which have or may have contact with a 3d line.
|
|
void CTriangleSelector::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
|
|
{
|
|
// Update my triangles if necessary
|
|
update();
|
|
|
|
core::aabbox3d<f32> box(line.start);
|
|
box.addInternalPoint(line.end);
|
|
|
|
// TODO: Could be optimized for line a little bit more.
|
|
getTriangles(triangles, arraySize, outTriangleCount,
|
|
box, transform, useNodeTransform, outTriangleInfo);
|
|
}
|
|
|
|
|
|
//! Returns amount of all available triangles in this selector
|
|
s32 CTriangleSelector::getTriangleCount() const
|
|
{
|
|
return Triangles.size();
|
|
}
|
|
|
|
|
|
/* Get the number of TriangleSelectors that are part of this one.
|
|
Only useful for MetaTriangleSelector others return 1
|
|
*/
|
|
u32 CTriangleSelector::getSelectorCount() const
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* Get the TriangleSelector based on index based on getSelectorCount.
|
|
Only useful for MetaTriangleSelector others return 'this' or 0
|
|
*/
|
|
ITriangleSelector* CTriangleSelector::getSelector(u32 index)
|
|
{
|
|
if (index)
|
|
return 0;
|
|
else
|
|
return this;
|
|
}
|
|
|
|
|
|
/* Get the TriangleSelector based on index based on getSelectorCount.
|
|
Only useful for MetaTriangleSelector others return 'this' or 0
|
|
*/
|
|
const ITriangleSelector* CTriangleSelector::getSelector(u32 index) const
|
|
{
|
|
if (index)
|
|
return 0;
|
|
else
|
|
return this;
|
|
}
|
|
|
|
|
|
} // end namespace scene
|
|
} // end namespace irr
|