Panda PhysX

Looks good, but you are right, 33 fps is not really a good at this stage of development. So far I don’t see any places where I could improve performance, but I keep an eye out. There are several other things you could try:

  • Put cloth meshes to sleep if the are not visible

  • Try to reduce the amount of triangles per cloth. Just enough so it still looks ok.

  • Read the NVIDIA PhysX documentation. Many of the cloth parameters have impact on the overall performace, like self-collision for example. Oh, and please tell me if you are missing flags or methods you see in the PhysX documentation and would like to have available in Python

enn0x

PS: things like AI should not be done “per frame”. Computation could require different amounts of time depending on what the AI has to decide, and the result would be a very instable framerate. In my opinion AI should be done in a seperate thread (Python thread are fine), and once the AI has reached a decision it puts the action it has decided on a queue where the render thread picks it up.

yeah, i’ve been reading the PhysX SDK docs - they have a full section on Cloth, and even some tips on performance. Tried different flags, including self-collision, as you suggesting. The problem with less vertices is that the ball then goes through the net. So it’s a balancing act in a sense: use more vertices - nice collisions, but frame rate drops. Use less vertices - nice framerate, but the ball travels through. I am using sleep for cloths and that does improve things a lot. (initially, before sleep logic was added, i was getting a pathetic 19 fps with both nets active at all times!)

Thanks for suggestion on the AI thread. I was thinking that maybe AI logic per frame is indeed not necessary, so i thought i’d have it performed every X frames or so, but i like your idea of a separate thread a lot more.

I’ll certainly keep an eye for things that PhysX C++ SDK provides, but PandaPhysX doesn’t, but so far i haven’t come across one yet.

i was able to improve the frame rate dramatically by tweaking the timesteps. Things are looking pretty good now :slight_smile: With variable steps, doing 2 steps per frame gives me about 90-100 fps, where previously it was 35-40. With fixed steps of 1/120.0 of a second, things are even faster. However, these were done with Panda’s sync-video option set to 0, so the frame-rate was uncapped. With sync-video enabled, the frame-rate is capped at 60 on my laptop, and it seems like variable steps give a slightly better performance, although both are working great.

I’ve read that fixed steps are strongly recommended, so my question is what is so dangerous about using variable steps? I understand that very small and very large steps can lead to instability in the simulation, but are there other reasons to avoid it?

(While this isn’t yet the case with my game, i’m thinking of the situation, where an application is already running hot, due to some other non-physics-related calculations. Using fixed small timesteps would contribute to slowing it down even further, right? With variable timesteps, the burden of physics simulation doesn’t increase, as the frame rate drops, although the accuracy/stability might be affected.)

Looks good.

About problems with variable timesteps: I don’t know any other problems, besides what you already mentioned. I think the main concerns are single frames which need much longer than the rest of the frame, e.g. an average of 40fps, with one or a few single frames requiring more than 0.2 seconds (just fictional numbers). I use variable timesteps myself.

This worries me a bit. It sounds like tunneling, but I would have expected this effect with low framerates and not high framerates. Anyway, if it is tunneling then…

…you have found one: CCD (continuous collision dynamics). Not yet done. I have to start working on this sometime.

enn0x

that could be it, but i think it’s also due to the way collision is done for cloth: only vertices are considered, not the links between them, so if the holes in the goal net are big enough, the ball slips through it even on small speeds. I had that problem initially when i didn’t have enough vertices in the net due to performance concerns, but since then i found the right balance, so now it only happens when the ball is travelling really fast.

With high-speed shots, i think you’re right - it’s the tunneling effect that causes the ball to go through. If you can tackle the CCD - it’d be totally awesome! Looking forward to that version of PandaPhysX :slight_smile:

I’m having problems building these wrappers. I get

*** Error in config_physx.h near line 33, column 47:
parse error

when trying to scons the sources. The compiler complains about

EXPTP_PANDAPHYSX

but I can’t anything more then that. Any clue to what I’m doing wrong ?

Wrong versions is my best bet. What versions are you using?

  • Panda3D
  • scons
  • C++ compiler (+SDK if windows)
  • Python

enn0x

Even simpler: scons can’t find your Panda3D header! Look into the scons files and set them properly.

enn0x.

Well, problem solved but I did run over some bugs:

  1. In SConscript.VC9 (top level dir) there are some path variables being set which do not work for Windows XP 64 bit systems. I altered them to point at “…\Program Files (x86)…” which works fine now.

  2. There was some strange bug in one of the NVIDIA PhysX headers. Located in “\NVIDIA PhysX SDK\v2.8.1\SDKs\Physics\include\NxShapeDesc.h” line 291 there was a return value missing for “NxShapeDesc& operator=( const NxShapeDesc& )”. The function is empty and I just inserted “return *this;” which works fine now. I was surprised to see this …

Anyway, it’s working fine now.

Congratulations. Since I don’t have a 64bit Windows and thus can’t compile for 64bit: do you want to share the binaries with others?

Not a bug, but a feature. People can install the required components wherever they want under Windows. For example Panda3D’s default install location is C:\Panda3D, not C:\Programme\Panda3D (by the way: German version of XP use C:\Programme.… and not C:\Program Files.…). So there needs to be a way to configure paths.

This is a bug, but not a NVIDIA PhysX bug. It is a bug in several versions of the Microsoft C++ compilers. gcc does not have a problem with this correct header file. I have modified my header files too, but forgot to make a note in the readme files.

enn0x

Sure, I can share the binaries. Write me a PM where to send them and I’ll do it.

Edit:
I have a two questions regarding your wrapper @ ennox:

  1. Is there a list of material indexes ? I wonder what kind of material corresponds with index 1, with index 2, etc.

  2. How would you create a box opened to one side ? I can’t seem to be able to “strip” a standard box object of one side yet I can’t see any way to create only portions of planes, e.g. some square or rectangle.

Thanks
Amnu

First, “material index” is a number created by the PhysX SDK whenever a material is created. A material index is not reused, even if the material is released already. So the material index can be higher than the number of materials currently created. So far there is no real usage of material index. Somewhere in the future I want to add material index e. g. to traingle meshes, to create terrains with “slippery” areas for example.

To get a list of all materials:

scene.getMaterials()

To get a list of all used material indices:

[ x.getMaterialIndex() for x in scene.getMaterials() ]

If it is a static object you can use a triangle mesh. If it is a dynamic object you can create one actor and add 5 shapes to it, one for each face of the open box. Each face could be a “thin” box, e.g. with dimmensions (10,10,1).

enn0x

I can see “libpandaphysx.lib” being built so I suppose the wrappers are ready for use with C++ Panda applications. My question is what do I have to consider/pay attention to ? Usually I would simply link to that library and include all necessary header files. The problem is, when trying to include all those necessary header files the list just keeps getting longer and longer and longer until I even have to include the path to the NVIDIA Physix SDK which seems a bit awkward.

I wouldn’t include the header files directly into your project; just add the appropriate directory(ies) to the INCLUDE_PATH setting in your project.

It seems appropriate to include the PhysX SDK on your paths.

David

Like drwr said… this would be the required directories on windows (no particular syntax):

INCLUDE : PANDAPHYSX-SOURCE-DIR
INCLUDEPATH : PHYSXDIR + '/Physics/include'
INCLUDEPATH : PHYSXDIR + '/PhysXLoader/include'
INCLUDEPATH : PHYSXDIR + '/NxCharacter/include'
INCLUDEPATH : PHYSXDIR + '/NxExtensions/include'
INCLUDEPATH : PHYSXDIR + '/Foundation/include'
INCLUDEPATH : PHYSXDIR + '/Cooking/include'

LIBPATH : PANDAPHYSX-BUILT-DIR (wherever libpandaphysx.lib is)
LIBPATH : PHYSXDIR + '/lib/Win32'

LIBS : 'pandaphysx'
LIBS : 'PhysXLoader' 
LIBS : 'NxCharacter'

Unfortunately the NVIDIA PhysX SDK has it’s header files in 6 different directories and not just one, like with most other SDK’s.

PandaPhysX wrapper classes have public (not PUBLISHED) methods to access pointers to the wrapped objects. This is why you have to add the PhysX include directories too.

enn0x

I think I’ve encountered some “design problem” inside the wrappers.

Trying to create a PhysScene in C++ I do

#include "PhysScene.h"

which will force me to

#include "PhysDebugNode.h"
#include "PhysMaterial.h"

since the PhysScene requires knowledge about these two types. Doing it this way

#include "PhysScene.h"
#include "PhysDebugNode.h"
#include "PhysMaterial.h"

works while

#include "PhysDebugNode.h"
#include "PhysMaterial.h"
#include "PhysScene.h"

will have the compiler complain about PhysDebugNode not being expressed correctly (“looks like function, skipping apparent body”). Since PhysDebugNode requires PhysScene, at least at the time of the forward decleration, I recommend ensuring it is also included at that time. I did not go into more depth, e.g. if it is included implicitly at some earlier stage. But from the looks of it I guess it PhysScene.h was supposed to be included by some other file and the include statement was therefore skipped in PhysDebugNode. I suggest including it explicetly so this error can’t happen at all.

But then again, I’m probably just doing some mistake yet again :slight_smile:

Would you mind giving me yet another hint ?

I’m having two problems with this code:

#include "asyncTaskManager.h"
#include "genericAsyncTask.h"
#include "pandaFramework.h"
#include "pandaSystem.h"
#include "PhysEngine.h"
#include "PhysScene.h"
#include "PhysSceneDesc.h"
#include "PhysDebugNode.h"
#include "PhysMaterial.h"

PandaFramework g_framework; 
PT(AsyncTaskManager) g_pTaskMgr;
PT(PhysScene) g_pPhysScene;
PT(ClockObject) g_pGlobalClock;

AsyncTask::DoneStatus RunPhysics(GenericAsyncTask* task, void* data)
{
  g_pPhysScene->do_physics(g_pGlobalClock->get_dt());
  return AsyncTask::DS_cont;
}

int main(int argc, char *argv[])
{
  g_pGlobalClock = ClockObject::get_global_clock();
  g_pTaskMgr = AsyncTaskManager::get_global_ptr();
  g_pTaskMgr->add(new GenericAsyncTask("Runs the physics engine", &RunPhysics, (void*) NULL));
  
  g_framework.open_framework(argc, argv);
  g_framework.set_window_title("My Panda3D Window");  
  WindowFramework *window = g_framework.open_window();

  PT(PhysSceneDesc) pPhysSceneDesc = new PhysSceneDesc();
  pPhysSceneDesc->set_gravity(LVector3f(0,0,-9.81));
  PT(PhysScene) pPhysScene =  PhysEngine::create_scene(*pPhysSceneDesc);
  //PT(PhysDebugNode) pDebugNode = pPhysScene->get_debug_node();
  //PhysDebugNode* test = pDebugNode;
  //window->get_render().attach_new_node(test);
 
  g_framework.main_loop();
  g_framework.close_framework();
  return (0);
}

The first issue is with the RunPhysics task. Right when it is run the first time it will cause an “invalid reading access” exception in PhysScene::do_physics() at the first assertion

nassertv( _error_type == ET_ok );

The next problem is with the commented lines dealing with the debug node. If I try to add the debug node to the renderer I get an access violation error from PointerToBase::update_Type().

Both problems leave me guessing…

I had a look at your code.
Good news: A few small changes and it works perfectly.
Bad news: I’m afraid to say the problem is just beginner mistakes.

Look at this piece of your code:

AsyncTask::DoneStatus RunPhysics(GenericAsyncTask* task, void* data)
{
  g_pPhysScene->do_physics(g_pGlobalClock->get_dt());
  return AsyncTask::DS_cont;
} 

Now look at your main method:

PT(PhysScene) pPhysScene =  PhysEngine::create_scene(*pPhysSceneDesc); 

g_pPhysScene has never been initialized! It is always NULL! In your main method you have created another, local pointer to a scene.

This one works fine on my computer:

#include "asyncTaskManager.h"
#include "genericAsyncTask.h"
#include "pandaFramework.h"
#include "pandaSystem.h"

#include "PhysEngine.h"
#include "PhysSceneDesc.h"
#include "PhysScene.h"
#include "PhysDebugNode.h"
#include "PhysMaterial.h"

PandaFramework g_framework;
PT(AsyncTaskManager) g_pTaskMgr;
PT(PhysScene) g_pPhysScene;
PT(ClockObject) g_pGlobalClock;
PT(PhysDebugNode) g_pDebugNode;

AsyncTask::DoneStatus RunPhysics(GenericAsyncTask* task, void* data)
{
  cout << "dt:" << g_pGlobalClock->get_dt() << "\n";
  g_pPhysScene->do_physics(g_pGlobalClock->get_dt());
  return AsyncTask::DS_cont;
}

int main(int argc, char *argv[])
{
  g_pGlobalClock = ClockObject::get_global_clock();
  g_pTaskMgr = AsyncTaskManager::get_global_ptr();
  g_pTaskMgr->add(new GenericAsyncTask("Runs the physics engine", &RunPhysics, (void*) NULL));
 
  g_framework.open_framework(argc, argv);
  g_framework.set_window_title("My Panda3D Window"); 
  WindowFramework *window = g_framework.open_window();

  PT(PhysSceneDesc) pPhysSceneDesc = new PhysSceneDesc();
  pPhysSceneDesc->set_gravity(LVector3f(0,0,-9.81));
  g_pPhysScene = PhysEngine::create_scene(*pPhysSceneDesc);

  g_pDebugNode = g_pPhysScene->get_debug_node();
  window->get_render().attach_new_node(g_pDebugNode);
 
  g_framework.main_loop();
  g_framework.close_framework();
  return (0);
}