cull library and graphic engine

hello.
We just acquire a licence for a efficient occlusion culling library
(http://www.umbra.fi/index.php?products&umbra)
and I’m trying to incorporate it into panda. I haven’t found anything that could be of help in the forum. I’m currently trying to understand the graphicsEngine.cxx in the panda source.

what’s the best way to do that ?

1/I could try to create my own graphics engine derivated from the one in panda ? and rewriting the “cull_and_draw_together” ?

2/ Is the “add_callback” function used in these cases ? if yes, how this is done ?

Is there a way to do this in python (or c++) without messing in the panda3d code (and without having to recompiling it) ?

Wow, you’re taking on the big challenges.

One of the algorithms I’m most interested in is called “Coherent Hierarchical Culling.” This algorithm seems to be the smartest use of hardware occlusion queries that I know of. Unfortunately, the CHC algorithm is an awkward fit for Panda3D’s internal design.

You see, Panda3D attempts to separate the scene traversal from the rendering. We have one class, the “CullTraverser,” whose job is to walk the scene graph and find everything that needs rendering. When it finds something, it invokes a method on a “CullHandler.” The CullHandler might render the object immediately, or it might put the object into bins for sorting and then rendering. Unfortunately, the CHC algorithm requires that the traverser and handler be tightly coupled. So Panda3D’s design isn’t quite right for this.

To implement CHC, you would have to create your own CullTraverser. However, this CullTraverser would not use the regular CullHandlers. It would have to use a specialized CullHandler that is tightly integrated with the CullTraverser. In fact, there’s a class in grutil called PipeOcclusionCullTraverser that actually derives from both CullTraverser and CullHandler, so that the traverser can be its own handler. This is probably what you would want to do.

But, there’s another stumbling block. CHC needs to record visibility information from frame to frame. Unfortunately, Panda3D uses a technique called “instancing.” Ie, a given node can be inserted into the scene graph in multiple places - it can have multiple parents. As a result, a node can be both visible and invisible at the same time. So you can’t just stick a visibility bit into the node. In fact, there really isn’t any convenient place to store visibility information. I think probably the best bet would be to store a table of the instances that were visible last frame.

Then, there’s one more stumbling block. CHC sends nodes to the graphics card in a strange order. That works fine as long as you’re not trying to do transparency depth-sorting. You would have to do something a little tricky to handle depth-sorting in conjunction with CHC.

Then, there’s this third-party library “umbra.” They say it uses occlusion queries. There are several possibilities:

  1. It uses CHC. If that’s true, I don’t see how a third-party library would help. CHC is a straightforward algorithm, the only tricky part is that our engine isn’t real compatible with it.

  2. It uses a method that’s more naive than CHC. If that’s the case, then I’d say it’s not worth implementing.

  3. It uses a super-cool algorithm that I’ve never heard of before. If that’s true, then it might be worth implementing, but I couldn’t tell you how to do it.

There’s one other occlusion algorithm other than CHC that I have an interest in. This algorithm uses a software hierarchical Z buffer. I don’t know how it would perform, though, so it would be a big experiment.

thank for your reply.
I guest it’s time for me to be a real programmer ! :stuck_out_tongue:

i think that I will start be derivating that pipeOcclusionCullTraverser then.
I have few question:

I hope that a library willing to be used in a modern engine can use Instancing. Otherwise, it’s going to be a lot more complicated.

I think that they have a way to handle that.
If i have use some kind of order ( binOrder) in my scene. I suppose that the same problem ? and that I should re-arrange the mesh myself within those who past the occlusion test ?

I don’t see the problem here. Given that I redefine a pipeOcclusionCullTraverser that use entirely their lib, I should be able to use their lib in panda, or am I missing something ?

I didn’t really say that there would be a problem integrating it. I just said I didn’t think I would need a library to implement CHC.

I’m still working on this subject.

It seems that i have to do a query using the CreateQuery function of DirectX

so, question: how can i get the directX device ( IDirect3dDevice ?) that was created in panda ?

Class GraphicsStateGuardian is a wrapper around the lower-level API (DirectX or OpenGL). I suspect it already supports queries in some way. If not, you will have to add a method to class GraphicsStateGuardian.

Thanks for this help, but I’m afraid I need a little more !

As a first test, I want to get the directX device and use it in the sample code that I have.

I have download the panda source, and managed to compiled it.

Question: how python extending works within panda ?
I mean : how can i created my small function so that it can be use in python ?

I first think that this was the PUBLISHED: macro was used for this, but even with it, my function is not incorporated in the python interface.

Am I missing something obvious ?

You are right about the PUBLISHED: part. But, it requires two additional steps, calling Pandas tool interrogate and interrogate_module.
A tiny bit is explained on this manual page:
panda3d.org/manual/index.php/Interrogate
Interrogate reads the PUBLISHED: stuff and automatically writes python bindings for it. Then, you will need to link in the generated _igate.cxx and _module.cxx files into your stuff as well.
You can take a look at the geoMipTerrain I wrote, compile it and see what steps it does. It is now included in Panda3D, yes, but it can still be interesting to see how the included makefile deals with the pregenerated _igate.cxx and _module.cxx files.

Oh, there is also the panda/src/skel/ directory, which provides an example for creating custom classes.

Thank for all your help, but I’m afraid i need a little more,…

I try to use interrogate,but as my aim is just to add a get_direct3D_device in the dxGraphicsStateguardian9 ( panda/src/dxgsg9 ), I though of adding the interrogate step directly inside the makepanda.

now after a lot of try, I 'm stuck with this:

in the makepanda.py I have replaced (near line 2047) :

TargetAdd('dxgsg9_dxGraphicsStateGuardian9.obj', opts=OPTS, input='dxGraphicsStateGuardian9.cxx')
TargetAdd('libpandadx9.dll', input='dxgsg9_dxGraphicsStateGuardian9.obj')

by

OPTS=['DIR:panda/src/dxgsg9/', 'SRCDIR:panda/src/dxgsg9', 'BUILDING:PANDADX','IMOD:panda', 'ILIB:libdxGraphicsStateGuardian9',"DX9","NVIDIACG","CGDX9",'ADVAPI', 'WINGDI', 'WINKERNEL', 'WINUSER', 'WINMM']
IGATEFILES=GetDirectoryContents('panda/src/dxgsg9', ["*.h", "dxGraphicsStateGardian9.cxx"])
TargetAdd('libdxGraphicsStateGardian9.in', opts=OPTS, input=IGATEFILES)    
TargetAdd('libdxGraphicsStateGuardian9_igate.obj', input='libdxGraphicsStateGuardian9.in', opts=["DEPENDENCYONLY"])
TargetAdd('libpandadx9.dll', input='libdxGraphicsStateGuardian9_igate.obj')

And I have this error:

***Error in /c/Program Files/Microsoft DirectX SDK (March 2008)/include/d3d9.h near line 160, column 2:
    parse error

the d3d9.H file, l 160:

interface DECLSPEC_UUID("D0223B96-BF7A-43fd-92BD-A43B0D82B9EB") IDirect3DDevice9;

So I’m thinking that I’m either missing a build dependency or that it’s a line interrogate is not made to comprehend .

Which is it ?

I have tried, to circle the problem, to include my function in the already parsed file graphicsStateGuardian ( panda/src/display ). There I can add some test function but not the one I want because interrogate can’t comprehend it(no error but the function does not appear in the python interface)

virtual void* get_device_ref();

it 's because of the void*, isn’t it ?
Is there a biased way I can use to pass my argument as an address ?

edit : I 've tried (int*), it get ignored the same way

Your first parse problem is just because interrogate can’t handle all the non-standard stuff that Microsoft puts in their DirectX headers. The easiest workaround would be to add empty, shadow headers to dtool/src/parser-inc, which is where we shadow all of these crazy nonstandard system header files.

Your second approach fails because, as you surmised, Python doesn’t have a void* type, so there’s no way to return void*. Python also doesn’t have int*. It does have int, so you could cast your pointer to int (or long, to be more 64-bit friendly) and return that.

But I wonder about the bigger picture here. Getting a handle to the Direct3D device outside of Panda sounds like it’s asking for trouble–Panda wants to be able to supervise the rendering of the frame from start to finish. If you muck about with the D3D state in the middle of the frame, you could leave Panda very confused. Furthermore, this is kind of violates one of Panda’s design principles, which is to be as platform-agnostic as possible: by putting a direct call to D3D, you’re writing code that will only work on DirectX. That’s OK if it’s really the only way to do it, but I wonder if there’s a better way.

If you’re really integrating some code that wants to make direct use of the D3D handle, shouldn’t that code be integrated tightly within the C++ layer itself, rather than passing it through Python?

Even still, if all you want is to get to the CreateQuery function, Panda already provides an interface for that, via GraphicsStateGuardian::begin_occlusion_query() and end_occlusion_query(). Using this interface has the advantage that it already supports OpenGL, DirectX8, and DirectX9. This interface isn’t exposed to Python, though, because occlusion query algorithms generally require tight integration in the middle of the draw loop, where Python has no business being involved (it’s too slow to handle the inner-loop drawing stuff).

David

Thank for your reply. I will test this first thing Monday morning.

As for the big picture,…you’re right, that I’m doing right now doesn’t make any sense, it 's just a test.

My goal is to completely integrate the library inside panda, probably inside a CullTraverser as josh suggested. But in the meantime, I’m not proficient with either the inside of panda3d or the use of that library.
I have a sample program for the lib. I wanted to integrated the sample inside my own application : the goal is just to hide or show my mesh on the lib instruction, completely outside the real panda render
This gonna be ugly, inefficient and useless, But it will check i have understood that i must do.
I have extended their code so that it can be used for my application… And prepared my python code. I just needed to get the DirectXDevice so I can initialize the lib the way it should be.
If that work, then I would start the real work in the cull traverser.

… I don’t know if that make any senses,…

Hi
I’m back,… with even more question ! :wink:

After a lot of work, I’m finally starting to understand how panda works. :slight_smile:

  1. Frustum: with Umbra I specify it with 6 values ( left, right, up, down, near and far ) with panda I’ve only found the lens.setFov() ( and setFar(), setNear() ). Am I missing a easy function / way to convert one way to another ?

  2. View matrix: working with the Direct3DDevice directly I’ve found that the ‘view’ matrix of Direct3D seems always the identity matrix. Is it true ? ( panda doesn’t use it ? ), then how panda place his objects , by multiplying object Position and camera position internally ?

  3. I’m just making some queries, and drawing some lines, but after I’ve drawn one thing directly ( in direct3DDrawPrimitive,…), I can’t see the pandaScene anymore ( my meshes disappears and won’t coming back )
    I know I must create a lot of mess by directly calling the direct3D device, like I said, it’s just for some test. I would like to know how can I do it without crushing everything

  4. Is there a way to ‘stop’ ( and restart) panda from rendering ? ( it will be easier to debug )

thank again.

I can only answer number four:

Yeah.
If you’re using python:
It is handled by the “igLoop” task (defined by showbase) in taskMgr.
So, after importing DirectStart, try this:

taskMgr.remove("igLoop")

It won’t render anything then.
To make it continue, you can use this very ugly hack:

taskMgr.add(base._ShowBase__igLoop, "igLoop")

I don’t know if there is a better way, though.

If you’re using C++:
Sounds simple. Just stop calling do_frame().

There are a dizzying array of methods to specify frustum properties on the Lens class. In addition to setNear() and setFar(), you also have setFov(), setFilmOffset(), setAspectRatio(), and more exotic things like setViewVector() and setKeystone().

For setting the frustum up from six planes, try setFrustumFromCorners(). Or, you can replace your normal PerspectiveLens with a MatrixLens, which has a setUserMat() method that allows you to completely specify the projection matrix.

Right. Panda doesn’t need to make a distinction between view matrix and world matrix. It doesn’t present this distinction to the Panda user, either–everything is just an object in its own space, including the camera.

All I can say is, be sure you return the Direct3D state to exactly the same way you found it. Panda assumes it is the only one modifying state variables, and if you change something, Panda has no way to know that it has been changed, so you’d better put it back.

David

Thanks for your answers ! That’s really helpful !:slight_smile:

Well I’m trying.
There is a lot of thing i can change without messing with panda. But if i doing a DrawPrimitive, everything gone !

DIRECT3DDEVICE->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST,
                                                        0,
                                  query->getVertexCount(),
                                query->getTriangleCount(),
                                    query->getTriangles(),
                                           D3DFMT_INDEX32,
                                     query->getVertices(),
                                         sizeof(Vector3));

I will try to do it while panda is stopped, see if that change anything.

EDIT: well no change if the igLoop is remove

EDIT 2: In the direct3D doc I’ve seen that :

I can suppose these are used by panda. I’ve try to reset them after the call, but there is no change yet.

            IDirect3DIndexBuffer9* ppIndexData;
            IDirect3DVertexBuffer9* ppStreamData;
            UINT poffsetinbytes;
            UINT pstride;

            D3D->GetStreamSource(0,&ppStreamData,&poffsetinbytes,&pstride);
            D3D->GetIndices(&ppIndexData);
            D3D->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST,
                                                         0,
                                   query->getVertexCount(),
                                 query->getTriangleCount(),
                                     query->getTriangles(),
                                            D3DFMT_INDEX32,
                                      query->getVertices(),
                                          sizeof(Vector3));
            
            D3D->SetIndices(ppIndexData);
            D3D->SetStreamSource(0,ppStreamData,poffsetinbytes,pstride);

Why do you need this direct stuff? Coudn’t you cull with just using panda3d API?

I’m stumped. I can’t see any reason why a simple call to DrawPrimitive() would be enough to screw up Panda’s state.

David

Two more thing in my research:

  1. I still can’t have the same projection matrix / frustum. even if I use the matrix lens, I don’t have the same rendering in screen.

So I checked the projection matrix of Direct3D, it’s always the same :

1,0,0,0,
0,1,0,0,
0,0,0.0005,0,
0,0,0.5,1

It seems it’s panda the use/create this projection matrix,… but what for ? How can I get rid of it ?

  1. about the directDrawPrimitive,… I can’t understand exactly myself why this command mess with panda. but I hoped you could help me there :’(

Like I said, I can and will once I start the ‘real’ stuff. But for now understanding how this can work is mandatory:

  • For the lib to work, I must assure that it’s frustum and panda3D’s are the same, therefore all my question with the lens, and projection matrix.
    I must send the query to directX, but the lib gave me the triangles in world space, so i had to understand if there are transform to make before sending them to panda.

Now panda continue to show things, but textures and shaders disappears It’s not great but I can work with that. If only I could deal that whole frustum thing,…