sceneCollisionManager.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. // Copyright (C) 2008-2012 Colin MacDonald
  2. // No rights reserved: this software is in the public domain.
  3. #include "testUtils.h"
  4. using namespace irr;
  5. using namespace core;
  6. using namespace scene;
  7. using namespace video;
  8. static bool testGetCollisionResultPosition(IrrlichtDevice * device,
  9. ISceneManager * smgr,
  10. ISceneCollisionManager * collMgr)
  11. {
  12. IMeshSceneNode * cubeNode = smgr->addCubeSceneNode(10.f);
  13. ITriangleSelector * cubeSelector = smgr->createTriangleSelectorFromBoundingBox(cubeNode);
  14. triangle3df triOut;
  15. vector3df hitPosition;
  16. bool falling;
  17. ISceneNode* hitNode;
  18. vector3df resultPosition =
  19. collMgr->getCollisionResultPosition(cubeSelector,
  20. vector3df(0, 50, 0),
  21. vector3df(10, 20, 10),
  22. vector3df(0, -100, 0),
  23. triOut,
  24. hitPosition,
  25. falling,
  26. hitNode);
  27. bool result = true;
  28. if(hitNode != cubeNode)
  29. {
  30. logTestString("Unexpected collision node\n");
  31. result = false;
  32. }
  33. if(!equals(resultPosition.Y, 25.f, 0.01f))
  34. {
  35. logTestString("Unexpected collision response position\n");
  36. result = false;
  37. }
  38. if(!equals(hitPosition.Y, 5.f, 0.01f))
  39. {
  40. logTestString("Unexpected collision position\n");
  41. result = false;
  42. }
  43. resultPosition =
  44. collMgr->getCollisionResultPosition(cubeSelector,
  45. vector3df(-20, 0, 0),
  46. vector3df(10, 20, 10),
  47. vector3df(100, 0, 0),
  48. triOut,
  49. hitPosition,
  50. falling,
  51. hitNode);
  52. if(hitNode != cubeNode)
  53. {
  54. logTestString("Unexpected collision node\n");
  55. result = false;
  56. }
  57. if(!equals(resultPosition.X, -15.f, 0.01f))
  58. {
  59. logTestString("Unexpected collision response position\n");
  60. result = false;
  61. }
  62. if(!equals(hitPosition.X, -5.f, 0.01f))
  63. {
  64. logTestString("Unexpected collision position\n");
  65. result = false;
  66. }
  67. assert_log(result);
  68. cubeSelector->drop();
  69. smgr->clear();
  70. return result;
  71. }
  72. // Test that getCollisionPoint() actually uses the closest point, not the closest triangle.
  73. static bool getCollisionPoint_ignoreTriangleVertices(IrrlichtDevice * device,
  74. ISceneManager * smgr,
  75. ISceneCollisionManager * collMgr)
  76. {
  77. // Create a cube with a Z face at 5, but corners close to 0
  78. ISceneNode * farSmallCube = smgr->addCubeSceneNode(10, 0, -1, vector3df(0, 0, 10));
  79. // Create a cube with a Z face at 0, but corners far from 0
  80. ISceneNode * nearBigCube = smgr->addCubeSceneNode(100, 0, -1, vector3df(0, 0, 50));
  81. IMetaTriangleSelector * meta = smgr->createMetaTriangleSelector();
  82. ITriangleSelector * selector = smgr->createTriangleSelectorFromBoundingBox(farSmallCube);
  83. meta->addTriangleSelector(selector);
  84. selector->drop();
  85. // We should expect a hit on this cube
  86. selector = smgr->createTriangleSelectorFromBoundingBox(nearBigCube);
  87. meta->addTriangleSelector(selector);
  88. selector->drop();
  89. line3df ray(0, 0, -5, 0, 0, 100);
  90. vector3df hitPosition;
  91. triangle3df hitTriangle;
  92. ISceneNode* hitNode;
  93. bool collision = collMgr->getCollisionPoint(ray, meta, hitPosition, hitTriangle, hitNode);
  94. meta->drop();
  95. if(hitNode != nearBigCube)
  96. {
  97. logTestString("getCollisionPoint_ignoreTriangleVertices: hit the wrong node.\n");
  98. return false;
  99. }
  100. if(!collision)
  101. {
  102. logTestString("getCollisionPoint_ignoreTriangleVertices: didn't get a hit.\n");
  103. return false;
  104. }
  105. if(hitPosition != vector3df(0, 0, 0))
  106. {
  107. logTestString("getCollisionPoint_ignoreTriangleVertices: unexpected hit position %f %f %f.\n",
  108. hitPosition.X, hitPosition.Y, hitPosition.Z );
  109. return false;
  110. }
  111. smgr->clear();
  112. return true;
  113. }
  114. static bool testGetSceneNodeFromScreenCoordinatesBB(IrrlichtDevice * device,
  115. ISceneManager * smgr,
  116. ISceneCollisionManager * collMgr)
  117. {
  118. // Create 3 nodes. The nearest node actually contains the camera.
  119. IMeshSceneNode * cubeNode1 = smgr->addCubeSceneNode(10.f, 0, -1, vector3df(0, 0, 4));
  120. IMeshSceneNode * cubeNode2 = smgr->addCubeSceneNode(10.f, 0, -1, vector3df(0, 0, 30));
  121. cubeNode2->setRotation(vector3df(90.f, 90.f, 90.f)); // Just check that rotation doesn't stop us hitting it.
  122. IMeshSceneNode * cubeNode3 = smgr->addCubeSceneNode(10.f, 0, -1, vector3df(0, 0, 40));
  123. cubeNode3->setRotation(vector3df(180.f, 180.f, 180.f)); // Just check that rotation doesn't stop us hitting it.
  124. smgr->addCameraSceneNode();
  125. device->run();
  126. smgr->drawAll(); // Get the camera in a good state
  127. ISceneNode * hitNode = collMgr->getSceneNodeFromScreenCoordinatesBB(position2d<s32>(80, 60));
  128. // Expect the first node to be hit, since we're starting the check from inside it.
  129. bool result = true;
  130. if(hitNode != cubeNode1)
  131. {
  132. logTestString("Unexpected node hit. Expected cubeNode1.\n");
  133. result = false;
  134. }
  135. // Now make cubeNode1 invisible and check that cubeNode2 is hit.
  136. cubeNode1->setVisible(false);
  137. hitNode = collMgr->getSceneNodeFromScreenCoordinatesBB(position2d<s32>(80, 60));
  138. if(hitNode != cubeNode2)
  139. {
  140. logTestString("Unexpected node hit. Expected cubeNode2.\n");
  141. result = false;
  142. }
  143. // Make cubeNode1 the parent of cubeNode2.
  144. cubeNode2->setParent(cubeNode1);
  145. // Check visibility.
  146. bool visible = cubeNode2->isVisible();
  147. if(!visible)
  148. {
  149. logTestString("cubeNode2 should think that it (in isolation) is visible.\n");
  150. result = false;
  151. }
  152. visible = cubeNode2->isTrulyVisible();
  153. if(visible)
  154. {
  155. logTestString("cubeNode2 should know that it (recursively) is invisible.\n");
  156. result = false;
  157. }
  158. // cubeNode2 should now be an invalid target as well, and so the final cube node should be hit.
  159. hitNode = collMgr->getSceneNodeFromScreenCoordinatesBB(position2d<s32>(80, 60));
  160. if(hitNode != cubeNode3)
  161. {
  162. logTestString("Unexpected node hit. Expected cubeNode3.\n");
  163. result = false;
  164. }
  165. // Now verify bitmasking
  166. // Test the 01010101010101010101010101010101 bitmask (0x55555555)
  167. cubeNode1->setVisible(true);
  168. cubeNode1->setID(0xAAAAAAAA);
  169. hitNode = collMgr->getSceneNodeFromScreenCoordinatesBB(position2d<s32>(80, 60), 0x55555555);
  170. if(hitNode != cubeNode2)
  171. {
  172. logTestString("Unexpected node hit. Expected cubeNode2.\n");
  173. result = false;
  174. }
  175. assert_log(result);
  176. smgr->clear();
  177. return result;
  178. }
  179. static bool getScaledPickedNodeBB(IrrlichtDevice * device,
  180. ISceneManager * smgr,
  181. ISceneCollisionManager * collMgr)
  182. {
  183. ISceneNode* farTarget = smgr->addCubeSceneNode(1.f);
  184. farTarget->setScale(vector3df(100.f, 100.f, 10.f));
  185. farTarget->setPosition(vector3df(0.f, 0.f, 500.f));
  186. farTarget->updateAbsolutePosition();
  187. // Create a node that's slightly further away than the closest node,
  188. // but thinner. Its furthest corner is closer, but the collision
  189. // position is further, so it should not be selected.
  190. ISceneNode* middleTarget = smgr->addCubeSceneNode(10.f);
  191. middleTarget->setPosition(vector3df(0.f, 0.f, 101.f));
  192. middleTarget->setScale(vector3df(1.f, 1.f, 0.5f));
  193. middleTarget->updateAbsolutePosition();
  194. ISceneNode* nearTarget = smgr->addCubeSceneNode(10.f);
  195. nearTarget->setPosition(vector3df(0.f, 0.f, 100.f));
  196. nearTarget->updateAbsolutePosition();
  197. // We'll rotate this node 90 degrees to show that we can hit its side.
  198. nearTarget->setRotation(vector3df(0.f, 90.f, 0.f));
  199. line3df ray(0.f, 0.f, 0.f, 0.f, 0.f, 500.f);
  200. const ISceneNode * const hit = collMgr->getSceneNodeFromRayBB(ray);
  201. bool result = (hit == nearTarget);
  202. if(hit == 0)
  203. logTestString("getSceneNodeFromRayBB() didn't hit anything.\n");
  204. else if(hit == farTarget)
  205. logTestString("getSceneNodeFromRayBB() hit the far (scaled) target.\n");
  206. else if(hit == middleTarget)
  207. logTestString("getSceneNodeFromRayBB() hit the middle (scaled) target.\n");
  208. assert_log(result);
  209. smgr->clear();
  210. return result;
  211. }
  212. // box intersection according to Kay et al., code from gamedev.net
  213. static bool IntersectBox(const core::vector3df& origin, const core::vector3df& dir, const core::aabbox3df& box)
  214. {
  215. core::vector3df minDist = (box.MinEdge - origin)/dir;
  216. core::vector3df maxDist = (box.MaxEdge - origin)/dir;
  217. core::vector3df realMin(core::min_(minDist.X, maxDist.X),core::min_(minDist.Y, maxDist.Y),core::min_(minDist.Z, maxDist.Z));
  218. core::vector3df realMax(core::max_(minDist.X, maxDist.X),core::max_(minDist.Y, maxDist.Y),core::max_(minDist.Z, maxDist.Z));
  219. f32 minmax = core::min_(realMax.X, realMax.Y, realMax.Z);
  220. // nearest distance to intersection
  221. f32 maxmin = core::max_(realMin.X, realMin.Y, realMin.Z);
  222. return (maxmin >=0 && minmax >= maxmin);
  223. }
  224. static bool checkBBoxIntersection(IrrlichtDevice * device,
  225. ISceneManager * smgr)
  226. {
  227. video::IVideoDriver* driver = device->getVideoDriver();
  228. // add camera
  229. scene::ICameraSceneNode* camera = smgr->addCameraSceneNode();
  230. camera->setPosition(core::vector3df(30, 30, 30));
  231. camera->setTarget(core::vector3df(8.f, 8.f, 8.f));
  232. camera->setID(0);
  233. // add a cube to pick
  234. scene::ISceneNode* cube = smgr->addCubeSceneNode(30, 0, -1, core::vector3df(0,0,0),core::vector3df(30,40,50));
  235. bool result=true;
  236. for (u32 round=0; round<2; ++round)
  237. {
  238. driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(100, 50, 50, 100));
  239. smgr->drawAll();
  240. driver->endScene();
  241. core::matrix4 invMat = cube->getAbsoluteTransformation();
  242. invMat.makeInverse();
  243. s32 hits=0;
  244. u32 start = device->getTimer()->getRealTime();
  245. for (u32 i=10; i<150; ++i)
  246. {
  247. for (u32 j=10; j<110; ++j)
  248. {
  249. const core::position2di pos(i, j);
  250. // get the line used for picking
  251. core::line3df ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(pos, camera);
  252. invMat.transformVect(ray.start);
  253. invMat.transformVect(ray.end);
  254. hits += (cube->getBoundingBox().intersectsWithLine(ray)?1:0);
  255. }
  256. }
  257. u32 duration = device->getTimer()->getRealTime()-start;
  258. logTestString("bbox intersection checks %d hits (of 14000).\n", hits);
  259. hits = -hits;
  260. start = device->getTimer()->getRealTime();
  261. for (u32 i=10; i<150; ++i)
  262. {
  263. for (u32 j=10; j<110; ++j)
  264. {
  265. const core::position2di pos(i, j);
  266. // get the line used for picking
  267. core::line3df ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(pos, camera);
  268. invMat.transformVect(ray.start);
  269. invMat.transformVect(ray.end);
  270. hits += (IntersectBox(ray.start, (ray.end-ray.start).normalize(), cube->getBoundingBox())?1:0);
  271. }
  272. }
  273. u32 duration2 = device->getTimer()->getRealTime()-start;
  274. logTestString("bbox intersection resulted in %d misses at a speed of %d (old) compared to %d (new).\n", abs(hits), duration, duration2);
  275. if (duration>(duration2*1.2f))
  276. logTestString("Consider replacement of bbox intersection test.\n");
  277. result &= (hits==0);
  278. assert_log(result);
  279. // second round without any hits, so check opposite direction
  280. camera->setTarget(core::vector3df(80.f, 80.f, 80.f));
  281. }
  282. ISceneNode* node = smgr->addSphereSceneNode(5.f, 16, 0, -1, core::vector3df(0, 0, 1), core::vector3df(), core::vector3df(0.3f, 0.3f, 0.3f));
  283. cube->remove();
  284. cube = smgr->addCubeSceneNode(10.f, 0, -1, core::vector3df(0, 6.5f, 1), core::vector3df(), core::vector3df(10, 0.1f, 1.f));
  285. camera->setPosition(core::vector3df(0, 0, 10));
  286. camera->setTarget(core::vector3df());
  287. u32 count=0;
  288. for (u32 i=0; i<30; ++i)
  289. {
  290. driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(100, 50, 50, 100));
  291. smgr->drawAll();
  292. driver->endScene();
  293. count += node->getTransformedBoundingBox().intersectsWithBox(cube->getTransformedBoundingBox())?1:0;
  294. node->setPosition(node->getPosition()+core::vector3df(.5f,.5f,0));
  295. if (i==8 && count != 0)
  296. result=false;
  297. if (i==17 && count != 9)
  298. result=false;
  299. }
  300. if (count != 9)
  301. result=false;
  302. smgr->clear();
  303. return result;
  304. }
  305. static bool compareGetSceneNodeFromRayBBWithBBIntersectsWithLine(IrrlichtDevice * device,
  306. ISceneManager * smgr,
  307. ISceneCollisionManager * collMgr)
  308. {
  309. video::IVideoDriver* driver = device->getVideoDriver();
  310. // add camera
  311. scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();
  312. camera->setPosition(core::vector3df(30, 30, 30));
  313. camera->setTarget(core::vector3df(-8.f, 8.f, -8.f));
  314. camera->setID(0);
  315. // add a dynamic light (this causes weirdness)
  316. smgr->addLightSceneNode(0, core::vector3df(4, 4, 4), video::SColorf(.2f, .3f, .2f));
  317. // add a cube to pick
  318. scene::ISceneNode* cube = smgr->addCubeSceneNode(15);
  319. driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(100, 50, 50, 100));
  320. smgr->drawAll();
  321. driver->endScene();
  322. core::matrix4 invMat = cube->getAbsoluteTransformation();
  323. invMat.makeInverse();
  324. bool result = true;
  325. for (u32 i=76; i<82; ++i)
  326. {
  327. for (u32 j=56; j<64; ++j)
  328. {
  329. const core::position2di pos(i, j);
  330. // get the line used for picking
  331. core::line3df ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(pos, camera);
  332. // find a selected node
  333. scene::ISceneNode* pick = smgr->getSceneCollisionManager()->getSceneNodeFromRayBB(ray, 1);
  334. invMat.transformVect(ray.start);
  335. invMat.transformVect(ray.end);
  336. const int a_hit = (pick == cube);
  337. const int b_hit = cube->getBoundingBox().intersectsWithLine(ray);
  338. result = (a_hit==b_hit);
  339. }
  340. }
  341. assert_log(result);
  342. smgr->clear();
  343. return result;
  344. }
  345. /** Test functionality of the sceneCollisionManager */
  346. bool sceneCollisionManager(void)
  347. {
  348. IrrlichtDevice * device = irr::createDevice(video::EDT_NULL, dimension2d<u32>(160, 120));
  349. assert_log(device);
  350. if(!device)
  351. return false;
  352. ISceneManager * smgr = device->getSceneManager();
  353. ISceneCollisionManager * collMgr = smgr->getSceneCollisionManager();
  354. bool result = testGetCollisionResultPosition(device, smgr, collMgr);
  355. smgr->clear();
  356. result &= testGetSceneNodeFromScreenCoordinatesBB(device, smgr, collMgr);
  357. result &= getScaledPickedNodeBB(device, smgr, collMgr);
  358. result &= getCollisionPoint_ignoreTriangleVertices(device, smgr, collMgr);
  359. result &= checkBBoxIntersection(device, smgr);
  360. result &= compareGetSceneNodeFromRayBBWithBBIntersectsWithLine(device, smgr, collMgr);
  361. device->closeDevice();
  362. device->run();
  363. device->drop();
  364. return result;
  365. }