Custom Debug Draw

Custom Debug Draw

Hi, I am using Havok for physics and Ogre3d for rendering. The visual debugger is great and all, but as part of the debug mode on my game, I would like to be able to see the physics objects in the actual program. It has been suggested to me that I'll have to go through the physics world and draw each collision shape individually. My question is in 2 parts. If it is possible to set up a debug draw of the physics objects in an easier way, then I would like to know how to do that. If it is not, then I'm a bit confused on how I would get each shape out of every object in the world. Help?

17 帖子 / 0 全新
最新文章
如需更全面地了解编译器优化,请参阅优化注意事项
Best Reply

Hi fiction:

Our StepByStep / Visualize Demo is a good sample for you to figure out "how to display the hkDebugDisplay function in your own render engine". Generally speaking, you need to manually implement the hkDebugDisplayHandler interface in your own render system. In the demo, we are using our hkgDisplayHandler created in RenderSystem.cpp. And then, you can use all display functions and macros defined in hkDebugDisplay.h Actually, our visual debugger is using this system as well.

As you know, each rigid body has a shape, and this shape can be represented by a geometry which is composed of vertices. There are some utility functions to help you create geometries from a single rigid body or shape. For example:
hkpGeometryConverter::appendGeometryFromRigidBody(...); // build geometry from rigid body
or
hkpShapeDisplayBuilder::buildDisplayGeometries(...); //get a hkDisplayGeometry list from a shape
hkDisplayGeometry::buildGeometry(); //build actual geometry from the list

Both methods above can give you a hkGeometry structure. It saved all vertices and triangles info (see hkGeometry,h). You can use them to pass into your render system.

Joe Developer Support Engineer Havok www.havok.com

I have part of it now working, thanks to you! A problem I am having though is that addGeometry() never seems to get called. All the other functions are called, including calls to removeGeometry(). Any ideas what the issue here could be?

Hi fiction:

In StepByStep / Visualize Demo, it happened in process->init(); (main.cpp line 123), the code prepare the DemoWorldData with all physics object, and add them one by one into the render queue. I think you'd like to call yourDisplayHandler::addGeometry() manually and add any object you want to render.

Joe Developer Support Engineer Havok www.havok.com

I'm a bit confused. I can always call addGeometry individually on objects that I want drawn, but I don't really understand what you are saying is happening in process->init(). If you are saying process->init() is responsible for the calls to addGeometry, then I'm confused as to why its not happening in my code when I am doing the exact same thing.

Hi fiction:

If you register hkpShapeDisplayViewer as one of your processes, like the Visualize/main.cpp: line 250 did, this viewer will help you render all shapes in your world automatically. process->init() will call addGeometry() for all entities which have already in the world. Afterwards, every time you add an entity into the world, a callback function in hkpShapeDisplayViewer will be called to addGeometry() into the viewer.

But I guess you do not want to render all shapes in the world, so you should at least register hkDebugDisplayProcess, (like Visualize/main.cpp: line 292 did) and then call HK_ADD_GEOMETRY macro for each entity you want to render. For this method, you need to manually call HK_UPDATE_GEOMETRY to update the rendering geometry to the new position.

If you are using hkpShapeDisplayViewer, and addGeometry() never get called. Please check if you overrode addGeometryLazily() or not. This function is a virtual function (not a pure virtual function) defined in hkDebugDisplayHandler. hkpShapeDisplayViewer will call it to create the geometry for shapes and then addGeometry(). You should not overrode it unless you want to build the display geometry by yourself.

Hope my explanation is clear

Joe Developer Support Engineer Havok www.havok.com

The issue was that I had overloaded addGeometryLazily() without realizing the effect. Thank you for your assistance!

I'm trying to render a terrain collision mesh. Is there a step by step guide or code sample to render a collision mesh in Ogre?

I do not have terrain in my project, but I would assume that you could render it the same way as the other objects. I created ManualObjects using the triangles and points given in addGeometry(), and then used updateGeometry() to transform the scene nodes of the ManualObjects. As far as I know you should be able to implement it in the same way. If you have any more questions about it I am glad to help.

Here is part of my code from addGeometry().


ManualObject *man = m_sceneMgr->createManualObject( idString ); //string version of the id given. use this to access the manual objects with id in update/remove geometry

man->begin( material, RenderOperation::OT_TRIANGLE_LIST );

Ogre::Vector3 v;

hkVector4 hv;

for( int i = 0; i < geometries[0]->getGeometry()->m_triangles.getSize(); ++i )

{

	for( int j = 1; j >= 0; --j )

	{

		hv = ( geometries[0]->getGeometry()->getVertex( i, j ) );

		HkToOgre::v3( hv, v );

		man->position( v );

	}

	hv = geometries[0]->getGeometry()->getVertex( i, 2 );

	HkToOgre::v3( hv, v );

	man->position( v );

}

man->end();

Ogre::SceneNode * manNode = m_sceneMgr->getRootSceneNode()->createChildSceneNode();

manNode->attachObject( man );

HkToOgre::v3( transform.getTranslation(), v );

manNode->setPosition( v );

Ogre::Quaternion q;

hkQuaternion hq;

hq.set( transform.getRotation() );

HkToOgre::quat( hq, q );

manNode->setOrientation( q );

return HK_SUCCESS;

Hi Andrew:

Sorry, there is no step by step guide for it. You can create your own display handle like what I mentioned before. I think the easier way for you is : generate display meshes from the collision mesh you are using, and then render it in Ogre.

for example:


hkpShapeDisplayBuilder::hkpShapeDisplayBuilderEnvironment env;

hkpShapeDisplayBuilder builder(env);
hkInplaceArray<hkDisplayGeometry*,8> geomList;

builder.buildDisplayGeometries( yourCollisionMeshShape, geomList );
for(int i = 0; ibuildGeometry();

	hkGeometry* geom = disp->getGeometry();
	// geom has all infos which you can directly use to render, see Source\CommonBase\Types\Geometry\hkGeometry.h

	...

}

Joe Developer Support Engineer Havok www.havok.com

This works correctly, I had to rotate the collision mesh and flip my terrain but it works. One problem is that the collision mesh is missing a vertex edge, so it's coming up short at 1 corner and not completely flush with the terrain. I thought maybe I'm missing an iteration somewhere but I'm not sure. Any thoughts?

Ogre::Image * heightMap = new Ogre::Image();

	heightMap->load("terrain.png","General");

	heightMap->flipAroundX();

	const int xRes = heightMap->getWidth();

	const int zRes = heightMap->getHeight();
	hkReal* mHeightData = hkAllocate(xRes * xRes, HK_MEMORY_CLASS_USER);

	hkpSampledHeightFieldBaseCinfo cInfo;

	cInfo.m_xRes = xRes;

	cInfo.m_zRes = zRes;

	cInfo.m_scale = hkVector4(mTerrainGroup->getTerrainWorldSize() / mTerrainGroup->getTerrainSize(),mTerrainGroup->getTerrainWorldSize() / mTerrainGroup->getTerrainSize(), mTerrainGroup->getTerrainWorldSize() / mTerrainGroup->getTerrainSize());

	for (int x = 0; x < xRes; x++)

	{

	   for (int z = 0; z < zRes; z++)

	   {

		  hkReal height = heightMap->getColourAt(x, z, 0).r * mTerrainGroup->getTerrainWorldSize() / (mTerrainGroup->getTerrainSize() - 54);
		  mHeightData[x * cInfo.m_xRes + z] = height;

	   }

	}
	hkpSampledHeightFieldShape *heightFieldShape = new OgreHavokTerrainShape(cInfo, mHeightData);
		// Now that we have a shape all we need is the fixed body to represent it in the

		// the simulation. Standard rigid body setup.

		hkpRigidBodyCinfo rci;

		rci.m_motionType = hkpMotion::MOTION_FIXED;

		rci.m_position.set(-mTerrainGroup->getTerrainWorldSize() / 2, 0, mTerrainGroup->getTerrainWorldSize() / 2);

		rci.m_shape = heightFieldShape;

		rci.m_friction = 0.2f;

		rci.m_rotation.setAxisAngle(hkVector4(0.0f, 1.0f, 0.0f), HK_REAL_PI / 2.0F); 
		hkpRigidBody* body = new hkpRigidBody( rci );

		mWorld->lock();

		mWorld->addEntity(body);

		body->removeReference();		
		hkpShapeDisplayBuilder::hkpShapeDisplayBuilderEnvironment env;

		hkpShapeDisplayBuilder builder(env);

		hkArray displayGeometries;

		builder.buildDisplayGeometries( heightFieldShape, displayGeometries );

		hkDebugDisplay::getInstance().addGeometry( displayGeometries, hkTransform::getIdentity(), 0, 0, 0 );
		// ------ Create debug draw of collision mesh

		ManualObject *man = mSceneMgr->createManualObject( "CollisionMesh" ); 
		man->begin( "ETTerrainMaterial", RenderOperation::OT_TRIANGLE_LIST );
		Ogre::Vector3 v;
		hkVector4 hv;
		for(int i = 0; i < displayGeometries.getSize(); i++ )

		{
			hkDisplayGeometry* disp = displayGeometries[i];
			disp->buildGeometry();
			hkGeometry* geom = disp->getGeometry();

			// geom has all infos which you can directly use to render, see SourceCommonBaseTypesGeometryhkGeometry.h
			for( int i = 0; i < displayGeometries[0]->getGeometry()->m_triangles.getSize(); i++ )
			{
				for( int j = 0; j <= 1; j++ )
				{
					hv = ( displayGeometries[0]->getGeometry()->getVertex( i, j ) );
					v = HkOgre::Convert::hkVec3toOgre( hv);
					man->position( v );
				}
				hv = displayGeometries[0]->getGeometry()->getVertex( i, 2 );
				v = HkOgre::Convert::hkVec3toOgre( hv);
				man->position( v );
			}

		}
		man->end();

		manNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();

		manNode->attachObject( man );
		hkTransform transform  = body->getTransform();

		v = HkOgre::Convert::hkVec3toOgre( transform.getTranslation());

		manNode->setPosition( v );

		Ogre::Quaternion q;

		hkQuaternion hq;

		hq.set( transform.getRotation() );

		q = HkOgre::Convert::hkQuatToOgre( hq  );

		manNode->setOrientation( q );

		heightFieldShape->removeReference();
		mWorld->unlock();

附件: 

I'm not entirely sure what you're doing because I haven't had to use a heightmap in havok yet. One thing that I notice is odd is that you are manually calling add geometry. I can't really tell if you have a special reason for this, but what I did is to register my DebugDisplay with Havok, and let Havok call it. When you add your heightmap to the world, Havok will automatically call addGeometry for you. I think Joe was suggesting to do it manually outside of a callback. It seems like you've kind of done both, so I'm not sure what to tell. As a note if this isn't helpful: I just figured out how to do what I needed very recently, so I'm not especially well versed in what you need to do for something like this.

Hi Andrew:

It seems like the transform for the collision mesh still not correct. It is not match with the terrain. hkpShapeDisplayBuilder::buildDisplayGeometries(...) dose not care about the body transform while building the mesh.

You probably can try hkpGeometryConverter::appendGeometryFromRigidBody(...), or hkpGeometryConverter::createSingleGeometryFromWorld(...) , see Demos\Physics\Test\Performance\RayCastBenchmark

Those functions will handle rigid body transform internally and return a single geometry.

Cheers

Joe Developer Support Engineer Havok www.havok.com

I setup all my drawing of my collision mesh and it's accurate so the collision debug mesh matches the collision mesh but the problem is my collision mesh still isn't matching up with my visual terrain . Both appendGeometryFromRigidBody and createSingleGeometryFromWorld have the same result so I suspect it's something to do with my height data reading from the heightmap, but everything looks fine to me -

	const int xRes = heightMap->getWidth();

	const int zRes = heightMap->getHeight();
	hkReal* mHeightData = hkAllocate(xRes * zRes, HK_MEMORY_CLASS_USER);

	hkpSampledHeightFieldBaseCinfo cInfo;

	cInfo.m_xRes = xRes;

	cInfo.m_zRes = zRes;

	cInfo.m_scale = hkVector4(mTerrainGroup->getTerrainWorldSize() / mTerrainGroup->getTerrainSize(),mTerrainGroup->getTerrainWorldSize() / mTerrainGroup->getTerrainSize(), mTerrainGroup->getTerrainWorldSize() / mTerrainGroup->getTerrainSize());

	for (int x = 0; x < xRes; x++)

	{

	   for (int z = 0; z < zRes; z++)

	   {

		  hkReal height = heightMap->getColourAt(x, z, 0).r * mTerrainGroup->getTerrainWorldSize() / (mTerrainGroup->getTerrainSize());
		  mHeightData[x * cInfo.m_xRes + z] = height;

	   }

	}

The only other possibility is the way that Ogre constructs a terrain from a heightmap is different from the way I'm calcing it.

I have a very accurate collision mesh now. Not perfect because scaling issues, which I'm still trying to figure out how to correctly scale it, but very close. Also, it looks as though Havok isn't flipping every other row, causing a wrong triangulation calc.How can I tell havok to flip every other row? Thanks for the help so far.

My current scaling code -

float Scale = mTerrainGroup->getTerrainWorldSize() / (float)(mTerrainGroup->getTerrainSize() - 1);

cInfo.m_scale = hkVector4(Scale, mTerrainGroup->getTerrainWorldSize() / (mTerrainGroup->getTerrainSize() - 44.3 ),Scale);

And my non working scaling code per HavokDavid -

	cInfo.m_maxHeight = mTerrainGroup->getTerrain(0,0)->getMaxHeight();

	cInfo.m_minHeight = mTerrainGroup->getTerrain(0,0)->getMinHeight();

	cInfo.m_scale = hkVector4(mTerrainGroup->getTerrainWorldSize() / (cInfo.m_xRes - 1.0f), mTerrainGroup->getTerrainWorldSize() /(cInfo.m_maxHeight - cInfo.m_minHeight), cInfo.m_zRes - 1.0f);

附件: 

Hi Andrew:

For triangle flip, you should override your own triangle flip function when implementing your custom height field class. hkpSampledHeightFieldShape::getTriangleFlipImpl(); Please refer to one of our sample : SampledHeightFieldDemo.cpp . line 54. It should match the way how ogre render a height map terrain.

By the way, Havok organize the height field mesh grid by grid, each grid has 2 triangles. You can find the code in [url]http://software.intel.com/en-us/forums/topic/335375[/url] . I do not know how ogre render the height field terrain by using height map texture.

For the scale problem, actually, the same as the problem before. You need to figure out how ogre creating the rendering terrain by using height map data, and then use the same way to create the havok custom height field shape class. According to the rendering code, the vertic in each grid can be calculated like:

p00.set( (hkReal) i, heightField->getHeightAt( i, j ), (hkReal) j );
p00.mul( scale );

You CAN probably adjust the scale value little by little to match the render terrain with height field shape, but seems like it is not a good idea. Because I think the way how ogre generate the height field is different between the way how Havok use. I think it should have some classes like that [url]http://www.ogre3d.org/tikiwiki/tiki-index.php?page=Ogre+Compatible+Heigh...

Joe Developer Support Engineer Havok www.havok.com

I fixed the scaling problem, I just have the flip problem. I don't know a way to tell Havok that the row is flipped every other row. I've implemented my own overridden getTriangleFlip and have it return true but it does nothing. I'm still thinking I have to manually come up with the vertex reordering.

登陆并发表评论。