Merge from 1.7 branch, revisions 3877-3908. Fix for getSphericalCoordinateAngles, some warnign fixes, md2 normal fix, several GUI fixes, isPointInside fix, zip endianess fix.

git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@3909 dfc29bdd-3216-0410-991c-e03cc46cb475
master
hybrid 2011-09-08 21:50:10 +00:00
parent 5d7b7931e6
commit afa978f2a5
12 changed files with 367 additions and 80 deletions

View File

@ -289,6 +289,16 @@ The following names can be queried for the given types:
-----------------------------
Changes in 1.7.3 (??.??.2011)
- editbox no longer moves text into next line when it fails wrapping creating senseless empty lines which mess up scrolling.
- Fix crash in editbox when scrolling up with empty first lines caused by textwrapping.
- triangle3d::isPointInside can now work with larger integers, old version failed already with values in the 3-digit range. It got also faster. (thx @ Eigen for report + testcase and REDDemon for patch proposal).
- Fix focus problem when removing an unfocused modal dialog reported by Reiko here: http://irrlicht.sourceforge.net/forum/viewtopic.php?f=7&t=44358
- Add integer template specialization for vector3d::getSphericalCoordinateAngles which rounds angles to nearest integer now.
- Recalculate FrameRect and ScrollPos in CGUIEditBox when AbsoluteRect gets changed (thx @ serengeor for report + testcase)
- Fix 'k' in bigfont.png (thx @ Scrappi for reporting)

View File

@ -90,7 +90,7 @@ namespace scene
//! Transparent effect scene nodes, drawn after Transparent nodes. They are sorted from back to front and drawn in that order.
ESNRP_TRANSPARENT_EFFECT =32,
//! Drawn after the transparent nodes, the time for drawing shadow volumes
//! Drawn after the solid nodes, before the transparent nodes, the time for drawing shadow volumes
ESNRP_SHADOW =64
};

View File

@ -81,21 +81,39 @@ namespace core
return d2 < d3 ? rbc : rca;
}
//! Check if a point is inside the triangle
//! Check if a point is inside the triangle (border-points count also as inside)
/** \param p Point to test. Assumes that this point is already
on the plane of the triangle.
\return True if the point is inside the triangle, otherwise false. */
bool isPointInside(const vector3d<T>& p) const
{
return (isOnSameSide(p, pointA, pointB, pointC) &&
isOnSameSide(p, pointB, pointA, pointC) &&
isOnSameSide(p, pointC, pointA, pointB));
const vector3d<T> a = pointC - pointA;
const vector3d<T> b = pointB - pointA;
const vector3d<T> c = p - pointA;
const f64 dotAA = a.dotProduct( a);
const f64 dotAB = a.dotProduct( b);
const f64 dotAC = a.dotProduct( c);
const f64 dotBB = b.dotProduct( b);
const f64 dotBC = b.dotProduct( c);
// get coordinates in barycentric coordinate system
const f64 invDenom = 1/(dotAA * dotBB - dotAB * dotAB);
const f64 u = (dotBB * dotAC - dotAB * dotBC) * invDenom;
const f64 v = (dotAA * dotBC - dotAB * dotAC ) * invDenom;
// We count border-points as inside to keep downward compatibility.
// That's why we use >= and <= instead of > and < as more commonly seen on the web.
return (u >= 0) && (v >= 0) && (u + v <= 1);
}
//! Check if a point is inside the triangle.
/** This method is an implementation of the example used in a
paper by Kasper Fauerby original written by Keidy from
Mr-Gamemaker.
This was once faster than an old isPointInside implementation, but the
current isPointInside is usualy as fast, sometimes even faster.
Border-points in isPointInsideFast are not defined, some are inside and some outside.
\param p Point to test. Assumes that this point is already
on the plane of the triangle.
\return True if point is inside the triangle, otherwise false. */

View File

@ -421,6 +421,26 @@ namespace core
template <>
inline vector3d<s32>& vector3d<s32>::operator /=(s32 val) {X/=val;Y/=val;Z/=val; return *this;}
template <>
inline vector3d<s32> vector3d<s32>::getSphericalCoordinateAngles()
{
vector3d<s32> angle;
const f64 length = X*X + Y*Y + Z*Z;
if (length)
{
if (X!=0)
{
angle.Y = round32((f32)(atan2((f64)Z,(f64)X) * RADTODEG64));
}
else if (Z<0)
angle.Y=180;
angle.X = round32((f32)(acos(Y * core::reciprocal_squareroot(length)) * RADTODEG64));
}
return angle;
}
//! Typedef for a f32 3d vector.
typedef vector3d<f32> vector3df;

View File

@ -15,8 +15,7 @@ namespace scene
{
const s32 MD2_FRAME_SHIFT = 2;
const f32 MD2_FRAME_SHIFT_RECIPROCAL = 1.f / ( 1 << MD2_FRAME_SHIFT );
const f32 MD2_FRAME_SHIFT_RECIPROCAL = 1.f / (1 << MD2_FRAME_SHIFT);
const s32 Q2_VERTEX_NORMAL_TABLE_SIZE = 162;
@ -194,27 +193,27 @@ struct SMD2AnimationType
static const SMD2AnimationType MD2AnimationTypeList[21] =
{
{ 0, 39, 9 }, // STAND
{ 40, 45, 10 }, // RUN
{ 46, 53, 10 }, // ATTACK
{ 54, 57, 7 }, // PAIN_A
{ 58, 61, 7 }, // PAIN_B
{ 62, 65, 7 }, // PAIN_C
{ 66, 71, 7 }, // JUMP
{ 72, 83, 7 }, // FLIP
{ 84, 94, 7 }, // SALUTE
{ 95, 111, 10 }, // FALLBACK
{ 112, 122, 7 }, // WAVE
{ 123, 134, 6 }, // POINT
{ 135, 153, 10 }, // CROUCH_STAND
{ 154, 159, 7 }, // CROUCH_WALK
{ 160, 168, 10 }, // CROUCH_ATTACK
{ 169, 172, 7 }, // CROUCH_PAIN
{ 173, 177, 5 }, // CROUCH_DEATH
{ 178, 183, 7 }, // DEATH_FALLBACK
{ 184, 189, 7 }, // DEATH_FALLFORWARD
{ 190, 197, 7 }, // DEATH_FALLBACKSLOW
{ 198, 198, 5 }, // BOOM
{ 0, 39, 9}, // STAND
{ 40, 45, 10}, // RUN
{ 46, 53, 10}, // ATTACK
{ 54, 57, 7}, // PAIN_A
{ 58, 61, 7}, // PAIN_B
{ 62, 65, 7}, // PAIN_C
{ 66, 71, 7}, // JUMP
{ 72, 83, 7}, // FLIP
{ 84, 94, 7}, // SALUTE
{ 95, 111, 10}, // FALLBACK
{112, 122, 7}, // WAVE
{123, 134, 6}, // POINT
{135, 153, 10}, // CROUCH_STAND
{154, 159, 7}, // CROUCH_WALK
{160, 168, 10}, // CROUCH_ATTACK
{169, 172, 7}, // CROUCH_PAIN
{173, 177, 5}, // CROUCH_DEATH
{178, 183, 7}, // DEATH_FALLBACK
{184, 189, 7}, // DEATH_FALLFORWARD
{190, 197, 7}, // DEATH_FALLBACKSLOW
{198, 198, 5}, // BOOM
};
@ -238,6 +237,7 @@ CAnimatedMeshMD2::~CAnimatedMeshMD2()
InterpolationBuffer->drop();
}
//! returns the amount of frames in milliseconds. If the amount is 1, it is a static (=non animated) mesh.
u32 CAnimatedMeshMD2::getFrameCount() const
{
@ -294,7 +294,6 @@ void CAnimatedMeshMD2::updateInterpolationBuffer(s32 frame, s32 startFrameLoop,
{
u32 firstFrame, secondFrame;
f32 div;
core::vector3df* NormalTable = (core::vector3df*)&Q2_VERTEX_NORMAL_TABLE;
// TA: resolve missing ipol in loop between end-start
@ -311,10 +310,10 @@ void CAnimatedMeshMD2::updateInterpolationBuffer(s32 frame, s32 startFrameLoop,
u32 e = endFrameLoop >> MD2_FRAME_SHIFT;
firstFrame = frame >> MD2_FRAME_SHIFT;
secondFrame = core::if_c_a_else_b ( firstFrame + 1 > e, s, firstFrame + 1 );
secondFrame = core::if_c_a_else_b(firstFrame + 1 > e, s, firstFrame + 1);
firstFrame = core::s32_min ( FrameCount - 1, firstFrame );
secondFrame = core::s32_min ( FrameCount - 1, secondFrame );
firstFrame = core::s32_min(FrameCount - 1, firstFrame);
secondFrame = core::s32_min(FrameCount - 1, secondFrame);
//div = (frame % (1<<MD2_FRAME_SHIFT)) / (f32)(1<<MD2_FRAME_SHIFT);
frame &= (1<<MD2_FRAME_SHIFT) - 1;
@ -329,17 +328,22 @@ void CAnimatedMeshMD2::updateInterpolationBuffer(s32 frame, s32 startFrameLoop,
const u32 count = FrameList[firstFrame].size();
for (u32 i=0; i<count; ++i)
{
core::vector3df one, two;
one.X = f32(first->Pos.X) * FrameTransforms[firstFrame].scale.X + FrameTransforms[firstFrame].translate.X;
one.Y = f32(first->Pos.Y) * FrameTransforms[firstFrame].scale.Y + FrameTransforms[firstFrame].translate.Y;
one.Z = f32(first->Pos.Z) * FrameTransforms[firstFrame].scale.Z + FrameTransforms[firstFrame].translate.Z;
two.X = f32(second->Pos.X) * FrameTransforms[secondFrame].scale.X + FrameTransforms[secondFrame].translate.X;
two.Y = f32(second->Pos.Y) * FrameTransforms[secondFrame].scale.Y + FrameTransforms[secondFrame].translate.Y;
two.Z = f32(second->Pos.Z) * FrameTransforms[secondFrame].scale.Z + FrameTransforms[secondFrame].translate.Z;
target->Pos = (two - one) * div + one;
target->Normal = (NormalTable[second->NormalIdx] - NormalTable[first->NormalIdx]) * div
+ NormalTable[first->NormalIdx];
const core::vector3df one = core::vector3df(f32(first->Pos.X) * FrameTransforms[firstFrame].scale.X + FrameTransforms[firstFrame].translate.X,
f32(first->Pos.Y) * FrameTransforms[firstFrame].scale.Y + FrameTransforms[firstFrame].translate.Y,
f32(first->Pos.Z) * FrameTransforms[firstFrame].scale.Z + FrameTransforms[firstFrame].translate.Z);
const core::vector3df two = core::vector3df(f32(second->Pos.X) * FrameTransforms[secondFrame].scale.X + FrameTransforms[secondFrame].translate.X,
f32(second->Pos.Y) * FrameTransforms[secondFrame].scale.Y + FrameTransforms[secondFrame].translate.Y,
f32(second->Pos.Z) * FrameTransforms[secondFrame].scale.Z + FrameTransforms[secondFrame].translate.Z);
target->Pos = two.getInterpolated(one, div);
const core::vector3df n1(
Q2_VERTEX_NORMAL_TABLE[first->NormalIdx][0],
Q2_VERTEX_NORMAL_TABLE[first->NormalIdx][2],
Q2_VERTEX_NORMAL_TABLE[first->NormalIdx][1]);
const core::vector3df n2(
Q2_VERTEX_NORMAL_TABLE[second->NormalIdx][0],
Q2_VERTEX_NORMAL_TABLE[second->NormalIdx][2],
Q2_VERTEX_NORMAL_TABLE[second->NormalIdx][1]);
target->Normal = n2.getInterpolated(n1, div);
++target;
++first;
++second;
@ -381,7 +385,7 @@ const core::aabbox3d<f32>& CAnimatedMeshMD2::getBoundingBox() const
//! set user axis aligned bounding box
void CAnimatedMeshMD2::setBoundingBox( const core::aabbox3df& box)
void CAnimatedMeshMD2::setBoundingBox(const core::aabbox3df& box)
{
InterpolationBuffer->BoundingBox = box;
}
@ -405,7 +409,7 @@ void CAnimatedMeshMD2::getFrameLoop(EMD2_ANIMATION_TYPE l,
outEnd = MD2AnimationTypeList[l].end << MD2_FRAME_SHIFT;
// correct to anim between last->first frame
outEnd += MD2_FRAME_SHIFT == 0 ? 1 : ( 1 << MD2_FRAME_SHIFT ) - 1;
outEnd += MD2_FRAME_SHIFT == 0 ? 1 : (1 << MD2_FRAME_SHIFT) - 1;
outFPS = MD2AnimationTypeList[l].fps << MD2_FRAME_SHIFT;
}
@ -420,7 +424,7 @@ bool CAnimatedMeshMD2::getFrameLoop(const c8* name,
{
outBegin = AnimationData[i].begin << MD2_FRAME_SHIFT;
outEnd = AnimationData[i].end << MD2_FRAME_SHIFT;
outEnd += MD2_FRAME_SHIFT == 0 ? 1 : ( 1 << MD2_FRAME_SHIFT ) - 1;
outEnd += MD2_FRAME_SHIFT == 0 ? 1 : (1 << MD2_FRAME_SHIFT) - 1;
outFPS = AnimationData[i].fps << MD2_FRAME_SHIFT;
return true;
}

View File

@ -520,7 +520,7 @@ bool CGUIEditBox::processKey(const SEvent& event)
{
s32 cp = CursorPos - BrokenTextPositions[lineNo];
if ((s32)BrokenText[lineNo-1].size() < cp)
CursorPos = BrokenTextPositions[lineNo-1] + (s32)BrokenText[lineNo-1].size()-1;
CursorPos = BrokenTextPositions[lineNo-1] + core::max_((u32)1, BrokenText[lineNo-1].size())-1;
else
CursorPos = BrokenTextPositions[lineNo-1] + cp;
}
@ -551,7 +551,7 @@ bool CGUIEditBox::processKey(const SEvent& event)
{
s32 cp = CursorPos - BrokenTextPositions[lineNo];
if ((s32)BrokenText[lineNo+1].size() < cp)
CursorPos = BrokenTextPositions[lineNo+1] + BrokenText[lineNo+1].size()-1;
CursorPos = BrokenTextPositions[lineNo+1] + core::max_((u32)1, BrokenText[lineNo+1].size())-1;
else
CursorPos = BrokenTextPositions[lineNo+1] + cp;
}
@ -1135,7 +1135,7 @@ void CGUIEditBox::breakText()
s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
s32 worldlgth = font->getDimension(word.c_str()).Width;
if (WordWrap && length + worldlgth + whitelgth > elWidth)
if (WordWrap && length + worldlgth + whitelgth > elWidth && line.size() > 0)
{
// break to next line
length = worldlgth;

View File

@ -33,7 +33,7 @@ bool CGUIModalScreen::canTakeFocus(IGUIElement* target) const
{
return (target && ((const IGUIElement*)target == this // this element can take it
|| isMyChild(target) // own children also
|| (target->getType() == EGUIET_MODAL_SCREEN )// other modals also fine
|| (target->getType() == EGUIET_MODAL_SCREEN ) // other modals also fine (is now on top or explicitely requested)
|| (target->getParent() && target->getParent()->getType() == EGUIET_MODAL_SCREEN ))) // children of other modals will do
;
}
@ -86,6 +86,13 @@ bool CGUIModalScreen::OnEvent(const SEvent& event)
switch(event.GUIEvent.EventType)
{
case EGET_ELEMENT_FOCUSED:
if ( event.GUIEvent.Caller == this && isMyChild(event.GUIEvent.Element) )
{
Environment->removeFocus(0); // can't setFocus otherwise at it still has focus here
Environment->setFocus(event.GUIEvent.Element);
MouseDownTime = os::Timer::getTime();
return true;
}
if ( !canTakeFocus(event.GUIEvent.Caller))
{
if ( !Children.empty() )

View File

@ -104,7 +104,7 @@ IFileArchive* CArchiveLoaderZIP::createArchive(io::IReadFile* file, bool ignoreC
file->read(&sig, 2);
#ifdef __BIG_ENDIAN__
os::Byteswap::byteswap(sig);
sig = os::Byteswap::byteswap(sig);
#endif
file->seek(0);
@ -126,7 +126,7 @@ bool CArchiveLoaderZIP::isALoadableFileFormat(io::IReadFile* file) const
file->read( &header.Sig, 4 );
#ifdef __BIG_ENDIAN__
os::Byteswap::byteswap(header.Sig);
header.Sig = os::Byteswap::byteswap(header.Sig);
#endif
return header.Sig == 0x04034b50 || // ZIP
@ -192,8 +192,8 @@ bool CZipReader::scanGZipHeader()
{
#ifdef __BIG_ENDIAN__
os::Byteswap::byteswap(header.sig);
os::Byteswap::byteswap(header.time);
header.sig = os::Byteswap::byteswap(header.sig);
header.time = os::Byteswap::byteswap(header.time);
#endif
// check header value
@ -209,7 +209,7 @@ bool CZipReader::scanGZipHeader()
File->read(&dataLen, 2);
#ifdef __BIG_ENDIAN__
os::Byteswap::byteswap(dataLen);
dataLen = os::Byteswap::byteswap(dataLen);
#endif
// skip it
@ -274,8 +274,8 @@ bool CZipReader::scanGZipHeader()
File->read(&entry.header.DataDescriptor.UncompressedSize, 4);
#ifdef __BIG_ENDIAN__
os::Byteswap::byteswap(entry.header.DataDescriptor.CRC32);
os::Byteswap::byteswap(entry.header.DataDescriptor.UncompressedSize);
entry.header.DataDescriptor.CRC32 = os::Byteswap::byteswap(entry.header.DataDescriptor.CRC32);
entry.header.DataDescriptor.UncompressedSize = os::Byteswap::byteswap(entry.header.DataDescriptor.UncompressedSize);
#endif
// now we've filled all the fields, this is just a standard deflate block

View File

@ -5,49 +5,43 @@
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
namespace
{
// Tests MD2 animations.
/** At the moment, this just verifies that the last frame of the animation produces the expected bitmap. */
bool md2Animation(void)
bool testLastFrame()
{
// Use EDT_BURNINGSVIDEO since it is not dependent on (e.g.) OpenGL driver versions.
IrrlichtDevice *device = createDevice( EDT_BURNINGSVIDEO, dimension2d<u32>(160, 120), 32);
assert(device);
IrrlichtDevice *device = createDevice(video::EDT_BURNINGSVIDEO, dimension2d<u32>(160, 120), 32);
if (!device)
return false;
IVideoDriver* driver = device->getVideoDriver();
ISceneManager * smgr = device->getSceneManager();
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager * smgr = device->getSceneManager();
IAnimatedMesh* mesh = smgr->getMesh("../media/sydney.md2");
IAnimatedMeshSceneNode* node;
assert(mesh);
scene::IAnimatedMesh* mesh = smgr->getMesh("../media/sydney.md2");
bool result = (mesh != 0);
if(mesh)
if (mesh)
{
node = smgr->addAnimatedMeshSceneNode(mesh);
assert(node);
scene::IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode(mesh);
if(node)
if (node)
{
node->setPosition(vector3df(20, 0, 30));
node->setMaterialFlag(EMF_LIGHTING, false);
node->setMaterialFlag(video::EMF_LIGHTING, false);
node->setMaterialTexture(0, driver->getTexture("../media/sydney.bmp"));
node->setLoopMode(false);
(void)smgr->addCameraSceneNode();
// Just jump to the last frame since that's all we're interested in.
node->setMD2Animation(EMAT_DEATH_FALLBACK);
node->setMD2Animation(scene::EMAT_DEATH_FALLBACK);
node->setCurrentFrame((f32)(node->getEndFrame()));
node->setAnimationSpeed(0);
device->run();
driver->beginScene(true, true, SColor(255, 255, 255, 0));
driver->beginScene(true, true, video::SColor(255, 255, 255, 0));
smgr->drawAll();
driver->endScene();
if (mesh->getBoundingBox() != mesh->getMesh(node->getEndFrame())->getBoundingBox())
@ -79,3 +73,56 @@ bool md2Animation(void)
return result;
}
// Tests MD2 normals.
bool testNormals()
{
// Use EDT_BURNINGSVIDEO since it is not dependent on (e.g.) OpenGL driver versions.
IrrlichtDevice *device = createDevice(video::EDT_BURNINGSVIDEO, dimension2d<u32>(160, 120), 32);
if (!device)
return false;
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager * smgr = device->getSceneManager();
scene::IAnimatedMesh* mesh = smgr->getMesh("../media/sydney.md2");
bool result = (mesh != 0);
if (mesh)
{
scene::IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode(mesh);
if (node)
{
node->setPosition(vector3df(20, 0, 30));
node->setMaterialFlag(video::EMF_LIGHTING, false);
node->setDebugDataVisible(scene::EDS_NORMALS);
node->setMaterialTexture(0, driver->getTexture("../media/sydney.bmp"));
node->setLoopMode(false);
(void)smgr->addCameraSceneNode();
node->setMD2Animation(scene::EMAT_STAND);
node->setAnimationSpeed(0);
device->run();
driver->beginScene(true, true, video::SColor(255, 255, 255, 0));
smgr->drawAll();
driver->endScene();
}
}
result &= takeScreenshotAndCompareAgainstReference(driver, "-md2Normals.png");
device->closeDevice();
device->run();
device->drop();
return result;
}
}
// test md2 features
bool md2Animation(void)
{
bool result = testLastFrame();
result &= testNormals();
return result;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -59,6 +59,164 @@ static bool testGetIntersectionWithLine(core::triangle3d<T>& triangle, const cor
return allExpected;
}
// modifying the same triangle in diverse ways get some more test-cases automatically
template<class T>
static bool stageModifications(int stage, triangle3d<T>& triangle)
{
switch ( stage )
{
case 0:
return true;
case 1:
swap(triangle.pointB, triangle.pointC);
return true;
case 2:
swap(triangle.pointA, triangle.pointC);
return true;
case 3:
triangle.pointA.Z += 1000;
triangle.pointB.Z += 1000;
triangle.pointC.Z += 1000;
return true;
case 4:
swap(triangle.pointA.Y, triangle.pointA.Z);
swap(triangle.pointB.Y, triangle.pointB.Z);
swap(triangle.pointC.Y, triangle.pointC.Z);
return true;
}
return false;
}
template<class T>
static void stageModifications(int stage, vector3d<T>& point)
{
switch ( stage )
{
case 3:
point.Z += 1000;
break;
case 4:
swap(point.Y, point.Z);
break;
}
}
template<class T>
static bool isPointInside(triangle3d<T> triangleOrig)
{
bool allExpected=true;
array< vector3d<T> > pointsInside;
pointsInside.push_back( vector3d<T>(0,0,0) );
pointsInside.push_back( (triangleOrig.pointA + triangleOrig.pointB + triangleOrig.pointC) / 3 );
pointsInside.push_back( (triangleOrig.pointA + triangleOrig.pointB)/2 + vector3d<T>(0,1,0) );
pointsInside.push_back( (triangleOrig.pointA + triangleOrig.pointC)/2 + vector3d<T>(1,0,0) );
pointsInside.push_back( (triangleOrig.pointB + triangleOrig.pointC)/2 - vector3d<T>(1,0,0) );
for (u32 stage=0; ; ++stage)
{
triangle3d<T> triangle = triangleOrig;
if ( !stageModifications(stage, triangle) )
break;
for ( u32 i=0; i < pointsInside.size(); ++i )
{
vector3d<T> point = pointsInside[i];
stageModifications(stage, point);
allExpected &= triangle.isPointInside( point );
if ( !allExpected )
{
logTestString("triangle3d::isPointInside pointsInside test failed in stage %d point %d\n", stage, i);
return false;
}
allExpected &= triangle.isPointInsideFast( point );
if ( !allExpected )
{
logTestString("triangle3d::isPointInsideFast pointsInside test failed in stage %d point %d\n", stage, i);
return false;
}
}
}
array< vector3d<T> > pointsOutside;
pointsOutside.push_back( triangleOrig.pointA - vector3d<T>(1,0,0) );
pointsOutside.push_back( triangleOrig.pointA - vector3d<T>(0,1,0) );
pointsOutside.push_back( triangleOrig.pointB + vector3d<T>(1,0,0) );
pointsOutside.push_back( triangleOrig.pointB - vector3d<T>(0,1,0) );
pointsOutside.push_back( triangleOrig.pointC - vector3d<T>(1,0,0) );
pointsOutside.push_back( triangleOrig.pointC + vector3d<T>(1,0,0) );
pointsOutside.push_back( triangleOrig.pointC + vector3d<T>(0,1,0) );
pointsOutside.push_back( (triangleOrig.pointA + triangleOrig.pointB)/2 - vector3d<T>(0,1,0) );
pointsOutside.push_back( (triangleOrig.pointA + triangleOrig.pointC)/2 - vector3d<T>(1,0,0) );
pointsOutside.push_back( (triangleOrig.pointB + triangleOrig.pointC)/2 + vector3d<T>(1,0,0) );
for (u32 stage=0; ; ++stage)
{
triangle3d<T> triangle = triangleOrig;
if ( !stageModifications(stage, triangle) )
break;
for ( u32 i=0; i < pointsOutside.size(); ++i )
{
vector3d<T> point = pointsOutside[i];
stageModifications(stage, point);
allExpected &= !triangle.isPointInside( point );
if ( !allExpected )
{
logTestString("triangle3d::isPointInside pointsOutside test failed in stage %d point %d\n", stage, i);
return false;
}
allExpected &= !triangle.isPointInsideFast( point );
if ( !allExpected )
{
logTestString("triangle3d::isPointInsideFast pointsOutside test failed in stage %d point %d\n", stage, i);
return false;
}
}
}
array< vector3d<T> > pointsBorder;
pointsBorder.push_back( triangleOrig.pointA );
pointsBorder.push_back( triangleOrig.pointB );
pointsBorder.push_back( triangleOrig.pointC );
pointsBorder.push_back( (triangleOrig.pointA + triangleOrig.pointB)/2 );
pointsBorder.push_back( (triangleOrig.pointA + triangleOrig.pointC)/2 );
pointsBorder.push_back( (triangleOrig.pointB + triangleOrig.pointC)/2 );
for (u32 stage=0; ; ++stage)
{
triangle3d<T> triangle = triangleOrig;
if ( !stageModifications(stage, triangle) )
break;
for ( u32 i=0; i < pointsBorder.size(); ++i )
{
vector3d<T> point = pointsBorder[i];
stageModifications(stage, point);
allExpected &= triangle.isPointInside( point );
if ( !allExpected )
{
logTestString("triangle3d::isPointInside pointsBorder test failed in stage %d point %d\n", stage, i);
return false;
}
/* results for isPointInsideFast are mixed for border cases, but I guess that's fine.
if ( triangle.isPointInsideFast( point ) )
logTestString("+ triangle3d::isPointInsideFast pointsBorder stage %d point %d is INSIDE\n", stage, i);
else
logTestString("- triangle3d::isPointInsideFast pointsBorder stage %d point %d is NOT inside\n", stage, i);
*/
}
}
return allExpected;
}
// Test the functionality of triangle3d<T>
/** Validation is done with asserts() against expected results. */
@ -89,6 +247,29 @@ bool testTriangle3d(void)
allExpected &= testGetIntersectionWithLine(triangle, ray);
}
bool testEigen = triangle3di(vector3di(250, 0, 0), vector3di(0, 0, 500), vector3di(500, 0, 500)).isPointInside(vector3di(300,0,300));
if ( !testEigen ) // test from Eigen from here: http://irrlicht.sourceforge.net/forum/viewtopic.php?f=7&t=44372&p=254331#p254331
logTestString("Test isPointInside fails with integers\n");
allExpected &= testEigen;
logTestString("Test isPointInside with f32\n");
{
triangle3d<f32> t(vector3d<f32>(-1000,-1000,0), vector3d<f32>(1000,-1000,0), vector3d<f32>(0,1000,0));
allExpected &= isPointInside(t);
}
logTestString("Test isPointInside with f64\n");
{
triangle3d<f64> t(vector3d<f64>(-1000,-1000,0), vector3d<f64>(1000,-1000,0), vector3d<f64>(0,1000,0));
allExpected &= isPointInside(t);
}
logTestString("Test isPointInside with s32\n");
{
triangle3d<s32> t(vector3d<s32>(-1000,-1000,0), vector3d<s32>(1000,-1000,0), vector3d<s32>(0,1000,0));
allExpected &= isPointInside(t);
}
if(allExpected)
logTestString("\nAll tests passed\n");
else