JV-ODE Physics Thread 8

Blitz3D Forums/Blitz3D Userlibs/JV-ODE Physics Thread 8

Now available for Blitz3D & BlitzMax (Win32)

The JV-ODE Physics Wrapper is an advanced user library supporting almost all of the Open Dynamics Engine functions wrapped inside a DLL (Dynamic Link Library).

The library can be used with any programming language that supports DLL's, including Blitz3D and BlitzMax (Win32). The Euler rotation system in JV-ODE has been adapted to match the left-handed Euler rotation used in Blitz3D, however functions for accessing Quaternion and Axis Angle rotation systems are also included. The full versions contain all of the necessary files to get you started, including over 30 demos showing the various features found in ODE and a complete Function Reference document.

The BlitzMax (Win32) version also features a built-in 'bare-bones' OpenGL 3D engine module and B3D mesh loader module for demo purposes.

To view more information and screenshots click here.

Options are now available to purchase the wrapper using either PayPal or Share*it.

The Blitz3D restricted demo version is now available for download here (211KB)

The BlitzMax (Win32) restricted demo version is now available for download here (290KB)

Have Fun :)

Previous JV-ODE Threads: 1 2 3 4 5 6 7
JV-ODE Player Factory Forum

Useful Links: ODE.org Website - ODE Wiki - ODE User Guide (PDF) - ODE User Guide (HTML)

Thanks for the reply in the old post... I suppose it could be nice to have some sort of FAQ file or something in your own website. Blitzforums are bit difficult place to deal with your customer support ;) It's quite difficult to find the answers by searching (and believe me - I'm trying to do that!) - so we end up asking you over and over.

Rightio, me again... :)

According to the official docs, when a collision occurs a temporary 'contact joint' is created (for the duration of the step).

Is this contact joint (and it's data) accesible through jv-ode?!?

If so, it could possible help me obtain accurate information of the forces involved when two rigid bodies collide - ie. if I then would be able to get some Feedback on that joint?!

thanks,
Danny.

Yes it is Dan,
Luckly for us back in JV-ODE v1.11 Vip3r added support for joint feedback back. Check out the Car Demo joint feedback.

The update included:

Added new joint feedback functions...

dJointFeedbackEnable(joint)
dJointFeedbackDisable(joint)
dJointFeedbackIsEnabled(joint)
dJointSetFeedback(joint,feedback)
dJointGetFeedbackForce1(joint)
dJointGetFeedbackTorque1(joint)
dJointGetFeedbackForce2(joint)
dJointGetFeedbackTorque2(joint)

Added new demos...

Demo-RagDolls (Update)
Demo-RagDolls-Zombie
CarDemo-JointFeedback

@Game Producer: You're welcome :) - It would be very difficult and time consuming to create an all encompassing JV-ODE FAQ, anyway I don't mind being asked over and over.

@Danny: The contact joint is used internally in JV-ODE and is supplied with data, it doesn't return any data. Collision depth provides values which you can interpret easily and are directly related to the force of the impact.

@Wayne, thanks mate. I'm familiar with the jointfeedback functions, but I was hoping to get feedback on the contact joints created when rigid bodies collide... But that won't be possible..

@Vip3r, ok you sold me dude! When I did first tests the collision Depth would give me weird and useless off-the-scale values. But I found out if you do NOT set any global 'dContactXXX' settings when initialising the simulator, it DOES give you values VERY close to what the collision FORCE should be!!! Especially when you multiply the collision Depth values by twice the mass of the rigid bodies. Check this examples where you will notice 'some' but very acceptable inaccuracies between Depth vs Force calculations:

; ###################################################################################################

AppTitle "JV-ODE - COLLISION FORCE / DEPTH / MASS test"

Include "JV-ODE.bb"

Graphics3D 800,600,0,2

Type ODEGeom
	Field body
	Field geom
	Field mesh
End Type

; ###################################################################################################

; ### Setup ODE

Global World=dWorldCreate()
Global Space=dHashSpaceCreate(0)
Global ContactGroup=dJointGroupCreate(0)

dWorldSetAutoDisableFlag(World,1)
dWorldSetGravity(World,0,-0.98,0)
;dContactSetMode(dContactBounce)		; Do NOT enable these!! It will cause the Collision Depth values
;dContactSetBounce(0.1)					; to go nuts and off the scale !!!!!
;dContactSetMu(48)


	RedWeight# = 1.0
	BlueWeight# = 10.0


; ### Create RED Test object

	ode.ODEGeom=New ODEGeom
	ode\body=dBodyCreate(World)
	dBodySetRotation(ode\body,0,0,0)
	dBodySetPosition(ode\body,-30,90,0)
	dBodySetAutoDisableFlag(ode\body,1)
	ode\geom=dCreateSphere(Space,4)
	dGeomSetBody(ode\geom,ode\body)
	ode\mesh=CreateSphere()
	ScaleMesh ode\mesh,4,4,4
	RotateMesh ode\mesh,0,0,0
	PositionMesh ode\mesh,0,0,0
	EntityColor ode\mesh,255,0,0

	RedBody = ode\body
	RedGeom = ode\geom

	;create RED mass
	mass = dMassCreate()
	dMassSetSphereTotal mass, RedWeight, 4
	dBodySetMass ode\body, mass

; ### Create BLUE Test object
	
	ode.ODEGeom=New ODEGeom
	ode\body=dBodyCreate(World)
	dBodySetRotation(ode\body,0,0,0)
	dBodySetPosition(ode\body,30,90,0)
	dBodySetAutoDisableFlag(ode\body,1)
	ode\geom=dCreateSphere(Space,4)
	dGeomSetBody(ode\geom,ode\body)
	ode\mesh=CreateSphere()
	ScaleMesh ode\mesh,4,4,4
	RotateMesh ode\mesh,0,0,0
	PositionMesh ode\mesh,0,0,0
	EntityColor ode\mesh,0,150,255

	BlueBody = ode\body
	BlueGeom = ode\geom
	
	;set BLUE mass
	mass = dMassCreate()
	dMassSetSphereTotal mass, BlueWeight, 4
	dBodySetMass ode\body, mass

; ### Create light
Global Light=CreateLight()
RotateEntity Light,45,-90,0
LightColor Light,255,255,255
AmbientLight 130,130,130

; ### Create camera
Global Camera=CreateCamera()
CameraClsColor Camera,0,0,0
CameraRange Camera,1,1000
PositionEntity Camera,0,90,-100
RotateEntity Camera,30,0,0

; ### Create plane
dCreatePlane(Space,0,1,0,0)
Plane=CreatePlane()
PlaneTexture=CreateTexture(128,128,9)
ClsColor 0,200,80
Cls
Color 255,255,255
Rect 0,0,64,64,1
Rect 64,64,64,64,1
CopyRect 0,0,128,128,0,0,BackBuffer(),TextureBuffer(PlaneTexture)
ScaleTexture PlaneTexture,80,80
EntityTexture Plane,PlaneTexture,0,0

CreateMirror()

Color 255,0,0
; ###################################################################################################

While Not KeyHit(1)

	PTime=MilliSecs()

	UpdateGeoms()

	dSpaceCollide(Space,World,ContactGroup)
	dWorldQuickStep(World,0.1)
	dJointGroupEmpty(ContactGroup)

	PhysicsTime#=MilliSecs()-PTime

	UpdateWorld
	RenderWorld

	Color 255,255,0
	Text 0,0,"JV-ODE Version "+dGetVersion()
	Text 0,15,"Physics Time:"+PhysicsTime

	HaveHit = False

	;get RED's vertical velocity
	If dGeom1CountCollisions(RedGeom)>0 Then
		dBodyGetLinearVel(redBody)
		nx# = dVectorX#()
		ny# = dVectorY#()
		nz# = dVectorZ#()
		rVel# = Sqr( nx*nx + ny*ny + nz*nz )
		rForce# = rVel# * RedWeight# ; F=M*A
		rPen# = dGeom1CollisionDepth(redGeom,1)
		HaveHit = True
	EndIf
	
	;get BLUE's vertical velocity
	If dGeom1CountCollisions(BlueGeom)>0 Then
		dBodyGetLinearVel(blueBody)
		nx# = dVectorX#()
		ny# = dVectorY#()
		nz# = dVectorZ#()
		bvel# = Sqr( nx*nx + ny*ny + nz*nz )
		bForce# = bVel# * BlueWeight ; F=M*A
		bPen# = dGeom1CollisionDepth(blueGeom,1)
		HaveHit = True
	EndIf
	
	If HaveHit Then
		HaveHit = False
		;multiply collision depth by MASS to get some sort of difference
		;then scale it up to get some more 'workable' value
		rPen# = ( RedWeight#*2) * rPen#
		bPen# = (BlueWeight#*2) * bPen#
		
		Text 0,50, "RED  BODY (1  kg!): velocity = "+rVel#+"  collision Force = "+rForce#+"  collision DEPTH ="+rPen#
		Text 0,65, "BLUE BODY (25 kg!): velocity = "+bVel#+"  collision Force = "+bForce#+"  collision DEPTH ="+bPen#
		
		Color 0,0,0
		Text GraphicsWidth()/2,GraphicsHeight()/2,"< PRESS ANY KEY - for next collision's info..>",1,1
		Flip
		WaitKey()
	EndIf
	Flip
Wend

dJointGroupDestroy(ContactGroup)
dSpaceDestroy(Space)
dWorldDestroy(World)
dCloseODE()

End

; ###################################################################################################

Function UpdateGeoms()

	For ode.ODEGeom=Each ODEGeom
		If dBodyIsEnabled(ode\body)
			RotateEntity ode\mesh,dGeomGetPitch#(ode\geom),dGeomGetYaw#(ode\geom),dGeomGetRoll#(ode\geom)
			PositionEntity ode\mesh,dGeomGetPositionX#(ode\geom),dGeomGetPositionY#(ode\geom),dGeomGetPositionZ#(ode\geom)
		End If
	Next
	
End Function


sweet! :)

You're getting there :)

However, you should be able to use 'Penetration*Mass' as a guide to the impact force and ignore the velocity stuff completely.

The reason for the odd results when used with the contact settings (which you will need to use), is the bounce value. Try it with the contact settings enabled but with '0.0' bounce. With bounce enabled, the objects will gradually penetrate further into another object before moving away from it, hence your wild values.

To return all collision depth data, you will need to cycle through all of the collisions using the index parameter. The spheres in your example will only register one contact each, but if you changed them for cubes you will notice each cube registers approx three contacts.

You will also need to use the dGeom2CollisionDepth() function to return the collision depth data for collisions with smaller objects.

Yep, got all that covered in my engine now with relatively little tweaking..
Work pretty good and 1000 times faster than using those cursed Sqr's ;)

Thanks for your troubles,
Danny

Stop!

I've thought of another way to get an indication of impact forces and it works perfectly using Joint Feedback, much better than collision depth. I'd even go as far as saying much better than using Joint Feedback on contact joints themselves.

But... while testing this idea, I've noticed that the Joint Feedback feature in JV-ODE isn't working correctly when multiple joints are feedback enabled.

The good news is it will be fixed and released very soon. I'll include a Collision Force demo showing the above idea too.

SHTOPPED! :)

You've made me very curious!
Guess it's a bit early for a drum roll? :)

Danny
p.s. Use the Force dude!

JV-ODE V1.18 Update Released

Please check your inbox :)

The new update corrects an issue found in the Joint Feedback feature of JV-ODE.

Please let me know if you experience any problems with the new update.

:)

@Danny: Here's the Collision Forces demo to work with JV-ODE V1.18
; ###################################################################################################
; #								   JV-ODE - Collision Forces Demo									#
; #									Code by Jim Williams (VIP3R)									#
; #								Devious Codeworks - Copyright © 2006								#
; ###################################################################################################

AppTitle "JV-ODE - Collision Forces Demo"

Include "JV-ODE.bb"

Graphics3D 800,600,0,2

Type ODEGeom
	Field body
	Field geom
	Field mesh
End Type

Dim Force(10)
Dim Joint(10)

; ###################################################################################################

; ### Setup ODE

Global World=dWorldCreate()
Global Space=dHashSpaceCreate(0)
Global ContactGroup=dJointGroupCreate(0)

dWorldSetAutoDisableFlag(World,1)
dWorldSetGravity(World,0,-0.98,0)
dContactSetMode(dContactBounce)
dContactSetBounce(0.95)
dContactSetMu(48)

; ### Create light

Global Light=CreateLight()

RotateEntity Light,45,-90,0
LightColor Light,255,255,255
AmbientLight 130,130,130

; ### Create camera

Global Camera=CreateCamera()
CameraClsColor Camera,0,0,0
CameraRange Camera,1,1000
PositionEntity Camera,0,40,-30
RotateEntity Camera,30,0,0

; ### Create plane

dCreatePlane(Space,0,1,0,0)

Plane=CreatePlane()

EntityAlpha Plane,0.5

PlaneTexture=CreateTexture(128,128,9)

ClsColor 0,200,80
Cls

Color 255,255,255

Rect 0,0,64,64,1
Rect 64,64,64,64,1

CopyRect 0,0,128,128,0,0,BackBuffer(),TextureBuffer(PlaneTexture)
ScaleTexture PlaneTexture,20,20
EntityTexture Plane,PlaneTexture,0,0

Mirror=CreateMirror()

AddObjects()

; ###################################################################################################

While Not KeyHit(1)

	PTime=MilliSecs()

	UpdateGeoms()

	dSpaceCollide(Space,World,ContactGroup)
	dWorldQuickStep(World,0.1)
	dJointGroupEmpty(ContactGroup)

	PhysicsTime#=MilliSecs()-PTime

	UpdateWorld

	RenderWorld

	Text 0,0,"JV-ODE Version "+dGetVersion()
	Text 0,15,"Physics Time:"+PhysicsTime

	Text 10,280,"Collision Force:"

	For count=1 To 10
		dJointGetFeedbackForce1(Joint(count))
		ShowBar(78+(count*58),320,dVectorY())
	Next

	Flip

Wend

dJointGroupDestroy(ContactGroup)
dSpaceDestroy(Space)
dWorldDestroy(World)
dCloseODE()

End

; ###################################################################################################

Function AddObjects()

For count=1 To 10
	; ### Body + Geom
	ode.ODEGeom=New ODEGeom
	ode\body=dBodyCreate(World)
	ParentBody=ode\body
	dBodySetRotation(ode\body,0,0,0)
	dBodySetPosition(ode\body,-55+(10*count),45,30)
	mass=dMassCreate()
	dMassSetSphereTotal(mass,0.5,4)
	dBodySetMass(ode\body,mass)
	dMassDestroy(mass)
	dBodySetAutoDisableFlag(ode\body,0)
	ode\geom=dCreateSphere(Space,4)
	dGeomContactSetMode(ode\geom,dContactBounce)
	dGeomContactSetBounce(ode\geom,count*0.1)
	dGeomSetBody(ode\geom,ode\body)
	ode\mesh=CreateSphere()
	ScaleMesh ode\mesh,4,4,4
	EntityColor ode\mesh,Rand(255),Rand(255),Rand(255)
	EntityAlpha ode\mesh,1
	EntityShininess ode\mesh,0.7

	; ### Core Body
	ode.ODEGeom=New ODEGeom
	ode\body=dBodyCreate(World)
	CoreBody=ode\body
	dBodySetRotation(ode\body,0,0,0)
	dBodySetPosition(ode\body,-55+(10*count),45,30)
	mass=dMassCreate()
	dMassSetSphereTotal(mass,0.5,4)
	dBodySetMass(ode\body,mass)
	dMassDestroy(mass)
	dBodySetAutoDisableFlag(ode\body,0)

	; ### Join Core + Parent
	Joint(count)=dJointCreateFixed(World,0)
	dJointAttach(Joint(count),CoreBody,ParentBody)
	dJointFeedbackEnable(Joint(count))
Next

End Function

; ###################################################################################################

Function ShowBar(xpos,ypos,yvec,size=8)

If yvec<0
	Color 240,0,140
	Else
	Color 0,240,140
	End If

Rect xpos,ypos,size,size+Abs(Int(yvec*2))

Color 255,255,255

End Function

; ###################################################################################################

Function UpdateGeoms()

For ode.ODEGeom=Each ODEGeom
	If ode\geom
		If dBodyIsEnabled(ode\body)
			RotateEntity ode\mesh,dGeomGetPitch#(ode\geom),dGeomGetYaw#(ode\geom),dGeomGetRoll#(ode\geom)
			PositionEntity ode\mesh,dGeomGetPositionX#(ode\geom),dGeomGetPositionY#(ode\geom),dGeomGetPositionZ#(ode\geom)
			End If
		End If
Next

End Function

; ###################################################################################################

You will notice in the AddObjects() function I've created an extra ODE body (with no geom), it is then connected to the parent ODE body using a fixed joint. To read the force and torque, you can call Joint Feedback on these fixed joints. The benefit to this method over Joint Feedback directly on contact joints is that you only need to deal with one value, rather than one value per contact (up to 4+ contacts per body). Let me know if it works well for you ;)

!!!super duper cool viper!!!

I'm gonna dive it to it right away... I'm especially curious to see if the joint feedback takes the body's mass into account as well...

thanks dude!!
d.

oooookay.... What about this then?!

If you print the forces in numbers by replacing this for..next in the main loop:
For count=1 To 10
	dJointGetFeedbackForce1(Joint(count))
	ShowBar(78+(count*58),320,dVectorY())
	Text 0,100 + (count*15), "Force = "+dVectorY#()
Next

Then you'll see that ALL bodies give EXACTLY the same numbers! AT THE SAME TIME! My feeling is that ALL 'dJointGetFeedBackForce' calls return only the value of the FIRST joint ?!?!
You'll see that the numbers stop changing as soon as the first ball comes to a rest - whilst the other, more bouncy ones on the right are still hapilly going up 'n down!

Might this be because of the double-rigid-fixed-joint technique you're using... or perhaps something wrong with the forcefeedback internals???

Danny
p.s. If you set all the body mass' to 1.0 in stead of 0.5 - you'll see that the eventual force when the bodies are at rest, is 0.98 ie. gravity! that's pretty cool!

Er... Danny you must still be using V1.17, the problem you've described is the exact issue that has been fixed in V1.18 ;)

Update your JV-ODE.dll

DOH!

Thanks for the update,Viper.
I have a question:
I create a sphere in the camera position to simulate I am throwing a grenade in the direction the camera is pointing, and then apply a force. I use dbodysetrotation (ode\body,entitypitch(camera),entityyaw(camera),entityroll(camera)) ,( I don´t remember the correct syntax,sorry) , but it doesn´t work and the sphere-grenade is always 0,0,0 rotated, and the force is applied in the same direction (pointing 0,0,0).
How can I create and rotate the sphere-grenade to match the direction the camera is facing?
Thanks.

I've always wondered about the car demos included in this package. The car always seems to act a bit strange for me, it's difficult to describe. I've never really played a car simulation that behaves like this one does, and I guess it just bothers me. It seems very "jerky", at times it seems like the car becomes almost weightless and flips over on its back as if it were made of cardboard.
Is this the way these demos are intended to behave? I just can't imagine someone wanting to use a car that behaves this way in a game so perhaps I'm missing something.
Great work on the library otherwise though, its nice to see this thing being continually updated.

@angel martinez: It sounds like you're always applying the force in the same direction (not relative to the current rotation). It's difficult to help without some code showing your problem, there could be several factors involved. If you still can't get it to work, post (or email) an example so that I can help you.

@chaduke: Thanks :) The car demo isn't intended to be a realistic car simulation, but to show new users how the 'hinge2' joints are used to create a wheeled vehicle. If a real car was made from four spheres and an evenly weighted rectangle, would that handle a bit strange? Almost definitely :)

Creating an accurate car simulation is very complex and thus unsuitable to use as demos for new users. You would have to create the car body using several ODE bodies, each with the correct mass etc. The weight in a real car is distributed, for example there's more mass near the engine. You also need to apply force and torque to the wheels properly, simulating the transmission etc, something which often gets overlooked. You then need to setup the contact surfaces correctly for both the tyres and ground surface, plus tweak the suspension. All of the above will affect the way the car will handle in the real world and it's the same in ODE. The more complex the simulation, the closer to reality it will be, but that's way beyond the purpose of the demos ;)

The code is like:

rx#=entityPitch(camera)
ry#=entityYaw(camera)
rz#=entityRoll(camera)
x#=entityX(camera)
y#=entityY(camera)
z#=entityZ(camera)

if keyhit(34) ; ////////////////////////
ode.ODEGeom=New ODEGeom
ode\body=dBodyCreate(World)
dBodySetRotation(ode\body,rx#,ry#,rz#)
dBodySetPosition(ode\body,x#,y#,z#)
dBodySetAutoDisableFlag(ode\body,1)
ode\geom=dCreateSphere(Space,1)
dGeomSetBody(ode\geom,ode\body)
ode\mesh=CreateSphere()
ScaleMesh ode\mesh,.5,.5,.5
EntityAlpha ode\mesh,1
EntityShininess ode\mesh,0.7

dBodyAddForce ode\body,0,0,50
end if ; /////////////////////////////

The sphere is throwed always in the direction 0,0,0 and not in the camera direction.


dBodyAddForce ode\body,0,0,50


You're always applying the force in the same direction (z axis), not relative to the current rotation of the camera.

To add force relative to the camera body you should use...

dBodyAddRelForce(ode\body,0,0,50)

:)

Thanks for the reply VIP3R, that explains a lot. Has anyone coded a more realistic car simulation using your library in Blitz3d yet? If so I'd love to look at it. If not, perhaps its a project I can work on, possibly porting someone elses code from C++.

No problem :)

Well, I've seen a few good car sims (mostly work in progress) but there are probably lots of projects using JV-ODE that I've not seen yet. The best JV-ODE car simulation I've seen so far has to be Tricky Tracks.

There are quite a few ODE car projects listed here. Porting C++ ODE stuff should be painless too as most functions in JV-ODE use the original API names and parameters.

I've started work on a JV-ODE car editor. You can load cars/wheel meshes into the garage, tweak your ODE settings with sliders, test your settings by cruising around a bit and tweak them even while driving... you can even lift the car and drop it to test the suspension. In the end you have the option to export your settings.

Anyway, I'm looking for ideas from you all as to what features you'd like to see.

I'm not in the car business myself but I'm guessing it would be cool if the editor can simply export the settings and build the car in the form of blitz code. Or in the form of some 'data file' with load/save_car() functions.

If you then supply some basic support functions (accelerate car, steer left/right, dunno) then I think a lot of people would be very greatfull to you :) ?!

http://static.4players.de/premium/Screenshots/2d/8a/98321-vollbild.jpg




just kidding :)


say a word, if you need any help! e.g. i've got a highly improved version of blitzui over here, if you need a decent gui...

Is that Live for Speed? Sweet game.

Free this wrapper!!!

We want it for free!!! :D

J/K

I have a question: (very noobish I know)
How can I test wether 2 geoms are colliding?
And can I test with how much force they are colliding?

I used the spheresample "BouncyWorld" and changed it a little. I changed the "Addobject"-function and made:

Function AddObject()
  ;Create Body Geom and Mesh
  Return ode\geom
End function

Now I want to do this:

global Testball=Addobject()
For ode.odegeom=each odegeom
   If Objectscollides(Testball,ode\geom)
      ode\damage=ode\damage+Getcolforce()
  endif
next


Good question FreeTimeCoder..

Just scroll up and the answer is there, I just asked the same question recently :)
You might also wat to check the posts at the end of the previous thread #7..

hope it helps and you figure it out, if not let me know..

d.

@FreetimeCoder: You can get full collision feedback. Check the 'CarDemo-CCount' and 'CarDemo-DetailedC' demos included with the wrapper. Also check the JV-ODE Function Reference under 'Unique JV-ODE Functions>Collision Functions' for more info. Note these features are not included in the demo version of JV-ODE.

For the second question, yes as Danny says we've just been working on getting some collision force feedback. There's a new demo a few posts up ^ to show the feature. Again this feature will only work with the full version of JV-ODE.

XD Sorry I haven't seen this.
Great it works!
But i have 1.16 ?
I have to check my E-mails again..

It was sent to the email address you registered during the purchase, which is different to the one in your profile.

Let me know if you didn't get it, or if you need your email address updating ;)

Got it!
When I purchased it i haven't got an email adress..
It is registered on my fathers adress. Will be nice, if you change it.

Forcedetection works GREAT!

No problem, I've updated it for you.

:)

Can you show how to create a static collision trimesh from a vertex and indice array? I don't want to use .b3d files.

--EDIT--

I can't see to get ODE trimesh creation function to work.

StaticGeom is a big collapsed section of the map. It has a vertex and an indice buffer:

'Buid physics collision data
        For staticgeom:TStaticGeom=EachIn TStaticGeom.list
                trimeshdata=dGeomTriMeshDataCreate()
                staticgeom.odevertexarray:TBank=CreateBank(staticgeom.rendersurface.vertexarray.size()/12*12)
                For n=0 To staticgeom.rendersurface.vertexarray.size()/4-1'12-1
                        PokeFloat staticgeom.odevertexarray,n*4,PeekFloat(staticgeom.rendersurface.vertexarray,n*4)*ODE_WORLDSCALE
                Next
                staticgeom.odeindicearray:TBank=CreateBank(staticgeom.rendersurface.indicearray.size()*2)
                For n=0 To staticgeom.rendersurface.indicearray.size()/6-1
                        PokeInt staticgeom.odeindicearray,n*12+0,PeekShort(staticgeom.rendersurface.indicearray,n*6+0)
                        PokeInt staticgeom.odeindicearray,n*12+4,PeekShort(staticgeom.rendersurface.indicearray,n*6+2)
                        PokeInt staticgeom.odeindicearray,n*12+8,PeekShort(staticgeom.rendersurface.indicearray,n*6+4)
                Next

                dGeomTriMeshDataBuildSimple trimeshdata,staticgeom.odevertexarray.buf(),staticgeom.odevertexarray.size()/12,staticgeom.odeindicearray.buf(),staticgeom.odeindicearray.size()/4
                trimesh=dCreateTriMesh(ODE_space,trimeshdata)

        Next


I tried flipping the faces, and objects pass right through the scene, with no collision.

Here you go, this is the CreateTriMesh() function from JV-ODE BMax which should provide all of the info needed...

' ###################################################################################################

Function CreateTriMesh:Int(trispace:Int,xscale:Float=1.0,yscale:Float=1.0,zscale:Float=1.0)

Global TriBank:TBank=CreateBank(NTris*3*4)
Global VertBank:TBank=CreateBank(NVerts*4*4)

Local Offset:Int=0
Local TriOffset:Int=0

Local VertData:Vrts
Local TriData:Tris

For VertData:Vrts=EachIn B3DVrtsList
	PokeFloat(VertBank,Offset,xscale*VertData.X)
	Offset:+4
	PokeFloat(VertBank,Offset,yscale*VertData.Y)
	Offset:+4
	PokeFloat(VertBank,Offset,zscale*VertData.Z)
	Offset:+8
Next

For TriData:Tris=EachIn B3DTrisList
	PokeInt(TriBank,TriOffset,TriData.V0)
	TriOffset:+4
	PokeInt(TriBank,TriOffset,TriData.V1)
	TriOffset:+4
	PokeInt(TriBank,TriOffset,TriData.V2)
	TriOffset:+4
Next

Local TriMeshData:Int=dGeomTriMeshDataCreate()

dGeomTriMeshDataBuildSimple(TriMeshData,BankBuf(VertBank),NVerts,BankBuf(TriBank),NTris*3)

Return dCreateTriMesh(trispace,TriMeshData)

End Function

' ###################################################################################################


B3DVrtsList contains the vertex array list and B3DTrisList contains the indice array list.

Note the vertex array (X/Y/Z) is poked into the bank, then you offset the bank by an extra 4 bytes as ODE expects (X/Y/Z/*).

Let me know if you need more help with it ;)

Thank you.

Hmm, that's exactly what I am doing, but my collisions still fail.

Double check you are supplying the correct data to the dGeomTriMeshDataBuildSimple() function...

Vertex Array (X/Y/Z/0) 16 bytes per vertex
Vertex Quantity (*1)
Indice Array (V0/V1/V2) 12 bytes per indice
Indice Quantity (*3)

If you don't supply the function with the exact data structure above, then the collisions will fail due to the mesh geom being built incorrectly internally.

What type of mesh are you using?

There was another CreateTriMesh() function by another user (KronosUK) for the BMax version, it's in the previous JV-ODE thread (7). Take a look at that and see if it helps too.

If you still have no luck, feel free to send a copy of the mesh over and I'll take a closer look at it for you.

JV-ODE V1.20 Update Released

Please check your inbox :)

JV-ODE is now compiled using ODE V0.7 which was released recently. To view the changes made to ODE, please visit the following ODE Wiki link: http://opende.sourceforge.net/wiki/index.php/Changelog

Added new functions:
dCreateHeightfield()
dGeomHeightfieldDataCreate()
dGeomHeightfieldDataDestroy()
dGeomHeightfieldDataBuildSingle()
dGeomHeightfieldDataSetBounds()
dGeomHeightfieldSetHeightfieldData()
dGeomHeightfieldGetHeightfieldData()
dJointCreatePlane2D()
dJointSetPlane2DXParam()
dJointSetPlane2DYParam()
dJointSetPlane2DAngleParam()


Added new demos:
CarDemo-See-Saw
Demo-2DSpheres
Demo-CollisionForces
Demo-TriMeshes


It should be easier to build 2D simulations now that an official 2D joint has been added.

'TriMesh to TriMesh' collisions are now working in ODE, but they're slow so use with care. The flat-ended cylinder geom now collides with TriMeshes in V0.7 of ODE, but they've broken the 'Cylinder to Cylinder' and 'Cylinder to Plane' collisions so I've used the previous stable version (the same as in previous JV-ODE versions) until they fix it.

Please let me know if you experience any problems with the new update.

:)

Thanks again vip3r,

Is there a clear overview (or a list) somewhere to find out exactly which geometry shapes don't collide properly with each other? Like trimesh vs pillshaped or flat-capped cylinders.... And now introducing the new 'heightfield' type of geometry and 2D stuff... ?!?

It's getting a tad confusion, and I'd like to know what 'not to use' untill they've been fixed and proven to work...

Danny

You're welcome :)

Flat-ended cylinders have always had collision issues and should be avoided if possible, in most cases you can use capsules (capped cylinders) instead.

The heightfield geom is bound to have teething problems as it's a new addition to the library, I haven't tried them yet so I've no idea.

Everything else (cubes, spheres, capsules, rays, trimeshes and planes) should collide fine.

Super big up for a serious guy. Thanks.. (heu french people. Don't worry about my english)

JPierre

Cool, thanks! Can I now create [heightfield type of] terrain (or something) using Heightfield functions? Would that be faster than trimesh-trimesh or more stable? Give me a demo! :)

You're welcome Mustang :)

No idea about the speed or stability compared to TriMesh, as soon as I've figured out how it works I'll build a demo for you ;)

Firstly thank you for the latest update. JV-ODE has been the best addon purchase I've made since being a Blitz3D user.

I've built a 3D scene with various static geometry, and a single ball bouncing around happily inside it.

I want to control the ball separately from the rest of the scene, i.e. the angle and velocity it moves at. I assume I would need to stop it being affected by gravity too - but I'm thinking that forcibly setting the angle and velocity would override gravity?

I have been trying to use the command:

dGeomGetRotation(geom)

The manual states this returns a vector, but I'm not sure how to define a vector and hence get the angle. Could you give an example?

You're welcome sonic, thanks :)

Gravity will counteract your forces, so you may need to disable it depending on your needs. You can use the dBodySetGravityMode(body,mode) function to disable gravity per body. Use mode '0' to disable.

To return a vector from the geom rotation, you first call the rotation function, then you call the JV-ODE vector functions to return the vector values...

dGeomGetRotation(geom)
Pitch#=dVectorX()
Yaw#=dVectorY()
Roll#=dVectorZ()


Or, alternatively you can call the following unique JV-ODE functions instead...

Pitch#=dGeomGetPitch(geom)
Yaw#=dGeomGetYaw(geom)
Roll#=dGeomGetRoll(geom)


Ah that's great - thanks! Will give it a go.

Ok thanks - I'm making progress now!

Next question is - say I want to introduce another object (say a bat, ala breakout games) to my scene, to be controlled by the player's mouse. This will be the player's piece.

I'm using:
dGeomSetPosition(obj\geom, (MouseX() - graphicswidth/2) / 8, 10, 0)

This positions the bat in the right place... however I get unpredictable results when moving the bat quickly and hitting the ball. For example, a very fast movement of the bat may mean the collision is missed, or the wrong side of the player's piece collides.

Any ideas what I can do to fix it? I'm looking at the hi speed car demo, however once again I've had a couple of beers and my concentration is drifting again :)

Manual positioning of geoms in real-time will upset the ODE simulation, the correct way would be to add force to the bat to make it move. You also might need to zero (or counteract) the forces on the Z and Y axis of the bat to prevent the ball from pushing it up/down etc. A slider joint might work to lock the bat onto the X axis.

To improve the accuracy of the collisions you should use multiple calls to the step/collide functions, the same as in the main loop of the CarDemo-HighSpeed demo.

Ah, I'd guessed as much. I'll change my input so I apply force depending on the speed of the mouse.

I often use transformed geometry but noticed a memory leak when 'trying' to destroy transformed geometry.
Or, let me put it like this; 'What is the proper way to dispose of transformed geometry'?
I thought the 'dGeomTransformSetCleanup(tgeom,1)' would automatically take care of it. But it seems it doesn't. :(

Any ideas anyone??

Here's a little example that when monitored in the Task Manager, you'll see the megabyte disappear in front of your eyes....

; ###################################################################################################

Include "JV-ODE.bb"

Graphics3D 800,600,0,2

; ### Setup ODE

Global World=dWorldCreate()
Global Space=dHashSpaceCreate(0)
Global ContactGroup=dJointGroupCreate(0)

dWorldSetAutoDisableFlag(World,1)
dWorldSetGravity(World,0,-0.98,0)
dWorldSetERP(World,WorldERP)

; ### Options

Cls
Color 255,255,255
Print "RigidBody / Geometry purging test"
Print ""
Print "Note: Watch the Blitzcc.exe process in TaskManager to see the memory crawling up an up :/"
Print ""
Print ""
Print "Choose Option:"
Print " 0 = create rigidbody and main geometry  - 
Print "     purge by removing geometry and rigid body (IS OK)."
Print ""
Print " 1 = create rigidbody and geometry plus a transformed geometry - 
Print "     purge by removing goemetry and rigid body (LEAKY!)"
Print ""
Print ""

; ### options
op$ = Input$("select option: ")
If op<0 Or op>2 Then End
Print ""

Print "Running test - press Escape to stop"
; ### Do test
num = purgingtest(op)
Print "Stopping.."

Print ""
Print "Did "+num+" runs.."

; ### done

Print ""
Print "<any key to exit>"
WaitKey()



; ###################################################################################################

dJointGroupDestroy(ContactGroup)
dSpaceDestroy(Space)
dWorldDestroy(World)
dCloseODE()

End

; ###################################################################################################

Function PurgingTest( option=0)

	While Not KeyHit(1)

		num = num + 1
		
		;-------------------

		;create RIGID BODY
		body = dBodyCreate(world)
		dBodySetRotation body, 0,0,0
		dBodySetPosition body, x,y,z

		;create main GEOMETRY
		geom = dCreateBox(space, 10,10,10)
		dGeomSetBody geom, body
		
		If option = 1 Then
			;add TRANFORMED GEOMETRY
			tgeom = dCreateGeomTransform(space)
			geomObject = dCreateBox(0,1,1,1)
			dGeomSetPosition geomObject, 0,0,0
			dGeomTransformSetGeom tgeom, geomObject
			dGeomTransformSetCleanup tgeom, 1			;SHOULD take care of automatic destruction!?!
			dGeomSetBody tgeom, body
		End If
		
		;-------------------
		;remove geometry then body
		;If tgeom <> 0 Then dGeomDestroy tgeom ;This doesn't help! :(
		dGeomDestroy geom
		dBodyDestroy body

	Wend
	
	FlushKeys
	Return num
	
End Function

; ###################################################################################################


danny

Ah bugger, well done you've found a memory leak indeed.

The collision data isn't being freed by the transform cleanup, that's all done internally in ODE (it isn't aware of the external data structure though).

The only way around this problem will probably be a new unique function, something like...

dGeomTransformDestroyData(tgeom)

...which will need to be called prior to destroying the parent geom.

It'll be fixed in the next update ;)

ok, cheers vip3r,

It would be even greater (if possible) when you'd only have to destroy the main or 'first' geometry - or the rigid body to which the other tgeom's are linked to (or something like that) because else I'd have to store & keep every tgeom handle as well... It's not an impossible task to store the pointers/handles of the geom created, but since (I assume) that data is 'already' stored internally inside ODE, it would make my life easier :)))

thanks,
Danny

If it was possible that would be the way I'd do it, but as the cleanup process is internal it can't be done without hacking the internal ODE stuff. The manual method is the only decent solution I can think of, I certainly don't intend to interfere with the inner workings of ODE to solve the problem.

Besides, storing the handles of the transform geoms should be done in any case, what if you decide you need to retrieve the transform geom collision data at some point? You will need access to those handles to do it.

Sorry, don't completely understand, so is the leak a JV- problem or an internal ODE problem?

If I need to store all handles, then I've got no choice but to do so ofcourse. But can't I get the tgeom handle (to check a collision) from using the 'getCollisionGeom' functions for example?! (sorry - havent got ode here, don't know the exact function name). In each body or geometry I create, I always use the 'user data' field to store a pointer to my internal game-database, so at collision time, I can retrieve 'what object' collided with what other object, and ode can tell me where and with what force... The slightly anoying thing is that my game objects can have anything between 0 and n geometries, and so far I have been able to get away with storing only 1 pointer to the 'main/first' geometry which I use to position my entities during the main loop. So if I can have a working system without having to store all other 'unknown amount' of geoms, that'd be nice - if you know what I mean..

thanks dude,
D

Well the data structure is defined by ODE itself, but it's up to the user to create it for each geom object. In JV-ODE this is done for you automatically whenever you create a geom, then when you destroy any geom, JV-ODE will also cleanup any data structure associated with the geom too. The problem here is that when ODE automatically cleans up the transform geoms, it doesn't cleanup any data structures associated with the geom object. There's no way to tell it to do so either, so this is clearly a problem within the transform cleanup process in ODE. Ideally it should be detecting and cleaning up the associated data structure too. It's unlikely that they will do anything about it now as the new Geom Offset feature is a replacement for Geom Transforms.

I've been testing the idea of destroying the data structure manually as I mentioned above, but alas it doesn't work at all. There's definitely something wrong in the ODE transform cleanup process somewhere.

But, I do have a solution which doesn't require modification to JV-ODE and it works 100%.

First you -must- set the transform cleanup mode to zero (disable) for this to work correctly...

dGeomTransformSetCleanup tgeom,0
Next, when you want to destroy the transform and parent geoms, you should do it in the following order...

dGeomDestroy geomObject
dGeomDestroy tgeom
dGeomDestroy geom
dBodyDestroy body
Then you will see that everything is cleaned up correctly and thus there's no memory leak.

Doing the above means you would need access to both the GeomObject and TGeom handles. If you didn't want to store all of the handles (they would only use 4 bytes each btw), you're right it might be possible to use dGeomTransformGetGeom(geom) instead.

ok that's cool. Thanks for explaining that...
I can check & test this tomorrow..

But before I do that, you did touch another question I've been wanting to ask for a while.. :)

You mention that the 'geom transforms' (which require you to create a Geometry to 0-space PLUS a tranformed geometry in your default 'Space' -as in my example code above) have been replaced by the Geom-Offset functions - this (and the docs) suggested to me that I can create 1 single geometry (as you normally do) and simply offset it's position & rotation using those two functions... But that doesn't seem to work!?!?! In my situation at least...

So, am I doing something wrong? Are there any examples on how to use them? I was starting to think those offset functions were made to 'further offset' or 'alter' an already off-set trans geom at run-time or something.. But I haven't tested this yet.. dunno.. Bit stuck on that one.. :)

D.

No problem :)

I haven't experimented with Geom Offsets yet, although it's simple enough to add the API for these features to JV-ODE, there's very little (if any) info on their usage at the moment as they're mostly undocumented. As far as I know you would create the geom normally, add it to the body, then offset it. For multiple geoms I'd imagine you add each of them to the body and offset them as needed.

The same is the case for the new heightfield geoms, I've no idea how those work either yet because again there's not enough info available on their usage.

As soon as I've figured out the correct usage of these features I'll provide demos to show them in action if possible, but until I have the necessary info it's going to be very difficult to put something together unfortunately.

I am trying to make a AI for cars using the ODE, but, unhappyly I am not obtaining to make the cars point to the Waypoints, or either, I do not obtain to make the vehicles follow the track.

Maybe somebody that already made this could help me or show the way for me?

@EmerGki: KuRiX has done some great AI work in his Granada Racer with ODE, he would probably be the best person to help you.

Here's four new demos to show how Geom Offsets are used. Geom Offsets are the replacement for the now obsolete Geom Transforms in ODE.

Geom Offset - Car
; ###################################################################################################
; #										  JV-ODE Car Demo											#
; #									Code by Jim Williams (VIP3R)									#
; #								Devious Codeworks - Copyright © 2006								#
; ###################################################################################################

AppTitle "JV-ODE Car Demo - Geom Offset - Car"

Include "JV-ODE.bb"

Graphics3D 800,600,0,2

Global CMass#=200			; ### Car Mass
Global WMass#=14			; ### Wheel Mass

Global WorldERP#=1			; ### World Error Correction
Global WorldFriction#=140	; ### World Friction

Global Torque#=24			; ### Joint Torque
Global SuspensionHS#=0.01	; ### Suspension Hardness/Softness (Higher=Softer - Lower=Harder)

Global Force#=0
Global Steer#=0

Global Car
Global CGeom
Global CMesh

Global CarStartY=20

Dim Wheel(4)
Dim WGeom(4)
Dim Joint(4)

Type ODEGeom
	Field body
	Field geom
	Field mesh
End Type

; ###################################################################################################

; ### Setup ODE

Global World=dWorldCreate()
Global Space=dHashSpaceCreate(0)
Global ContactGroup=dJointGroupCreate(0)

dWorldSetAutoDisableFlag(World,1)
dWorldSetGravity(World,0,-0.98,0)
dWorldSetERP(World,WorldERP)

dContactSetMode(dContactSlip1)
dContactSetMu(WorldFriction)

; ### Create car body

ode.ODEGeom=New ODEGeom
ode\body=dBodyCreate(World)
Car=ode\body
dBodySetRotation(ode\body,0,0,0)
dBodySetPosition(ode\body,0,CarStartY-1,0)
mass=dMassCreate()
dMassSetBoxTotal(mass,CMass,3,1,4)
dBodySetMass(ode\body,mass)
dMassDestroy(mass)
ode\geom=dCreateBox(Space,0,0,0)
CGeom=ode\geom
dGeomSetBody(ode\geom,ode\body)
ode\mesh=CreateCube()
CMesh=ode\mesh
ScaleMesh ode\mesh,1.5,0.5,2
RotateMesh ode\mesh,0,0,0
PositionMesh ode\mesh,0,0,0
EntityColor ode\mesh,255,40,80
EntityAlpha ode\mesh,0.4

GeomObject=dCreateBox(Space,3,1,4)
dGeomSetBody(GeomObject,ode\body)
dGeomSetOffsetPosition(GeomObject,0,1,0)

GeomMesh=CreateCube(ode\mesh)
ScaleMesh GeomMesh,1.5,0.5,2
RotateMesh GeomMesh,0,0,0
PositionMesh GeomMesh,0,1,0
EntityColor GeomMesh,40,80,255
EntityAlpha GeomMesh,0.7
EntityShininess GeomMesh,0.7

; ### Create wheels

For count=1 To 4
	ode.ODEGeom=New ODEGeom
	ode\body=dBodyCreate(World)
	Wheel(count)=ode\body
	dBodySetRotation(ode\body,0,0,0)
	dBodySetPosition(ode\body,0,0,0)
	mass=dMassCreate()
	dMassSetSphereTotal(mass,WMass,0.7)
	dBodySetMass(ode\body,mass)
	dMassDestroy(mass)
	ode\geom=dCreateSphere(Space,0.7)
	WGeom(count)=ode\geom
	dGeomSetBody(ode\geom,ode\body)
	ode\mesh=CreateCylinder(8)
	ScaleMesh ode\mesh,0.7,0.25,0.7
	RotateMesh ode\mesh,0,0,90
	PositionMesh ode\mesh,0,0,0
	EntityColor ode\mesh,255,255,255
Next

dBodySetPosition(Wheel(1),-2,CarStartY-0.5,+2)
dBodySetPosition(Wheel(2),+2,CarStartY-0.5,+2)
dBodySetPosition(Wheel(3),-2,CarStartY-0.5,-2)
dBodySetPosition(Wheel(4),+2,CarStartY-0.5,-2)

; ### Create some objects

SeedRnd MilliSecs()

For spheres=1 To 20
	ode.ODEGeom=New ODEGeom
	ode\body=dBodyCreate(World)
	dBodySetRotation(ode\body,0,0,0)
	dBodySetPosition(ode\body,Rnd(-100,100),30,Rnd(-100,100))
	ode\geom=dCreateSphere(Space,4)
	dGeomSetBody(ode\geom,ode\body)
	ode\mesh=CreateSphere()
	ScaleMesh ode\mesh,4,4,4
	RotateMesh ode\mesh,0,0,0
	PositionMesh ode\mesh,0,0,0
	EntityColor ode\mesh,Rnd(255),Rnd(255),Rnd(255)
	EntityAlpha ode\mesh,1
	EntityShininess ode\mesh,0.7
Next

For capsules=1 To 20
	ode.ODEGeom=New ODEGeom
	ode\body=dBodyCreate(World)
	dBodySetRotation(ode\body,90,0,0)
	dBodySetPosition(ode\body,Rnd(-100,100),30,Rnd(-100,100))
	ode\geom=dCreateCapsule(Space,1,8)
	dGeomSetBody(ode\geom,ode\body)
	ode\mesh=CreateCylinder(8)
	ScaleMesh ode\mesh,1,5,1
	RotateMesh ode\mesh,90,0,0	; <<< Mesh X-Axis must be rotated 90 degrees to fix cylinder alignment
	PositionMesh ode\mesh,0,0,0
	EntityColor ode\mesh,Rnd(255),Rnd(255),Rnd(255)
	EntityAlpha ode\mesh,1
	EntityShininess ode\mesh,0.7
Next

; ### Create light

Global Light=CreateLight()

RotateEntity Light,45,-90,0
LightColor Light,255,255,255
AmbientLight 130,130,130

; ### Create camera

Global CameraPivot=CreatePivot(CMesh)
PositionEntity CameraPivot,0,2,-7

Global Camera=CreateCamera()
CameraClsColor Camera,0,0,0
CameraRange Camera,1,1000

; ### Create plane

dCreatePlane(Space,0,1,0,0)

Plane=CreatePlane()

EntityAlpha Plane,0.8

PlaneTexture=CreateTexture(128,128,9)

ClsColor 0,200,80
Cls

Color 255,255,255

Rect 0,0,64,64,1
Rect 64,64,64,64,1

CopyRect 0,0,128,128,0,0,BackBuffer(),TextureBuffer(PlaneTexture)
ScaleTexture PlaneTexture,20,20
EntityTexture Plane,PlaneTexture,0,0

Mirror=CreateMirror()

; ###################################################################################################

; ### BLITZ STATIC OBJECT (White)

blitzobject=CreateCube()
ScaleMesh blitzobject,1,4,20
RotateMesh blitzobject,-10,8,4
PositionMesh blitzobject,10,5,40

EntityColor blitzobject,255,255,255
EntityAlpha blitzobject,1

; ### ODE STATIC OBJECT (Blue)

ode.ODEGeom=New ODEGeom
ode\geom=dCreateBox(Space,2,8,40)
dGeomSetRotation(ode\geom,-10,8,4)
dGeomSetPosition(ode\geom,20,5,40)
ode\mesh=CreateCube()
ScaleMesh ode\mesh,1,4,20
RotateMesh ode\mesh,0,0,0
PositionMesh ode\mesh,0,0,0

EntityColor ode\mesh,0,100,200
EntityAlpha ode\mesh,1

; ### ODE STATIC OBJECT (Red)

ode.ODEGeom=New ODEGeom
ode\geom=dCreateBox(Space,18,0.2,40)
dGeomSetRotation(ode\geom,-14,0,0)
dGeomSetPosition(ode\geom,-20,4.9,40)
ode\mesh=CreateCube()
ScaleMesh ode\mesh,9,0.1,20
RotateMesh ode\mesh,0,0,0
PositionMesh ode\mesh,0,0,0

EntityColor ode\mesh,200,0,100
EntityAlpha ode\mesh,1

; ###################################################################################################

SetupCar()

While Not KeyHit(1)

	UpdateKeys()

	PTime=MilliSecs()

	UpdateCar()

	UpdateGeoms()

	dSpaceCollide(Space,World,ContactGroup)
	dWorldQuickStep(World,0.1)
	dJointGroupEmpty(ContactGroup)

	PhysicsTime#=MilliSecs()-PTime

	UpdateCam()

	UpdateWorld

	RenderWorld

	Text 0,0,"JV-ODE Version "+dGetVersion()
	Text 0,15,"Physics Time:"+PhysicsTime
	Text 0,50,"Force:"+Force
	Text 0,65,"Torque:"+Torque
	Text 0,100,"Blue = Geom Offset"
	Text 0,115,"Red = Body/Mass Position"
	Text 640,0,"A - Accelerate"
	Text 640,15,"Z - Brake/Reverse"
	Text 640,30,"Arrows - Steering"
	Text 640,45,"Space - Reset Car"

	Flip

Wend

dJointGroupDestroy(ContactGroup)
dSpaceDestroy(Space)
dWorldDestroy(World)
dCloseODE()

End

; ###################################################################################################

Function SetupCar()

For count=1 To 4
	Joint(count)=dJointCreateHinge2(World,0)
	dJointAttach(Joint(count),Car,Wheel(count))
	dJointSetHinge2Anchor(Joint(count),dBodyGetPositionX(Wheel(count)),dBodyGetPositionY(Wheel(count)),dBodyGetPositionZ(Wheel(count)))
	dJointSetHinge2Axis1(Joint(count),0,1,0)
	dJointSetHinge2Axis2(Joint(count),-1,0,0)
	dJointSetHinge2Param(Joint(count),dParamSuspensionERP,0.8)
	dJointSetHinge2Param(Joint(count),dParamSuspensionCFM,SuspensionHS)
	If count>2
		dJointSetHinge2Param(Joint(count),dParamLoStop,0)
		dJointSetHinge2Param(Joint(count),dParamHiStop,0)
		End If
Next

Force=0

End Function

; ###################################################################################################

Function UpdateKeys()

If KeyDown(30)=1
	Force=Force+0.08
	If Force>30 Then Force=30
	End If

If KeyDown(44)=1
	Force=Force-0.1
	If Force<-10 Then Force=-10
	If Force>0 Then Force=Force*0.97
	End If

If KeyDown(30)=0 And KeyDown(44)=0 Then Force=Force*0.99

If KeyDown(203)=1 Or KeyDown(205)=1
	If KeyDown(203)=1
		Steer=0.5
		Else
		Steer=-0.5
		End If
	Else
	Steer=0
	End If

If KeyHit(57)=1
	Force=0
	dBodySetRotation(Car,dGeomGetPitch#(CGeom),dGeomGetYaw#(CGeom),0)
	End If

End Function

; ###################################################################################################

Function UpdateCar()

For count=1 To 4
	dBodyEnable(Wheel(count))
Next

dBodyEnable(Car)

For count=1 To 4
	dJointSetHinge2Param(Joint(count),dParamVel2,Force)
	dJointSetHinge2Param(Joint(count),dParamFMax2,Torque)
Next

For count=1 To 2
	angle#=Steer-dJointGetHinge2Angle1(Joint(count))
	dJointSetHinge2Param(Joint(count),dParamVel,angle)
	dJointSetHinge2Param(Joint(count),dParamFMax,400)
Next

End Function

; ###################################################################################################

Function UpdateCam()

PositionEntity CameraPivot,0,3,-12

camx#=EntityX(CameraPivot,1)-EntityX(Camera)
camy#=EntityY(CameraPivot,1)-EntityY(Camera)
camz#=EntityZ(CameraPivot,1)-EntityZ(Camera)

TranslateEntity Camera,camx*0.5,camy*0.5,camz*0.5

PointEntity Camera,CMesh

End Function

; ###################################################################################################

Function UpdateGeoms()

For ode.ODEGeom=Each ODEGeom
	RotateEntity ode\mesh,dGeomGetPitch#(ode\geom),dGeomGetYaw#(ode\geom),dGeomGetRoll#(ode\geom)
	PositionEntity ode\mesh,dGeomGetPositionX#(ode\geom),dGeomGetPositionY#(ode\geom),dGeomGetPositionZ#(ode\geom)
Next

End Function

; ###################################################################################################


Geom Offset - Cube
; ###################################################################################################
; #										  JV-ODE Car Demo											#
; #									Code by Jim Williams (VIP3R)									#
; #								Devious Codeworks - Copyright © 2006								#
; ###################################################################################################

AppTitle "JV-ODE Car Demo - Geom Offset - Cube"

Include "JV-ODE.bb"

Graphics3D 800,600,0,2

Global CMass#=200			; ### Car Mass
Global WMass#=14			; ### Wheel Mass

Global WorldERP#=1			; ### World Error Correction
Global WorldFriction#=70	; ### World Friction

Global Torque#=24			; ### Joint Torque
Global SuspensionHS#=0.01	; ### Suspension Hardness/Softness (Higher=Softer - Lower=Harder)

Global Force#=0
Global Steer#=0

Global Car
Global CGeom
Global CMesh

Global CarStartY=10

Dim Wheel(4)
Dim WGeom(4)
Dim Joint(4)

Type ODEGeom
	Field body
	Field geom
	Field mesh
End Type

; ###################################################################################################

; ### Setup ODE

Global World=dWorldCreate()
Global Space=dHashSpaceCreate(0)
Global ContactGroup=dJointGroupCreate(0)

dWorldSetAutoDisableFlag(World,1)
dWorldSetGravity(World,0,-0.98,0)
dWorldSetERP(World,WorldERP)

dContactSetMode(dContactSlip1)
dContactSetMu(WorldFriction)

; ### Create car body

ode.ODEGeom=New ODEGeom
ode\body=dBodyCreate(World)
Car=ode\body
dBodySetRotation(ode\body,0,0,0)
dBodySetPosition(ode\body,0,CarStartY,0)
mass=dMassCreate()
dMassSetBoxTotal(mass,CMass,3,1,4)
dMassTranslate(mass,0,-1,0)
dBodySetMass(ode\body,mass)
dMassDestroy(mass)
ode\geom=dCreateBox(Space,3,1,4)
CGeom=ode\geom
dGeomSetBody(ode\geom,ode\body)
ode\mesh=CreateCube()
CMesh=ode\mesh
ScaleMesh ode\mesh,1.5,0.5,2
RotateMesh ode\mesh,0,0,0
PositionMesh ode\mesh,0,0,0
EntityColor ode\mesh,40,80,255
EntityAlpha ode\mesh,1

; ### Create wheels

For count=1 To 4
	ode.ODEGeom=New ODEGeom
	ode\body=dBodyCreate(World)
	Wheel(count)=ode\body
	dBodySetRotation(ode\body,0,0,0)
	dBodySetPosition(ode\body,0,0,0)
	mass=dMassCreate()
	dMassSetSphereTotal(mass,WMass,0.7)
	dBodySetMass(ode\body,mass)
	dMassDestroy(mass)
	ode\geom=dCreateSphere(Space,0.7)
	WGeom(count)=ode\geom
	dGeomSetBody(ode\geom,ode\body)
	ode\mesh=CreateCylinder(8)
	ScaleMesh ode\mesh,0.7,0.25,0.7
	RotateMesh ode\mesh,0,0,90
	PositionMesh ode\mesh,0,0,0
	EntityColor ode\mesh,255,255,255
Next

dBodySetPosition(Wheel(1),-2,CarStartY-0.5,+2)
dBodySetPosition(Wheel(2),+2,CarStartY-0.5,+2)
dBodySetPosition(Wheel(3),-2,CarStartY-0.5,-2)
dBodySetPosition(Wheel(4),+2,CarStartY-0.5,-2)

; ### Create light

Global Light=CreateLight()

RotateEntity Light,45,-90,0
LightColor Light,255,255,255
AmbientLight 130,130,130

; ### Create camera

Global CameraPivot=CreatePivot(CMesh)
PositionEntity CameraPivot,0,2,-7

Global Camera=CreateCamera()
CameraClsColor Camera,0,0,0
CameraRange Camera,1,1000

; ### Create plane

dCreatePlane(Space,0,1,0,0)

Plane=CreatePlane()

EntityAlpha Plane,0.8

PlaneTexture=CreateTexture(128,128,9)

ClsColor 0,200,80
Cls

Color 255,255,255

Rect 0,0,64,64,1
Rect 64,64,64,64,1

CopyRect 0,0,128,128,0,0,BackBuffer(),TextureBuffer(PlaneTexture)
ScaleTexture PlaneTexture,20,20
EntityTexture Plane,PlaneTexture,0,0

Mirror=CreateMirror()

; ###################################################################################################

SetupCar()

SetupObjects()

While Not KeyHit(1)

	UpdateKeys()

	PTime=MilliSecs()

	UpdateCar()

	UpdateGeoms()

	dSpaceCollide(Space,World,ContactGroup)
	dWorldQuickStep(World,0.1)
	dJointGroupEmpty(ContactGroup)

	PhysicsTime#=MilliSecs()-PTime

	UpdateCam()

	UpdateWorld

	RenderWorld

	Text 0,0,"JV-ODE Version "+dGetVersion()
	Text 0,15,"Physics Time:"+PhysicsTime
	Text 0,50,"Force:"+Force
	Text 0,65,"Torque:"+Torque
	Text 640,0,"A - Accelerate"
	Text 640,15,"Z - Brake/Reverse"
	Text 640,30,"Arrows - Steering"
	Text 640,45,"Space - Reset Car"

	Flip

Wend

dJointGroupDestroy(ContactGroup)
dSpaceDestroy(Space)
dWorldDestroy(World)
dCloseODE()

End

; ###################################################################################################

Function SetupCar()

For count=1 To 4
	Joint(count)=dJointCreateHinge2(World,0)
	dJointAttach(Joint(count),Car,Wheel(count))
	dJointSetHinge2Anchor(Joint(count),dBodyGetPositionX(Wheel(count)),dBodyGetPositionY(Wheel(count)),dBodyGetPositionZ(Wheel(count)))
	dJointSetHinge2Axis1(Joint(count),0,1,0)
	dJointSetHinge2Axis2(Joint(count),-1,0,0)
	dJointSetHinge2Param(Joint(count),dParamSuspensionERP,0.8)
	dJointSetHinge2Param(Joint(count),dParamSuspensionCFM,SuspensionHS)
	If count>2
		dJointSetHinge2Param(Joint(count),dParamLoStop,0)
		dJointSetHinge2Param(Joint(count),dParamHiStop,0)
		End If
Next

Force=0

End Function

; ###################################################################################################

Function SetupObjects()

For count=1 To 30
	AddObjects((xp*34)-120,17,(zp*34)+100)

	xp=xp+1
	If xp>7
		xp=0
		zp=zp+1
		End If
Next

End Function

; ###################################################################################################

Function AddObjects(objectxp,objectyp,objectzp)

ode.ODEGeom=New ODEGeom
ode\body=dBodyCreate(World)
dBodySetRotation(ode\body,0,0,0)
dBodySetPosition(ode\body,objectxp,objectyp,objectzp)
ode\geom=dCreateBox(Space,14,14,14)
dGeomSetBody(ode\geom,ode\body)

ode\mesh=CreateCube()
ScaleMesh ode\mesh,7,7,7
RotateMesh ode\mesh,0,0,0
PositionMesh ode\mesh,0,0,0
EntityColor ode\mesh,255,255,255
EntityAlpha ode\mesh,0.8
EntityShininess ode\mesh,0.7

transxp=-7
transyp=0

For count=1 To 3
	GeomObject=dCreateBox(Space,7,7,7)
	dGeomSetBody(GeomObject,ode\body)
	dGeomSetOffsetPosition(GeomObject,transxp,transyp,0)

	GeomMesh=CreateCube(ode\mesh)
	ScaleMesh GeomMesh,3.5,3.5,3.5
	RotateMesh GeomMesh,0,0,0
	PositionMesh GeomMesh,transxp,transyp,0
	EntityColor GeomMesh,count*10,count*70,255
	EntityAlpha GeomMesh,1
	EntityShininess GeomMesh,0.7

	If count=2
		transxp=0
		transyp=-7
		Else
		transxp=transxp+14
		End If
Next

End Function

; ###################################################################################################

Function UpdateKeys()

If KeyDown(30)=1
	Force=Force+0.08
	If Force>30 Then Force=30
	End If

If KeyDown(44)=1
	Force=Force-0.1
	If Force<-10 Then Force=-10
	If Force>0 Then Force=Force*0.97
	End If

If KeyDown(30)=0 And KeyDown(44)=0 Then Force=Force*0.99

If KeyDown(203)=1 Or KeyDown(205)=1
	If KeyDown(203)=1
		Steer=0.5
		Else
		Steer=-0.5
		End If
	Else
	Steer=0
	End If

If KeyHit(57)=1
	Force=0
	dBodySetRotation(Car,dGeomGetPitch#(CGeom),dGeomGetYaw#(CGeom),0)
	End If

End Function

; ###################################################################################################

Function UpdateCar()

For count=1 To 4
	dBodyEnable(Wheel(count))
Next

dBodyEnable(Car)

For count=1 To 4
	dJointSetHinge2Param(Joint(count),dParamVel2,Force)
	dJointSetHinge2Param(Joint(count),dParamFMax2,Torque)
Next

For count=1 To 2
	angle#=Steer-dJointGetHinge2Angle1(Joint(count))
	dJointSetHinge2Param(Joint(count),dParamVel,angle)
	dJointSetHinge2Param(Joint(count),dParamFMax,400)
Next

End Function

; ###################################################################################################

Function UpdateCam()

PositionEntity CameraPivot,0,3,-10

camx#=EntityX(CameraPivot,1)-EntityX(Camera)
camy#=EntityY(CameraPivot,1)-EntityY(Camera)
camz#=EntityZ(CameraPivot,1)-EntityZ(Camera)

TranslateEntity Camera,camx*0.5,camy*0.5,camz*0.5

PointEntity Camera,CMesh

End Function

; ###################################################################################################

Function UpdateGeoms()

For ode.ODEGeom=Each ODEGeom
	RotateEntity ode\mesh,dGeomGetPitch#(ode\geom),dGeomGetYaw#(ode\geom),dGeomGetRoll#(ode\geom)
	PositionEntity ode\mesh,dGeomGetPositionX#(ode\geom),dGeomGetPositionY#(ode\geom),dGeomGetPositionZ#(ode\geom)
Next

End Function

; ###################################################################################################


Geom Offset - Mixed
; ###################################################################################################
; #										  JV-ODE Car Demo											#
; #									Code by Jim Williams (VIP3R)									#
; #								Devious Codeworks - Copyright © 2006								#
; ###################################################################################################

AppTitle "JV-ODE Car Demo - Geom Offset - Mixed"

Include "JV-ODE.bb"

Graphics3D 800,600,0,2

Global CMass#=200			; ### Car Mass
Global WMass#=14			; ### Wheel Mass

Global WorldERP#=1			; ### World Error Correction
Global WorldFriction#=70	; ### World Friction

Global Torque#=24			; ### Joint Torque
Global SuspensionHS#=0.01	; ### Suspension Hardness/Softness (Higher=Softer - Lower=Harder)

Global Force#=0
Global Steer#=0

Global Car
Global CGeom
Global CMesh

Global CarStartY=10

Dim Wheel(4)
Dim WGeom(4)
Dim Joint(4)

Type ODEGeom
	Field body
	Field geom
	Field mesh
End Type

; ###################################################################################################

; ### Setup ODE

Global World=dWorldCreate()
Global Space=dHashSpaceCreate(0)
Global ContactGroup=dJointGroupCreate(0)

dWorldSetAutoDisableFlag(World,1)
dWorldSetGravity(World,0,-0.98,0)
dWorldSetERP(World,WorldERP)

dContactSetMode(dContactSlip1)
dContactSetMu(WorldFriction)

; ### Create car body

ode.ODEGeom=New ODEGeom
ode\body=dBodyCreate(World)
Car=ode\body
dBodySetRotation(ode\body,0,0,0)
dBodySetPosition(ode\body,0,CarStartY,0)
mass=dMassCreate()
dMassSetBoxTotal(mass,CMass,3,1,4)
dMassTranslate(mass,0,-1,0)
dBodySetMass(ode\body,mass)
dMassDestroy(mass)
ode\geom=dCreateBox(Space,3,1,4)
CGeom=ode\geom
dGeomSetBody(ode\geom,ode\body)
ode\mesh=CreateCube()
CMesh=ode\mesh
ScaleMesh ode\mesh,1.5,0.5,2
RotateMesh ode\mesh,0,0,0
PositionMesh ode\mesh,0,0,0
EntityColor ode\mesh,40,80,255
EntityAlpha ode\mesh,1

; ### Create wheels

For count=1 To 4
	ode.ODEGeom=New ODEGeom
	ode\body=dBodyCreate(World)
	Wheel(count)=ode\body
	dBodySetRotation(ode\body,0,0,0)
	dBodySetPosition(ode\body,0,0,0)
	mass=dMassCreate()
	dMassSetSphereTotal(mass,WMass,0.7)
	dBodySetMass(ode\body,mass)
	dMassDestroy(mass)
	ode\geom=dCreateSphere(Space,0.7)
	WGeom(count)=ode\geom
	dGeomSetBody(ode\geom,ode\body)
	ode\mesh=CreateCylinder(8)
	ScaleMesh ode\mesh,0.7,0.25,0.7
	RotateMesh ode\mesh,0,0,90
	PositionMesh ode\mesh,0,0,0
	EntityColor ode\mesh,255,255,255
Next

dBodySetPosition(Wheel(1),-2,CarStartY-0.5,+2)
dBodySetPosition(Wheel(2),+2,CarStartY-0.5,+2)
dBodySetPosition(Wheel(3),-2,CarStartY-0.5,-2)
dBodySetPosition(Wheel(4),+2,CarStartY-0.5,-2)

; ### Create light

Global Light=CreateLight()

RotateEntity Light,45,-90,0
LightColor Light,255,255,255
AmbientLight 130,130,130

; ### Create camera

Global CameraPivot=CreatePivot(CMesh)
PositionEntity CameraPivot,0,2,-7

Global Camera=CreateCamera()
CameraClsColor Camera,0,0,0
CameraRange Camera,1,1000

; ### Create plane

dCreatePlane(Space,0,1,0,0)

Plane=CreatePlane()

EntityAlpha Plane,0.8

PlaneTexture=CreateTexture(128,128,9)

ClsColor 0,200,80
Cls

Color 255,255,255

Rect 0,0,64,64,1
Rect 64,64,64,64,1

CopyRect 0,0,128,128,0,0,BackBuffer(),TextureBuffer(PlaneTexture)
ScaleTexture PlaneTexture,20,20
EntityTexture Plane,PlaneTexture,0,0

Mirror=CreateMirror()

; ###################################################################################################

SetupCar()

SetupObjects()

While Not KeyHit(1)

	UpdateKeys()

	PTime=MilliSecs()

	UpdateCar()

	UpdateGeoms()

	dSpaceCollide(Space,World,ContactGroup)
	dWorldQuickStep(World,0.1)
	dJointGroupEmpty(ContactGroup)

	PhysicsTime#=MilliSecs()-PTime

	UpdateCam()

	UpdateWorld

	RenderWorld

	Text 0,0,"JV-ODE Version "+dGetVersion()
	Text 0,15,"Physics Time:"+PhysicsTime
	Text 0,50,"Force:"+Force
	Text 0,65,"Torque:"+Torque
	Text 640,0,"A - Accelerate"
	Text 640,15,"Z - Brake/Reverse"
	Text 640,30,"Arrows - Steering"
	Text 640,45,"Space - Reset Car"

	Flip

Wend

dJointGroupDestroy(ContactGroup)
dSpaceDestroy(Space)
dWorldDestroy(World)
dCloseODE()

End

; ###################################################################################################

Function SetupCar()

For count=1 To 4
	Joint(count)=dJointCreateHinge2(World,0)
	dJointAttach(Joint(count),Car,Wheel(count))
	dJointSetHinge2Anchor(Joint(count),dBodyGetPositionX(Wheel(count)),dBodyGetPositionY(Wheel(count)),dBodyGetPositionZ(Wheel(count)))
	dJointSetHinge2Axis1(Joint(count),0,1,0)
	dJointSetHinge2Axis2(Joint(count),-1,0,0)
	dJointSetHinge2Param(Joint(count),dParamSuspensionERP,0.8)
	dJointSetHinge2Param(Joint(count),dParamSuspensionCFM,SuspensionHS)
	If count>2
		dJointSetHinge2Param(Joint(count),dParamLoStop,0)
		dJointSetHinge2Param(Joint(count),dParamHiStop,0)
		End If
Next

Force=0

End Function

; ###################################################################################################

Function SetupObjects()

For count=1 To 30
	SeedRnd MilliSecs()

	AddObjects(Rnd(0,1),Rnd(0,1),Rnd(3,5),(xp*34)-120,17,(zp*34)+100)

	xp=xp+1
	If xp>7
		xp=0
		zp=zp+1
		End If
Next

End Function

; ###################################################################################################

Function AddObjects(objecttype,offsettype,transamount,objectxp,objectyp,objectzp)

ode.ODEGeom=New ODEGeom
ode\body=dBodyCreate(World)
dBodySetRotation(ode\body,0,0,0)
dBodySetPosition(ode\body,objectxp,objectyp,objectzp)

If objecttype=0
	ode\geom=dCreateSphere(Space,8)
	Else
	ode\geom=dCreateBox(Space,14,14,14)
	End If

dGeomSetBody(ode\geom,ode\body)

If objecttype=0
	ode\mesh=CreateSphere()
	ScaleMesh ode\mesh,8,8,8
	Else
	ode\mesh=CreateCube()
	ScaleMesh ode\mesh,7,7,7
	End If

RotateMesh ode\mesh,0,0,0
PositionMesh ode\mesh,0,0,0
EntityColor ode\mesh,255,255,255
EntityAlpha ode\mesh,0.8
EntityShininess ode\mesh,0.7

For count=1 To transamount
	If count=1 Then transxp=-7 : transyp=0 : transzp=0
	If count=2 Then transxp=7 : transyp=0 : transzp=0
	If count=3 Then transxp=0 : transyp=-7 : transzp=0
	If count=4 Then transxp=0 : transyp=0 : transzp=-7
	If count=5 Then transxp=0 : transyp=0 : transzp=7

	If offsettype=0
		GeomObject=dCreateSphere(Space,4)
		Else
		GeomObject=dCreateBox(Space,7,7,7)
		End If

	dGeomSetBody(GeomObject,ode\body)
	dGeomSetOffsetPosition(GeomObject,transxp,transyp,transzp)

	If offsettype=0
		GeomMesh=CreateSphere(8,ode\mesh)
		ScaleMesh GeomMesh,4,4,4
		Else
		GeomMesh=CreateCube(ode\mesh)
		ScaleMesh GeomMesh,3.5,3.5,3.5
		End If

	RotateMesh GeomMesh,0,0,0
	PositionMesh GeomMesh,transxp,transyp,transzp
	EntityColor GeomMesh,count*10,count*70,255
	EntityAlpha GeomMesh,1
	EntityShininess GeomMesh,0.7
Next

End Function

; ###################################################################################################

Function UpdateKeys()

If KeyDown(30)=1
	Force=Force+0.08
	If Force>30 Then Force=30
	End If

If KeyDown(44)=1
	Force=Force-0.1
	If Force<-10 Then Force=-10
	If Force>0 Then Force=Force*0.97
	End If

If KeyDown(30)=0 And KeyDown(44)=0 Then Force=Force*0.99

If KeyDown(203)=1 Or KeyDown(205)=1
	If KeyDown(203)=1
		Steer=0.5
		Else
		Steer=-0.5
		End If
	Else
	Steer=0
	End If

If KeyHit(57)=1
	Force=0
	dBodySetRotation(Car,dGeomGetPitch#(CGeom),dGeomGetYaw#(CGeom),0)
	End If

End Function

; ###################################################################################################

Function UpdateCar()

For count=1 To 4
	dBodyEnable(Wheel(count))
Next

dBodyEnable(Car)

For count=1 To 4
	dJointSetHinge2Param(Joint(count),dParamVel2,Force)
	dJointSetHinge2Param(Joint(count),dParamFMax2,Torque)
Next

For count=1 To 2
	angle#=Steer-dJointGetHinge2Angle1(Joint(count))
	dJointSetHinge2Param(Joint(count),dParamVel,angle)
	dJointSetHinge2Param(Joint(count),dParamFMax,400)
Next

End Function

; ###################################################################################################

Function UpdateCam()

PositionEntity CameraPivot,0,3,-10

camx#=EntityX(CameraPivot,1)-EntityX(Camera)
camy#=EntityY(CameraPivot,1)-EntityY(Camera)
camz#=EntityZ(CameraPivot,1)-EntityZ(Camera)

TranslateEntity Camera,camx*0.5,camy*0.5,camz*0.5

PointEntity Camera,CMesh

End Function

; ###################################################################################################

Function UpdateGeoms()

For ode.ODEGeom=Each ODEGeom
	RotateEntity ode\mesh,dGeomGetPitch#(ode\geom),dGeomGetYaw#(ode\geom),dGeomGetRoll#(ode\geom)
	PositionEntity ode\mesh,dGeomGetPositionX#(ode\geom),dGeomGetPositionY#(ode\geom),dGeomGetPositionZ#(ode\geom)
Next

End Function

; ###################################################################################################


Geom Offset - Sphere
; ###################################################################################################
; #										  JV-ODE Car Demo											#
; #									Code by Jim Williams (VIP3R)									#
; #								Devious Codeworks - Copyright © 2006								#
; ###################################################################################################

AppTitle "JV-ODE Car Demo - Geom Offset - Sphere"

Include "JV-ODE.bb"

Graphics3D 800,600,0,2

Global CMass#=200			; ### Car Mass
Global WMass#=14			; ### Wheel Mass

Global WorldERP#=1			; ### World Error Correction
Global WorldFriction#=70	; ### World Friction

Global Torque#=24			; ### Joint Torque
Global SuspensionHS#=0.01	; ### Suspension Hardness/Softness (Higher=Softer - Lower=Harder)

Global Force#=0
Global Steer#=0

Global Car
Global CGeom
Global CMesh

Global CarStartY=10

Dim Wheel(4)
Dim WGeom(4)
Dim Joint(4)

Type ODEGeom
	Field body
	Field geom
	Field mesh
End Type

; ###################################################################################################

; ### Setup ODE

Global World=dWorldCreate()
Global Space=dHashSpaceCreate(0)
Global ContactGroup=dJointGroupCreate(0)

dWorldSetAutoDisableFlag(World,1)
dWorldSetGravity(World,0,-0.98,0)
dWorldSetERP(World,WorldERP)

dContactSetMode(dContactSlip1)
dContactSetMu(WorldFriction)

; ### Create car body

ode.ODEGeom=New ODEGeom
ode\body=dBodyCreate(World)
Car=ode\body
dBodySetRotation(ode\body,0,0,0)
dBodySetPosition(ode\body,0,CarStartY,0)
mass=dMassCreate()
dMassSetBoxTotal(mass,CMass,3,1,4)
dMassTranslate(mass,0,-1,0)
dBodySetMass(ode\body,mass)
dMassDestroy(mass)
ode\geom=dCreateBox(Space,3,1,4)
CGeom=ode\geom
dGeomSetBody(ode\geom,ode\body)
ode\mesh=CreateCube()
CMesh=ode\mesh
ScaleMesh ode\mesh,1.5,0.5,2
RotateMesh ode\mesh,0,0,0
PositionMesh ode\mesh,0,0,0
EntityColor ode\mesh,40,80,255
EntityAlpha ode\mesh,1

; ### Create wheels

For count=1 To 4
	ode.ODEGeom=New ODEGeom
	ode\body=dBodyCreate(World)
	Wheel(count)=ode\body
	dBodySetRotation(ode\body,0,0,0)
	dBodySetPosition(ode\body,0,0,0)
	mass=dMassCreate()
	dMassSetSphereTotal(mass,WMass,0.7)
	dBodySetMass(ode\body,mass)
	dMassDestroy(mass)
	ode\geom=dCreateSphere(Space,0.7)
	WGeom(count)=ode\geom
	dGeomSetBody(ode\geom,ode\body)
	ode\mesh=CreateCylinder(8)
	ScaleMesh ode\mesh,0.7,0.25,0.7
	RotateMesh ode\mesh,0,0,90
	PositionMesh ode\mesh,0,0,0
	EntityColor ode\mesh,255,255,255
Next

dBodySetPosition(Wheel(1),-2,CarStartY-0.5,+2)
dBodySetPosition(Wheel(2),+2,CarStartY-0.5,+2)
dBodySetPosition(Wheel(3),-2,CarStartY-0.5,-2)
dBodySetPosition(Wheel(4),+2,CarStartY-0.5,-2)

; ### Create light

Global Light=CreateLight()

RotateEntity Light,45,-90,0
LightColor Light,255,255,255
AmbientLight 130,130,130

; ### Create camera

Global CameraPivot=CreatePivot(CMesh)
PositionEntity CameraPivot,0,2,-7

Global Camera=CreateCamera()
CameraClsColor Camera,0,0,0
CameraRange Camera,1,1000

; ### Create plane

dCreatePlane(Space,0,1,0,0)

Plane=CreatePlane()

EntityAlpha Plane,0.8

PlaneTexture=CreateTexture(128,128,9)

ClsColor 0,200,80
Cls

Color 255,255,255

Rect 0,0,64,64,1
Rect 64,64,64,64,1

CopyRect 0,0,128,128,0,0,BackBuffer(),TextureBuffer(PlaneTexture)
ScaleTexture PlaneTexture,20,20
EntityTexture Plane,PlaneTexture,0,0

Mirror=CreateMirror()

; ###################################################################################################

SetupCar()

SetupObjects()

While Not KeyHit(1)

	UpdateKeys()

	PTime=MilliSecs()

	UpdateCar()

	UpdateGeoms()

	dSpaceCollide(Space,World,ContactGroup)
	dWorldQuickStep(World,0.1)
	dJointGroupEmpty(ContactGroup)

	PhysicsTime#=MilliSecs()-PTime

	UpdateCam()

	UpdateWorld

	RenderWorld

	Text 0,0,"JV-ODE Version "+dGetVersion()
	Text 0,15,"Physics Time:"+PhysicsTime
	Text 0,50,"Force:"+Force
	Text 0,65,"Torque:"+Torque
	Text 640,0,"A - Accelerate"
	Text 640,15,"Z - Brake/Reverse"
	Text 640,30,"Arrows - Steering"
	Text 640,45,"Space - Reset Car"

	Flip

Wend

dJointGroupDestroy(ContactGroup)
dSpaceDestroy(Space)
dWorldDestroy(World)
dCloseODE()

End

; ###################################################################################################

Function SetupCar()

For count=1 To 4
	Joint(count)=dJointCreateHinge2(World,0)
	dJointAttach(Joint(count),Car,Wheel(count))
	dJointSetHinge2Anchor(Joint(count),dBodyGetPositionX(Wheel(count)),dBodyGetPositionY(Wheel(count)),dBodyGetPositionZ(Wheel(count)))
	dJointSetHinge2Axis1(Joint(count),0,1,0)
	dJointSetHinge2Axis2(Joint(count),-1,0,0)
	dJointSetHinge2Param(Joint(count),dParamSuspensionERP,0.8)
	dJointSetHinge2Param(Joint(count),dParamSuspensionCFM,SuspensionHS)
	If count>2
		dJointSetHinge2Param(Joint(count),dParamLoStop,0)
		dJointSetHinge2Param(Joint(count),dParamHiStop,0)
		End If
Next

Force=0

End Function

; ###################################################################################################

Function SetupObjects()

For count=1 To 30
	AddObjects((xp*34)-120,17,(zp*34)+100)

	xp=xp+1
	If xp>7
		xp=0
		zp=zp+1
		End If
Next

End Function

; ###################################################################################################

Function AddObjects(objectxp,objectyp,objectzp)

ode.ODEGeom=New ODEGeom
ode\body=dBodyCreate(World)
dBodySetRotation(ode\body,0,0,0)
dBodySetPosition(ode\body,objectxp,objectyp,objectzp)
ode\geom=dCreateSphere(Space,8)
dGeomSetBody(ode\geom,ode\body)

ode\mesh=CreateSphere()
ScaleMesh ode\mesh,8,8,8
RotateMesh ode\mesh,0,0,0
PositionMesh ode\mesh,0,0,0
EntityColor ode\mesh,255,255,255
EntityAlpha ode\mesh,0.8
EntityShininess ode\mesh,0.7

transxp=-7
transyp=0

For count=1 To 3
	GeomObject=dCreateSphere(Space,4)
	dGeomSetBody(GeomObject,ode\body)
	dGeomSetOffsetPosition(GeomObject,transxp,transyp,0)

	GeomMesh=CreateSphere(8,ode\mesh)
	ScaleMesh GeomMesh,4,4,4
	RotateMesh GeomMesh,0,0,0
	PositionMesh GeomMesh,transxp,transyp,0
	EntityColor GeomMesh,count*10,count*70,255
	EntityAlpha GeomMesh,1
	EntityShininess GeomMesh,0.7

	If count=2
		transxp=0
		transyp=-7
		Else
		transxp=transxp+14
		End If
Next

End Function

; ###################################################################################################

Function UpdateKeys()

If KeyDown(30)=1
	Force=Force+0.08
	If Force>30 Then Force=30
	End If

If KeyDown(44)=1
	Force=Force-0.1
	If Force<-10 Then Force=-10
	If Force>0 Then Force=Force*0.97
	End If

If KeyDown(30)=0 And KeyDown(44)=0 Then Force=Force*0.99

If KeyDown(203)=1 Or KeyDown(205)=1
	If KeyDown(203)=1
		Steer=0.5
		Else
		Steer=-0.5
		End If
	Else
	Steer=0
	End If

If KeyHit(57)=1
	Force=0
	dBodySetRotation(Car,dGeomGetPitch#(CGeom),dGeomGetYaw#(CGeom),0)
	End If

End Function

; ###################################################################################################

Function UpdateCar()

For count=1 To 4
	dBodyEnable(Wheel(count))
Next

dBodyEnable(Car)

For count=1 To 4
	dJointSetHinge2Param(Joint(count),dParamVel2,Force)
	dJointSetHinge2Param(Joint(count),dParamFMax2,Torque)
Next

For count=1 To 2
	angle#=Steer-dJointGetHinge2Angle1(Joint(count))
	dJointSetHinge2Param(Joint(count),dParamVel,angle)
	dJointSetHinge2Param(Joint(count),dParamFMax,400)
Next

End Function

; ###################################################################################################

Function UpdateCam()

PositionEntity CameraPivot,0,3,-10

camx#=EntityX(CameraPivot,1)-EntityX(Camera)
camy#=EntityY(CameraPivot,1)-EntityY(Camera)
camz#=EntityZ(CameraPivot,1)-EntityZ(Camera)

TranslateEntity Camera,camx*0.5,camy*0.5,camz*0.5

PointEntity Camera,CMesh

End Function

; ###################################################################################################

Function UpdateGeoms()

For ode.ODEGeom=Each ODEGeom
	RotateEntity ode\mesh,dGeomGetPitch#(ode\geom),dGeomGetYaw#(ode\geom),dGeomGetRoll#(ode\geom)
	PositionEntity ode\mesh,dGeomGetPositionX#(ode\geom),dGeomGetPositionY#(ode\geom),dGeomGetPositionZ#(ode\geom)
Next

End Function

; ###################################################################################################


Hmmm... interesting.. I tried using the dGeomSetOffsetPosition functions before but for some reason that didn't work as simple as you show in these demos. I obviously must have done something wrong..
I just spend 2 hours last weekend bypassing that geomTransform memoryleak stuff we talked about before :)
Anyway, I'm happy to see that these Offset functions DO work - and should only take a jiffy to implement as opposed to how I got things now :)

I got to check this, but I assume you'd still have to destroy every geometry you create - as opposed to 'just the body' they're attached to....

Cheers dude!

danny.

You're welcome :)

Yep, you will need to destroy every geom created to clean up correctly. Destroying the body only will not destroy the associated geoms, the geoms would become static objects unless they're also destroyed.

ok, first noob question, is there no "dCreateCone()" function?? If not, is there any other way to use a cone shape?

John

There's no cone geometry in ODE unfortunately. The easiest way to use a cone shape would be to create a cone mesh in a 3D mesh editor and save it as .b3d or .3ds etc. Then you can use it to create a TriMesh in ODE. You need to keep in mind that moving TriMeshes are not as fast as static TriMeshes.

If you look at the 'Demo-TriMeshes' demo, you can see that any geometry shape can be used if they're not natively provided within ODE. It just means that you would have to build the mesh in an editor and create a TriMesh from it.

There may be other solutions available, but I'd need more detail on how you would like to use the cones.

Hope this helps ;)

Thank you,

I've got it sorted now (almost). I made a trimesh using a blitz cone. Just wanted to add a cone the falling shapes demo.

Might have a go at ragdolls over the weekend. There's a few things I want to try out like clicking and dragging objects. Will let you know how I get on ;o)

More questions to come...

Could someone please explain to me what a "ray" is and how it is used?

A ray is an infinitely thin line in 3D space.

You can use it for pin point physics responses like forces from a bullet hitting a target.

Or, you can use it for line picking (same as Blitz3D line pick stuff). To do this without a physics response you will need to process the collisions without stepping the simulation.

Thanks VIP3R ;o)

I'm trying to do chain physics now, based on the example. I want to be able to make any number of chains so I'm putting the arrays for the chain links and joints into the ODE type. When I make the chain now, it's rigid and won't move. Any ideas?

EDIT

Removed code, got it working now :o)

[Reposted From Blitz3D Thread]

Kestrel (Posted 22 hours ago) Edit

Hi,
I was doing some tinkering with the Demo's for JV-ODE (With Blitz 3D 1.98), specifically Spheres, which just repeatedly drops spheres onto a plane and I get a Stack Overflow from Blitz after increasing the drop rate when I get to about 1500 objects.
I realize that my frame computation time at this stage is high and usefulness of 1500 balls rolling around a plane had limited uses (=P), but my RAM was far from exhausted and I would have anticipated that this would have been the sole restriction for this type of activity.
Is this expected behaviour, can the stack be increased, or it the demo doing something functionally recursive (apart from adding another ball to a list)?

Thanks =)



Wayne (Posted 21 hours ago)

I would expect that to happen.
I believe one could recompile with a larger stack, but as you pointed out it would be of little practical use.



Kestrel (Posted 21 hours ago) Edit

Ok,
I was looking at doing some 'many thousand object' experiments and just playing it back after computation even if each frame took several minutes.
I wonder if it would be possible to get the stack size as a command line argument (that defaults to current value) that was dynamically allocated so you could fully utilize your machine with more objects if you chose?



VIP3R (Posted 2 hours ago)

The problem you are experiencing is due to creating too many objects which are inside each others collision zone. It would also cause horrendous slow down.

To stop the Stack Overflow occurring you need to make sure they don't overlap during creation.

Btw, this should be in the JV-ODE threads really as it's not a Blitz3D issue ;)


JV-ODE Physics V1.20 - Official JV-ODE Threads: 1 2 3 4 5 6 7 8 - Available for Blitz3D & BlitzMax (Win32)
Specs: WinXP SP2 - Pentium 4 2.7GHz - ATI Radeon 9800 128DDR - DirectX 9.0c - 1GB DDR RAM


Kestrel (Posted 5 seconds ago) Edit

Thanks VIP3R,
I wasn't sure if it would be a JV-ODE issue as I figured it just wrapped the ODE library and there would be no reason there would be a limit the amount of objects, you're simply constrained by your own resources.
From what I see of the code, aren't the object already created in such a way that they don't overlap upon creation, i.e. X,Y = Rand(-8 to 8)? From what you said, I guess that would mean I could create 10,000 even spaced spheres for example and they would sit on a plane quite happily (this is not the restriction), but if I put a wall around them all and rolled them into one corner, the system would start to fall over once the collision zone intercepts became bigger than the stack could handle?
I realize the horrendous slowdown, but that is acceptable for what I'm doing. Any way I'll have to delve deeper in to the documentation of how the collision detection (zones) work.

Thanks for the feedback. =)

[I'll Repost this in a JV-ODE Thread]

You're correct, the objects are created within a small area of 16 units square (-8 to 8). There's more to it than that though, firstly there's the timer... If MilliSecs()-Timer>700 and secondly there's gravity. The timer is there to allow the objects to fall by gravity to a safe distance before the next object is added, increasing the rate as you did will not allow enough time for the objects to drop before the next is created thus causing the collision overlap and the overflow.

You could change the creation location by adding a much larger random coordinate area... X and Z = Rand(-2000,2000), then whatever timer rate you use it should be ok, if not increase the random area values accordingly.

That makes sense, Thanks VIP3R. I'll keep fiddling =P

Hi Vip3r,

I still have somewhat of a struggle when it comes to offsetting geometry. Do you have any inside knowledge of how ODE responds when the FIRST (ie. the 'main') geometry is off-set in relation to the rigidbody?

You demo's above illustrate that geom offsetting works fine. But, in those demos you create a 'first' geometry (that also acts to position the ode\mesh's later) and ONLY offset the following additional geometries.

When I offset the first geom, the rigid body behaves and moves around very unpredictably! It's not just as-if the body's Center of Gravity has moved, but on occasion the body starts moving, roling and bouncing around by itself as well. Good trick if you're looking to study the internal dynamics behind mexican jumping beans; But otherwise pretty useless for what I'm trying to do :)

For example, say you model a wine glass. In that case i want to model the bottom of the glass at 0,0,0 - makes more sense and easier to handle when placing, etc. But this means the center of gravity (or the center of the mesh) is NOT at 0,0,0 but a couple centimeters above it, so I need to offset my first and only geometry for that model...

Any insights -as always- appreciated!

Danny
p.s. I can come up with several workaround to this problem, so not a massive problem. I'd just like to understand what it's doing ;)

It doesn't make any difference if you use only one geom and offset it. The only demo which would be suitable to show this would be the GeomOffset-Car demo. It currently uses two geoms for a direct comparison to the GeomTransform-Car demo, but the final version to be released with a future update will only use one geom which is offset.

Wherever you create the body of the ODE object will be the center of gravity, adding one geom and offsetting it will have the exact same effect as moving the center of gravity, but the center of gravity will still be at the location of the body, not the geom.

Here's a version of the GeomOffset-Car demo which uses only one geom offset from the body...

; ###################################################################################################
; #										  JV-ODE Car Demo											#
; #									Code by Jim Williams (VIP3R)									#
; #								Devious Codeworks - Copyright © 2006								#
; ###################################################################################################

AppTitle "JV-ODE Car Demo - Geom Offset - Car"

Include "JV-ODE.bb"

Graphics3D 800,600,0,2

Global CMass#=200			; ### Car Mass
Global WMass#=14			; ### Wheel Mass

Global WorldERP#=1			; ### World Error Correction
Global WorldFriction#=140	; ### World Friction

Global Torque#=24			; ### Joint Torque
Global SuspensionHS#=0.01	; ### Suspension Hardness/Softness (Higher=Softer - Lower=Harder)

Global Force#=0
Global Steer#=0

Global Car
Global CGeom
Global CMesh

Global CarStartY=20

Dim Wheel(4)
Dim WGeom(4)
Dim Joint(4)

Type ODEGeom
	Field body
	Field geom
	Field mesh
End Type

; ###################################################################################################

; ### Setup ODE

Global World=dWorldCreate()
Global Space=dHashSpaceCreate(0)
Global ContactGroup=dJointGroupCreate(0)

dWorldSetAutoDisableFlag(World,1)
dWorldSetGravity(World,0,-0.98,0)
dWorldSetERP(World,WorldERP)

dContactSetMode(dContactSlip1)
dContactSetMu(WorldFriction)

; ### Create car body

ode.ODEGeom=New ODEGeom
ode\body=dBodyCreate(World)
Car=ode\body
dBodySetRotation(ode\body,0,0,0)
dBodySetPosition(ode\body,0,CarStartY-1,0)
mass=dMassCreate()
dMassSetBoxTotal(mass,CMass,3,1,4)
dBodySetMass(ode\body,mass)
dMassDestroy(mass)
ode\geom=dCreateBox(Space,3,1,4)
CGeom=ode\geom
dGeomSetBody(ode\geom,ode\body)
dGeomSetOffsetPosition(ode\geom,0,1,0)
ode\mesh=CreateCube()
CMesh=ode\mesh
ScaleMesh ode\mesh,1.5,0.5,2
RotateMesh ode\mesh,0,0,0
PositionMesh ode\mesh,0,0,0
EntityColor ode\mesh,40,80,255
EntityAlpha ode\mesh,0.7

BodyPosition=CreateCube(ode\mesh)
ScaleMesh BodyPosition,1.5,0.5,2
RotateMesh BodyPosition,0,0,0
PositionMesh BodyPosition,0,-1,0
EntityColor BodyPosition,255,40,80
EntityAlpha BodyPosition,0.4
EntityShininess BodyPosition,0.7

; ### Create wheels

For count=1 To 4
	ode.ODEGeom=New ODEGeom
	ode\body=dBodyCreate(World)
	Wheel(count)=ode\body
	dBodySetRotation(ode\body,0,0,0)
	dBodySetPosition(ode\body,0,0,0)
	mass=dMassCreate()
	dMassSetSphereTotal(mass,WMass,0.7)
	dBodySetMass(ode\body,mass)
	dMassDestroy(mass)
	ode\geom=dCreateSphere(Space,0.7)
	WGeom(count)=ode\geom
	dGeomSetBody(ode\geom,ode\body)
	ode\mesh=CreateCylinder(8)
	ScaleMesh ode\mesh,0.7,0.25,0.7
	RotateMesh ode\mesh,0,0,90
	PositionMesh ode\mesh,0,0,0
	EntityColor ode\mesh,255,255,255
Next

dBodySetPosition(Wheel(1),-2,CarStartY-0.5,+2)
dBodySetPosition(Wheel(2),+2,CarStartY-0.5,+2)
dBodySetPosition(Wheel(3),-2,CarStartY-0.5,-2)
dBodySetPosition(Wheel(4),+2,CarStartY-0.5,-2)

; ### Create some objects

SeedRnd MilliSecs()

For spheres=1 To 20
	ode.ODEGeom=New ODEGeom
	ode\body=dBodyCreate(World)
	dBodySetRotation(ode\body,0,0,0)
	dBodySetPosition(ode\body,Rnd(-100,100),30,Rnd(-100,100))
	ode\geom=dCreateSphere(Space,4)
	dGeomSetBody(ode\geom,ode\body)
	ode\mesh=CreateSphere()
	ScaleMesh ode\mesh,4,4,4
	RotateMesh ode\mesh,0,0,0
	PositionMesh ode\mesh,0,0,0
	EntityColor ode\mesh,Rnd(255),Rnd(255),Rnd(255)
	EntityAlpha ode\mesh,1
	EntityShininess ode\mesh,0.7
Next

For capsules=1 To 20
	ode.ODEGeom=New ODEGeom
	ode\body=dBodyCreate(World)
	dBodySetRotation(ode\body,90,0,0)
	dBodySetPosition(ode\body,Rnd(-100,100),30,Rnd(-100,100))
	ode\geom=dCreateCapsule(Space,1,8)
	dGeomSetBody(ode\geom,ode\body)
	ode\mesh=CreateCylinder(8)
	ScaleMesh ode\mesh,1,5,1
	RotateMesh ode\mesh,90,0,0	; <<< Mesh X-Axis must be rotated 90 degrees to fix cylinder alignment
	PositionMesh ode\mesh,0,0,0
	EntityColor ode\mesh,Rnd(255),Rnd(255),Rnd(255)
	EntityAlpha ode\mesh,1
	EntityShininess ode\mesh,0.7
Next

; ### Create light

Global Light=CreateLight()

RotateEntity Light,45,-90,0
LightColor Light,255,255,255
AmbientLight 130,130,130

; ### Create camera

Global CameraPivot=CreatePivot(CMesh)
PositionEntity CameraPivot,0,2,-7

Global Camera=CreateCamera()
CameraClsColor Camera,0,0,0
CameraRange Camera,1,1000

; ### Create plane

dCreatePlane(Space,0,1,0,0)

Plane=CreatePlane()

EntityAlpha Plane,0.8

PlaneTexture=CreateTexture(128,128,9)

ClsColor 0,200,80
Cls

Color 255,255,255

Rect 0,0,64,64,1
Rect 64,64,64,64,1

CopyRect 0,0,128,128,0,0,BackBuffer(),TextureBuffer(PlaneTexture)
ScaleTexture PlaneTexture,20,20
EntityTexture Plane,PlaneTexture,0,0

Mirror=CreateMirror()

; ###################################################################################################

; ### BLITZ STATIC OBJECT (White)

blitzobject=CreateCube()
ScaleMesh blitzobject,1,4,20
RotateMesh blitzobject,-10,8,4
PositionMesh blitzobject,10,5,40

EntityColor blitzobject,255,255,255
EntityAlpha blitzobject,1

; ### ODE STATIC OBJECT (Blue)

ode.ODEGeom=New ODEGeom
ode\geom=dCreateBox(Space,2,8,40)
dGeomSetRotation(ode\geom,-10,8,4)
dGeomSetPosition(ode\geom,20,5,40)
ode\mesh=CreateCube()
ScaleMesh ode\mesh,1,4,20
RotateMesh ode\mesh,0,0,0
PositionMesh ode\mesh,0,0,0

EntityColor ode\mesh,0,100,200
EntityAlpha ode\mesh,1

; ### ODE STATIC OBJECT (Red)

ode.ODEGeom=New ODEGeom
ode\geom=dCreateBox(Space,18,0.2,40)
dGeomSetRotation(ode\geom,-14,0,0)
dGeomSetPosition(ode\geom,-20,4.9,40)
ode\mesh=CreateCube()
ScaleMesh ode\mesh,9,0.1,20
RotateMesh ode\mesh,0,0,0
PositionMesh ode\mesh,0,0,0

EntityColor ode\mesh,200,0,100
EntityAlpha ode\mesh,1

; ###################################################################################################

SetupCar()

While Not KeyHit(1)

	UpdateKeys()

	PTime=MilliSecs()

	UpdateCar()

	UpdateGeoms()

	dSpaceCollide(Space,World,ContactGroup)
	dWorldQuickStep(World,0.1)
	dJointGroupEmpty(ContactGroup)

	PhysicsTime#=MilliSecs()-PTime

	UpdateCam()

	UpdateWorld

	RenderWorld

	Text 0,0,"JV-ODE Version "+dGetVersion()
	Text 0,15,"Physics Time:"+PhysicsTime
	Text 0,50,"Force:"+Force
	Text 0,65,"Torque:"+Torque
	Text 0,100,"Blue = Geom Offset"
	Text 0,115,"Red = Body/Mass Position"
	Text 640,0,"A - Accelerate"
	Text 640,15,"Z - Brake/Reverse"
	Text 640,30,"Arrows - Steering"
	Text 640,45,"Space - Reset Car"

	Flip

Wend

dJointGroupDestroy(ContactGroup)
dSpaceDestroy(Space)
dWorldDestroy(World)
dCloseODE()

End

; ###################################################################################################

Function SetupCar()

For count=1 To 4
	Joint(count)=dJointCreateHinge2(World,0)
	dJointAttach(Joint(count),Car,Wheel(count))
	dJointSetHinge2Anchor(Joint(count),dBodyGetPositionX(Wheel(count)),dBodyGetPositionY(Wheel(count)),dBodyGetPositionZ(Wheel(count)))
	dJointSetHinge2Axis1(Joint(count),0,1,0)
	dJointSetHinge2Axis2(Joint(count),-1,0,0)
	dJointSetHinge2Param(Joint(count),dParamSuspensionERP,0.8)
	dJointSetHinge2Param(Joint(count),dParamSuspensionCFM,SuspensionHS)
	If count>2
		dJointSetHinge2Param(Joint(count),dParamLoStop,0)
		dJointSetHinge2Param(Joint(count),dParamHiStop,0)
		End If
Next

Force=0

End Function

; ###################################################################################################

Function UpdateKeys()

If KeyDown(30)=1
	Force=Force+0.08
	If Force>30 Then Force=30
	End If

If KeyDown(44)=1
	Force=Force-0.1
	If Force<-10 Then Force=-10
	If Force>0 Then Force=Force*0.97
	End If

If KeyDown(30)=0 And KeyDown(44)=0 Then Force=Force*0.99

If KeyDown(203)=1 Or KeyDown(205)=1
	If KeyDown(203)=1
		Steer=0.5
		Else
		Steer=-0.5
		End If
	Else
	Steer=0
	End If

If KeyHit(57)=1
	Force=0
	dBodySetRotation(Car,dGeomGetPitch#(CGeom),dGeomGetYaw#(CGeom),0)
	End If

End Function

; ###################################################################################################

Function UpdateCar()

For count=1 To 4
	dBodyEnable(Wheel(count))
Next

dBodyEnable(Car)

For count=1 To 4
	dJointSetHinge2Param(Joint(count),dParamVel2,Force)
	dJointSetHinge2Param(Joint(count),dParamFMax2,Torque)
Next

For count=1 To 2
	angle#=Steer-dJointGetHinge2Angle1(Joint(count))
	dJointSetHinge2Param(Joint(count),dParamVel,angle)
	dJointSetHinge2Param(Joint(count),dParamFMax,400)
Next

End Function

; ###################################################################################################

Function UpdateCam()

PositionEntity CameraPivot,0,3,-12

camx#=EntityX(CameraPivot,1)-EntityX(Camera)
camy#=EntityY(CameraPivot,1)-EntityY(Camera)
camz#=EntityZ(CameraPivot,1)-EntityZ(Camera)

TranslateEntity Camera,camx*0.5,camy*0.5,camz*0.5

PointEntity Camera,CMesh

End Function

; ###################################################################################################

Function UpdateGeoms()

For ode.ODEGeom=Each ODEGeom
	RotateEntity ode\mesh,dGeomGetPitch#(ode\geom),dGeomGetYaw#(ode\geom),dGeomGetRoll#(ode\geom)
	PositionEntity ode\mesh,dGeomGetPositionX#(ode\geom),dGeomGetPositionY#(ode\geom),dGeomGetPositionZ#(ode\geom)
Next

End Function

; ###################################################################################################


As you can see the demo works exactly as expected, if you're experiencing strange effects it's very likely that you're doing something wrong somewhere.

Try changing the following two lines in the above demo...

dGeomSetOffsetPosition(ode\geom,0,1,0)
...
PositionMesh BodyPosition,0,-1,0
to
dGeomSetOffsetPosition(ode\geom,0,10,0)
...
PositionMesh BodyPosition,0,-10,0
Now you will see the position of the geom has been offset much higher, but the center of gravity (and the body/mass) still remains at its original location. You can test this by trying to overturn the car.

And no mexican jumping bean syndrome to be seen at all ;)

Are you using any mass? Zero mass could cause odd effects.

yep, I had mass 'temp disabled' to test the offsets. And found a bug in my own code correcting for rotation offset. But got that working fine now! thanks dude!

D.

Hi Vip3r, here I'm again... I need to understand one thing about the TriMesh. When I load some models I have the "physics time" between 0 and 1, and, sometimes, I made other models and when I load, the "physics time" go to 7 or 8 or more... Why this happen?

And, other thing, using car physics... Some objects (TriMesh) make the car flick, or, the wheels "jumping", why?

If the physics time changes significantly with different models then it may be due to the use of much more complex meshes, or it could indicate some meshes are overlapping and wrongly creating collision contacts. There could be a multitude of reasons why the physics time increases though, I would need to see some code that reproduces the problem to correctly identify the cause of the increased physics time.

The jumping/twitching can be caused once again for lots of different reasons, the TriMesh may be too stretched making the mesh triangles too large or too small, the mass of the car/wheels could be too light or too heavy, the surface contact depths may be too shallow, etc. Again, without seeing the problem reproduced it's difficult to say for sure.

Hi Vip3r, I'll send an email with the .3ds objects to you see...

Hi EmerGki,

Thanks for sending the meshes.

There's nothing wrong with the meshes which is good news.

I definitely think the problem is due to overlapping TriMeshes triggering lots of collision contacts per time step, especially as you don't seem to be using multiple spaces for the TriMeshes in your example code.

You need to place all of those scene meshes into a sub-space instead of the top level 'Space'...
Global Space=dHashSpaceCreate(0)
Global SceneSpace=dHashSpaceCreate(Space)
...
trimesh=CreateTriMesh(SceneSpace,Road)
...
trimesh=CreateTriMesh(SceneSpace,po)

...then you should disable internal space collisions with the following function...
dSetInternalSpaceCollideMode(0)

Doing the above will prevent the meshes colliding with each other and triggering collision contacts.

You can see the above multiple space method used in the 'CarDemo-TriMesh-X4' demo included with JV-ODE. There's also some information in the JV-ODE Function Ref about the above space collide mode function.

Let me know if this fixes the problem.

Thanks a lot Vip3r, I'll Try =D.

Hey there,

I'm trying to do the simplest of tasks here.
I have a box (my car in this case), and i have a cylinder next to the box (my wheel).

I have created another box (which is like a bar going from the car to the wheel) and i want to hinge one end of this box to the car and the other to the wheel... its the first part of creating visible suspension.
But i just cant do it.

I tried and the bar flew all over the place. Is there anyone out there that can just give me a really simple example of how to work joints and hinges, because i think thats my problem.

Thanks.

There's lots of info about setting up joints in the official ODE docs and also previous JV-ODE threads.

What joint types are you using?

I suspect it's probably due to the joint types being used rather than the joint settings. For example, you can't use hinge joints for both the bar and the wheel, the bar would need to be fixed not hinged.

It would be best to post some code showing the problem tbh.

Ok cool, i managed to fix it, but i have another couple of questions... more like "is this possible?"

Is there any way to make multiple gravities? E.g. planets that have their own gravity?

Also, is it possible to make a collision object out of an animated mesh? E.g. a robot arm swings out and knocks a few jars off a shelf.

I would have no idea if these are even possible and would have no idea how to go about them, so and help is awesome. :D

No, gravity is global so it can't have multiple strengths. There is a workaround though, you can adjust the mass of all objects to be affected by the gravity to achieve the same effect. For example, 2x mass would be the same as 2x gravity.

You can make collision meshes from an animated mesh but it must remain static, which doesn't sound suitable for your example.

So there is no way to have say a truck drive along a winding road (custom animated) and have boxes in the back bouncing around and colliding off the truck?

You mean the truck and the road are one animated mesh? If that's what you mean then it won't work, as ODE wouldn't be aware the animated truck had moved. The boxes would collide correctly with both the road and the truck, but ODE would only detect the start frame of animation. No matter where the truck animation was positioned, ODE will always think it was at the starting position.

I'm not sure that scenario would work with any physics system tbh, if it did it would be incredibly slow as the simulation would need to recreate the simulation from each animation frame during runtime.

The other problem is physics engines like ODE need full control over every object in the simulation, otherwise it's unable to create physics responses when a collision is detected between the objects. In effect you would be manually positioning the truck, which would override the physics response and cause collision problems.

You could still achieve what you want, but you need to take a different approach. For example, create the truck separately and move it through waypoints by applying forces instead.

I recall a NovodeX demo which involved a galloping horse (an animated mesh) which could interact with other objects--if a box happened to be in the way, then the horse would knock it out of the way as it galloped past. If you pressed a certain key, then the horse would 'die' --meaning it would suddenly become a rag doll.

I think this is a similar scenario to the one Icecap is describing---before the horse is turned into a ragdoll. The trick to making it work, is that the galloping horse is controlled by its own animated bones. The program creates some physics bodies, which it manually moves around to match the current position of the animated bones--and so the horse appears to be able to interact with other objects.

Turning the horse into a ragdoll reverses things. Animation is turned off, and now the physics library takes control of how the physics bodies move. Wherever they go, the bones of the horse mesh are now manually made to follow.

I'd love to see this pulled off within Blitz, but I suspect the lack of access to animated mesh info may unfortunately keep that from happening. Maybe not...

It's not that hard to do.
ODE is quite forgiving when you 'force' rigid bodies at a certain location (unlike tokamak if I recall correctly).
Even though every body (wisely) recommends to use forces in stead to drive something..

So for a character/horse/truck you create rigid bodies that roughly match the geomtry, disable their gravity and force them to follow the bone positions/rotations each frame (and they will kick/push boxes out of the way) whilst in animation mode.
Then you simply 'stop doing that' when you want ragdoll effects (and enable gravity again on the rigid bodies)...

d.

Hmm, all of that is still possible with ODE but with one crucial exception... the manual positioning to match the animated bones would cause collision penetration problems. It would work to some extent as Danny says, maybe even well enough for the truck/road example, but there will definitely be some penetration problems with the boxes.

It could also probably be done by processing the collisions and adding forces manually, it might get quite complex though.

I'll have a think and see if there's some other way around it.

I'd expected that it wouldn't be a problem on the physics library side, but determining the position/orientation of a given bone as effected by an animation... I've not been able to crack that one. Although, I still think it may be possible.

Yes vip3r is right that the danger might be the 'physics explosion' - where one body might suddenly intersect another body from one frame to another. But I think you can limited this unwanted effect using dWorldSetContactMaxCorrectingVel() - and possibly measuring the total force on collided objects and limit that to a certain (realistic) maximum.

I've been playing with this for a while (is good fun! :) by 'attaching' a box to your mouse pointer, and moving it around IN a pile of boxes. ODe seems to react really well, but Yes, if you make FAST and sudden movements, you will get a collision-penetration problem that will cause the target to shoot-away at high speed. I haven't tested it but I'm nearly convinced that that effect can be 'tamed' :)

@barliesque: Not sure what you mean. You can access the bones in a character like any other blitz entity. I don't know if bones give a 'false reading' when they are animated (I never use animated characters - not enough control). But if that works then in your main loop simply:

- animate character (animmesh)
- read bone position & rotation
- apply position & rotation to rigid body
- reset forces on rigid body

And bob's your uncle.

unless I'm missing something...?!

D.

Yeah I just tested it and setting dWorldSetContactMaxCorrectingVel() to 2 (in my situation) does nicely prevent bodies from 'exploding' away from each other in case of sudden collision penetration. ODE rocks!

You do have to wath your weight ofcourse ;) For example if a 1 kilogram body is (forced via position/rotation) INto colliding with a stationairy 3000 kilogram body, then the(slightly anoyed) 3000kg body refuses to move much, thus your 1kg body starts to 'jitter' due to the urge to get the hell out of there since it's obviously not very comfortable.

Hope this makes sense :)

Danny

Naturally, that's exactly what I did, but Bob did not turn out to be my uncle. The bone readings would move, clearly affected by the animation, but would be sort of inside out. I can't quite recall the details of the experiment I was working on way back when, but suffice to say that my ostrich exploded.

Bones need to be 'unparented' when you let ODE drive them. Because (normally) bones are all parented together, so if you change one's position, you're affecting the entire chain of bones.
Also, as soon as you want to switch an animated character to ragdoll mode you have to manually reposition every rigid body ANd joint to the proper location, else it will implode as well.


Here's a quick demo I just hacked together to show an animated character using ODE geometry to 'walk between boxes' and push or knock them over. This was done by simply COPYING the bone position/rotation to the rigid bodies. So not by 'applying forces' to TRY and push objects to an exact xyz location,etc.

You can use space-bar to toggle between animated character & ragdoll. BUT NOTE: I DID NOT take the time to properly setup the ragdoll - so it will go all wonky - but it does demonstrates the idea...

This demo plays ok on my system (speed wise) and doesn't even use/need the dWorldSetContactMaxCorrectingVel() to prevent objects 'exploding'. But I'm curious to feedback if that's the case on everybody's system..
www.xs4all.nl/~dendanny/Blitz/ODEAnimatedCollision.zip


Hope this helps,

Danny.

> Bones need to be 'unparented' when you let ODE drive them.

Yes of course. Please understand, there are two scenarios I've described above: An animated mesh driving the physics bodies; and the reverse, where the animation is turned off and the physics bodies drive the bones, to turn the figure into a ragdoll. Standard stuff, really. When you kill an enemy character (animated mesh) in loads of games, they turn into ragdolls.

> Here's a quick demo...

I'm on the wrong machine to look at that just now, but I look forward to having a look when I get a chance. I'm having trouble recalling precisely what went wrong with my experiment... Obviously I'll have to blow the dust off that one.

Hi Barliesque,

The same scenario you've described is used in the PaceMaker application (using JV-ODE) created by Ricky Smith. Once it's setup correctly it can work very well, there was some basic info on this subject in the previous JV-ODE threads which might be helpful.

I like the ragdoll demo there. I was playing around with ragdolls a couple of months back but I never had time to finish. Your way of setting up the bones is much better and simpler than what I was trying. Are you going to fix the ragdoll mode in the demo so it doesn't collapse?

Thanks ja2, But it's just a really quick & dirty demo to show you can use ode collision on animated characters - and that you can switch between animated & ragdoll characters.

Everybody's got their own system for storing ragdoll data as well as a methode of rigging a character themselves; so I don't think it's worth the effort trying to get that character to work - since the next character will need a different approach. (It's sunday and I'm a lazy sod ;)p

D.

How would I go about making a motorbike in ODE? Something similar to the car in the car demo but with only 2 wheels?

Create the bike body with two wheels beneath it using spheres and hinge2 joints (same as the car). The position of the bike body should be much lower than its geom so that it lowers the center of gravity and helps balance the bike to stop it falling over when stationary. The same method is used to a smaller degree on the car body in the car demos, you can either use geom offset or mass translate to offset the geom from the body. Make sure to add enough mass to the wheels so that they generate a gyroscopic effect when rotating. Then it will need lots of testing/tweaking to get the suspension setup and handling working well.

Thank you very much. I'll give that a try ;)

EDIT: Ok I have it working a bit now. I'm having trouble with this tho,

Make sure to add enough mass to the wheels so that they generate a gyroscopic effect when rotating


When the front wheel turns, the bike just falls over...

making a motorbike for your firsr\t ode project might be challenging. What are you using to control the bike?

It's not quite my first project. I've been busy working on other things in ODE, chains and ropes mainly. This is my first attempt at using vehicles and I quite like a challenge :)

I was thinking about using 4 wheels like the car but only showing 2 models for the actual bike mesh, but it doesn't look quite right and it's cheating anyway LOL

I'm using the car demo as a basis for my motorbike. I've lowered the center of gravity and increased the mass of wheels as suggested.

At the risk of sounding stupid, it is this bit that I don't get: so that they generate a gyroscopic effect when rotating.

The gyroscopic effect is what helps keep bikes from falling over when moving, don't worry about it too much as the lowered center of gravity will do most of this for you.

You should only turn the front wheel when moving at very slow speeds, for turning at faster speeds it's the same as real life - you tilt the bike body left/right. In reality this is obviously done by the rider leaning left/right, but you can add relative force (sideways) to the bike body to achieve the same effect.

Thank you VIP3R, just about got it working now. It seems that the bike stays at the same pitch now tho having a lowered mass.

You're welcome JA2 :)

You can try setting the center of gravity at different levels, until you find a good setup. Also, experiment with different mass values for the wheels and bike body.

Hi vip3r,

i can desperately use your help with this silly problem...

I got two boxes above each other (a chest and it's lid), they are connected by a hinge joint. The setup seems to work, ie. the boxes collide with the trimesh floor or any other rigid bodies, the hinge constrains the two boxes and keeps them together. BUT the two collision boxes do NOT collide with each other. in that they simply sink in to each other as if the other one doesn't exist.
I've made the boxes slightly smaller so that there's space between them - so they have some room to hinge.

I've tried different settings on 'dSetInternalSpaceCollideMode' but that doesn't seem to make a difference.

Any idea what I'm doing wrong or forgot? Posting code would be a bit tricky... I know I've had this before and solved it. But right now I haven't got a clue what i'm missing :/

Thanks,
Danny.

Hi Danny,

No problem, when two objects are 'directly' jointed in ODE they will be automatically excluded from colliding with each other. You're not doing anything wrong, it's quite normal.

The easiest/best way to solve it is to set the joint limits so that the hinge itself restricts the lid movement, preventing it from penetrating into the box. Another way is to attach another geom either to the rim of the box or the lid to act as a stopper, you can use the fixed joint to attach it. The stopper will collide with the geom it's not jointed to directly.

Thanks for the quick reply vip3r,

That does explain it indeed. I always thought you could 'toggle' that exclusion as option...

But ok, that collision exclusion applies ONLY to the joint's 'connected bodies', right?! - For example, if you have a chain of 3 boxes - with 2 hinge joints in between. Does that mean the first & last box WILL collide 'normally'?

D.

Yep, the first and last boxes will collide normally, only objects directly jointed to each other will be excluded.

cool. cheers mate!

After some advice if possible.

I was wondering how I was to setup a trimesh for use in 2D, all the demos only show terrains loaded in using the b3d loader.

Basically imagine the 2d balls demo with a sloped terrain along the bottom of the screen - peaks and dips........

- sorry re-posted in the BMAX thread......

See your other thread ;)

Hi VIP3R, I'm having some problems with the Truck and Trailers in my game. Maybe you can help me...

The problems happens when the truck pass in ascent roads, I'll explain better in the picture, because when one part of my truck are in one height and the other part in other height, I lost the control of the truck, because not all whells are in the floor, and the trailers, not pitch following the floor.


In this first image, you can see what the wheels are not in the floor


In this second image, you can see what the truck and trailers not follow the height of the floor.


Hi EmerGki,

I'm guessing you're connecting the trailers with a hinge joint which only allows for movement on a single axis (sideways in this case), because of this it won't allow movement up and down too.

You have two options, one option would be to replace the hinge joints with ball joints, which will allow movement in all directions. Note that if you're currently relying on joint limits to limit the sideways motion of the trailer, you might need to add some 'stoppers' to restrict the movement instead, as ball joints do not allow for joint limits. There was discussion about using stoppers with Danny above.

Or, you can add an additional hinge joint with another small flat block to allow the up and down movement. So you would have a small block attached to the truck body connected with one hinge (allowing sideways movement) and then you connect the small block to the trailer with another hinge (allowing up/down movement). This option can be used with joint limits to restrict movement.

JV-ODE V1.21 Update Released

Please check your inbox :)

The new update includes the following additions...

Fixed dBodyGetMass() issue

Added new functions:
dMassRotateQuaternion(mass,qw#,qx#,qy#,qz#) - alternative rotation system for dMassRotate()
dMassRotateAxisAngle(mass,angle#,rx#,ry#,rz#) - alternative rotation system for dMassRotate()


Added new demos:
CarDemo-GeomOffset-Car
CarDemo-GeomOffset-Cube
CarDemo-GeomOffset-Mixed
CarDemo-GeomOffset-Sphere


Please let me know if you experience any problems with the new update.

:)

just tried the new demos and a few others, and everything is honky dory :)

reminds me, to get back on some physics projects i started way back, including fluids and tearable clothes =)

I checked out the car stuff and made some mods thought some of you may enjoy this car. It's not ray cast but uses standard ODE.
The air friction isn't in just yet. Controls: Q,A,Z and Arrow keys.

; ###################################################################################################
; #										  JV-ODE Car Demo											#
; #									Code by Jim Williams (VIP3R)									#
; #								Devious Codeworks - Copyright © 2007								#
; ###################################################################################################

AppTitle "JV-ODE Car Demo - High Speed with 427 Hemi"

Include "JV-ODE.bb"

Graphics3D 800,600,0,2

Global tstep#=.05	; Physics Time Step
Global steps=3		; Number of steps

Global CMass#=150; 200		; ### Car Mass
Global WMass#=10;14			; ### Wheel Mass

Global WorldERP#=0.8		; ### World Error Correction
Global WorldFriction#=80	; ### World Friction

Global Torque#=48			; ### Joint Torque
Global MinTorque#=48
Global MaxTorque#=115
Global SuspensionHS#=0.01	; ### Suspension Hardness/Softness (Higher=Softer - Lower=Harder)

Global Force#=0
Global Steer#=0

Global f_mu#=125		;Front tire friction
Global r_mu#=250		;Rear tire friction

Global MaxForce#=10
Global MaxAccel#=2
Global Accel#=.2


Global Car
Global CGeom
Global CMesh

Global CarStartY=4

Dim Wheel(4)
Dim WGeom(4)
Dim Joint(4)

Type ODEGeom
	Field body
	Field geom
	Field mesh
End Type

; ###################################################################################################

; ### Setup ODE

Global World=dWorldCreate()
Global Space=dHashSpaceCreate(0)
Global ContactGroup=dJointGroupCreate(0)

dWorldSetAutoDisableFlag(World,1)
dWorldSetGravity(World,0,-0.98,0)
dWorldSetERP(World,WorldERP)

;dContactSetMode(dContactSlip1)
dContactSetMode(dContactSlip1)
dContactSetBounce(0.0)

dContactSetMu(WorldFriction)

; ### Create car body

ode.ODEGeom=New ODEGeom
ode\body=dBodyCreate(World)
Car=ode\body
dBodySetRotation(ode\body,0,0,0)
dBodySetPosition(ode\body,0,CarStartY,0)
mass=dMassCreate()
dMassSetBoxTotal(mass,CMass,3,1,4)
dMassTranslate(mass,0,-1,0)
dBodySetMass(ode\body,mass)
dMassDestroy(mass)
ode\geom=dCreateBox(Space,3,1,4)
CGeom=ode\geom
dGeomSetBody(ode\geom,ode\body)
ode\mesh=CreateCube()
CMesh=ode\mesh
ScaleMesh ode\mesh,1.5,0.5,2
RotateMesh ode\mesh,0,0,0
PositionMesh ode\mesh,0,0,0
EntityColor ode\mesh,40,80,255
EntityAlpha ode\mesh,1

; ### Create wheels

For count=1 To 4
	ode.ODEGeom=New ODEGeom
	ode\body=dBodyCreate(World)
	Wheel(count)=ode\body
	dBodySetRotation(ode\body,0,0,0)
	dBodySetPosition(ode\body,0,0,0)
	mass=dMassCreate()
	dMassSetSphereTotal(mass,WMass,0.7)
	dBodySetMass(ode\body,mass)
	dMassDestroy(mass)
	ode\geom=dCreateSphere(Space,0.7)
	WGeom(count)=ode\geom
	dGeomSetBody(ode\geom,ode\body)
	ode\mesh=CreateCylinder(8)
	ScaleMesh ode\mesh,0.7,0.25,0.7
	RotateMesh ode\mesh,0,0,90
	PositionMesh ode\mesh,0,0,0
	EntityColor ode\mesh,255,255,255
	
	; Set wheel friction
	Select count
		Case 1
			dGeomContactSetMu(ode\geom,f_mu)
		Case 2
			dGeomContactSetMu(ode\geom,f_mu)
		Case 3
			dGeomContactSetMu(ode\geom,r_mu)
		Case 4
			dGeomContactSetMu(ode\geom,r_mu)
	End Select
	
Next

dBodySetPosition(Wheel(1),-2,CarStartY-0.5,+2)
dBodySetPosition(Wheel(2),+2,CarStartY-0.5,+2)
dBodySetPosition(Wheel(3),-2,CarStartY-0.5,-2)
dBodySetPosition(Wheel(4),+2,CarStartY-0.5,-2)

; ### Create some objects

SeedRnd MilliSecs()

For spheres=1 To 40
	ode.ODEGeom=New ODEGeom
	ode\body=dBodyCreate(World)
	dBodySetRotation(ode\body,0,0,0)
	dBodySetPosition(ode\body,Rnd(-800,800),30,Rnd(-800,800))
	ode\geom=dCreateSphere(Space,4)
	dGeomSetBody(ode\geom,ode\body)
	ode\mesh=CreateSphere()
	ScaleMesh ode\mesh,4,4,4
	RotateMesh ode\mesh,0,0,0
	PositionMesh ode\mesh,0,0,0
	EntityColor ode\mesh,Rnd(255),Rnd(255),Rnd(255)
	EntityAlpha ode\mesh,1
	EntityShininess ode\mesh,0.7
Next

For capsules=1 To 40
	ode.ODEGeom=New ODEGeom
	ode\body=dBodyCreate(World)
	dBodySetRotation(ode\body,90,0,0)
	dBodySetPosition(ode\body,Rnd(-800,800),30,Rnd(-800,800))
	ode\geom=dCreateCapsule(Space,1,8)
	dGeomSetBody(ode\geom,ode\body)
	ode\mesh=CreateCylinder(8)
	ScaleMesh ode\mesh,1,5,1
	RotateMesh ode\mesh,90,0,0	; <<< Mesh X-Axis must be rotated 90 degrees to fix cylinder alignment
	PositionMesh ode\mesh,0,0,0
	EntityColor ode\mesh,Rnd(255),Rnd(255),Rnd(255)
	EntityAlpha ode\mesh,1
	EntityShininess ode\mesh,0.7
Next

; ### Create light

Global Light=CreateLight()

RotateEntity Light,45,-90,0
LightColor Light,255,255,255
AmbientLight 130,130,130

; ### Create camera

Global CameraPivot=CreatePivot(CMesh)
PositionEntity CameraPivot,0,2,-7

Global Camera=CreateCamera()
CameraClsColor Camera,0,0,0
CameraRange Camera,1,1000

; ### Create plane

dCreatePlane(Space,0,1,0,0)

Plane=CreatePlane()

EntityAlpha Plane,0.8

PlaneTexture=CreateTexture(128,128,9)

ClsColor 0,200,80
Cls

Color 255,255,255

Rect 0,0,64,64,1
Rect 64,64,64,64,1

CopyRect 0,0,128,128,0,0,BackBuffer(),TextureBuffer(PlaneTexture)
ScaleTexture PlaneTexture,20,20
EntityTexture Plane,PlaneTexture,0,0

Mirror=CreateMirror()

; ###################################################################################################

; ### BLITZ STATIC OBJECT (White)

blitzobject=CreateCube()
ScaleMesh blitzobject,1,4,20
RotateMesh blitzobject,-10,8,4
PositionMesh blitzobject,10,5,40

EntityColor blitzobject,255,255,255
EntityAlpha blitzobject,1

; ### ODE STATIC OBJECT (Blue)

ode.ODEGeom=New ODEGeom
ode\geom=dCreateBox(Space,2,8,40)
dGeomSetRotation(ode\geom,-10,8,4)
dGeomSetPosition(ode\geom,20,5,40)
ode\mesh=CreateCube()
ScaleMesh ode\mesh,1,4,20
RotateMesh ode\mesh,0,0,0
PositionMesh ode\mesh,0,0,0

EntityColor ode\mesh,0,100,200
EntityAlpha ode\mesh,1

; ### ODE STATIC OBJECT (Red)

ode.ODEGeom=New ODEGeom
ode\geom=dCreateBox(Space,18,0.2,40)
dGeomSetRotation(ode\geom,-14,0,0)
dGeomSetPosition(ode\geom,-20,4.9,40)
ode\mesh=CreateCube()
ScaleMesh ode\mesh,9,0.1,20
RotateMesh ode\mesh,0,0,0
PositionMesh ode\mesh,0,0,0

EntityColor ode\mesh,200,0,100
EntityAlpha ode\mesh,1

; ###################################################################################################

SetupCar()

While Not KeyHit(1)

; Calculate Time 
If T=0 Then T=MilliSecs()
T=MilliSecs() 

	

; Run Physics every 20ms ( APROX 50/sec )
If T > Time3 + 20 Then
	Time3=T	
	
	UpdateKeys()

	PTime=MilliSecs()

	UpdateCar()

	UpdateGeoms()
	
	For i=1 To steps
		dSpaceCollide(Space,World,ContactGroup)
		dWorldQuickStep(World,tstep)
		dJointGroupEmpty(ContactGroup)
	Next
	
	;UpdateGeoms()
	UpdateCam()
	UpdateWorld
	
	PhysicsTime#=MilliSecs()-PTime
End If

	RenderWorld

	Text 0,0,"JV-ODE Version "+dGetVersion()
	Text 0,15,"Physics Time:"+PhysicsTime
	Text 0,50,"Force:"+Force
	Text 0,65,"Torque:"+Torque
	Text 640,0,"A - Accelerate"
	Text 640,15,"Z - Brake/Reverse"
	Text 640,30,"Arrows - Steering"
	Text 640,45,"Space - Reset Car"

	Flip

Wend

dJointGroupDestroy(ContactGroup)
dSpaceDestroy(Space)
dWorldDestroy(World)
dCloseODE()

End

; ###################################################################################################

Function SetupCar()

For count=1 To 4
	Joint(count)=dJointCreateHinge2(World,0)
	dJointAttach(Joint(count),Car,Wheel(count))
	dJointSetHinge2Anchor(Joint(count),dBodyGetPositionX(Wheel(count)),dBodyGetPositionY(Wheel(count)),dBodyGetPositionZ(Wheel(count)))
	dJointSetHinge2Axis1(Joint(count),0,1,0)
	dJointSetHinge2Axis2(Joint(count),-1,0,0)
	dJointSetHinge2Param(Joint(count),dParamSuspensionERP,0.8)
	dJointSetHinge2Param(Joint(count),dParamSuspensionCFM,SuspensionHS)
	If count>2
		dJointSetHinge2Param(Joint(count),dParamLoStop,0)
		dJointSetHinge2Param(Joint(count),dParamHiStop,0)
	End If
	
Next

Force=0

End Function

; ###################################################################################################

Function UpdateKeys()

; (A)cceleration Normal
If KeyDown(30)=1
	Force=Force+Accel
	If Force>MaxForce Then Force=MaxForce
	Torque#=MinTorque
End If

; (Q)uick Acceleration
If KeyDown(16)=1
	Force=Force+MaxAccel
	Torque#=MaxTorque
	If Force>MaxForce Then Force=MaxForce
End If

If KeyDown(44)=1
	Force=Force-0.1
	If Force<-10 Then Force=-10
	If Force>0 Then Force=Force*0.97
	Torque#=MinTorque
End If

If KeyDown(30)=0 And KeyDown(16)=0 And KeyDown(44)=0 Then 
	Force=Force*0.75
	Torque#=MinTorque
End If

If KeyDown(203)=1 Or KeyDown(205)=1
	If KeyDown(203)=1
		Steer=0.5
		Else
		Steer=-0.5
		End If
	Else
	Steer=0
	End If

If KeyHit(57)=1
	Force=0
	dBodySetRotation(Car,dGeomGetPitch#(CGeom),dGeomGetYaw#(CGeom),0)
	End If

End Function

; ###################################################################################################

Function UpdateCar()

For count=1 To 4
	dBodyEnable(Wheel(count))
Next

dBodyEnable(Car)

; Rear wheel drive
For count=3 To 4
	dJointSetHinge2Param(Joint(count),dParamVel2,Force)
	dJointSetHinge2Param(Joint(count),dParamFMax2,Torque)
Next

For count=1 To 2
	angle#=Steer-dJointGetHinge2Angle1(Joint(count))
	dJointSetHinge2Param(Joint(count),dParamVel,angle)
	dJointSetHinge2Param(Joint(count),dParamFMax,400)
Next

End Function

; ###################################################################################################

Function UpdateCam()

PositionEntity CameraPivot,0,3,-12

camx#=EntityX(CameraPivot,1)-EntityX(Camera)
camy#=EntityY(CameraPivot,1)-EntityY(Camera)
camz#=EntityZ(CameraPivot,1)-EntityZ(Camera)

TranslateEntity Camera,camx*0.5,camy*0.5,camz*0.5

PointEntity Camera,CMesh

End Function

; ###################################################################################################

Function AirDrag()
; Purpose: Calculate the drag due to air friction.
; This will limit the maximum upper velocity of the car.
;
; fdrag=-(1/2)*C*p*A*v^2
; C=Drag Coff
; p=AirDensity
; A=Cross sectional area
; v=velocity
Local C#,p#,A#,v#
C#=1
p#=12
A#=10

dBodyGetLinearVel(CAR) 


End Function

; ###################################################################################################

Function UpdateGeoms()

For ode.ODEGeom=Each ODEGeom
	RotateEntity ode\mesh,dGeomGetPitch#(ode\geom),dGeomGetYaw#(ode\geom),dGeomGetRoll#(ode\geom)
	PositionEntity ode\mesh,dGeomGetPositionX#(ode\geom),dGeomGetPositionY#(ode\geom),dGeomGetPositionZ#(ode\geom)
Next

End Function

; ###################################################################################################


hey, that works really well. thanks for sharing! would be nice if you could explain what you changed, except from increasing the step count...

and...

just for your information, this:
; Calculate Time 
If T=0 Then T=MilliSecs()
T=MilliSecs() 

; Run Physics every 20ms ( APROX 50/sec )
If T > Time3 + 20 Then
	Time3=T	

is one ugly piece of code, since it doesnt work at all, if millisecs() is returning a negative number! which happens if your system is up longer than ~24,85 days (because blitz returns a signed int32). and yes, my system is up since 32 days ;)

setting a 'Time3 = MilliSecs()' before the main loop fixed that issue temporarily, but you definitly should try to use a proper timer-system :)

Good call, I usually reboot sooner then 24 days, just old habits I guess. Hows this timer code look?

SetupCar()
Time3 = MilliSecs()

While Not KeyHit(1)

; Calculate Time 
T=MilliSecs() 

Select True

	; Run Physics every 20ms ( APROX 50/sec )
	Case Abs(T-Time3)>20		
			Time3=T	
			
			UpdateKeys()
			UpdateCar()
			UpdateGeoms()
			
			PTime=MilliSecs()			
			For i=1 To steps
				dSpaceCollide(Space,World,ContactGroup)
				dWorldQuickStep(World,tstep)
				dJointGroupEmpty(ContactGroup)
			Next
			
			UpdateCam()
			UpdateWorld
			
			PhysicsTime#=MilliSecs()-PTime
	
End Select


Hi Wayne,

Sorry for the delay, I've been snowed under :)

Nice demo, could you put it in a codebox instead though, the scroll wheel is smokin' here ;)

Couple of things I've noticed about the demo:

The car speed is heavily restricted, was that intentional?

On a framerate of 60 fps it's very jerky but that may be due to the way you're applying the timing/stepping.

It's very unadvisable to use a non-fixed time step as this will cause the simulation to behave differently depending on the framerate/computer system. Always use a fixed time step if possible.

The other thing is you're not separating the physics update and the graphics update, they should run independently from each other, so these...

UpdateKeys()
UpdateCar()

dSpaceCollide(Space,World,ContactGroup)
dWorldQuickStep(World,0.1)
dJointGroupEmpty(ContactGroup)


...should be updated separately to these...

UpdateGeoms()
UpdateCam()
UpdateWorld


...as the latter are all rendering processes.

Hi Viper,
Yes, the car speed restriction was intended.

The time stepping is fixed, I used the variables to adjust the fixed settings.

I agree with separating physics and rendering, was just a little sloppy with the code. 8 )

Would like to understand why it appears jerky on your machine. Runs clean on 1.2ghz, and 2.4ghz.

I have a new version that sports air friction, front and rear brakes, so I'll post it and perhaps we can look at the jerky thing.

I purchased the jv-ode and use it with Blitz. It works great but I also have Game Studio commercial and would like an example of the code I would have to use to change the header or decls so as to use with gamestudio. This may not be possible, but games studio will use a DLL if I can set it up proper.

Thanks in advance

@Wayne: Ah, I didn't notice the stepping variable :)

It looked like the render updates were not in sync with the display framerate (@ 60fps), what framerate are you using?

New demo sounds good, look forward to trying it :)

Don't forget to use [ codebox ] instead of [ code ] if possible ;)

@JIMBO45: I don't have access to Game Studio unfortunately. If you can let me know the specifications Game Studio uses for DLLs, then I can take a look at it for you. All of the JV-ODE functions use the C/C++ _stdcall calling convention.

Hi VIP3R

This is what the manual has to say about it
I am not current in the writing of DLL's

It looks like they don't use token names for the Name of the function called in the dll
hope this helps----It may not be possible without a major change


Now you can begin to add your own functions to the DLL that can then later be called by a script, or by the renderer. To be recognized by the engine, all such functions must be of type DLLFUNC var function(...), like this:

// returns the value of x * 2n
DLLFUNC var ldexpc(var x,var n)
{
return (_VAR(_FLOAT(x)*pow(2.0,_FLOAT(n))));
}
This example function just returns an arithmetic expression of its arguments. DLLFUNC is just a convenience shortcut for declaring DLL export functions. var is the all-purpose numeric variable type of C-Script - a long integer that can be used either as 22.10 fixed point value, or as a pointer. Both are declared in the header file adll.h together with some conversion functions. It might be useful to look at them:

#define DLLFUNC extern "C" __declspec(dllexport)

typedef long var; // fixed point 22.10 number format used by C-Script
inline var _VAR(int i) { return i<<10; } // int -> var
inline var _VAR(double f) { return (var)(f*(1<<10)); } // double -> var, overloaded
inline int _INT(var x) { return x>>10; } // var -> int
inline float _FLOAT(var x) { return ((float)x)/(1<<10); } // var -> float
inline char* _CHR(STRING* s) { return s->chars; } // STRING* -> char*

The engine will expect all numbers - coordinates, variables, no matter what - in var type. So use the _VAR macro to convert any number to var before you return it to the engine, like in the example above.

Using the DLL in C-Script
Ready? Now compile your DLL - let's assume that you named it plugin.dll - and copy it into your acknex_plugins folder. How can we now call our ldexpc function by a script? We have to declare the function by a dllfunction prototype in the script, or in a script header file:

dllfunction ldexpc(x,n); // declaration of a DLL function
This makes our ldexpc function known to C-Script. You can now enjoy that C-Script has gotten one extra instruction!

Just to say thanks for another update - not had a chance to play with it, but it's downloaded and ready to play with :)

@JIMBO45: The amount of work needed to convert JV-ODE to those specs would be immense, and that's assuming it would all work correctly. I won't say it will never happen, but it's very unlikely, sorry :(

@sonicfactory: Thanks, you're welcome :)

VIP3R

Thanks for taking time to check

I read that this question has been asked before - but after hours of trawling thru the forum I cant see the answer.

A sphere is created on a flat plane, force applied to it, how do you make it stop due to friction ?

Im aplying force using dBodySetForce()
Setting values in dContactSetMu() seem to have no effect ?

No problem JIMBO45 :)

@OvineByDesign: You apply an angular damping velocity to the sphere, Mu doesn't apply rolling friction so as you've discovered it won't have any effect. I think it was discussed in JV-ODE Physics Thread 3. There's also more info here...

http://ode.org/cgi-bin/wiki.pl?RollingFrictionOrAerodynamicDrag

Here's PsychicParrots angular velocity sphere demo which shows the above method in use...

; JV-ODE - Key Controlled Sphere Demo Using Angular Velocities
; Code by Jim Williams (VIP3R)
; Arrow key control by Jeff (PsychicParrot)

AppTitle "JV-ODE - Key Controlled sphere Demo"

Include "jv-ode.bb"

Graphics3D 800,600,0,2

Type ODEGeom

	Field body
	Field geom
	Field mesh
	Field pivot ; To find direction vectors
	Field head  ; Just a stupid head to show which direction it's going in!
	Field inair ; To tell if player is on the ground or not!!
	
End Type

; ### Setup ODE

Global World=dWorldCreate()
Global Space=dHashSpaceCreate(0)
Global ContactGroup=dJointGroupCreate(0)

dWorldSetGravity(World,0,-1,0)
dContactSetMode(dContactBounce)
dContactSetBounce(0.2)
dContactSetMu(38)

; ### Create light

Global Light=CreateLight()

RotateEntity Light,45,-90,0
LightColor Light,255,255,255
AmbientLight 130,130,130

; ### Create camera

Global Camera=CreateCamera()
CameraClsColor Camera,0,0,0
CameraRange Camera,1,1000
PositionEntity Camera,0,20,-50
RotateEntity Camera,30,0,0

; ### Control vars

Global potato#=0 ; y rotation of thingy (rotater,rotato,potato!!)
Global spud#
Global fwd_key
Global back_key
Global left_key
Global right_key
Global jump_key

; ### Create plane

dCreatePlane(Space,0,1,0,0)

Plane=CreatePlane()

EntityAlpha Plane,0.8

PlaneTexture=CreateTexture(128,128,9)

ClsColor 0,200,80
Cls

Color 255,255,255

Rect 0,0,64,64,1
Rect 64,64,64,64,1

CopyRect 0,0,128,128,0,0,BackBuffer(),TextureBuffer(PlaneTexture)
ScaleTexture PlaneTexture,20,20
EntityTexture Plane,PlaneTexture,0,0
EntityPickMode plane,2

Mirror=CreateMirror()

AddObject() ; Add our thingy to the world

While Not KeyHit(1)

	PTime=MilliSecs()

	; Update controls
	
	c.ODEGeom=First ODEGeom
	Getkeys(c)
	movethingy(c)
	lookforground(c)
	
	PointEntity (camera,c\mesh)
	
	UpdateGeoms()

	dSpaceCollide(Space,World,ContactGroup)
	dWorldQuickStep(World,0.14)
	dJointGroupEmpty(ContactGroup)

	PhysicsTime#=MilliSecs()-PTime

	UpdateWorld

	RenderWorld

	Text 0,0,"JV-ODE Version "+dGetVersion()
	Text 0,15,"Physics Time:"+PhysicsTime

	Flip

Wend

dJointGroupDestroy(ContactGroup)
dSpaceDestroy(Space)
dWorldDestroy(World)
dCloseODE()

End

Function getkeys(c.ODEGeom)

	fwd_key=False
	back_key=False
	left_key=False
	right_key=False
	jump_key=False

	If c\inair=False Then ; Only let player control 'thrust' when on the ground
	
		If KeyDown(200) Then fwd_key=True
		If KeyDown(208) Then back_key=True

	EndIf
	
		If KeyDown(205) Then left_key=True
		If KeyDown(203) Then right_key=True
		If KeyDown(57) Then jump_key=True

	;EndIf

End Function

Function lookforground(c.ODEGeom)

	If LinePick(EntityX(c\mesh,True),EntityY(c\mesh,True)+1,EntityZ(c\mesh,True),0,-4,0)<>0 Then ; Check for anything 'under' player
		c\inair=False
	  Else
		c\inair=True
	EndIf

End Function

Function movethingy(c.ODEGeom)

	If fwd_key=True Then
		spud=spud+.1
	EndIf

	If back_key=True Then
		spud=spud-.1
	EndIf
	
	If left_key=True Then
		potato=potato-2
	End If
	
	If right_key=True Then
		potato=potato+2
	End If

	If jump_key=True And c\inair=False Then		
		dBodyAddForce(c\body,0,16 ,0)		
	End If

	RotateEntity c\head,0,potato#,0
	
	; Add forward force

	TFormVector 0,0,spud,c\head,0
	
	vx# = dBodyGetAngularVelX(c\body) + TFormedX()
	vy# = dBodyGetAngularVelY(c\body) + TFormedY()
	vz# = dBodyGetAngularVelZ(c\body) + TFormedZ()

	damping#=.8
	dBodySetAngularVel(c\body,vx#*damping#,vy#*damping#,vz#*damping#)

	spud=spud*.9
	
End Function

Function AddObject()

xp=Rand(-8,8)
zp=Rand(-8,8)
ode.ODEGeom=New ODEGeom
ode\body=dBodyCreate(World)
dBodySetRotation(ode\body,0,0,0)
dBodySetPosition(ode\body,xp,30,zp)
dBodySetAutoDisableFlag(ode\body,0)
ode\geom=dCreateSphere(Space,3)
dGeomSetBody(ode\geom,ode\body)
ode\mesh=CreateSphere()
ScaleMesh ode\mesh,3,3,3
EntityColor ode\mesh,Rand(255),Rand(255),Rand(255)
EntityAlpha ode\mesh,1
EntityShininess ode\mesh,0.7
ode\head=CreateCube()

ScaleMesh ode\head,1.5,1.5,1.5

ode\pivot=CreatePivot() ; Create pivot to get direction vectors

End Function


Function UpdateGeoms()

For ode.ODEGeom=Each ODEGeom
	
		RotateEntity ode\mesh,dGeomGetPitch#(ode\geom),dGeomGetYaw#(ode\geom),dGeomGetRoll#(ode\geom)
		
		PositionEntity ode\mesh,dGeomGetPositionX#(ode\geom),dGeomGetPositionY#(ode\geom),dGeomGetPositionZ#(ode\geom)
		
		PositionEntity ode\head,dGeomGetPositionX#(ode\geom),dGeomGetPositionY#(ode\geom)+4.5,dGeomGetPositionZ#(ode\geom)
	
Next

End Function


Thanks for that - thought I was missing an in built command so the engine could do it for me.

How do I apply stiffness to a hinge joint? I'm trying to make a chest of drawers using hinge joints but they never seem to stop moving...

Hmm, I don't understand how the hinge joint would be useful when simulating a chest of drawers.

There are a few ways to make a joint stiff:

You can apply a constant low force as is demonstrated on the ramp in the CarDemo-See-Saw demo (see Roll Ramp section).

Another way is used in the Car Demos for the steering, you return the angle of the joint then apply an opposite force, it can be offset to precisely control the angle of the joint manually (see UpdateCar function).

LOL sorry, I meant a slider joint...

I'll have a look at those examples. Thanks VIP3R :)

Ah, slider joint makes more sense :)

Try experimenting with the following functions using the same methods as mentioned above (constant low force and opposite forces)...

dJointSetSliderParam(joint,param,value#)
dJointAddSliderForce(joint,force#)
dJointGetSliderPosition#(joint)
dJointGetSliderPositionRate#(joint)


Awesome, that fixed it a treat. Thank you VIP3R :)

No problem :)

Quick question ....

If you dBodyDestroy(body) does it destory all of its linked geoms ? or do you have todo it manually ?

No, you need to destroy the geoms manually.

[edit] Normal service has resumed :)

Hi all,

I was wondering if anyone would have an idea 'how and if' it's possible to 'manually move/control a rigid body that's at the end of a chain of bodies that are connected with joints'?! - much like an effector works on an IK chain.

For example: to be able to 'drag the foot on a ragdoll' - expecting that the upper/lower leg, knee joint, etc. will 'adjust' accordingly to the foot's new position.

I've tried several basic approaches without much success( adding forces, manually positioning bodies); And i do have some Ik routines lying on the shelf, but if possible I'd like ODE to 'solve' the IK-chain for me - so that I know for sure that the bodies remain within the limits of the joints...

?!

The rope demo included in JV-ODE does that, force is applied to the lowest sphere body in the rope/chain.

Also, the Blitz3D RagDoll demos do a similar thing when the mouse is clicked on any body part. The body parts always remain within their joint limits.

Or am I misunderstanding?

Yes, sorry adding a force does 'affect' it, but in a completely uncontrollable way - in that I can not predict where it will end up (I can't possibly predict a chain's "stiffness" pending the amount & type of joints, their lo/histops, simulator speed, etc).

I know of two possible ways to solve this, but both would mean I'll most likely be unable to respect the initial joint limits - and I just can't afford the potential hospital bills for broken knees, ankles or spines :/

I'm working on something like an animation editor and I'd like to be able to control the 'end of the rope' using the mouse ie. set it at an 'exact' position.

d.

Ok, I got this far in that I figured a way to control the 'end of the rope'.
By 'temporarily' creating an anchor joint (hinge joint who's A and B body are one and the same), move the body + the joint at the same time, and then removing the temp anchor joint.

Is naice!.. but doesn't work when you want to move a body 'in the middle of the rope' :/

How do I add mass to a trimesh?

My program crashes if I add mass to an ode body that has a trimesh for the geom.

Use box mass for TriMeshes, try replacing the AddObject() function with the following code in the 'Demo-TriMeshes' demo included with JV-ODE to see it working...

; ###################################################################################################

Function AddObject()

xp=Rand(-10,10)
zp=Rand(-10,10)
ode.ODEGeom=New ODEGeom
ode\body=dBodyCreate(World)
dBodySetRotation(ode\body,0,0,0)
dBodySetPosition(ode\body,xp,30,zp)
dBodySetAutoDisableFlag(ode\body,1)

; ### Add mass
mass=dMassCreate()
dMassSetBoxTotal(mass,200,3,3,1)
dBodySetMass(ode\body,mass)
dMassDestroy(mass)

ode\mesh=LoadMesh("media\torus.b3d")
ScaleMesh ode\mesh,3,3,3
EntityColor ode\mesh,Rand(255),Rand(255),Rand(255)
EntityAlpha ode\mesh,1
EntityShininess ode\mesh,0.7

ode\geom=CreateTriMesh(Space,ode\mesh)
dGeomSetBody(ode\geom,ode\body)

End Function

; ###################################################################################################


Awesome... Thanks VIP3R :)

Here's a couple of new demos to play with...

Water Demo
; ###################################################################################################
; #										JV-ODE - Water Demo											#
; #									Code by Jim Williams (VIP3R)									#
; #								Devious Codeworks - Copyright © 2007								#
; ###################################################################################################

AppTitle "JV-ODE - Water Demo"

Include "JV-ODE.bb"

Graphics3D 800,600,0,2

Type ODEGeom
	Field body
	Field geom
	Field mesh
End Type

; ###################################################################################################

; ### Setup ODE

Global World=dWorldCreate()
Global Space=dHashSpaceCreate(0)
Global ContactGroup=dJointGroupCreate(0)

dWorldSetAutoDisableFlag(World,1)
dWorldSetGravity(World,0,-0.98,0)
dContactSetMode(dContactSlip1)
dContactSetMu(0.5)

; ### Create light

Global Light=CreateLight()

RotateEntity Light,45,-90,0
LightColor Light,255,255,255
AmbientLight 130,130,130

; ### Create camera

Global Camera=CreateCamera()
CameraClsColor Camera,0,0,0
CameraRange Camera,1,1000
PositionEntity Camera,0,45,-70
RotateEntity Camera,30,0,0

; ### Create plane

dCreatePlane(Space,0,1,0,0)

Plane=CreatePlane()

EntityAlpha Plane,0.8

PlaneTexture=CreateTexture(128,128,9)

ClsColor 0,200,80
Cls

Color 255,255,255

Rect 0,0,64,64,1
Rect 64,64,64,64,1

CopyRect 0,0,128,128,0,0,BackBuffer(),TextureBuffer(PlaneTexture)
ScaleTexture PlaneTexture,20,20
EntityTexture Plane,PlaneTexture,0,0

Mirror=CreateMirror()

; ###################################################################################################

; ### Create water

ode.ODEGeom=New ODEGeom
ode\geom=dCreateBox(Space,100,15,100)
dGeomSetRotation(ode\geom,0,0,0)
dGeomSetPosition(ode\geom,0,7.5,0)
dGeomContactSetMode(ode\geom,dContactBounce+dContactSoftCFM)
dGeomContactSetBounce(ode\geom,0.1)		; ### Water bounce
dGeomContactSetSoftCFM(ode\geom,0.8)	; ### Water density
dGeomContactSetMu(ode\geom,0.05)		; ### Water friction
ode\mesh=CreateCube()
ScaleMesh ode\mesh,50,7.5,50
EntityColor ode\mesh,0,100,200
EntityAlpha ode\mesh,0.7
EntityShininess ode\mesh,0.7

; ### Create barriers

ode.ODEGeom=New ODEGeom
ode\geom=dCreateBox(Space,100,18,2)
dGeomSetRotation(ode\geom,0,0,0)
dGeomSetPosition(ode\geom,0,9,-50)
ode\mesh=CreateCube()
ScaleMesh ode\mesh,50,9,1
EntityColor ode\mesh,240,240,240
EntityAlpha ode\mesh,1

ode.ODEGeom=New ODEGeom
ode\geom=dCreateBox(Space,100,18,2)
dGeomSetRotation(ode\geom,0,0,0)
dGeomSetPosition(ode\geom,0,9,50)
ode\mesh=CreateCube()
ScaleMesh ode\mesh,50,9,1
EntityColor ode\mesh,240,240,240
EntityAlpha ode\mesh,1

ode.ODEGeom=New ODEGeom
ode\geom=dCreateBox(Space,2,18,100)
dGeomSetRotation(ode\geom,0,0,0)
dGeomSetPosition(ode\geom,-50,9,0)
ode\mesh=CreateCube()
ScaleMesh ode\mesh,1,9,50
EntityColor ode\mesh,240,240,240
EntityAlpha ode\mesh,1

ode.ODEGeom=New ODEGeom
ode\geom=dCreateBox(Space,2,18,100)
dGeomSetRotation(ode\geom,0,0,0)
dGeomSetPosition(ode\geom,50,9,0)
ode\mesh=CreateCube()
ScaleMesh ode\mesh,1,9,50
EntityColor ode\mesh,240,240,240
EntityAlpha ode\mesh,1

; ###################################################################################################

While Not KeyHit(1)

	PTime=MilliSecs()

	If MilliSecs()-Timer>700
		AddObject()
		Timer=MilliSecs()
		End If

	UpdateGeoms()

	dSpaceCollide(Space,World,ContactGroup)
	dWorldQuickStep(World,0.1)
	dJointGroupEmpty(ContactGroup)

	PhysicsTime#=MilliSecs()-PTime

	UpdateWorld

	RenderWorld

	Text 0,0,"JV-ODE Version "+dGetVersion()
	Text 0,15,"Physics Time:"+PhysicsTime

	Flip

Wend

dJointGroupDestroy(ContactGroup)
dSpaceDestroy(Space)
dWorldDestroy(World)
dCloseODE()

End

; ###################################################################################################

Function AddObject()

xp=Rand(-20,20)
zp=Rand(-20,20)
ode.ODEGeom=New ODEGeom
ode\body=dBodyCreate(World)
dBodySetRotation(ode\body,0,0,0)
dBodySetPosition(ode\body,xp,30,zp)
dBodySetAutoDisableFlag(ode\body,1)
mass=dMassCreate()
dMassSetSphereTotal(mass,15,3)
dBodySetMass(ode\body,mass)
dMassDestroy(mass)
ode\geom=dCreateSphere(Space,3)
dGeomSetBody(ode\geom,ode\body)
ode\mesh=CreateSphere()
ScaleMesh ode\mesh,3,3,3
EntityColor ode\mesh,Rand(255),Rand(255),Rand(255)
EntityAlpha ode\mesh,1
EntityShininess ode\mesh,0.7

End Function

; ###################################################################################################

Function UpdateGeoms()

For ode.ODEGeom=Each ODEGeom
	RotateEntity ode\mesh,dGeomGetPitch#(ode\geom),dGeomGetYaw#(ode\geom),dGeomGetRoll#(ode\geom)
	PositionEntity ode\mesh,dGeomGetPositionX#(ode\geom),dGeomGetPositionY#(ode\geom),dGeomGetPositionZ#(ode\geom)
Next

End Function

; ###################################################################################################


Iceberg Demo
; ###################################################################################################
; #										JV-ODE - Iceberg Demo										#
; #									Code by Jim Williams (VIP3R)									#
; #								Devious Codeworks - Copyright © 2007								#
; ###################################################################################################

AppTitle "JV-ODE - Iceberg Demo"

Include "JV-ODE.bb"

Graphics3D 800,600,0,2

Type ODEGeom
	Field body
	Field geom
	Field mesh
End Type

; ###################################################################################################

; ### Setup ODE

Global World=dWorldCreate()
Global Space=dHashSpaceCreate(0)
Global ContactGroup=dJointGroupCreate(0)

dWorldSetAutoDisableFlag(World,1)
dWorldSetGravity(World,0,-0.98,0)
dContactSetMode(dContactSlip1)
dContactSetMu(0.1)

; ### Create light

Global Light=CreateLight()

RotateEntity Light,45,-90,0
LightColor Light,255,255,255
AmbientLight 130,130,130

; ### Create camera

Global Camera=CreateCamera()
CameraClsColor Camera,0,0,0
CameraRange Camera,1,1000
PositionEntity Camera,0,45,-70
RotateEntity Camera,30,0,0

; ### Create plane

dCreatePlane(Space,0,1,0,0)

Plane=CreatePlane()

EntityAlpha Plane,0.8

PlaneTexture=CreateTexture(128,128,9)

ClsColor 0,200,80
Cls

Color 255,255,255

Rect 0,0,64,64,1
Rect 64,64,64,64,1

CopyRect 0,0,128,128,0,0,BackBuffer(),TextureBuffer(PlaneTexture)
ScaleTexture PlaneTexture,20,20
EntityTexture Plane,PlaneTexture,0,0

Mirror=CreateMirror()

; ###################################################################################################

; ### Create water

ode.ODEGeom=New ODEGeom
ode\geom=dCreateBox(Space,100,15,100)
dGeomSetRotation(ode\geom,0,0,0)
dGeomSetPosition(ode\geom,0,7.5,0)
dGeomContactSetMode(ode\geom,dContactBounce+dContactSoftCFM)
dGeomContactSetBounce(ode\geom,0.1)		; ### Water bounce
dGeomContactSetSoftCFM(ode\geom,0.8)	; ### Water density
dGeomContactSetMu(ode\geom,0.0)			; ### Water friction
ode\mesh=CreateCube()
ScaleMesh ode\mesh,50,7.5,50
EntityColor ode\mesh,0,100,200
EntityAlpha ode\mesh,0.7
EntityShininess ode\mesh,0.7

; ### Create iceberg

ode.ODEGeom=New ODEGeom
ode\body=dBodyCreate(World)
IcebergBody=ode\body
dBodySetRotation(ode\body,0,0,0)
dBodySetPosition(ode\body,0,14,0)
dBodySetAutoDisableFlag(ode\body,1)
mass=dMassCreate()
dMassSetBoxTotal(mass,40,40,8,40)
dBodySetMass(ode\body,mass)
dMassDestroy(mass)
ode\geom=dCreateBox(Space,40,8,40)
dGeomContactSetMu(ode\geom,0.001)		; ### Iceberg friction
dGeomSetBody(ode\geom,ode\body)
ode\mesh=CreateCube()
ScaleMesh ode\mesh,20,4,20
EntityColor ode\mesh,255,255,255
EntityAlpha ode\mesh,1
EntityShininess ode\mesh,0.7

; ### Iceberg joint

IJoint=dJointCreateBall(World,0)
dJointAttach(IJoint,0,IcebergBody)		; ### Fix iceberg to ODE world
dJointSetBallAnchor(IJoint,0,4,0)

; ### Create barriers

ode.ODEGeom=New ODEGeom
ode\geom=dCreateBox(Space,100,18,2)
dGeomSetRotation(ode\geom,0,0,0)
dGeomSetPosition(ode\geom,0,9,-50)
ode\mesh=CreateCube()
ScaleMesh ode\mesh,50,9,1
EntityColor ode\mesh,240,240,240
EntityAlpha ode\mesh,1

ode.ODEGeom=New ODEGeom
ode\geom=dCreateBox(Space,100,18,2)
dGeomSetRotation(ode\geom,0,0,0)
dGeomSetPosition(ode\geom,0,9,50)
ode\mesh=CreateCube()
ScaleMesh ode\mesh,50,9,1
EntityColor ode\mesh,240,240,240
EntityAlpha ode\mesh,1

ode.ODEGeom=New ODEGeom
ode\geom=dCreateBox(Space,2,18,100)
dGeomSetRotation(ode\geom,0,0,0)
dGeomSetPosition(ode\geom,-50,9,0)
ode\mesh=CreateCube()
ScaleMesh ode\mesh,1,9,50
EntityColor ode\mesh,240,240,240
EntityAlpha ode\mesh,1

ode.ODEGeom=New ODEGeom
ode\geom=dCreateBox(Space,2,18,100)
dGeomSetRotation(ode\geom,0,0,0)
dGeomSetPosition(ode\geom,50,9,0)
ode\mesh=CreateCube()
ScaleMesh ode\mesh,1,9,50
EntityColor ode\mesh,240,240,240
EntityAlpha ode\mesh,1

; ###################################################################################################

While Not KeyHit(1)

	PTime=MilliSecs()

	If MilliSecs()-Timer>1400
		AddObject()
		Timer=MilliSecs()
		End If

	UpdateGeoms()

	dSpaceCollide(Space,World,ContactGroup)
	dWorldQuickStep(World,0.1)
	dJointGroupEmpty(ContactGroup)

	PhysicsTime#=MilliSecs()-PTime

	UpdateWorld

	RenderWorld

	Text 0,0,"JV-ODE Version "+dGetVersion()
	Text 0,15,"Physics Time:"+PhysicsTime

	Flip

Wend

dJointGroupDestroy(ContactGroup)
dSpaceDestroy(Space)
dWorldDestroy(World)
dCloseODE()

End

; ###################################################################################################

Function AddObject()

xp=Rand(-20,20)
zp=Rand(-20,20)
ode.ODEGeom=New ODEGeom
ode\body=dBodyCreate(World)
dBodySetRotation(ode\body,0,0,0)
dBodySetPosition(ode\body,xp,30,zp)
dBodySetAutoDisableFlag(ode\body,1)
mass=dMassCreate()
dMassSetBoxTotal(mass,30,3,3,3)
dBodySetMass(ode\body,mass)
dMassDestroy(mass)
ode\geom=dCreateBox(Space,3,3,3)
dGeomSetBody(ode\geom,ode\body)
ode\mesh=CreateCube()
ScaleMesh ode\mesh,1.5,1.5,1.5
EntityColor ode\mesh,Rand(255),Rand(255),Rand(255)
EntityAlpha ode\mesh,1
EntityShininess ode\mesh,0.7

End Function

; ###################################################################################################

Function UpdateGeoms()

For ode.ODEGeom=Each ODEGeom
	RotateEntity ode\mesh,dGeomGetPitch#(ode\geom),dGeomGetYaw#(ode\geom),dGeomGetRoll#(ode\geom)
	PositionEntity ode\mesh,dGeomGetPositionX#(ode\geom),dGeomGetPositionY#(ode\geom),dGeomGetPositionZ#(ode\geom)
Next

End Function

; ###################################################################################################


He wow, that's seriously impressive vip3r!!
I would never had thought it could be done so simple and elegantly!
Question: How long would it take to implement a body of water in my game using ODE?
Answer: How about 5 seconds?

I'll be expecting a lot of 'wet games' to flood the market soon...

Thanks man!

D

By the way you can use dContactMotion1 & dContactMotion2 to set the water's flow direction (and force)! Making things float in a given direction...

Works pretty sweet! ;)

d.

I'm in need of some help/advice (again)

In my program, I have objects that you can pick up with the mouse. Force is applied to the object using the mouse speed. It all works quite well but I can't decide whether to have the mouse Y axis control the object Y axis or Z axis i.e up and down or forward and back.

For some objects like doors and drawers, I think a forward and backward movement would be best but for others i.e a box, I think up and down would be best. I hope all this makes sense LOL

What do you guys think? I want all the objects in my program to be as controllable as possible.

Nice tip Danny, thanks :)

You can also construct water falls by stacking several water boxes, when any objects approach the edge of the water box they will slide down its side to the next water box.

@JA2: What about using the left button for the Y axis and the right button for the Z axis?

yes the sliding down the edges is a bit of an unwanted side-effect.. Nice to do waterfalls perhaps, but not if you want it to stay near the surface when it touches the other 'banks'...
I tried making the 'water box' larger than it's surrounding barriers, but it doesn't seem to matter what edge it touches.. :/

VIP3R,

I had been using a similar control to left and right mouse buttons to push and pull objects. Ideally, I'd like to use just the one mouse button to do everything.

I think I'll leave it as X and Y axis and have the player move with the arrows for X and Z axis control.

Nice water demos by the way. Lots of fun to play about with ;)

Thanks JA2 :)

@Danny: If you prevent the objects floating to the edge of the water cube it does in fact stop the sinking objects issue. I've intentionally placed the barriers in those water demos to cover the edges of the water cube for this reason. During your experiments I suspect you've extended the visible geometry for the water box, but may have forgotten to extend the ODE geom as well.

What is the best way to add a first person camera that collides with the simulation?

I'm having problems getting the camera body moving around with the keys. Seems like it should be simple but it just won't behave. Anyone have a simple bit of code I could have a look at?

It sounds like you're manually repositioning the camera body, you should add force instead to move it around.

Have a look at PsychicParrots angular velocity sphere demo a few posts up, it may help. To make it first person view, parent the camera to the cube above the sphere.

Thanks VIP3R, got it fixed now thanks to PsychicParrots example. Working on moving my objects around relative to the camera next...

Quite inspired by the game Penumbra ;)

EDIT:

Right, stuck again LOL

How can I make an object move relative to the camera? This is the code I'm using at the moment. Speeds are taken from the mouse and strength is the players strength (set to 2 on my game)...

dBodyAddForce OE\Body,SpeedX*Strength,0,-SpeedY*Strength

This slides drawers in and out and opens and closes doors using the mouse Y speed. If I move to the side of the object then I still have to drag the mouse up and down, rather than left and right.

Hope this makes sense :)

Use dBodyAddRelForce(body,fx#,fy#,fz#) instead :)

Now the models seem to be moving relative to their own position but it doesn't take into account which way the camera is facing...

Is there a way I can calculate where the camera is pointed and make x y z forces based on that?

Hi! I'm new here and i have a few questions.
Can i put static trimesh collision in realtime?
Or can i make racer game with 10-15 cars and 40K-50K triangle levelmesh ?

Lyon, yes you can use trimesh collision for your scene no problem. Even with heavy scenes ODE seems to do fine. Not sure how much the cars will 'cost' you, vip3r has more experience with that but I think that should work easily.

If your levelmesh is 40k-50k, you can also create a low-poly version of it (4k-5k?) and use THAT version JUST for the collision! Whilst using the 40/50k version for real time rendering.

I'm too lazy to build low-poly versions and have levels with 200K+, a dozen characters and several scores of random dynamic objects - without any problem
Although I have a relatively fast machine with a good graphics card, so it's best to keep it down to make sure it works on slower computers as well...

Hope that helps,

Danny

@JA2: You would probably be better off using dBodyAddForce() if it needs to be relative to the camera only, I misunderstood earlier. It's easier than you might imagine, you can use camera picking. Take a look at the UpdateControls() function in the 'Demo-RagDolls' demo, you can use the camera pick method to apply forces relative to the camera in the same way. In the demo, forces are applied relative to the camera when clicking the mouse on the RagDoll bodies, it's also relative to the facing triangle of the geometry which should be ideal for opening doors etc.

@Lyon: Welcome :) As Danny says you can use static TriMesh collision during runtime. The amount of cars would depend more on the processing power of the computer as each car would add a little overhead, the more you add, the more it accumulates. JV-ODE can easily handle 10-15 cars on an average computer. A 40K-50K mesh would also work but try not to over tax the engine when it isn't necessary, use a low poly version as Danny suggests, or break it up into multiple segments instead. Such a high triangle count in your level mesh will have much more impact on the rendering engine rather than the physics engine tbh.

When rendering it's always advisable to use some kind of LOD (level of detail) system to streamline the process, if you can get away with lower detail for distant geometry then do it if possible. The exact same thing applies to physics geometry.

@VIP3R: Using picked normals won't be any good for what I'm trying to do. I use camerapick only once when I 'grab' the object and then use the mouse speed to move it around until you let go.

I'm really stuck on this :( Here's my code. The problem is in Funcion Game(), move objects section...

I knocked together a quick example which doesn't need any media to run :)

;-------------------------------------------------------------------------
; Setup
;-------------------------------------------------------------------------

Include "JV-ODE.bb"

Graphics3D 800,600,0,2
SetBuffer BackBuffer()

Type ODE

	Field Body,Geom,Mesh,Limit#,Slide

End Type

Type Joint

	Field Joint,JointType,Damp#,Stiff#
	
End Type

Global World,Space,ContactGroup,Gravity#,WindX#,WindZ#

SetupODE (0.000001,1,70,0,0,-0.98,0,dContacBounce,0,0,0,True)

;-------------------------------------------------------------------------
; Functions
;-------------------------------------------------------------------------

Function EntityScale# (Model,Axis)

Local ScaleX# = GetMatElement (Model,Axis,0)
Local ScaleY# = GetMatElement (Model,Axis,1)
Local ScaleZ# = GetMatElement (Model,Axis,2)

Return Sqr (ScaleX*ScaleX+ScaleY*ScaleY+ScaleZ*ScaleZ)

End Function

Function Point (Entity,X#,Y#,Z#)

Local XDiff#,YDiff#,ZDiff#,Dist#,Pitch#,Yaw#

XDiff	= EntityX (Entity) - X
YDiff	= EntityY (Entity) - Y
ZDiff	= EntityZ (Entity) - Z

Dist	= Sqr (XDiff*XDiff+ZDiff*ZDiff)

Pitch	= ATan2 (YDiff,Dist)
Yaw		= ATan2 (XDiff,-ZDiff)

RotateEntity Entity,Pitch,Yaw,0

End Function

;-------------------------------------------------------------------------
; ODE Functions
;-------------------------------------------------------------------------

Function SetupODE (CFM#=0.00001,ERP#=1,Mu#=70,Bounce#=0.1,X#=0,Y#=-1,Z#=0,Contact=dContactSlip1,FDX#=0,FDY#=0,FDZ#=0,Auto=True)

World			= dWorldCreate()
Space			= dHashSpaceCreate (0)
ContactGroup 	= dJointGroupCreate (0)
WindX			= X
Gravity			= Y
WindZ			= Z

dWorldSetAutoDisableFlag (World,Auto)
dWorldSetGravity (World,X,Y,Z)
dWorldSetERP (World,ERP)
dContactSetMode (Contact)
dWorldSetCFM (World,CFM)
dContactSetMu (Mu)
dContactSetBounce (Bounce)
dContactSetFDir1 (FDX,FDY,FDZ)

End Function

Function Physics (Model,Wrap=0,XP#=0,YP#=0,ZP#=0,XR#=0,YR#=0,ZR#=0,Mass#=1,MX#=0,MY#=0,MZ#=0,Auto=True,Pick=False,Slide=False)
	
Local OE.ODE,NewMass,XS#,YS#,ZS#
	
XS = MeshWidth  (Model) * EntityScale (Model,0)
YS = MeshHeight (Model) * EntityScale (Model,1)
ZS = MeshDepth  (Model) * EntityScale (Model,2)
	
NewMass	= dMassCreate()
	
OE.ODE		= New ODE
OE\Body		= dBodyCreate (World)
OE\Mesh		= Model
OE\Slide	= Slide
	
Select Wrap
			
	Case 0
		
	OE\Geom	= dCreateBox (Space,XS,YS,ZS)
		
	dMassSetBoxTotal NewMass,Mass,XS,YS,ZS
			
	Case 1
		
	OE\Geom	= dCreateSphere (Space,YS/2)
		
	dMassSetSphereTotal (NewMass,Mass,YS/2)
		
	Case 2
		
	OE\Geom	= dCreateCylinder (Space,XS/2,YS)
		
	dMassSetCylinderTotal (NewMass,Mass,1,XS/2,YS/2)
		
	Case 3
		
	OE\Geom	= CreateTriMesh (Space,OE\Mesh)
	
	dMassSetBoxTotal NewMass,Mass,XS,YS,ZS
		
End Select
	
dMassTranslate NewMass,MX,MY,MZ
dBodySetMass OE\Body,NewMass
dMassDestroy NewMass
dBodySetRotation OE\Body,XR,YR,ZR
dBodySetPosition OE\Body,XP,YP,ZP
dBodySetAutoDisableFlag OE\Body,Auto
dGeomSetBody OE\Geom,OE\Body
	
If Pick = True Then EntityPickMode OE\Mesh,2
	
EntityColor OE\Mesh,Rand(0,255),Rand(0,255),Rand(0,255)
	
End Function

Function Connect (ModelA,ModelB,Joint=0,Min#=0,Max#=1,XA=0,YA=0,ZA=0,XP#=0,YP#=0,ZP#=0,Limit#=99999,Damp#=0.9,Stiff#=20)

Local OE.ODE,OE2.ODE,JT.Joint

For OE.ODE = Each ODE
	
	For OE2.ODE = Each ODE

		If OE\Mesh = ModelA And OE2\Mesh = ModelB
		
			OE\Limit	= Limit
			OE2\Limit	= Limit

			Select Joint

				;--------;
				; Slider ;
				;--------;
				
				Case 0

				JT.Joint 		= New Joint
				JT\Joint		= dJointCreateSlider (World,0)
				JT\JointType	= Joint
				JT\Damp			= Damp
				JT\Stiff		= Stiff

				dJointAttach JT\Joint,OE\Body,OE2\Body
				dJointSetSliderAxis JT\Joint,XA,YA,ZA
				dJointSetSliderParam JT\Joint,dParamLoStop,Min
				dJointSetSliderParam JT\Joint,dParamHiStop,Max

				;-------;
				; Hinge ;
				;-------;
					
				Case 1

				JT.Joint		= New Joint
				JT\Joint		= dJointCreateHinge (World,0)
				JT\JointType	= Joint
				JT\Damp			= Damp
				JT\Stiff		= Stiff
				
				dJointAttach JT\Joint,OE\Body,OE2\Body
				dJointSetHingeAxis JT\Joint,XA,YA,ZA
				dJointSetHingeAnchor JT\Joint,XP,YP,ZP
				dJointSetHingeParam JT\Joint,dParamLoStop,Min
				dJointSetHingeParam JT\Joint,dParamHiStop,Max
				dJointSetHingeParam JT\Joint,dParamFMax,Stiff
					
			End Select

		EndIf
	
	Next

Next

End Function

Function LimitJoint (Body,Limit#)

Local OE.ODE,Joints,Joint,A,AX#,AY#,AZ#,BX#,BY#,BZ#,ForceA#,ForceB#

For OE.ODE = Each ODE

	If dGeomIsEnabled (OE\Geom)

		If OE\Body = Body

			Joints = dBodyGetNumJoints (OE\Body)

			If Joints > 0 

				For A = 0 To Joints-1
				
					Joint = dBodyGetJoint (OE\Body,A)
					
					If dJointGetType (Joint) <> dJointTypeContact

				  		AX = dBodyGetLinearVelX (OE\Body)
				  		AY = dBodyGetLinearVelY (OE\Body)
					    AZ = dBodyGetLinearVelZ (OE\Body)
					
				  		BX = dBodyGetAngularVelX (OE\Body)
				  		BY = dBodyGetAngularVelY (OE\Body)
						BZ = dBodyGetAngularVelZ (OE\Body)
						
						ForceA = Sqr (AX*AX+AY*AY+AZ*AZ)
						ForceB = Sqr (BX*BX+BY*BY+BZ*BZ)
						
						If ForceA > Limit Or ForceB > Limit

							dJointAttach (Joint,0,0)

							LinearDamp (OE\Body,0.004)
							AngularDamp (OE\Body,0.004)
							
							OE\Slide = False
									
						End If
						
					End If

				Next

			End If

		End If

	End If

Next

End Function

Function LinearDamp (Body,Damp#)

  dBodyAddForce Body,dBodyGetLinearVelX(Body)*Damp,dBodyGetLinearVelY(Body)*Damp,dBodyGetLinearVelZ(Body)*Damp

End Function

Function AngularDamp (Body,Damp#)

  dBodyAddTorque Body,dBodyGetAngularVelX(Body)*Damp,dBodyGetAngularVelY(Body)*Damp,dBodyGetAngularVelZ(Body)*Damp

End Function

Function UpdateODE (Speed#=0.1)

Local OE.ODE

For OE.ODE = Each ODE

	If dBodyIsEnabled (OE\Body)

		RotateEntity OE\Mesh,dGeomGetPitch(OE\Geom),dGeomGetYaw(OE\Geom),dGeomGetRoll(OE\Geom)
		PositionEntity OE\Mesh,dGeomGetPositionX(OE\Geom),dGeomGetPositionY(OE\Geom),dGeomGetPositionZ(OE\Geom)

	End If

Next

dSpaceCollide (Space,World,ContactGroup)
dWorldQuickStep	(World,Speed)
dJointGroupEmpty (ContactGroup)

End Function

Function FreeODE()

dJointGroupDestroy ContactGroup
dSpaceDestroy Space
dWorldDestroy World
dCloseODE()

End Function

;-------------------------------------------------------------------------
; Setup
;-------------------------------------------------------------------------

Global Light,Camera,Plane,Mirror,PlaneTexture,Player,Pivot

Function Setup()

Light = CreateLight()
RotateEntity Light,45,-90,0

Pivot	= CreatePivot()
Player	= CreateSphere (8)
Camera	= CreateCamera(Pivot)

CameraRange Camera,0.1,1000
ScaleEntity Player,20,20,20
HideEntity Player

Physics (Player,1,0,35,-100,0,0,0,50)

dCreatePlane (Space,0,1,0,0)

Plane	= CreatePlane()
Mirror	= CreateMirror()

EntityAlpha Plane,0.8

PlaneTexture = CreateTexture (128,128,9)

ClsColor 0,0,150
Cls
Color 0,0,250
Rect 0,0,64,64,1
Rect 64,64,64,64,1
CopyRect 0,0,128,128,0,0,BackBuffer(),TextureBuffer(PlaneTexture)
ScaleTexture PlaneTexture,100,100
EntityTexture Plane,PlaneTexture,0,0

End Function

;-------------------------------------------------------------------------
; Game Loop
;-------------------------------------------------------------------------

Function Game()

Local PTime,ItemModel,OE.ODE,JT.Joint,Angle#,Rate#,A,Strength#,VX#,VY#,VZ#,MoveX#,MoveZ#,RotX#,RotY#
Local PhysicsTime#,Joint,PX#,PY#,PZ#

Strength = 5

Repeat

	SpeedX = MouseXSpeed()
	SpeedY = MouseYSpeed()
	SpeedZ = MouseZSpeed()

	MoveMouse GraphicsWidth()/2,GraphicsHeight()/2

	;------------------------------------------------------------------------
	; Move Player
	;------------------------------------------------------------------------

	For OE.ODE = Each ODE

		If OE\Mesh = Player

			dBodyEnable OE\Body

			If KeyDown (200) Then MoveZ = MoveZ + 1
			If KeyDown (208) Then MoveZ = MoveZ - 1
			If KeyDown (203) Then MoveX = MoveX - 1
			If KeyDown (205) Then MoveX = MoveX + 1

			RotX = EntityYaw (Pivot) - SpeedX
			RotY = EntityPitch (Pivot) + SpeedY

			RotateEntity Pivot,RotY,RotX,0

			TFormVector MoveX,0,MoveZ,Pivot,0

			VX# = dBodyGetLinearVelX (OE\Body) + TFormedX()
			VY# = dBodyGetLinearVelY (OE\Body)
			VZ# = dBodyGetLinearVelZ (OE\Body) + TFormedZ()
			
			dBodySetlinearVel (OE\Body,VX*0.8,VY,VZ*0.8)

			PositionEntity Pivot,dGeomGetPositionX(OE\Geom),dGeomGetPositionY(OE\Geom)+30,dGeomGetPositionZ(OE\Geom)
			
			MoveX = MoveX * 0.8
			MoveZ = MoveZ * 0.8

		EndIf
		
	Next

	;------------------------------------------------------------------------
	; Grab Objects
	;------------------------------------------------------------------------

	If MouseHit (3)

		ItemModel = LoadMesh ("Model\Drawer.3ds")

		ScaleEntity ItemModel,0.1,0.1,0.1

		Physics (ItemModel,0,0,60,0,0,0,0,1,0,0,0,True,True)
		
	EndIf

	If MouseHit (1)

		Grabbed = 0

		CameraPick (Camera,MouseX(),MouseY())

		For OE.ODE = Each ODE
		
			If PickedEntity() = OE\Mesh
			
				Grabbed = OE\Mesh
				
				;Connect (OE\Mesh,Player);STATIC JOINT?

				PX = PickedX() - EntityX (Grabbed)
				PY = PickedY() - EntityY (Grabbed)
				PZ = PickedZ() - EntityZ (Grabbed)
				
			EndIf
			
		Next
		
	EndIf
	
	;------------------------------------------------------------------------
	; Move Objects
	;------------------------------------------------------------------------
	
	If Grabbed <> 0

		Point (Pivot,EntityX(Grabbed)+PX,EntityY(Grabbed)+PY,EntityZ(Grabbed)+PZ)
		;PointEntity Pivot,Grabbed
	
		If MouseDown (1)

			For OE.ODE = Each ODE
	
				If Grabbed = OE\Mesh
				
					dBodyEnable OE\Body
			
					If OE\Slide = True
						
						dBodyAddForce OE\Body,SpeedX*Strength,0,-SpeedY*Strength
						;dBodyAddRelForce OE\Body,SpeedX*Strength,0,-SpeedY*Strength
					
					Else
						
						;dBodyAddForce OE\Body,VX*Strength,-VY*Strength,VZ*Strength
						
						dBodyAddForce OE\Body,SpeedX*Strength,-SpeedY*Strength,0
						;dBodyAddRelForce OE\Body,SpeedX*Strength,-SpeedY*Strength,0
				
					EndIf

					If MouseHit (2)
	
						dBodyAddForce OE\Body,0,0,Strength*1000
						;dBodyAddRelForce OE\Body,0,0,Strength*1000

						Grabbed = 0
					
					EndIf

				EndIf

				LimitJoint (OE\Body,OE\Limit)

			Next

		Else
		
			Grabbed = 0

		EndIf

	EndIf
	
	;------------------------------------------------------------------------
	; Update Joints
	;------------------------------------------------------------------------
	
	For JT.Joint = Each Joint

		For A = 0 To 1
		
			Joint = dJointGetBody (JT\Joint,A)
			
			If Joint <> 0

				If JT\JointType = 0

					Rate = dJointGetSliderPositionRate (JT\Joint)
		
					dJointAddSliderForce JT\Joint,-Rate*JT\Damp
			
				EndIf
			
			EndIf
			
		Next

	Next

	;------------------------------------------------------------------------
	; Update & Render
	;------------------------------------------------------------------------

	PTime = MilliSecs()

	UpdateODE()

	PhysicsTime = MilliSecs()-PTime

	UpdateWorld
	RenderWorld

	Text 0,0,"Physics Time: " + PhysicsTime

	Flip

Until KeyHit (1)

FreeODE()

End

End Function

;-------------------------------------------------------------------------
; Examples
;-------------------------------------------------------------------------

Function DrawerExample()

Local DrawerModelA	= LoadMesh ("Model\Drawers.3ds")
Local DrawerModelB	= LoadMesh ("Model\Drawer.3ds")
Local DrawerModelC	= CopyEntity (DrawerModelB)
Local DrawerModelD	= CopyEntity (DrawerModelB)

Physics (DrawerModelA,3,0,40,0,0,0,0,20,0,-2,0,True,True,False)
Physics (DrawerModelB,3,0,40,0,0,0,0,2,0,0,0,True,True,True)
Physics (DrawerModelC,3,0,57,0,0,0,0,2,0,0,0,True,True,True)
Physics (DrawerModelD,3,0,23,0,0,0,0,2,0,0,0,True,True,True)

Connect (DrawerModelA,DrawerModelB,0,0,20,0,0,1,0,0,0,99,0.9)
Connect (DrawerModelA,DrawerModelC,0,0,20,0,0,1,0,0,0,99,0.9)
Connect (DrawerModelA,DrawerModelD,0,0,20,0,0,1,0,0,0,99,0.9)

End Function

Function LockerExample()

Local LockerModelA	= LoadMesh ("Model\Locker.3ds")
Local LockerModelB	= LoadMesh ("Model\Door.3ds")

Physics (LockerModelA,3,0,50,0,0,0,0,200,0,-2,0,True,True,False)
Physics (LockerModelB,0,0,50,-16,0,0,0,20,0,0,0,True,True,True)

Connect (LockerModelA,LockerModelB,1,0,3,0,1,0,14.5,40,-15.5,7,0,50)

End Function

Function PlanksExample()

Local PlankA = LoadMesh ("Model\Plank.3ds")
Local PlankB = CopyEntity (PlankA)
Local PlankC = CopyEntity (PlankA)

Physics (PlankA,0,0,20,0,0,0,0,10,0,0,0,True,True)
Physics (PlankB,0,0,40,0,0,0,0,10,0,0,0,True,True)
Physics (PlankC,0,0,60,0,0,0,0,10,0,0,0,True,True)

End Function

Function ShapesExample()

Local ShapeA = CreateCube()
Local ShapeB = CreateCube()
Local ShapeC = CreateCube()

ScaleEntity ShapeA,5,2,40
ScaleEntity ShapeB,5,2,40
ScaleEntity ShapeC,5,2,40

Physics (ShapeA,0,0,20,0,0,0,0,10,0,0,0,True,True)
Physics (ShapeB,0,0,40,0,0,0,0,10,0,0,0,True,True)
Physics (ShapeC,0,0,60,0,0,0,0,10,0,0,0,True,True)

End Function

Global Grabbed,SpeedX#,SpeedY#,SpeedZ#

;DrawerExample()
;LockerExample()
;PlanksExample()
ShapesExample()

Setup()
Game()


Ah, the example explains the problem a lot better :)

It really goes beyond physics support, but I'll point you in the right direction anyway. It would have probably been better to ask in the Blitz3D programming forum.

Take the camera yaw angle and use Cos and Sin to spread the force between the X and Z axis.

Change the following code in the Move Objects section...
If OE\Slide = True

	;dBodyAddForce OE\Body,SpeedX*Strength,0,-SpeedY*Strength
	;dBodyAddRelForce OE\Body,SpeedX*Strength,0,-SpeedY*Strength

Else

	xforce#=Cos(EntityYaw(Pivot))*(SpeedX*Strength)
	zforce#=Sin(EntityYaw(Pivot))*(SpeedX*Strength)

	dBodyAddForce OE\Body,xforce,-SpeedY*Strength,zforce

EndIf


I've only applied it to the non-slide condition, but it gives you an idea of how it should work.

Note that the current camera yaw is updated continuously above, but you might want it to work only on the initial camera yaw when the mouse button is clicked. If so, add the camera yaw to a variable when the mouse is initially clicked, then use that yaw value until the mouse button is released again.

That's awesome. Thank you VIP3R :)

thanks for the help

Sorry if I'm becoming a pain, but I have another question :)

Is it possible to add force to a particlar part of model? For example, I click one end of my cube and drag it upwards, that end should be pulled up first. When I drag my cube up at the moment, the whole model is lifted no matter what part is clicked. I have tried adjusting where the mass of the object is but it makes no difference :(

No worries Ja2, that's what these forums are for...

When I'm searching for something I always check the jv-ode.decls for any functions that might 'sound' like what I'm looking for ;)

Normally when you move a body, you move it by it's center-of-gravity. Since that's perfectly centered, it won't rotate or react to where your mouse was.

What you need is to apply a force (to move the object) BUT that force should be from a specific point - like NOT the center of gravity.

I found these functions, seems like they take a Force vector (Fxyz) and a Position (Pxyz). Check the ode reference manual to find out how to use 'em and what the specific differences are between them. Never used them myself to be honest, so haven't go a clue ;)

dBodyAddForceAtPos(body,fx#,fy#,fz#,px#,py#,pz#)
dBodyAddForceAtRelPos(body,fx#,fy#,fz#,px#,py#,pz#)
dBodyAddRelForceAtPos(body,fx#,fy#,fz#,px#,py#,pz#)
dBodyAddRelForceAtRelPos(body,fx#,fy#,fz#,px#,py#,pz#)

good luck,
D

Thanks Danny :)

I'm having limited luck with dBodyAddForceAtPos. It works great for larger objects like my chest of drawers but for smaller objects it makes them spin like crazy :(

I didn't think this would be so difficult to do LOL

EDIT:

Yay I fixed it :) I made a pivot and parented it to the clicked mesh at picked xyz and used the dBodyAddForceAtPos using the pivots xyz :)

Seems alot of work to get a ray collision. Is there another way ?

RayGeom =dCreateRay(Space, speed)
dGeomRaySet(RayGeom,x ,y,0,vx,vy,0)
dSpaceCollide(Space,World,ContactGroup)

If (dGeom1CountCollisions(RayGeom)+dGeom2CountCollisions(RayGeom)) >= 1 Then
;Hit
EndIf
   			
dJointGroupEmpty(ContactGroup)
dGeomDestroy(RayGeom)



I have a bullet routine that relies on Ray collision to detect if the bullet can move or not. Seems by selecting dSpaceCollide() after every ray cast is a system drain ? or is it quick anyway and I shouldnt worry ?

Not completely sure what I'm saying here is 'the best' way or even correct (haven't played with rays yet). But I'd think one normally would:

- create a bullet with a ray attach to it - to handle the collision detection.
- let the bullet fly (given direction vector + speed/force)
- Then check in your main loop IF the bullet (ray) collides with anything (perhaps it wont - when fired in the air).

This way the bullet (Ray) is part of the normal collision system - not needing any extra calls.

That's assuming a ray can be used much like a normal rigid body..

It looks as if your example is more alternate method to Blitz's LinePick() function..

hope this helps,
D.

Sorry to butt in but Im having trouble getting version 1.21 of jv-ode to dregister

all I get is user lib not found on dregister(4 keys in hex?)

I have the keys and everything seems to be updated and in right place but when I rem out dregister to get jvode include to run I get version 1.0???and demo version performance i.e sim stops after 1 min

can anybody help ?

maybe you've got a different version in your project folder than you have in Blitz's \userlibs folder - creating some sort of conflict?!??

thanx

but file versions dont appear in dll!
all i get is version 0.0.0.0?!

all other demos of dll and decls. were deleted!

the file size of my new dll is 230kb is that latest?

230kb is the latest yes. But so was 1.20 :) Great help eh.

Sorry mate, don't know what it could be. If you've deleted all other possible versions of dlls and decls. I'd suggest to check your original email you received from vip3r - which should have your personal unique registration code in it. That same number should be used in the dRegister() call at the top of your jv-ode.bb...
Including that jv-ode.bb would automatically call that function and unlock jvode.. but you already knew that..
d.

yeah mate tried that sunday ,but i checked dependency walker just now (which takes apart dll`s) and it came up with ..

MPR.dll

error:

AT LEAST ONE MODULE HAS AN URESOLVED IMPORT DUE TO A MISSING EXPORT FUNCTION IN A DELAY-LOAD DEPENDANT MODULE

..which i ASSUME means that the dregister function cant communicate over the net to obtain resource info from the registration site

perhaps?!

Is this a firewall issue?...its on!?...but i dont want to switch it off!

or maybe MPR is broke?...fixing..

Does the 0.0.0.0 version on the JV-ode.dll change to something else??

dontcha just lvvvrve computers? as annoying as Uma thurman!

Danny

Ah ! Very true - Ive now got the ray as part of the bullet object.

Would be handy to have a version of linepick() that only returned if it hit something or not - not returned the object etc... But for now RAY is working a treat.

@OvineByDesign: There's a ray demo in 'JV-ODE Physics Thread 7' that you can use for reference, it's a demo for the BlitzMax version of JV-ODE but it should be easy enough to follow.

@shaun freeman: You're receiving the error on the dRegisterODE() function because Blitz3D can't find either your JV-ODE decls file (which should be in your Blitz3D userlibs folder) or the JV-ODE DLL itself. Ignore the Dependency Walker error, it's normal. The JV-ODE DLL doesn't access or need a network connection. Double check you have the correct decls file (it should be 26KB) and that it's in the Blitz3D userlibs folder. If you still can't get it working please feel free to email me for help.



THREAD 8 IS ABOUT TO CLOSE

Please continue on the new thread located here

Thanks :)