1 // Copyright (C) 2008-2012 Colin MacDonald
\r
2 // No rights reserved: this software is in the public domain.
\r
4 #include "testUtils.h"
\r
7 using namespace core;
\r
8 using namespace scene;
\r
9 using namespace video;
\r
11 static bool testGetCollisionResultPosition(IrrlichtDevice * device,
\r
12 ISceneManager * smgr,
\r
13 ISceneCollisionManager * collMgr)
\r
15 IMeshSceneNode * cubeNode = smgr->addCubeSceneNode(10.f);
\r
16 ITriangleSelector * cubeSelector = smgr->createTriangleSelectorFromBoundingBox(cubeNode);
\r
19 vector3df hitPosition;
\r
21 ISceneNode* hitNode;
\r
23 vector3df resultPosition =
\r
24 collMgr->getCollisionResultPosition(cubeSelector,
\r
25 vector3df(0, 50, 0),
\r
26 vector3df(10, 20, 10),
\r
27 vector3df(0, -100, 0),
\r
35 if(hitNode != cubeNode)
\r
37 logTestString("Unexpected collision node\n");
\r
41 if(!equals(resultPosition.Y, 25.f, 0.01f))
\r
43 logTestString("Unexpected collision response position\n");
\r
47 if(!equals(hitPosition.Y, 5.f, 0.01f))
\r
49 logTestString("Unexpected collision position\n");
\r
54 collMgr->getCollisionResultPosition(cubeSelector,
\r
55 vector3df(-20, 0, 0),
\r
56 vector3df(10, 20, 10),
\r
57 vector3df(100, 0, 0),
\r
63 if(hitNode != cubeNode)
\r
65 logTestString("Unexpected collision node\n");
\r
69 if(!equals(resultPosition.X, -15.f, 0.01f))
\r
71 logTestString("Unexpected collision response position\n");
\r
75 if(!equals(hitPosition.X, -5.f, 0.01f))
\r
77 logTestString("Unexpected collision position\n");
\r
83 cubeSelector->drop();
\r
90 // Test that getCollisionPoint() actually uses the closest point, not the closest triangle.
\r
91 static bool getCollisionPoint_ignoreTriangleVertices(IrrlichtDevice * device,
\r
92 ISceneManager * smgr,
\r
93 ISceneCollisionManager * collMgr)
\r
95 // Create a cube with a Z face at 5, but corners close to 0
\r
96 ISceneNode * farSmallCube = smgr->addCubeSceneNode(10, 0, -1, vector3df(0, 0, 10));
\r
98 // Create a cube with a Z face at 0, but corners far from 0
\r
99 ISceneNode * nearBigCube = smgr->addCubeSceneNode(100, 0, -1, vector3df(0, 0, 50));
\r
101 IMetaTriangleSelector * meta = smgr->createMetaTriangleSelector();
\r
103 ITriangleSelector * selector = smgr->createTriangleSelectorFromBoundingBox(farSmallCube);
\r
104 meta->addTriangleSelector(selector);
\r
107 // We should expect a hit on this cube
\r
108 selector = smgr->createTriangleSelectorFromBoundingBox(nearBigCube);
\r
109 meta->addTriangleSelector(selector);
\r
112 line3df ray(0, 0, -5, 0, 0, 100);
\r
113 vector3df hitPosition;
\r
114 triangle3df hitTriangle;
\r
115 ISceneNode* hitNode;
\r
117 bool collision = collMgr->getCollisionPoint(ray, meta, hitPosition, hitTriangle, hitNode);
\r
121 if(hitNode != nearBigCube)
\r
123 logTestString("getCollisionPoint_ignoreTriangleVertices: hit the wrong node.\n");
\r
129 logTestString("getCollisionPoint_ignoreTriangleVertices: didn't get a hit.\n");
\r
133 if(hitPosition != vector3df(0, 0, 0))
\r
135 logTestString("getCollisionPoint_ignoreTriangleVertices: unexpected hit position %f %f %f.\n",
\r
136 hitPosition.X, hitPosition.Y, hitPosition.Z );
\r
146 static bool testGetSceneNodeFromScreenCoordinatesBB(IrrlichtDevice * device,
\r
147 ISceneManager * smgr,
\r
148 ISceneCollisionManager * collMgr)
\r
150 // Create 3 nodes. The nearest node actually contains the camera.
\r
151 IMeshSceneNode * cubeNode1 = smgr->addCubeSceneNode(10.f, 0, -1, vector3df(0, 0, 4));
\r
152 IMeshSceneNode * cubeNode2 = smgr->addCubeSceneNode(10.f, 0, -1, vector3df(0, 0, 30));
\r
153 cubeNode2->setRotation(vector3df(90.f, 90.f, 90.f)); // Just check that rotation doesn't stop us hitting it.
\r
154 IMeshSceneNode * cubeNode3 = smgr->addCubeSceneNode(10.f, 0, -1, vector3df(0, 0, 40));
\r
155 cubeNode3->setRotation(vector3df(180.f, 180.f, 180.f)); // Just check that rotation doesn't stop us hitting it.
\r
157 smgr->addCameraSceneNode();
\r
159 smgr->drawAll(); // Get the camera in a good state
\r
161 ISceneNode * hitNode = collMgr->getSceneNodeFromScreenCoordinatesBB(position2d<s32>(80, 60));
\r
163 // Expect the first node to be hit, since we're starting the check from inside it.
\r
164 bool result = true;
\r
165 if(hitNode != cubeNode1)
\r
167 logTestString("Unexpected node hit. Expected cubeNode1.\n");
\r
171 // Now make cubeNode1 invisible and check that cubeNode2 is hit.
\r
172 cubeNode1->setVisible(false);
\r
173 hitNode = collMgr->getSceneNodeFromScreenCoordinatesBB(position2d<s32>(80, 60));
\r
174 if(hitNode != cubeNode2)
\r
176 logTestString("Unexpected node hit. Expected cubeNode2.\n");
\r
180 // Make cubeNode1 the parent of cubeNode2.
\r
181 cubeNode2->setParent(cubeNode1);
\r
183 // Check visibility.
\r
184 bool visible = cubeNode2->isVisible();
\r
187 logTestString("cubeNode2 should think that it (in isolation) is visible.\n");
\r
191 visible = cubeNode2->isTrulyVisible();
\r
194 logTestString("cubeNode2 should know that it (recursively) is invisible.\n");
\r
198 // cubeNode2 should now be an invalid target as well, and so the final cube node should be hit.
\r
199 hitNode = collMgr->getSceneNodeFromScreenCoordinatesBB(position2d<s32>(80, 60));
\r
200 if(hitNode != cubeNode3)
\r
202 logTestString("Unexpected node hit. Expected cubeNode3.\n");
\r
207 // Now verify bitmasking
\r
209 // Test the 01010101010101010101010101010101 bitmask (0x55555555)
\r
210 cubeNode1->setVisible(true);
\r
211 cubeNode1->setID(0xAAAAAAAA);
\r
212 hitNode = collMgr->getSceneNodeFromScreenCoordinatesBB(position2d<s32>(80, 60), 0x55555555);
\r
213 if(hitNode != cubeNode2)
\r
215 logTestString("Unexpected node hit. Expected cubeNode2.\n");
\r
218 assert_log(result);
\r
226 static bool getScaledPickedNodeBB(IrrlichtDevice * device,
\r
227 ISceneManager * smgr,
\r
228 ISceneCollisionManager * collMgr)
\r
230 ISceneNode* farTarget = smgr->addCubeSceneNode(1.f);
\r
231 farTarget->setScale(vector3df(100.f, 100.f, 10.f));
\r
232 farTarget->setPosition(vector3df(0.f, 0.f, 500.f));
\r
233 farTarget->updateAbsolutePosition();
\r
235 // Create a node that's slightly further away than the closest node,
\r
236 // but thinner. Its furthest corner is closer, but the collision
\r
237 // position is further, so it should not be selected.
\r
238 ISceneNode* middleTarget = smgr->addCubeSceneNode(10.f);
\r
239 middleTarget->setPosition(vector3df(0.f, 0.f, 101.f));
\r
240 middleTarget->setScale(vector3df(1.f, 1.f, 0.5f));
\r
241 middleTarget->updateAbsolutePosition();
\r
243 ISceneNode* nearTarget = smgr->addCubeSceneNode(10.f);
\r
244 nearTarget->setPosition(vector3df(0.f, 0.f, 100.f));
\r
245 nearTarget->updateAbsolutePosition();
\r
246 // We'll rotate this node 90 degrees to show that we can hit its side.
\r
247 nearTarget->setRotation(vector3df(0.f, 90.f, 0.f));
\r
249 line3df ray(0.f, 0.f, 0.f, 0.f, 0.f, 500.f);
\r
251 const ISceneNode * const hit = collMgr->getSceneNodeFromRayBB(ray);
\r
253 bool result = (hit == nearTarget);
\r
256 logTestString("getSceneNodeFromRayBB() didn't hit anything.\n");
\r
257 else if(hit == farTarget)
\r
258 logTestString("getSceneNodeFromRayBB() hit the far (scaled) target.\n");
\r
259 else if(hit == middleTarget)
\r
260 logTestString("getSceneNodeFromRayBB() hit the middle (scaled) target.\n");
\r
262 assert_log(result);
\r
270 // box intersection according to Kay et al., code from gamedev.net
\r
271 static bool IntersectBox(const core::vector3df& origin, const core::vector3df& dir, const core::aabbox3df& box)
\r
273 core::vector3df minDist = (box.MinEdge - origin)/dir;
\r
274 core::vector3df maxDist = (box.MaxEdge - origin)/dir;
\r
276 core::vector3df realMin(core::min_(minDist.X, maxDist.X),core::min_(minDist.Y, maxDist.Y),core::min_(minDist.Z, maxDist.Z));
\r
277 core::vector3df realMax(core::max_(minDist.X, maxDist.X),core::max_(minDist.Y, maxDist.Y),core::max_(minDist.Z, maxDist.Z));
\r
279 f32 minmax = core::min_(realMax.X, realMax.Y, realMax.Z);
\r
280 // nearest distance to intersection
\r
281 f32 maxmin = core::max_(realMin.X, realMin.Y, realMin.Z);
\r
283 return (maxmin >=0 && minmax >= maxmin);
\r
286 static bool checkBBoxIntersection(IrrlichtDevice * device,
\r
287 ISceneManager * smgr)
\r
289 video::IVideoDriver* driver = device->getVideoDriver();
\r
292 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode();
\r
293 camera->setPosition(core::vector3df(30, 30, 30));
\r
294 camera->setTarget(core::vector3df(8.f, 8.f, 8.f));
\r
297 // add a cube to pick
\r
298 scene::ISceneNode* cube = smgr->addCubeSceneNode(30, 0, -1, core::vector3df(0,0,0),core::vector3df(30,40,50));
\r
301 for (u32 round=0; round<2; ++round)
\r
303 driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(100, 50, 50, 100));
\r
305 driver->endScene();
\r
307 core::matrix4 invMat = cube->getAbsoluteTransformation();
\r
308 invMat.makeInverse();
\r
311 u32 start = device->getTimer()->getRealTime();
\r
312 for (u32 i=10; i<150; ++i)
\r
314 for (u32 j=10; j<110; ++j)
\r
316 const core::position2di pos(i, j);
\r
318 // get the line used for picking
\r
319 core::line3df ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(pos, camera);
\r
321 invMat.transformVect(ray.start);
\r
322 invMat.transformVect(ray.end);
\r
324 hits += (cube->getBoundingBox().intersectsWithLine(ray)?1:0);
\r
327 u32 duration = device->getTimer()->getRealTime()-start;
\r
328 logTestString("bbox intersection checks %d hits (of 14000).\n", hits);
\r
331 start = device->getTimer()->getRealTime();
\r
332 for (u32 i=10; i<150; ++i)
\r
334 for (u32 j=10; j<110; ++j)
\r
336 const core::position2di pos(i, j);
\r
338 // get the line used for picking
\r
339 core::line3df ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(pos, camera);
\r
341 invMat.transformVect(ray.start);
\r
342 invMat.transformVect(ray.end);
\r
344 hits += (IntersectBox(ray.start, (ray.end-ray.start).normalize(), cube->getBoundingBox())?1:0);
\r
347 u32 duration2 = device->getTimer()->getRealTime()-start;
\r
348 logTestString("bbox intersection resulted in %d misses at a speed of %d (old) compared to %d (new).\n", abs(hits), duration, duration2);
\r
349 if (duration>(duration2*1.2f))
\r
350 logTestString("Consider replacement of bbox intersection test.\n");
\r
352 result &= (hits==0);
\r
353 assert_log(result);
\r
354 // second round without any hits, so check opposite direction
\r
355 camera->setTarget(core::vector3df(80.f, 80.f, 80.f));
\r
358 ISceneNode* node = smgr->addSphereSceneNode(5.f, 16, 0, -1, core::vector3df(0, 0, 1), core::vector3df(), core::vector3df(0.3f, 0.3f, 0.3f));
\r
360 cube = smgr->addCubeSceneNode(10.f, 0, -1, core::vector3df(0, 6.5f, 1), core::vector3df(), core::vector3df(10, 0.1f, 1.f));
\r
361 camera->setPosition(core::vector3df(0, 0, 10));
\r
362 camera->setTarget(core::vector3df());
\r
365 for (u32 i=0; i<30; ++i)
\r
367 driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(100, 50, 50, 100));
\r
369 driver->endScene();
\r
371 count += node->getTransformedBoundingBox().intersectsWithBox(cube->getTransformedBoundingBox())?1:0;
\r
372 node->setPosition(node->getPosition()+core::vector3df(.5f,.5f,0));
\r
373 if (i==8 && count != 0)
\r
375 if (i==17 && count != 9)
\r
387 static bool compareGetSceneNodeFromRayBBWithBBIntersectsWithLine(IrrlichtDevice * device,
\r
388 ISceneManager * smgr,
\r
389 ISceneCollisionManager * collMgr)
\r
391 video::IVideoDriver* driver = device->getVideoDriver();
\r
394 scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();
\r
395 camera->setPosition(core::vector3df(30, 30, 30));
\r
396 camera->setTarget(core::vector3df(-8.f, 8.f, -8.f));
\r
399 // add a dynamic light (this causes weirdness)
\r
400 smgr->addLightSceneNode(0, core::vector3df(4, 4, 4), video::SColorf(.2f, .3f, .2f));
\r
402 // add a cube to pick
\r
403 scene::ISceneNode* cube = smgr->addCubeSceneNode(15);
\r
405 driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(100, 50, 50, 100));
\r
407 driver->endScene();
\r
409 core::matrix4 invMat = cube->getAbsoluteTransformation();
\r
410 invMat.makeInverse();
\r
412 bool result = true;
\r
413 for (u32 i=76; i<82; ++i)
\r
415 for (u32 j=56; j<64; ++j)
\r
417 const core::position2di pos(i, j);
\r
419 // get the line used for picking
\r
420 core::line3df ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(pos, camera);
\r
422 // find a selected node
\r
423 scene::ISceneNode* pick = smgr->getSceneCollisionManager()->getSceneNodeFromRayBB(ray, 1);
\r
425 invMat.transformVect(ray.start);
\r
426 invMat.transformVect(ray.end);
\r
428 const int a_hit = (pick == cube);
\r
429 const int b_hit = cube->getBoundingBox().intersectsWithLine(ray);
\r
430 result = (a_hit==b_hit);
\r
434 assert_log(result);
\r
442 /** Test functionality of the sceneCollisionManager */
\r
443 bool sceneCollisionManager(void)
\r
445 IrrlichtDevice * device = irr::createDevice(video::EDT_NULL, dimension2d<u32>(160, 120));
\r
446 assert_log(device);
\r
450 ISceneManager * smgr = device->getSceneManager();
\r
451 ISceneCollisionManager * collMgr = smgr->getSceneCollisionManager();
\r
453 bool result = testGetCollisionResultPosition(device, smgr, collMgr);
\r
457 result &= testGetSceneNodeFromScreenCoordinatesBB(device, smgr, collMgr);
\r
459 result &= getScaledPickedNodeBB(device, smgr, collMgr);
\r
461 result &= getCollisionPoint_ignoreTriangleVertices(device, smgr, collMgr);
\r
463 result &= checkBBoxIntersection(device, smgr);
\r
465 result &= compareGetSceneNodeFromRayBBWithBBIntersectsWithLine(device, smgr, collMgr);
\r
467 device->closeDevice();
\r