JV-ODE: Bug in dSpaceDestroy()

Blitz3D Forums/Blitz3D Userlibs/JV-ODE: Bug in dSpaceDestroy()

JV-ODE: Bug in dSpaceDestroy()

Before I load a new level (or game) I have to purge all the existing level data; include all bodies, joints and geometry I created for ODE. In stead of doing everything manually (and having a natural lazyness to prevent doing things not necesary) I use these three function to destroy whatever game data is used by ODE:
   dJointGroupDestroy(contactGroup)
   dSpaceDestroy(Space)
   dWorldDestroy(World)
According to the manual I think I should be safe. Especially with dSpaceDestroy which according to the docs is supposed to "When a space is destroyed, if it's cleanup mode is 1 (the default) then ALL THE GEOMS IN THAT SPACE ARE AUTOMATICALLY DESTROYED AS WELL".

I think not! My experience is that if I don't manually destroy each piece of geometry I created before, I get a guaranteed MAV the next time my game goes into the main loop and hits the ODE update routine:
   dSpaceCollide(space,world,contactGroup)    ; THIS WILL GENERATE A MEMORY ACCESS VIOLATION!
   dWorldQuickStep(world, 0.05)
   dJointGroupEmpty(contactGroup)
I've included an example here where I succesfully re-start ODE 3 times (manually purging geometry) but the 4th time will give a MAV because I didn't manually purge the geometry the 3rd time, and 'relied' on dSpaceDestroy() to do that for me...

Anyone had this problem?! Am I missing something?!

Include "JV-ODE.bb"


;# reset test
;# 
;#


Graphics3D 800,600,0,2

Type odeGeom
	Field body
	Field geom
	Field mesh
End Type

;Global
Global World, Space, ContactGroup



;#### CREATE "Level 1"

	Print "1. Loading Level 1"
	ODE_init()							; create ODE World, Space and ContactGroup

	obj1.odeGeom = object_add()			; add 2 objects
	obj2.odeGeom = object_add()

	;<insert game code here>
	Print "2. playing level 1"
	ODE_Update()
	ODE_Update()

	Print "3. Purging level 1"
	object_remove(obj1)					; Level completed - remove all objects
	object_remove(obj2)

	Print "4. resetting ODE"
	ODE_exit()							; purge physics simulator data

;#### CREATE "Level 2"

	Print "-----------------------------"

	Print "1. Loading Level 2"
	ODE_init()							; create ODE World, Space and ContactGroup

	obj1.odeGeom = object_add()			; add 2 objects
	obj2.odeGeom = object_add()

	;<insert game code here>
	Print "2. playing level 2"
	ODE_Update()
	ODE_Update()

	Print "3. Purging level 2"
	object_remove(obj1)					; Level completed - remove all objects
	object_remove(obj2)

	Print "4. resetting ODE"
	ODE_exit()							; purge physics simulator data


;#### CREATE "Level 3"

	Print "-----------------------------"

	Print "1. Loading Level 3"
	ODE_init()							; create ODE World, Space and ContactGroup

	obj1.odeGeom = object_add()			; add 2 objects
	obj2.odeGeom = object_add()

	;<insert game code here>
	Print "2. playing level 3"
	ODE_Update()
	ODE_Update()

	Print "3. NOTE: THIS TIME I AM NOT DESTROYING GEOMETRY MANUALLY!"
	Print "         This will result that when restarted, the first ODE_Update() will hang with a Memory Access Violation!'
;//	object_remove(obj1)					; Level completed - remove all objects
;//	object_remove(obj2)

	Print "4. resetting ODE"
	ODE_exit()							; purge physics simulator data


;#### CREATE "Level 4"

	Print "-----------------------------"

	Print "1. Loading Level 4"
	ODE_init()							; create ODE World, Space and ContactGroup

	obj1.odeGeom = object_add()			; add 2 objects
	obj2.odeGeom = object_add()

	;<insert game code here>
	Print "2. playing level 4"
	ODE_Update()					;----> THIS WILL FAIL and produce MAV!!!
	ODE_Update()

	Print "3. Purging level 4"
	object_remove(obj1)					; Level completed - remove all objects
	object_remove(obj2)

	Print "4. resetting ODE"
	ODE_exit()							; purge physics simulator data


;#### End of program

;shut down forever
dCloseODE()

Print "-----------------------------"
Print "Game finished. Hope you enjoyed it! <key>"
WaitKey()
End

;----

Function object_add.odeGeom()

	ode.odegeom = New odegeom
	ode\body=dBodyCreate(World)
	dBodySetRotation ode\body,0,0,0
	dBodySetPosition ode\body,0,0,0

	ode\geom=dCreateBox (Space,1,1,1)
	dGeomSetBody ode\geom,ode\body

	ode\mesh=CreateCube()
	ScaleMesh ode\mesh, 0.5, 0.5, 0.5

	Return ode

End Function

;----

Function object_remove(ode.odeGeom)

;NOTE: BODY & ENTITY are commented out to illustrate it really is the dGeomDestroy that is NECESARY!

	dGeomDestroy ode\geom	;remove geometry
;	dBodyDestroy ode\body	;remove body
;	FreeEntity ode\mesh		;remove mesh
	Delete ode				;remove type

End Function

;----

Function ODE_Init()

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

	dWorldSetAutoDisableFlag(World,1)
	dWorldSetGravity(World,0,-0.98,0)
	dContactSetMode(dContactBounce)
	dContactSetBounce(0.01)
	dContactSetMu(50)

End Function

;----

Function ODE_Exit()

	;confirm default
	dSpaceSetCleanup Space, 1

	;purge all ODE data
	dJointGroupDestroy(ContactGroup)
	dSpaceDestroy(Space)
	dWorldDestroy(World)

	;dSpaceDestroy:	"When a SPACE is DESTROYED, if it's cleanup mode is 1 (the default) then ALL the GEOM
	;				in THAT SPACE are AUTOMATICALLY destroyed as well."

	;Conclusion: This ain't TRUE! If I don't remove my geometry manually (using object_remove in this case)
	;			 ODE will hang next time & initialise & update the first frame!!

End Function

;----

Function ODE_Update()

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

End Function

;----


Cheers,
Danny.

Hmm, it's not a bug in that sense as ODE is actually correctly deleting the geoms when you destroy the space. However, it does highlight a problem that's been overlooked.

The problem arises because in JV-ODE, the geoms each have their own contact information attached (used to retrieve the detailed collision data). When you use the dGeomDestroy() function, this data is cleaned up and destroyed also.

But, when using the dSpaceDestroy() function, although ODE correctly destroys all geom instances, it doesn't cleanup and destroy the data that was attached to the old geoms (it isn't aware of the data internally). When you re-init the space and create new geoms, the old geom data still exists, so when you try to process the contacts using dSpaceCollide() it throws a MAV.

For the time being you will need to delete all geoms manually with dGeomDestroy() to correctly destroy the geom data and prevent the MAVs.

I agree this definitely needs looking at, I'll see if I can add the geom data cleanup code to the dSpaceDestroy() process. All should work as expected then.

Thanks for the detailed bug report Danny, I'll look into this right away ;)

Super reply vip3r, thanks for that!

Yes it would be good if you or one of the ode guys can incorporate cleaning that extra data with dSpaceDestroy()..

Cheers,
Danny

Ok, the dSpaceDestroy() issue is now fixed and will be included in the next JV-ODE update V1.12.

:)

Whoo-ah - bugs sure have a hard time trying to stay alive in JV-ODE! :)

Heh, the same problem lies in the old-old-old ODE wrapper i used in the racer. :D Luckily I dont need to free/recreate everything on a new game loop. JV-ODE sure is a solid peice of kit.