git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6000 dfc29bdd-3216-0410-991c-e03cc46cb475
		
			
				
	
	
		
			472 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			472 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright (C) 2008-2012 Colin MacDonald
 | |
| // No rights reserved: this software is in the public domain.
 | |
| 
 | |
| #include "testUtils.h"
 | |
| 
 | |
| using namespace irr;
 | |
| using namespace core;
 | |
| using namespace scene;
 | |
| using namespace video;
 | |
| 
 | |
| static bool testGetCollisionResultPosition(IrrlichtDevice * device,
 | |
| 					   ISceneManager * smgr,
 | |
| 					   ISceneCollisionManager * collMgr)
 | |
| {
 | |
| 	IMeshSceneNode * cubeNode = smgr->addCubeSceneNode(10.f);
 | |
| 	ITriangleSelector * cubeSelector = smgr->createTriangleSelectorFromBoundingBox(cubeNode);
 | |
| 
 | |
| 	triangle3df triOut;
 | |
| 	vector3df hitPosition;
 | |
| 	bool falling;
 | |
| 	ISceneNode* hitNode;
 | |
| 
 | |
| 	vector3df resultPosition =
 | |
| 		collMgr->getCollisionResultPosition(cubeSelector,
 | |
| 						vector3df(0, 50, 0),
 | |
| 						vector3df(10, 20, 10),
 | |
| 						vector3df(0, -100, 0),
 | |
| 						triOut,
 | |
| 						hitPosition,
 | |
| 						falling,
 | |
| 						hitNode);
 | |
| 
 | |
| 	bool result = true;
 | |
| 
 | |
| 	if(hitNode != cubeNode)
 | |
| 	{
 | |
| 		logTestString("Unexpected collision node\n");
 | |
| 		result = false;
 | |
| 	}
 | |
| 
 | |
| 	if(!equals(resultPosition.Y, 25.f, 0.01f))
 | |
| 	{
 | |
| 		logTestString("Unexpected collision response position\n");
 | |
| 		result = false;
 | |
| 	}
 | |
| 
 | |
| 	if(!equals(hitPosition.Y, 5.f, 0.01f))
 | |
| 	{
 | |
| 		logTestString("Unexpected collision position\n");
 | |
| 		result = false;
 | |
| 	}
 | |
| 
 | |
| 	resultPosition =
 | |
| 		collMgr->getCollisionResultPosition(cubeSelector,
 | |
| 						vector3df(-20, 0, 0),
 | |
| 						vector3df(10, 20, 10),
 | |
| 						vector3df(100, 0, 0),
 | |
| 						triOut,
 | |
| 						hitPosition,
 | |
| 						falling,
 | |
| 						hitNode);
 | |
| 
 | |
| 	if(hitNode != cubeNode)
 | |
| 	{
 | |
| 		logTestString("Unexpected collision node\n");
 | |
| 		result = false;
 | |
| 	}
 | |
| 
 | |
| 	if(!equals(resultPosition.X, -15.f, 0.01f))
 | |
| 	{
 | |
| 		logTestString("Unexpected collision response position\n");
 | |
| 		result = false;
 | |
| 	}
 | |
| 
 | |
| 	if(!equals(hitPosition.X, -5.f, 0.01f))
 | |
| 	{
 | |
| 		logTestString("Unexpected collision position\n");
 | |
| 		result = false;
 | |
| 	}
 | |
| 
 | |
| 	assert_log(result);
 | |
| 
 | |
| 	cubeSelector->drop();
 | |
| 	smgr->clear();
 | |
| 
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| 
 | |
| // Test that getCollisionPoint() actually uses the closest point, not the closest triangle.
 | |
| static bool getCollisionPoint_ignoreTriangleVertices(IrrlichtDevice * device,
 | |
| 						ISceneManager * smgr,
 | |
| 						ISceneCollisionManager * collMgr)
 | |
| {
 | |
| 	// Create a cube with a Z face at 5, but corners close to 0
 | |
| 	ISceneNode * farSmallCube = smgr->addCubeSceneNode(10, 0, -1, vector3df(0, 0, 10));
 | |
| 
 | |
| 	// Create a cube with a Z face at 0, but corners far from 0
 | |
| 	ISceneNode * nearBigCube = smgr->addCubeSceneNode(100, 0, -1, vector3df(0, 0, 50));
 | |
| 
 | |
| 	IMetaTriangleSelector * meta = smgr->createMetaTriangleSelector();
 | |
| 
 | |
| 	ITriangleSelector * selector = smgr->createTriangleSelectorFromBoundingBox(farSmallCube);
 | |
| 	meta->addTriangleSelector(selector);
 | |
| 	selector->drop();
 | |
| 
 | |
| 	// We should expect a hit on this cube
 | |
| 	selector = smgr->createTriangleSelectorFromBoundingBox(nearBigCube);
 | |
| 	meta->addTriangleSelector(selector);
 | |
| 	selector->drop();
 | |
| 
 | |
| 	line3df ray(0, 0, -5, 0, 0, 100);
 | |
| 	vector3df hitPosition;
 | |
| 	triangle3df hitTriangle;
 | |
| 	ISceneNode* hitNode;
 | |
| 
 | |
| 	bool collision = collMgr->getCollisionPoint(ray, meta, hitPosition, hitTriangle, hitNode);
 | |
| 
 | |
| 	meta->drop();
 | |
| 
 | |
| 	if(hitNode != nearBigCube)
 | |
| 	{
 | |
| 		logTestString("getCollisionPoint_ignoreTriangleVertices: hit the wrong node.\n");
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	if(!collision)
 | |
| 	{
 | |
| 		logTestString("getCollisionPoint_ignoreTriangleVertices: didn't get a hit.\n");
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	if(hitPosition != vector3df(0, 0, 0))
 | |
| 	{
 | |
| 		logTestString("getCollisionPoint_ignoreTriangleVertices: unexpected hit position %f %f %f.\n",
 | |
| 			hitPosition.X, hitPosition.Y, hitPosition.Z );
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	smgr->clear();
 | |
| 
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| 
 | |
| static bool testGetSceneNodeFromScreenCoordinatesBB(IrrlichtDevice * device,
 | |
| 						ISceneManager * smgr,
 | |
| 						ISceneCollisionManager * collMgr)
 | |
| {
 | |
| 	// Create 3 nodes. The nearest node actually contains the camera.
 | |
| 	IMeshSceneNode * cubeNode1 = smgr->addCubeSceneNode(10.f, 0, -1, vector3df(0, 0, 4));
 | |
| 	IMeshSceneNode * cubeNode2 = smgr->addCubeSceneNode(10.f, 0, -1, vector3df(0, 0, 30));
 | |
| 	cubeNode2->setRotation(vector3df(90.f, 90.f, 90.f)); // Just check that rotation doesn't stop us hitting it.
 | |
| 	IMeshSceneNode * cubeNode3 = smgr->addCubeSceneNode(10.f, 0, -1, vector3df(0, 0, 40));
 | |
| 	cubeNode3->setRotation(vector3df(180.f, 180.f, 180.f)); // Just check that rotation doesn't stop us hitting it.
 | |
| 
 | |
| 	smgr->addCameraSceneNode();
 | |
| 	device->run();
 | |
| 	smgr->drawAll(); // Get the camera in a good state
 | |
| 
 | |
| 	ISceneNode * hitNode = collMgr->getSceneNodeFromScreenCoordinatesBB(position2d<s32>(80, 60));
 | |
| 
 | |
| 	// Expect the first node to be hit, since we're starting the check from inside it.
 | |
| 	bool result = true;
 | |
| 	if(hitNode != cubeNode1)
 | |
| 	{
 | |
| 		logTestString("Unexpected node hit. Expected cubeNode1.\n");
 | |
| 		result = false;
 | |
| 	}
 | |
| 
 | |
| 	// Now make cubeNode1 invisible and check that cubeNode2 is hit.
 | |
| 	cubeNode1->setVisible(false);
 | |
| 	hitNode = collMgr->getSceneNodeFromScreenCoordinatesBB(position2d<s32>(80, 60));
 | |
| 	if(hitNode != cubeNode2)
 | |
| 	{
 | |
| 		logTestString("Unexpected node hit. Expected cubeNode2.\n");
 | |
| 		result = false;
 | |
| 	}
 | |
| 
 | |
| 	// Make cubeNode1 the parent of cubeNode2.
 | |
| 	cubeNode2->setParent(cubeNode1);
 | |
| 
 | |
| 	// Check visibility.
 | |
| 	bool visible = cubeNode2->isVisible();
 | |
| 	if(!visible)
 | |
| 	{
 | |
| 		logTestString("cubeNode2 should think that it (in isolation) is visible.\n");
 | |
| 		result = false;
 | |
| 	}
 | |
| 
 | |
| 	visible = cubeNode2->isTrulyVisible();
 | |
| 	if(visible)
 | |
| 	{
 | |
| 		logTestString("cubeNode2 should know that it (recursively) is invisible.\n");
 | |
| 		result = false;
 | |
| 	}
 | |
| 
 | |
| 	// cubeNode2 should now be an invalid target as well, and so the final cube node should be hit.
 | |
| 	hitNode = collMgr->getSceneNodeFromScreenCoordinatesBB(position2d<s32>(80, 60));
 | |
| 	if(hitNode != cubeNode3)
 | |
| 	{
 | |
| 		logTestString("Unexpected node hit. Expected cubeNode3.\n");
 | |
| 		result = false;
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	// Now verify bitmasking
 | |
| 
 | |
| 	// Test the 01010101010101010101010101010101 bitmask (0x55555555)
 | |
| 	cubeNode1->setVisible(true);
 | |
| 	cubeNode1->setID(0xAAAAAAAA);
 | |
| 	hitNode = collMgr->getSceneNodeFromScreenCoordinatesBB(position2d<s32>(80, 60), 0x55555555);
 | |
| 	if(hitNode != cubeNode2)
 | |
| 	{
 | |
| 		logTestString("Unexpected node hit. Expected cubeNode2.\n");
 | |
| 		result = false;
 | |
| 	}
 | |
| 	assert_log(result);
 | |
| 
 | |
| 	smgr->clear();
 | |
| 
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| 
 | |
| static bool getScaledPickedNodeBB(IrrlichtDevice * device,
 | |
| 				ISceneManager * smgr,
 | |
| 				ISceneCollisionManager * collMgr)
 | |
| {
 | |
| 	ISceneNode* farTarget = smgr->addCubeSceneNode(1.f);
 | |
| 	farTarget->setScale(vector3df(100.f, 100.f, 10.f));
 | |
| 	farTarget->setPosition(vector3df(0.f, 0.f, 500.f));
 | |
| 	farTarget->updateAbsolutePosition();
 | |
| 
 | |
| 	// Create a node that's slightly further away than the closest node,
 | |
| 	// but thinner.  Its furthest corner is closer, but the collision
 | |
| 	// position is further, so it should not be selected.
 | |
| 	ISceneNode* middleTarget = smgr->addCubeSceneNode(10.f);
 | |
| 	middleTarget->setPosition(vector3df(0.f, 0.f, 101.f));
 | |
| 	middleTarget->setScale(vector3df(1.f, 1.f, 0.5f));
 | |
| 	middleTarget->updateAbsolutePosition();
 | |
| 
 | |
| 	ISceneNode* nearTarget = smgr->addCubeSceneNode(10.f);
 | |
| 	nearTarget->setPosition(vector3df(0.f, 0.f, 100.f));
 | |
| 	nearTarget->updateAbsolutePosition();
 | |
| 	// We'll rotate this node 90 degrees to show that we can hit its side.
 | |
| 	nearTarget->setRotation(vector3df(0.f, 90.f, 0.f));
 | |
| 
 | |
| 	line3df ray(0.f, 0.f, 0.f, 0.f, 0.f, 500.f);
 | |
| 
 | |
| 	const ISceneNode * const hit = collMgr->getSceneNodeFromRayBB(ray);
 | |
| 
 | |
| 	bool result = (hit == nearTarget);
 | |
| 
 | |
| 	if(hit == 0)
 | |
| 		logTestString("getSceneNodeFromRayBB() didn't hit anything.\n");
 | |
| 	else if(hit == farTarget)
 | |
| 		logTestString("getSceneNodeFromRayBB() hit the far (scaled) target.\n");
 | |
| 	else if(hit == middleTarget)
 | |
| 		logTestString("getSceneNodeFromRayBB() hit the middle (scaled) target.\n");
 | |
| 
 | |
| 	assert_log(result);
 | |
| 
 | |
| 	smgr->clear();
 | |
| 
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| 
 | |
| // box intersection according to Kay et al., code from gamedev.net
 | |
| static bool IntersectBox(const core::vector3df& origin, const core::vector3df& dir, const core::aabbox3df& box)
 | |
| {
 | |
| 	core::vector3df minDist = (box.MinEdge - origin)/dir;
 | |
| 	core::vector3df maxDist = (box.MaxEdge - origin)/dir;
 | |
| 
 | |
| 	core::vector3df realMin(core::min_(minDist.X, maxDist.X),core::min_(minDist.Y, maxDist.Y),core::min_(minDist.Z, maxDist.Z));
 | |
| 	core::vector3df realMax(core::max_(minDist.X, maxDist.X),core::max_(minDist.Y, maxDist.Y),core::max_(minDist.Z, maxDist.Z));
 | |
| 
 | |
| 	f32 minmax = core::min_(realMax.X, realMax.Y, realMax.Z);
 | |
| 	// nearest distance to intersection
 | |
| 	f32 maxmin = core::max_(realMin.X, realMin.Y, realMin.Z);
 | |
| 
 | |
| 	return (maxmin >=0 && minmax >= maxmin);
 | |
| }
 | |
| 
 | |
| static bool checkBBoxIntersection(IrrlichtDevice * device,
 | |
| 				ISceneManager * smgr)
 | |
| {
 | |
| 	video::IVideoDriver* driver = device->getVideoDriver();
 | |
| 
 | |
| 	// add camera
 | |
| 	scene::ICameraSceneNode* camera = smgr->addCameraSceneNode();
 | |
| 	camera->setPosition(core::vector3df(30, 30, 30));
 | |
| 	camera->setTarget(core::vector3df(8.f, 8.f, 8.f));
 | |
| 	camera->setID(0);
 | |
| 
 | |
| 	// add a cube to pick
 | |
| 	scene::ISceneNode* cube = smgr->addCubeSceneNode(30, 0, -1, core::vector3df(0,0,0),core::vector3df(30,40,50));
 | |
| 
 | |
| 	bool result=true;
 | |
| 	for (u32 round=0; round<2; ++round)
 | |
| 	{
 | |
| 		driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(100, 50, 50, 100));
 | |
| 		smgr->drawAll();
 | |
| 		driver->endScene();
 | |
| 
 | |
| 		core::matrix4 invMat = cube->getAbsoluteTransformation();
 | |
| 		invMat.makeInverse();
 | |
| 
 | |
| 		s32 hits=0;
 | |
| 		u32 start = device->getTimer()->getRealTime();
 | |
| 		for (u32 i=10; i<150; ++i)
 | |
| 		{
 | |
| 			for (u32 j=10; j<110; ++j)
 | |
| 			{
 | |
| 				const core::position2di pos(i, j);
 | |
| 
 | |
| 				// get the line used for picking
 | |
| 				core::line3df ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(pos, camera);
 | |
| 
 | |
| 				invMat.transformVect(ray.start);
 | |
| 				invMat.transformVect(ray.end);
 | |
| 
 | |
| 				hits += (cube->getBoundingBox().intersectsWithLine(ray)?1:0);
 | |
| 			}
 | |
| 		}
 | |
| 		u32 duration = device->getTimer()->getRealTime()-start;
 | |
| 		logTestString("bbox intersection checks %d hits (of 14000).\n", hits);
 | |
| 		hits = -hits;
 | |
| 
 | |
| 		start = device->getTimer()->getRealTime();
 | |
| 		for (u32 i=10; i<150; ++i)
 | |
| 		{
 | |
| 			for (u32 j=10; j<110; ++j)
 | |
| 			{
 | |
| 				const core::position2di pos(i, j);
 | |
| 
 | |
| 				// get the line used for picking
 | |
| 				core::line3df ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(pos, camera);
 | |
| 
 | |
| 				invMat.transformVect(ray.start);
 | |
| 				invMat.transformVect(ray.end);
 | |
| 
 | |
| 				hits += (IntersectBox(ray.start, (ray.end-ray.start).normalize(), cube->getBoundingBox())?1:0);
 | |
| 			}
 | |
| 		}
 | |
| 		u32 duration2 = device->getTimer()->getRealTime()-start;
 | |
| 		logTestString("bbox intersection resulted in %d misses at a speed of %d (old) compared to %d (new).\n", abs(hits), duration, duration2);
 | |
| 		if (duration>(duration2*1.2f))
 | |
| 			logTestString("Consider replacement of bbox intersection test.\n");
 | |
| 
 | |
| 		result &= (hits==0);
 | |
| 		assert_log(result);
 | |
| 		// second round without any hits, so check opposite direction
 | |
| 		camera->setTarget(core::vector3df(80.f, 80.f, 80.f));
 | |
| 	}
 | |
| 
 | |
| 	ISceneNode* node = smgr->addSphereSceneNode(5.f, 16, 0, -1, core::vector3df(0, 0, 1), core::vector3df(), core::vector3df(0.3f, 0.3f, 0.3f));
 | |
| 	cube->remove();
 | |
| 	cube = smgr->addCubeSceneNode(10.f, 0, -1, core::vector3df(0, 6.5f, 1), core::vector3df(), core::vector3df(10, 0.1f, 1.f));
 | |
| 	camera->setPosition(core::vector3df(0, 0, 10));
 | |
| 	camera->setTarget(core::vector3df());
 | |
| 
 | |
| 	u32 count=0;
 | |
| 	for (u32 i=0; i<30; ++i)
 | |
| 	{
 | |
| 		driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(100, 50, 50, 100));
 | |
| 		smgr->drawAll();
 | |
| 		driver->endScene();
 | |
| 
 | |
| 		count += node->getTransformedBoundingBox().intersectsWithBox(cube->getTransformedBoundingBox())?1:0;
 | |
| 		node->setPosition(node->getPosition()+core::vector3df(.5f,.5f,0));
 | |
| 		if (i==8 && count != 0)
 | |
| 			result=false;
 | |
| 		if (i==17 && count != 9)
 | |
| 			result=false;
 | |
| 	}
 | |
| 	if (count != 9)
 | |
| 		result=false;
 | |
| 
 | |
| 	smgr->clear();
 | |
| 
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| 
 | |
| static bool compareGetSceneNodeFromRayBBWithBBIntersectsWithLine(IrrlichtDevice * device,
 | |
| 				ISceneManager * smgr,
 | |
| 				ISceneCollisionManager * collMgr)
 | |
| {
 | |
| 	video::IVideoDriver* driver = device->getVideoDriver();
 | |
| 
 | |
| 	// add camera
 | |
| 	scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();
 | |
| 	camera->setPosition(core::vector3df(30, 30, 30));
 | |
| 	camera->setTarget(core::vector3df(-8.f, 8.f, -8.f));
 | |
| 	camera->setID(0);
 | |
| 
 | |
| 	// add a dynamic light (this causes weirdness)
 | |
| 	smgr->addLightSceneNode(0, core::vector3df(4, 4, 4), video::SColorf(.2f, .3f, .2f));
 | |
| 
 | |
| 	// add a cube to pick
 | |
| 	scene::ISceneNode* cube = smgr->addCubeSceneNode(15);
 | |
| 
 | |
| 	driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(100, 50, 50, 100));
 | |
| 	smgr->drawAll();
 | |
| 	driver->endScene();
 | |
| 
 | |
| 	core::matrix4 invMat = cube->getAbsoluteTransformation();
 | |
| 	invMat.makeInverse();
 | |
| 
 | |
| 	bool result = true;
 | |
| 	for (u32 i=76; i<82; ++i)
 | |
| 	{
 | |
| 		for (u32 j=56; j<64; ++j)
 | |
| 		{
 | |
| 			const core::position2di pos(i, j);
 | |
| 
 | |
| 			// get the line used for picking
 | |
| 			core::line3df ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(pos, camera);
 | |
| 
 | |
| 			// find a selected node
 | |
| 			scene::ISceneNode* pick = smgr->getSceneCollisionManager()->getSceneNodeFromRayBB(ray, 1);
 | |
| 
 | |
| 			invMat.transformVect(ray.start);
 | |
| 			invMat.transformVect(ray.end);
 | |
| 
 | |
| 			const int a_hit = (pick == cube);
 | |
| 			const int b_hit = cube->getBoundingBox().intersectsWithLine(ray);
 | |
| 			result = (a_hit==b_hit);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	assert_log(result);
 | |
| 
 | |
| 	smgr->clear();
 | |
| 
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| 
 | |
| /** Test functionality of the sceneCollisionManager */
 | |
| bool sceneCollisionManager(void)
 | |
| {
 | |
| 	IrrlichtDevice * device = irr::createDevice(video::EDT_NULL, dimension2d<u32>(160, 120));
 | |
| 	assert_log(device);
 | |
| 	if(!device)
 | |
| 		return false;
 | |
| 
 | |
| 	ISceneManager * smgr = device->getSceneManager();
 | |
| 	ISceneCollisionManager * collMgr = smgr->getSceneCollisionManager();
 | |
| 
 | |
| 	bool result = testGetCollisionResultPosition(device, smgr, collMgr);
 | |
| 
 | |
| 	smgr->clear();
 | |
| 
 | |
| 	result &= testGetSceneNodeFromScreenCoordinatesBB(device, smgr, collMgr);
 | |
| 
 | |
| 	result &= getScaledPickedNodeBB(device, smgr, collMgr);
 | |
| 
 | |
| 	result &= getCollisionPoint_ignoreTriangleVertices(device, smgr, collMgr);
 | |
| 
 | |
| 	result &= checkBBoxIntersection(device, smgr);
 | |
| 
 | |
| 	result &= compareGetSceneNodeFromRayBBWithBBIntersectsWithLine(device, smgr, collMgr);
 | |
| 
 | |
| 	device->closeDevice();
 | |
| 	device->run();
 | |
| 	device->drop();
 | |
| 	return result;
 | |
| }
 |