| Author |
Physics Sample
Code |
Jeff Genesis God
Joined: Dec 06,
1998 Posts:
946 From:
Oxnard, CA, USA
|
Posted: 2001-08-30
18:36
Here is some
sample code on how to implement a very simple physics system. It
only has one physics object, and no forces are applied. The only
thing that will move the object is gravity.
I wrote this
code fast, and cut a lot of it out from another program. This is not
a full program, it's only intended to give you an idea of how to
implement physics using G3D. Also, there are probably typos and
error.
If you need any further explanation, post you
questions, and I'll try to answer them.
Code:
|
gePhysicsSystem *PS;
gePhysicsObject *PO;
geXForm3d Xform;
geExtBox ExtBox;
geActor *Actor;
BOOL Running = TRUE;
DWORD Timer1 = timeGetTime();
DWORD Timer2 = Timer1;
geVec3d OldVec,NewVec;
GE_Collision Col;
// Load an actor
LoadActor(Actor,"Dema.act");
geXForm3d_SetIdentity(&Xform);
geActor_GetDynamicExtBox(Actor,&ExtBox);
// Create physics system
PS = gePhysicsSystem_Create();
// Create physics object
PO = gePhysicsObject_Create(&Xform.Translation,1.0f,GE_TRUE,GE_TRUE,0.00005f,0.00005f,&ExtBox.Min,&ExtBox.Max,1.0f);
// Add physics object to physics system
gePhysicsSystem_AddObject(PS,PO);
// Set physics object XForm
gePhysicsObject_SetXForm(PO, &Xform, 0);
// physics and rendering loop
while(Running)
{
// This is used to limit the number of times the physics are updated
// The time used will entierly depend on your program
// Basiclly this make the physics independent of rendering
if ((timeGetTime()-Timer1) > 2)
{
// reset the timer
Timer1 = timeGetTime();
// Save the xform's position before updating
OldVec = Xform.Translation;
// Update the physics system, the largest number is 0.03f and
// it only makes a differance if physics joints are added to the physics system.
// This function will call gePhysicsObject_ComputeForces() and
// gePhysicsObject_Integrate() for every physics object in the physics system.
gePhysicsSystem_Iterate(PS,0.03f);
// Get the updated XForm
gePhysicsObject_GetXFormInEditorSpace(PO, &Xform, 0);
// save the xform's position after updating
NewVec = Xform.Translation;
// test if there was a collision
if (geWorld_Collision(World,&ExtBox.Min,&ExtBox.Max,&OldVec,&NewVec,GE_CONTENTS_SOLID_CLIP,GE_COLLIDE_ALL,0xffffffff,NULL,NULL,&Col))
{
// Collision found do something
Xform.Translation = Col.Impact;
gePhysicsObject_SetXForm(PO, &Xform, 0);
}
// Set actors xform
geActor_ClearPose(Actor, &Xform);
}
// Limit rendering to 30 FPS
// This is needed because physics are slow and when
// trying to render every frame the physics slows to a crawl
if ((timeGetTime()-Timer2)>34)
{
Timer2=timeGetTime();
if (!geEngine_BeginFrame(Engine, Camera, GE_TRUE))
Running = 0;
if (!geEngine_RenderWorld(Engine, World, Camera, 0.0f))
Running = 0;
if (!geEngine_EndFrame(Engine))
Running = 0;
}
}
|
|
|
jwvanderbeck Administrator
Joined: Dec 18,
2000 Posts:
2213 From: Palm
Bay, FL, USA
|
Posted: 2001-08-30
20:16
Thanks a million
for this Jeff. I'm printing it out right now
One thing
though, is I could really learn from seeing hot to handle the
collision between two actors as it relates to the physics system.
I'm really unclear on that, since the physics system doesn't do the
collision. So I am unclear what information I need to gather from
the collion, and how/what to pass on to the physics system.
_________________ - John Vanderbeck - Producer, Dark
Relic "Better leave the lights on"
- Novus Delta "Change is
coming..."
|
jwvanderbeck Administrator
Joined: Dec 18,
2000 Posts:
2213 From: Palm
Bay, FL, USA
|
Posted: 2001-08-30
20:24
A question. If
you are ding your physics, seperately from the main loop, wouldn't
that cause problems with it bien gupdated more often then everythign
else? I.E. An object falling further than it should because the
gravity was applied twice for only one pass of the main loop?
Or maybe I misunderstood your comments here. If so, I
apoligize for my idiocy
_________________ - John Vanderbeck - Producer, Dark
Relic "Better leave the lights on"
- Novus Delta "Change is
coming..."
|
Jeff Genesis God
Joined: Dec 06,
1998 Posts:
946 From:
Oxnard, CA, USA
|
Posted: 2001-08-30
23:19
Quote:
|
One thing though, is I could really learn
from seeing hot to handle the collision between two actors
as it relates to the physics system. I'm really unclear on
that, since the physics system doesn't do the collision. So
I am unclear what information I need to gather from the
collion, and how/what to pass on to the physics system.
|
|
I really
haven't done collisions with two actors yet, so I basically would be
guessing how to do it.
Quote:
|
A question. If you are ding your physics,
seperately from the main loop, wouldn't that cause problems
with it bien gupdated more often then everythign else? I.E.
An object falling further than it should because the gravity
was applied twice for only one pass of the main loop?
|
|
Your
question made me go look through the source code again and I made a
mistake in the comment about gePhysicsSystem_Iterate() function,
0.03f is the highest number that can be used and it is used as the
delta time for the gePhysicsObject_Integrate() function. So
basically what that means is every time you call
gePhysicsSystem_Iterate() with a value of 0.03f it is updating the
physics system 30 milliseconds. So you need to call that function
about 34 times a second to get it to update in real time.
So
the code should look like this for real time:
Code:
|
.
.
.
if ((timeGetTime()-Timer1) > 30)
{
Timer1 = timeGetTime();
OldVec = Xform.Translation;
gePhysicsSystem_Iterate(PS,0.03f);
.
.
.
|
|
or it can look
like this:
Code:
|
.
.
.
while(Running)
{
OldVec = Xform.Translation;
gePhysicsSystem_Iterate(PS,(float)(timeGetTime()-Timer1)/1000);
Timer1 = timeGetTime();
gePhysicsObject_GetXFormInEditorSpace(PO, &Xform, 0);
.
.
.
|
|
The first
method updates the physics system only every 30 milliseconds. The
second method updates the physics system every time through the loop
but using the delta time. Both methods work and both have advantages
and disadvantage.
The first method doesn't take up as much
CPU power. But it has a fixed rate of movement. You could decrease
the time from 30 millisecond to a lower number but you also need to
lower the 0.03f in the gePhysicsSystem_Iterate() function.
The second method takes more of the CPU because it get
called more often but you probably get smoother physics.
Hopefully I answered your question and you understand what I
said.
|
jwvanderbeck Administrator
Joined: Dec 18,
2000 Posts:
2213 From: Palm
Bay, FL, USA
|
Posted: 2001-08-30
23:29
Actually, I don't
see how the second method is goign to take more power. If you limit
your rendering loop to 30FPS, and your PhysicsIterate is based on
your rendering loop, then it too should only be calced 30FPS.
_________________ - John Vanderbeck - Producer, Dark
Relic "Better leave the lights on"
- Novus Delta "Change is
coming..."
|
jwvanderbeck Administrator
Joined: Dec 18,
2000 Posts:
2213 From: Palm
Bay, FL, USA
|
Posted: 2001-08-30
23:33
And in actuality,
basing it on delta time coudl SAVE cpu time..What if a render loop
takes a bit longer? Then instead of still doing it every 30ms, you
might actually do a few less calcs that cyle, just with a longer
iteration. _________________ - John Vanderbeck -
Producer, Dark
Relic "Better leave the lights on"
- Novus Delta "Change is
coming..."
|
Jeff Genesis God
Joined: Dec 06,
1998 Posts:
946 From:
Oxnard, CA, USA
|
Posted: 2001-08-30
23:57
Quote:
|
Actually, I don't see how the second method
is goign to take more power. If you limit your rendering
loop to 30FPS, and your PhysicsIterate is based on your
rendering loop, then it too should only be calced 30FPS.
|
|
The
physics are not tied to the rendering, I just removed the first time
check not the second one. It is still rendering at 30 FPS but the
physics could be getting updated 100 times a second. And this will
take up more of the CPU time opposed to just updating the physics
every 30 milliseconds which would be about 34 times a second.
For what your doing the second method is probably better
because you can keep track of the time with in your physics class
and when ever the update method is called you can calculate the
delta time. But for this example the first method is probably better
because it's not updating the physics a 100 times a second leaving
room for other thing like AI or a particle system. It all depends on
how you use the physics.
|
Jeff Genesis God
Joined: Dec 06,
1998 Posts:
946 From:
Oxnard, CA, USA
|
Posted: 2001-08-31
00:37
Quote:
|
And in actuality, basing it on delta time
coudl SAVE cpu time..What if a render loop takes a bit
longer? Then instead of still doing it every 30ms, you might
actually do a few less calcs that cyle, just with a longer
iteration.
|
|
For this
example it is not the case because all it's doing is physics and
rendering but in a real program you are exacly right. It all depends
on your program. The level I used for this example only has a few
polys. 
|
securitron Genesis God
Joined: Sep 21,
1999 Posts:
478 From:
CANADA
|
Posted: 2001-08-31
00:57
there may be a
problem with the timer1 and the > sign. if rendering takes awhile
then the test at timer 1 could be way off imo
|
Jeff Genesis God
Joined: Dec 06,
1998 Posts:
946 From:
Oxnard, CA, USA
|
Posted: 2001-08-31
02:25
Quote:
|
On 2001-08-31 00:57, securitron wrote:
there may be a problem with the timer1 and the >
sign. if rendering takes awhile then the test at timer 1
could be way off imo
|
|
If you
are talking about:
if ((timeGetTime()-Timer1) > 2)
in the original code, I updated it in one of my previous
posts to:
if ((timeGetTime()-Timer1) > 30)
But you are correct, if the rendering does take longer
than 30 milliseconds your physics will not be in real time and you
probably won't be getting 30 FPS.
The only way to correct
this is to ether reduce the rendering time by removing polys or
using a lower frame rate. Or to call gePhysicsSystem_Iterate() twice
to make up for the extra time. But this will cause the step between
actor updates to be large and might cause jerky movement for the
actor.
|
ZaG Godling
Joined: Mar 29,
2000 Posts:
312 From:
Ohio
|
Posted: 2001-08-31
06:15
Well, now I'm
lost. I had essentially the exact same thing set up to do gravity
for my actors but it didn't work. The only difference between this
and what I did was I didn't use a gePhysicsSystem. I just create a
gePhysicsObject for my actor and then I called
gePhysicsObject_ComputeForces and gePhysicsObject_Integrate myself
instead of using the gePhysicsSystem_Iterate to do it. But mine
doesn't work. The actor floats above the ground never going down.
Weird.
ZaG
|
Jeff Genesis God
Joined: Dec 06,
1998 Posts:
946 From:
Oxnard, CA, USA
|
Posted: 2001-08-31
12:57
Quote:
|
Well, now I'm lost. I had essentially the
exact same thing set up to do gravity for my actors but it
didn't work. The only difference between this and what I did
was I didn't use a gePhysicsSystem. I just create a
gePhysicsObject for my actor and then I called
gePhysicsObject_ComputeForces and gePhysicsObject_Integrate
myself instead of using the gePhysicsSystem_Iterate to do
it. But mine doesn't work. The actor floats above the ground
never going down. Weird.
|
|
I just
tried to remove the physics system and just use a physics object and
it didn't work. So I looked at the G3D source code and found that
the ConfigIndex used for the physics object is manipulated by the
physics system. I didn't really understand what it was doing but I
do know that the ConfigIndex can only be 0 or 1 and that the physics
system alternates between them every time gePhysicsSystem_Iterate()
is called. I tried to emulate what it was doing with no success. I
would suggest just sticking to using a physics system and adding an
object to it. If you really want to have your objects separated you
can use a different physics system for every object.
|
ZaG Godling
Joined: Mar 29,
2000 Posts:
312 From:
Ohio
|
Posted: 2001-08-31
13:41
Yeah, I switched
to using a PhysicsSystem this morning. After trying to manipulate
the config indexes myself I decided to try using a instead. Still
doesn't work but I'm tracing through it and at least the xform is
changing now which it wasn't before so I'm a step
further.
|
Jeff Genesis God
Joined: Dec 06,
1998 Posts:
946 From:
Oxnard, CA, USA
|
Posted: 2001-08-31
15:12
Here is some
updated code. I changed a few of the things that have been discussed
in this thread.
I added a high performance counter,
timeGetTime() was not getting the job done. All the code for it was
taken from GTest. I also removed the timer that limited the physics
and it now just uses the high performance counter to calculate the
delta time. Another addition is the
gePhysicsSystem_GetSourceConfigIndex() function. It will get the
current config from the physics system and the returned number
should be used as the config index for the physics objects. This
number just seems to alternate between 0 and 1.
Here is the
code:
Code:
|
// Needed for high performance counter
static void SubLarge(LARGE_INTEGER *start, LARGE_INTEGER *end, LARGE_INTEGER *delta)
{
_asm {
mov ebx,dword ptr [start]
mov esi,dword ptr [end]
mov eax,dword ptr [esi+0]
sub eax,dword ptr [ebx+0]
mov edx,dword ptr [esi+4]
sbb edx,dword ptr [ebx+4]
mov ebx,dword ptr [delta]
mov dword ptr [ebx+0],eax
mov dword ptr [ebx+4],edx
}
}
// sample physics system
void Physics()
{
LARGE_INTEGER CurTick,DeltaTick,OldTick,Freq;
float ElapsedTime;
gePhysicsSystem *PS;
gePhysicsObject *PO;
geXForm3d Xform;
geExtBox ExtBox;
geActor *Actor;
int ConfigIndex = 0;
BOOL Running = TRUE;
DWORD Timer = timeGetTime();
geVec3d OldVec,NewVec;
GE_Collision Col;
// Load an actor
LoadActor(Actor,"Dema.act");
geXForm3d_SetIdentity(&Xform);
geActor_GetDynamicExtBox(Actor,&ExtBox);
// Create physics system
PS = gePhysicsSystem_Create();
// Create physics object
PO = gePhysicsObject_Create(&Xform.Translation,1.0f,GE_TRUE,GE_TRUE,0.00005f,0.00005f,&ExtBox.Min,&ExtBox.Max,1.0f);
// Add physics object to physics system
gePhysicsSystem_AddObject(PS,PO);
// Set physics object XForm
gePhysicsObject_SetXForm(PO, &Xform, 0);
// Needed for high performance counter
QueryPerformanceFrequency(&Freq);
QueryPerformanceCounter(&OldTick);
// physics and rendering loop
while(Running)
{
// Save the xform's position before updating
OldVec = Xform.Translation;
//High performace counter
QueryPerformanceCounter(&CurTick);
SubLarge(&OldTick, &CurTick, &DeltaTick);
OldTick = CurTick;
if (DeltaTick.LowPart > 0)
ElapsedTime = 1.0f / (((float)Freq.LowPart / (float)DeltaTick.LowPart));
else
ElapsedTime = 0.001f;
// Update the physics system, the largest number is 0.03f and
// This function will call gePhysicsObject_ComputeForces() and
// gePhysicsObject_Integrate() for every physics object in the physics system.
gePhysicsSystem_Iterate(PS,ElapsedTime);
// Get The current config index for the physics system
ConfigIndex = gePhysicsSystem_GetSourceConfigIndex(PS);
// Get the updated XForm
gePhysicsObject_GetXFormInEditorSpace(PO, &Xform, ConfigIndex);
// save the xform's position after updating
NewVec = Xform.Translation;
// test if there was a collision
if (geWorld_Collision(World,&ExtBox.Min,&ExtBox.Max,&OldVec,&NewVec,GE_CONTENTS_SOLID_CLIP,GE_COLLIDE_ALL,0xffffffff,NULL,NULL,&Col))
{
// Collision found do something
Xform.Translation = Col.Impact;
gePhysicsObject_SetXForm(PO, &Xform, ConfigIndex);
}
// Set actors xform
geActor_ClearPose(Actor, &Xform);
// Limit rendering to 30 FPS
// This is needed because physics are slow and when
// trying to render every frame the physics slows to a crawl
if ((timeGetTime()-Timer)>=34)
{
Timer=timeGetTime();
if (!geEngine_BeginFrame(Engine, Camera, GE_TRUE))
Running = 0;
if (!geEngine_RenderWorld(Engine, World, Camera, 0.0f))
Running = 0;
if (!geEngine_EndFrame(Engine))
Running = 0;
}
}
}
|
|
This code
works but it doesn't seem to be in real time. My level is the same
scale as GTest, maybe that scale is just large. Can someone tell me
what scale GTest uses and how it relates to real world mesurements?
My actor could be falling 10 feet every textile and I just don't
know it.
[ This Message was edited by:
Jeff on 2001-08-31 15:30 ]
|
securitron Genesis God
Joined: Sep 21,
1999 Posts:
478 From:
CANADA
|
Posted: 2001-08-31
20:07
i like the
addition of queryperformance, when i got up to around 100 actors
animating thats when i saw a noticable difference using the more
accurate counter.
jeff lol now i'm going to whinge about
exactly the same thing *again*, i'm still not sold on the way that
you are controlling the FPS with that check on the timer ( its the
> sign that bugs me ).
Here's how i'm doing that
check:
... QueryPerformanceCounter(QPCount);
while QPCount < LastQPCount+FPS do begin
QueryPerformanceCounter(QPCount);
Application.ProcessMessages end;
QueryPerformanceCounter(LastQPCount); ...
So
physics stuff would go where/with Application.ProcessMessages is,
and I would further slice up whats left of the time pie imo.
|
Jeff Genesis God
Joined: Dec 06,
1998 Posts:
946 From:
Oxnard, CA, USA
|
Posted: 2001-08-31
20:49
Quote:
|
jeff lol now i'm going to whinge about
exactly the same thing *again*, i'm still not sold on the
way that you are controlling the FPS with that check on the
timer ( its the > sign that bugs me ).
|
|
I guess I
don't understand what you mean by "its the > sign that bugs me".
Here is what it's doing.
The timeGetTime() function returns
the elapsed system time in milliseconds. When the Timer variable is
initialized it is set to equal timeGetTime().
So lets
evaluate this statement:
if ((timeGetTime()-Timer) >= 33)
{ Timer = timeGetTime(); //render }
The
(timeGetTime() - Timer) part is equal to the delta time. This is the
time that has elapsed since Timer was set to timeGetTime(). Now if
it is greater than or equal to 33 millisecond than it sets the Timer
variable to the current time and renders. If it's less than 33 then
it just continues without resetting the Timer variable or rendering.
The 33 is computed by taking 1000 milliseconds and dividing
it by 30 FPS which give 33.333333333 milliseconds. Since were
working with DWORD values I rounded to 33.
So basically what
it is saying is if 33 milliseconds or greater have passed since last
rendering then render. if not don't render. It should never render
more that 30 FPS but it can render less if the rendering and physics
take longer than 33 milliseconds.
[ This
Message was edited by: Jeff on 2001-08-31 20:52 ]
|
securitron Genesis God
Joined: Sep 21,
1999 Posts:
478 From:
CANADA
|
Posted: 2001-08-31
22:34
right, i know
that your calculations come out correctly, what i meant i guess, was
that it shouldn't imo be in the physics procedure, not sure if that
was intentional or just for testing...
|
Jeff Genesis God
Joined: Dec 06,
1998 Posts:
946 From:
Oxnard, CA, USA
|
Posted: 2001-08-31
23:31
Quote:
|
right, i know that your calculations come
out correctly, what i meant i guess, was that it shouldn't
imo be in the physics procedure, not sure if that was
intentional or just for testing...
|
|
Ok now I
get what your saying, and your correct. The rendering and physics
should not be mixed like this in a real program. This is meant to
just be a sample of how to do physics. I didn't want people having
to flip back and forth through a bunch of functions or classes
trying to figure out what was going on, so I placed everything in
one loop. In a real program you should have the physics in it's own
function or class and the rendering in another.
Also, I
might have confused the issue when I named the function Physics
maybe I should have called it SampleCode.
|
securitron Genesis God
Joined: Sep 21,
1999 Posts:
478 From:
CANADA
|
Posted: 2001-08-31
23:49
i have a constant
in my ge_physicsobject.pas ( your PhysicsObject.c supposedly) that
was 'automatically' generated out by neils delphi generator:
const PHYSOB_GRAVITY: gefloat = (-3.9)
and
appears that it is never called. in the c file jason says
'integrate a gephysicsObjects equations of motion by time step
deltatime' so, per frame imo
i did manage to put in a 'pause
game' tonight. lol.
|
Jeff Genesis God
Joined: Dec 06,
1998 Posts:
946 From:
Oxnard, CA, USA
|
Posted: 2001-09-01
01:22
Quote:
|
have a constant in my ge_physicsobject.pas (
your PhysicsObject.c supposedly) that was 'automatically'
generated out by neils delphi generator:
const
PHYSOB_GRAVITY: gefloat = (-3.9)
and appears
that it is never called.
|
|
#define
PHYSICSOBJECT_GRAVITY (-3.9)
The above line appears in the
PhysicsObject.h header file and is used in the
gePhysicsObject_ComputeForces() function which is located in the
PhysicsObject.c file.
Maybe the program that generated the
code used (-3.9) instead of using PHYSOB_GRAVITY.
|
securitron Genesis God
Joined: Sep 21,
1999 Posts:
478 From:
CANADA
|
Posted: 2001-09-01
11:44
i just forgot to
check the h file doh so that 3.9 would have to be adjusted for
different scaling then.
| |