]> git.lizzy.rs Git - irrlicht.git/blob - tests/sceneCollisionManager.cpp
Fix bug introduced in last merge from svn trunk
[irrlicht.git] / tests / sceneCollisionManager.cpp
1 // Copyright (C) 2008-2012 Colin MacDonald\r
2 // No rights reserved: this software is in the public domain.\r
3 \r
4 #include "testUtils.h"\r
5 \r
6 using namespace irr;\r
7 using namespace core;\r
8 using namespace scene;\r
9 using namespace video;\r
10 \r
11 static bool testGetCollisionResultPosition(IrrlichtDevice * device,\r
12                                            ISceneManager * smgr,\r
13                                            ISceneCollisionManager * collMgr)\r
14 {\r
15         IMeshSceneNode * cubeNode = smgr->addCubeSceneNode(10.f);\r
16         ITriangleSelector * cubeSelector = smgr->createTriangleSelectorFromBoundingBox(cubeNode);\r
17 \r
18         triangle3df triOut;\r
19         vector3df hitPosition;\r
20         bool falling;\r
21         ISceneNode* hitNode;\r
22 \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
28                                                 triOut,\r
29                                                 hitPosition,\r
30                                                 falling,\r
31                                                 hitNode);\r
32 \r
33         bool result = true;\r
34 \r
35         if(hitNode != cubeNode)\r
36         {\r
37                 logTestString("Unexpected collision node\n");\r
38                 result = false;\r
39         }\r
40 \r
41         if(!equals(resultPosition.Y, 25.f, 0.01f))\r
42         {\r
43                 logTestString("Unexpected collision response position\n");\r
44                 result = false;\r
45         }\r
46 \r
47         if(!equals(hitPosition.Y, 5.f, 0.01f))\r
48         {\r
49                 logTestString("Unexpected collision position\n");\r
50                 result = false;\r
51         }\r
52 \r
53         resultPosition =\r
54                 collMgr->getCollisionResultPosition(cubeSelector,\r
55                                                 vector3df(-20, 0, 0),\r
56                                                 vector3df(10, 20, 10),\r
57                                                 vector3df(100, 0, 0),\r
58                                                 triOut,\r
59                                                 hitPosition,\r
60                                                 falling,\r
61                                                 hitNode);\r
62 \r
63         if(hitNode != cubeNode)\r
64         {\r
65                 logTestString("Unexpected collision node\n");\r
66                 result = false;\r
67         }\r
68 \r
69         if(!equals(resultPosition.X, -15.f, 0.01f))\r
70         {\r
71                 logTestString("Unexpected collision response position\n");\r
72                 result = false;\r
73         }\r
74 \r
75         if(!equals(hitPosition.X, -5.f, 0.01f))\r
76         {\r
77                 logTestString("Unexpected collision position\n");\r
78                 result = false;\r
79         }\r
80 \r
81         assert_log(result);\r
82 \r
83         cubeSelector->drop();\r
84         smgr->clear();\r
85 \r
86         return result;\r
87 }\r
88 \r
89 \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
94 {\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
97 \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
100 \r
101         IMetaTriangleSelector * meta = smgr->createMetaTriangleSelector();\r
102 \r
103         ITriangleSelector * selector = smgr->createTriangleSelectorFromBoundingBox(farSmallCube);\r
104         meta->addTriangleSelector(selector);\r
105         selector->drop();\r
106 \r
107         // We should expect a hit on this cube\r
108         selector = smgr->createTriangleSelectorFromBoundingBox(nearBigCube);\r
109         meta->addTriangleSelector(selector);\r
110         selector->drop();\r
111 \r
112         line3df ray(0, 0, -5, 0, 0, 100);\r
113         vector3df hitPosition;\r
114         triangle3df hitTriangle;\r
115         ISceneNode* hitNode;\r
116 \r
117         bool collision = collMgr->getCollisionPoint(ray, meta, hitPosition, hitTriangle, hitNode);\r
118 \r
119         meta->drop();\r
120 \r
121         if(hitNode != nearBigCube)\r
122         {\r
123                 logTestString("getCollisionPoint_ignoreTriangleVertices: hit the wrong node.\n");\r
124                 return false;\r
125         }\r
126 \r
127         if(!collision)\r
128         {\r
129                 logTestString("getCollisionPoint_ignoreTriangleVertices: didn't get a hit.\n");\r
130                 return false;\r
131         }\r
132 \r
133         if(hitPosition != vector3df(0, 0, 0))\r
134         {\r
135                 logTestString("getCollisionPoint_ignoreTriangleVertices: unexpected hit position %f %f %f.\n",\r
136                         hitPosition.X, hitPosition.Y, hitPosition.Z );\r
137                 return false;\r
138         }\r
139 \r
140         smgr->clear();\r
141 \r
142         return true;\r
143 }\r
144 \r
145 \r
146 static bool testGetSceneNodeFromScreenCoordinatesBB(IrrlichtDevice * device,\r
147                                                 ISceneManager * smgr,\r
148                                                 ISceneCollisionManager * collMgr)\r
149 {\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
156 \r
157         smgr->addCameraSceneNode();\r
158         device->run();\r
159         smgr->drawAll(); // Get the camera in a good state\r
160 \r
161         ISceneNode * hitNode = collMgr->getSceneNodeFromScreenCoordinatesBB(position2d<s32>(80, 60));\r
162 \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
166         {\r
167                 logTestString("Unexpected node hit. Expected cubeNode1.\n");\r
168                 result = false;\r
169         }\r
170 \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
175         {\r
176                 logTestString("Unexpected node hit. Expected cubeNode2.\n");\r
177                 result = false;\r
178         }\r
179 \r
180         // Make cubeNode1 the parent of cubeNode2.\r
181         cubeNode2->setParent(cubeNode1);\r
182 \r
183         // Check visibility.\r
184         bool visible = cubeNode2->isVisible();\r
185         if(!visible)\r
186         {\r
187                 logTestString("cubeNode2 should think that it (in isolation) is visible.\n");\r
188                 result = false;\r
189         }\r
190 \r
191         visible = cubeNode2->isTrulyVisible();\r
192         if(visible)\r
193         {\r
194                 logTestString("cubeNode2 should know that it (recursively) is invisible.\n");\r
195                 result = false;\r
196         }\r
197 \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
201         {\r
202                 logTestString("Unexpected node hit. Expected cubeNode3.\n");\r
203                 result = false;\r
204         }\r
205 \r
206 \r
207         // Now verify bitmasking\r
208 \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
214         {\r
215                 logTestString("Unexpected node hit. Expected cubeNode2.\n");\r
216                 result = false;\r
217         }\r
218         assert_log(result);\r
219 \r
220         smgr->clear();\r
221 \r
222         return result;\r
223 }\r
224 \r
225 \r
226 static bool getScaledPickedNodeBB(IrrlichtDevice * device,\r
227                                 ISceneManager * smgr,\r
228                                 ISceneCollisionManager * collMgr)\r
229 {\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
234 \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
242 \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
248 \r
249         line3df ray(0.f, 0.f, 0.f, 0.f, 0.f, 500.f);\r
250 \r
251         const ISceneNode * const hit = collMgr->getSceneNodeFromRayBB(ray);\r
252 \r
253         bool result = (hit == nearTarget);\r
254 \r
255         if(hit == 0)\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
261 \r
262         assert_log(result);\r
263 \r
264         smgr->clear();\r
265 \r
266         return result;\r
267 }\r
268 \r
269 \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
272 {\r
273         core::vector3df minDist = (box.MinEdge - origin)/dir;\r
274         core::vector3df maxDist = (box.MaxEdge - origin)/dir;\r
275 \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
278 \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
282 \r
283         return (maxmin >=0 && minmax >= maxmin);\r
284 }\r
285 \r
286 static bool checkBBoxIntersection(IrrlichtDevice * device,\r
287                                 ISceneManager * smgr)\r
288 {\r
289         video::IVideoDriver* driver = device->getVideoDriver();\r
290 \r
291         // add camera\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
295         camera->setID(0);\r
296 \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
299 \r
300         bool result=true;\r
301         for (u32 round=0; round<2; ++round)\r
302         {\r
303                 driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(100, 50, 50, 100));\r
304                 smgr->drawAll();\r
305                 driver->endScene();\r
306 \r
307                 core::matrix4 invMat = cube->getAbsoluteTransformation();\r
308                 invMat.makeInverse();\r
309 \r
310                 s32 hits=0;\r
311                 u32 start = device->getTimer()->getRealTime();\r
312                 for (u32 i=10; i<150; ++i)\r
313                 {\r
314                         for (u32 j=10; j<110; ++j)\r
315                         {\r
316                                 const core::position2di pos(i, j);\r
317 \r
318                                 // get the line used for picking\r
319                                 core::line3df ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(pos, camera);\r
320 \r
321                                 invMat.transformVect(ray.start);\r
322                                 invMat.transformVect(ray.end);\r
323 \r
324                                 hits += (cube->getBoundingBox().intersectsWithLine(ray)?1:0);\r
325                         }\r
326                 }\r
327                 u32 duration = device->getTimer()->getRealTime()-start;\r
328                 logTestString("bbox intersection checks %d hits (of 14000).\n", hits);\r
329                 hits = -hits;\r
330 \r
331                 start = device->getTimer()->getRealTime();\r
332                 for (u32 i=10; i<150; ++i)\r
333                 {\r
334                         for (u32 j=10; j<110; ++j)\r
335                         {\r
336                                 const core::position2di pos(i, j);\r
337 \r
338                                 // get the line used for picking\r
339                                 core::line3df ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(pos, camera);\r
340 \r
341                                 invMat.transformVect(ray.start);\r
342                                 invMat.transformVect(ray.end);\r
343 \r
344                                 hits += (IntersectBox(ray.start, (ray.end-ray.start).normalize(), cube->getBoundingBox())?1:0);\r
345                         }\r
346                 }\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
351 \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
356         }\r
357 \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
359         cube->remove();\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
363 \r
364         u32 count=0;\r
365         for (u32 i=0; i<30; ++i)\r
366         {\r
367                 driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(100, 50, 50, 100));\r
368                 smgr->drawAll();\r
369                 driver->endScene();\r
370 \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
374                         result=false;\r
375                 if (i==17 && count != 9)\r
376                         result=false;\r
377         }\r
378         if (count != 9)\r
379                 result=false;\r
380 \r
381         smgr->clear();\r
382 \r
383         return result;\r
384 }\r
385 \r
386 \r
387 static bool compareGetSceneNodeFromRayBBWithBBIntersectsWithLine(IrrlichtDevice * device,\r
388                                 ISceneManager * smgr,\r
389                                 ISceneCollisionManager * collMgr)\r
390 {\r
391         video::IVideoDriver* driver = device->getVideoDriver();\r
392 \r
393         // add camera\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
397         camera->setID(0);\r
398 \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
401 \r
402         // add a cube to pick\r
403         scene::ISceneNode* cube = smgr->addCubeSceneNode(15);\r
404 \r
405         driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(100, 50, 50, 100));\r
406         smgr->drawAll();\r
407         driver->endScene();\r
408 \r
409         core::matrix4 invMat = cube->getAbsoluteTransformation();\r
410         invMat.makeInverse();\r
411 \r
412         bool result = true;\r
413         for (u32 i=76; i<82; ++i)\r
414         {\r
415                 for (u32 j=56; j<64; ++j)\r
416                 {\r
417                         const core::position2di pos(i, j);\r
418 \r
419                         // get the line used for picking\r
420                         core::line3df ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(pos, camera);\r
421 \r
422                         // find a selected node\r
423                         scene::ISceneNode* pick = smgr->getSceneCollisionManager()->getSceneNodeFromRayBB(ray, 1);\r
424 \r
425                         invMat.transformVect(ray.start);\r
426                         invMat.transformVect(ray.end);\r
427 \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
431                 }\r
432         }\r
433 \r
434         assert_log(result);\r
435 \r
436         smgr->clear();\r
437 \r
438         return result;\r
439 }\r
440 \r
441 \r
442 /** Test functionality of the sceneCollisionManager */\r
443 bool sceneCollisionManager(void)\r
444 {\r
445         IrrlichtDevice * device = irr::createDevice(video::EDT_NULL, dimension2d<u32>(160, 120));\r
446         assert_log(device);\r
447         if(!device)\r
448                 return false;\r
449 \r
450         ISceneManager * smgr = device->getSceneManager();\r
451         ISceneCollisionManager * collMgr = smgr->getSceneCollisionManager();\r
452 \r
453         bool result = testGetCollisionResultPosition(device, smgr, collMgr);\r
454 \r
455         smgr->clear();\r
456 \r
457         result &= testGetSceneNodeFromScreenCoordinatesBB(device, smgr, collMgr);\r
458 \r
459         result &= getScaledPickedNodeBB(device, smgr, collMgr);\r
460 \r
461         result &= getCollisionPoint_ignoreTriangleVertices(device, smgr, collMgr);\r
462 \r
463         result &= checkBBoxIntersection(device, smgr);\r
464 \r
465         result &= compareGetSceneNodeFromRayBBWithBBIntersectsWithLine(device, smgr, collMgr);\r
466 \r
467         device->closeDevice();\r
468         device->run();\r
469         device->drop();\r
470         return result;\r
471 }\r