sceneCollisionManager.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  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. ICameraSceneNode * camera = 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. // Make cubeNode3 invisible and check that the camera node is hit (since it has a valid bounding box).
  166. cubeNode3->setVisible(false);
  167. hitNode = collMgr->getSceneNodeFromScreenCoordinatesBB(position2d<s32>(80, 60));
  168. if(hitNode != camera)
  169. {
  170. logTestString("Unexpected node hit. Expected the camera node.\n");
  171. result = false;
  172. }
  173. // Now verify bitmasking
  174. camera->setID(0xAAAAAAAA); // == 101010101010101010101010101010
  175. hitNode = collMgr->getSceneNodeFromScreenCoordinatesBB(position2d<s32>(80, 60), 0x02);
  176. if(hitNode != camera)
  177. {
  178. logTestString("Unexpected node hit. Expected the camera node.\n");
  179. result = false;
  180. }
  181. // Test the 01010101010101010101010101010101 bitmask (0x55555555)
  182. hitNode = collMgr->getSceneNodeFromScreenCoordinatesBB(position2d<s32>(80, 60), 0x55555555);
  183. if(hitNode != 0)
  184. {
  185. logTestString("A node was hit when none was expected.\n");
  186. result = false;
  187. }
  188. assert_log(result);
  189. smgr->clear();
  190. return result;
  191. }
  192. static bool getScaledPickedNodeBB(IrrlichtDevice * device,
  193. ISceneManager * smgr,
  194. ISceneCollisionManager * collMgr)
  195. {
  196. ISceneNode* farTarget = smgr->addCubeSceneNode(1.f);
  197. farTarget->setScale(vector3df(100.f, 100.f, 10.f));
  198. farTarget->setPosition(vector3df(0.f, 0.f, 500.f));
  199. farTarget->updateAbsolutePosition();
  200. // Create a node that's slightly further away than the closest node,
  201. // but thinner. Its furthest corner is closer, but the collision
  202. // position is further, so it should not be selected.
  203. ISceneNode* middleTarget = smgr->addCubeSceneNode(10.f);
  204. middleTarget->setPosition(vector3df(0.f, 0.f, 101.f));
  205. middleTarget->setScale(vector3df(1.f, 1.f, 0.5f));
  206. middleTarget->updateAbsolutePosition();
  207. ISceneNode* nearTarget = smgr->addCubeSceneNode(10.f);
  208. nearTarget->setPosition(vector3df(0.f, 0.f, 100.f));
  209. nearTarget->updateAbsolutePosition();
  210. // We'll rotate this node 90 degrees to show that we can hit its side.
  211. nearTarget->setRotation(vector3df(0.f, 90.f, 0.f));
  212. line3df ray(0.f, 0.f, 0.f, 0.f, 0.f, 500.f);
  213. const ISceneNode * const hit = collMgr->getSceneNodeFromRayBB(ray);
  214. bool result = (hit == nearTarget);
  215. if(hit == 0)
  216. logTestString("getSceneNodeFromRayBB() didn't hit anything.\n");
  217. else if(hit == farTarget)
  218. logTestString("getSceneNodeFromRayBB() hit the far (scaled) target.\n");
  219. else if(hit == middleTarget)
  220. logTestString("getSceneNodeFromRayBB() hit the middle (scaled) target.\n");
  221. assert_log(result);
  222. smgr->clear();
  223. return result;
  224. }
  225. // box intersection according to Kay et al., code from gamedev.net
  226. static bool IntersectBox(const core::vector3df& origin, const core::vector3df& dir, const core::aabbox3df& box)
  227. {
  228. core::vector3df minDist = (box.MinEdge - origin)/dir;
  229. core::vector3df maxDist = (box.MaxEdge - origin)/dir;
  230. core::vector3df realMin(core::min_(minDist.X, maxDist.X),core::min_(minDist.Y, maxDist.Y),core::min_(minDist.Z, maxDist.Z));
  231. core::vector3df realMax(core::max_(minDist.X, maxDist.X),core::max_(minDist.Y, maxDist.Y),core::max_(minDist.Z, maxDist.Z));
  232. f32 minmax = core::min_(realMax.X, realMax.Y, realMax.Z);
  233. // nearest distance to intersection
  234. f32 maxmin = core::max_(realMin.X, realMin.Y, realMin.Z);
  235. return (maxmin >=0 && minmax >= maxmin);
  236. }
  237. static bool checkBBoxIntersection(IrrlichtDevice * device,
  238. ISceneManager * smgr)
  239. {
  240. video::IVideoDriver* driver = device->getVideoDriver();
  241. // add camera
  242. scene::ICameraSceneNode* camera = smgr->addCameraSceneNode();
  243. camera->setPosition(core::vector3df(30, 30, 30));
  244. camera->setTarget(core::vector3df(8.f, 8.f, 8.f));
  245. camera->setID(0);
  246. // add a cube to pick
  247. scene::ISceneNode* cube = smgr->addCubeSceneNode(30, 0, -1, core::vector3df(0,0,0),core::vector3df(30,40,50));
  248. bool result=true;
  249. for (u32 round=0; round<2; ++round)
  250. {
  251. driver->beginScene(true, true, video::SColor(100, 50, 50, 100));
  252. smgr->drawAll();
  253. driver->endScene();
  254. core::matrix4 invMat = cube->getAbsoluteTransformation();
  255. invMat.makeInverse();
  256. s32 hits=0;
  257. u32 start = device->getTimer()->getRealTime();
  258. for (u32 i=10; i<150; ++i)
  259. {
  260. for (u32 j=10; j<110; ++j)
  261. {
  262. const core::position2di pos(i, j);
  263. // get the line used for picking
  264. core::line3df ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(pos, camera);
  265. invMat.transformVect(ray.start);
  266. invMat.transformVect(ray.end);
  267. hits += (cube->getBoundingBox().intersectsWithLine(ray)?1:0);
  268. }
  269. }
  270. u32 duration = device->getTimer()->getRealTime()-start;
  271. logTestString("bbox intersection checks %d hits (of 14000).\n", hits);
  272. hits = -hits;
  273. start = device->getTimer()->getRealTime();
  274. for (u32 i=10; i<150; ++i)
  275. {
  276. for (u32 j=10; j<110; ++j)
  277. {
  278. const core::position2di pos(i, j);
  279. // get the line used for picking
  280. core::line3df ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(pos, camera);
  281. invMat.transformVect(ray.start);
  282. invMat.transformVect(ray.end);
  283. hits += (IntersectBox(ray.start, (ray.end-ray.start).normalize(), cube->getBoundingBox())?1:0);
  284. }
  285. }
  286. u32 duration2 = device->getTimer()->getRealTime()-start;
  287. logTestString("bbox intersection resulted in %d misses at a speed of %d (old) compared to %d (new).\n", abs(hits), duration, duration2);
  288. if (duration>(duration2*1.2f))
  289. logTestString("Consider replacement of bbox intersection test.\n");
  290. result &= (hits==0);
  291. assert_log(result);
  292. // second round without any hits, so check opposite direction
  293. camera->setTarget(core::vector3df(80.f, 80.f, 80.f));
  294. }
  295. ISceneNode* node = smgr->addSphereSceneNode(5.f, 16, 0, -1, core::vector3df(0, 0, 1), core::vector3df(), core::vector3df(0.3f, 0.3f, 0.3f));
  296. cube->remove();
  297. cube = smgr->addCubeSceneNode(10.f, 0, -1, core::vector3df(0, 6.5f, 1), core::vector3df(), core::vector3df(10, 0.1f, 1.f));
  298. camera->setPosition(core::vector3df(0, 0, 10));
  299. camera->setTarget(core::vector3df());
  300. u32 count=0;
  301. for (u32 i=0; i<30; ++i)
  302. {
  303. driver->beginScene(true, true, video::SColor(100, 50, 50, 100));
  304. smgr->drawAll();
  305. driver->endScene();
  306. count += node->getTransformedBoundingBox().intersectsWithBox(cube->getTransformedBoundingBox())?1:0;
  307. node->setPosition(node->getPosition()+core::vector3df(.5f,.5f,0));
  308. if (i==8 && count != 0)
  309. result=false;
  310. if (i==17 && count != 9)
  311. result=false;
  312. }
  313. if (count != 9)
  314. result=false;
  315. smgr->clear();
  316. return result;
  317. }
  318. static bool compareGetSceneNodeFromRayBBWithBBIntersectsWithLine(IrrlichtDevice * device,
  319. ISceneManager * smgr,
  320. ISceneCollisionManager * collMgr)
  321. {
  322. video::IVideoDriver* driver = device->getVideoDriver();
  323. // add camera
  324. scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();
  325. camera->setPosition(core::vector3df(30, 30, 30));
  326. camera->setTarget(core::vector3df(-8.f, 8.f, -8.f));
  327. camera->setID(0);
  328. // add a dynamic light (this causes weirdness)
  329. smgr->addLightSceneNode(0, core::vector3df(4, 4, 4), video::SColorf(.2f, .3f, .2f));
  330. // add a cube to pick
  331. scene::ISceneNode* cube = smgr->addCubeSceneNode(15);
  332. driver->beginScene(true, true, video::SColor(100, 50, 50, 100));
  333. smgr->drawAll();
  334. driver->endScene();
  335. core::matrix4 invMat = cube->getAbsoluteTransformation();
  336. invMat.makeInverse();
  337. bool result = true;
  338. for (u32 i=76; i<82; ++i)
  339. {
  340. for (u32 j=56; j<64; ++j)
  341. {
  342. const core::position2di pos(i, j);
  343. // get the line used for picking
  344. core::line3df ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(pos, camera);
  345. // find a selected node
  346. scene::ISceneNode* pick = smgr->getSceneCollisionManager()->getSceneNodeFromRayBB(ray, 1);
  347. invMat.transformVect(ray.start);
  348. invMat.transformVect(ray.end);
  349. const int a_hit = (pick == cube);
  350. const int b_hit = cube->getBoundingBox().intersectsWithLine(ray);
  351. result = (a_hit==b_hit);
  352. }
  353. }
  354. assert_log(result);
  355. smgr->clear();
  356. return result;
  357. }
  358. /** Test functionality of the sceneCollisionManager */
  359. bool sceneCollisionManager(void)
  360. {
  361. IrrlichtDevice * device = irr::createDevice(video::EDT_NULL, dimension2d<u32>(160, 120));
  362. assert_log(device);
  363. if(!device)
  364. return false;
  365. ISceneManager * smgr = device->getSceneManager();
  366. ISceneCollisionManager * collMgr = smgr->getSceneCollisionManager();
  367. bool result = testGetCollisionResultPosition(device, smgr, collMgr);
  368. smgr->clear();
  369. result &= testGetSceneNodeFromScreenCoordinatesBB(device, smgr, collMgr);
  370. result &= getScaledPickedNodeBB(device, smgr, collMgr);
  371. result &= getCollisionPoint_ignoreTriangleVertices(device, smgr, collMgr);
  372. result &= checkBBoxIntersection(device, smgr);
  373. result &= compareGetSceneNodeFromRayBBWithBBIntersectsWithLine(device, smgr, collMgr);
  374. device->closeDevice();
  375. device->run();
  376. device->drop();
  377. return result;
  378. }