Advertisement

Bullet Physics Debug Drawer

Started by June 06, 2013 01:01 AM
23 comments, last by xexuxjy 11 years, 8 months ago

The main problem is that Bullet debug drawer has never meant to be used for massive drawing, but rather to draw simplified debug tests cases.

Consider drawLine. This is an extremely fine-grained control.

The issue was recently pointed out on Bullet forum as well. It is basically the same of using immediate mode VS buffer-based rendering.

Except it's actually worse because even if you have empty implementations doing NOP thus saving the drawcalls themselves, you still end up calling drawLine thousands of times to do something which could be pulled out in a single shot!

Compared to standard function calls, indirected function calls are not much slower. Problem is function calls are damn slow by themselves compared to optimized code. In my experience, breaking a piece of optimized code with a single function call can make it easily 20x slower. If this is a high-frequency, hot piece of code, such as the case of a renderer, we're going to have quite some issues with it!

So what do you do to fix it?

Nothing, you cannot. This is expected behavior.

Personally I wish bullet could just pass us information about what to draw and where (draw this collision shape here), just as the case for DrawSphere, DrawCylinder, DrawCapsule... Looks like heightmap terrains don't have this right to exist which is a bit of a shame but understandable to a certain degree.

Previously "Krohm"

@Steve_Segreto:

You didn't understand me correctly.

I made a class from the interface btIDebugDraw that actually draw lines by overriding drawLine() now it's working and drawing lines for physics debugging, however the terrain is causing it to extremely slow down (I'm guessing because the terrain is large maybe?)

I thought that drawing lines could be slowing down rendering, so I decided to comment drawLine() method, now even it's drawing nothing but I'm still getting the same problem when debug drawing the terrain.

Advertisement

@Krohm: I think you got very close to the point, now what to do? Should I never debug draw everything? Maybe I should only debug draw close meshes?

I don't understand why the physics engine don't just draw the physical mesh at once (with wireframe enabled) instead of drawing too many lines.

What do you expect debugDraw is for? It's not for using 100% of the time to render your application. Its there for those emergency situations when you need to see why the physics are behaving a certain way - so you tie it to a keypress, enable when the user needs it and disable it when they are done.

I don't understand why the physics engine don't just draw the physical mesh at once (with wireframe enabled) instead of drawing too many lines.

Why would a physics engine draw anything for you? That's what a rendering engine is for. You supposedly have the collision geometry before you send it to Bullet anyway, so you can do more optimized rendering at that moment if all you ever wished to display was a collision wireframe. The whole point of debugDraw is for you to see visually that Bullet correctly recognizes your collision shape, rotation, translation and scaling and all :)

I don't understand why the physics engine don't just draw the physical mesh at once (with wireframe enabled) instead of drawing too many lines.

Of course because it cannot. First, the poor thing has no idea what API you're using. It doesn't know how to integrate its data in your rendered (but for debug purposes, we can let this pass). It has no MVP nor anything else.

It has an interface to tell you that... one that is somewhat broken... but ok-ish for its purpose.

Anyway, I wouldn't draw-debug a so massive test. It's meant for debug only on test data.

You supposedly have the collision geometry before you send it to Bullet anyway

But the collision geometry isn't always drawable. Some collision shapes cannot actually be drawn by neither the physics nor the real graphics engines. The hull for example has no explicit connectivity and can only be drawn as a set of lines (we sure can figure it out but it will take some work). The heightmap is a bit easier, at least we know how to place the triangles.

Yet we would have no chance at intercepting those as the current interface does not allow to pull shape information, unless there's a specific call for it.

Previously "Krohm"

Advertisement

Bullet uses a per triangle call-back for processing terrain shapes (height maps, bvh meshes) - the default debug draw one will just draw each triangle as 3 lines with no vertex sharing , or any cleverness to avoid un-necessary work, on large meshes that can get very very slow. Can you provide your debug-draw implementation so we can comment more?


BulletDebugDrawer::BulletDebugDrawer(LPDIRECT3DDEVICE9 lpDevice): device(lpDevice), m_debugMode(0)
{

}


BulletDebugDrawer::~BulletDebugDrawer()
{
}

void BulletDebugDrawer::drawLine(const btVector3& from,const btVector3& to,const btVector3& fromColor, const btVector3& toColor)
{

}

void BulletDebugDrawer::drawLine(const btVector3& from,const btVector3& to,const btVector3& color)
{
D3DXVECTOR3 vPoints[2];
vPoints[0].x = from.getX();
vPoints[0].y = from.getY();
vPoints[0].z = from.getZ();
vPoints[1].x = to.getX();
vPoints[1].y = to.getY();
vPoints[1].z = to.getZ();
d3ddev->SetFVF(D3DFVF_XYZ);
d3ddev->SetVertexShader(NULL);
d3ddev->SetPixelShader(NULL);
D3DXMATRIX worldMatrix;
D3DXMatrixIdentity(&worldMatrix);
device->SetTransform(D3DTS_WORLD, &worldMatrix);
device->DrawPrimitiveUP(D3DPT_LINELIST, 1, vPoints, sizeof(D3DXVECTOR3));
}

void BulletDebugDrawer::drawBox(const btVector3& bbMin, const btVector3& bbMax, const btVector3& color)
{


}


void BulletDebugDrawer::drawSphere (const btVector3& p, btScalar radius, const btVector3& color)
{

}

void BulletDebugDrawer::drawTriangle(const btVector3& a,const btVector3& b,const btVector3& c,const btVector3& color,btScalar alpha)
{

}

void BulletDebugDrawer::setDebugMode(int debugMode)
{

}

void BulletDebugDrawer::draw3dText(const btVector3& location,const char* textString)
{

}

void BulletDebugDrawer::drawContactPoint(const btVector3& pointOnB,const btVector3& normalOnB,btScalar distance,int lifeTime,const btVector3& color)
{

}

void BulletDebugDrawer::reportErrorWarning(const char* warningString)
{

}

That is very performance heavy. You could for example accumulate vertices when drawLine is called.

Noone is forcing you to draw something in function that is called Draw...Something.


struct Line
{
    D3DXVECTOR3 from;
    D3DXVECTOR3 to;
};
std::vector<Line> vLines;
...
void BulletDebugDrawer::drawLine(const btVector3& from,const btVector3& to,const btVector3& color)
{
    Line ln;
    ln.from = D3DXVECTOR3(from.getX(), from.getY(), from.getZ());
    ln.to = D3DXVECTOR3(to.getX(), to.getY(), to.getZ());
    vLines.push_back(ln);
}

then after you update bullet and his debug drawer:


void update()
{
    ...
    device->DrawPrimitiveUP(D3DPT_LINELIST, vLines.size(), &vLines[0], sizeof(D3DXVECTOR3));
    vLines.clear();// empty it after drawing to be ready for next frame
}

Or you could do it even faster with device->DrawPrimitive(...) but you need to create dynamic vertex buffer for lines, i leave you to implement that.

@belfegor: I have to create vertex and index buffer in the class constructor, I don't know how many vertices and indices I will be using, so how many vertices and indices should I create using device->CreateVertexBuffer() and device->CreateIndexBuffer()?

This topic is closed to new replies.

Advertisement