Making a Space simulation...

Hi, i’m working on a multiplayer (max 4 Players) space physics simulation.
Players can “make” planets (spheres), let them grow (add mass and volume) and apply a pushing force (accelerating the planet away from the camera) to them. then there is a physics engine, that should manage collisions and gravity of the planets.

Now my question: how can i setup a physics engine in my scene, that does NOT drop all objects to the ground (because we are in space=no gravity), but apply gravity forces to all planets=meshes with a mass attribute AND manage collisions (i think of ODE spheres) of them?
Oh and if the “pushing force” could be simulated using particles or something similar, so that it can also be used to rotate a planet (by clicking near the “border” instead the center of the planet), that would be best.

for collision: the planets don’t have to deform on collision, the bigger (heavier) one should get bigger (because of parts of the smaller one sticking in it) and the smaller one should break in 2 parts, which will be accellerated away from the bigger one, as far as needed to keep them from instantly getting pulled back by the gravity

for applying forces/add planets/grow them i want to use a 3D-cursor (in the center of the screen, camera rotation via mouse ; Z can be controlled by the mouse wheel).
maybe it’s possible to use anaglyph stereo images for visualizing the szene, then it would be easier to do the 3D-operations without VR-gloves or similar.

EDIT: i do have the anaglyph stereo function, look at my post on: discourse.panda3d.org/viewtopic … 2951#42951

well. the good news is. physic engines, unlike real world, allow to set up forces including gravity in whatever way you want. since you already mentioned ODE, that should do the job. try searching the forum for “demomaster” there are a few ODE samples included which might be helpful.
about simulating particles as forces, the physical simulation is independant from the rendering part , so you can visualize those pushing forces with pretty much everything that your creative mind can come up with.
latest version of panda should include the get the events caused by ode-collisions, which you can use merge planets.
since i never really used ode i cant provide you with code. but pretty much everything you mentioned should be quite possible.

btw. your 3d-code sounds cool:)

I’m not sure if you want 2d or 3d physics, if you only need 2 dimensions you would likely get better physics by using box2d. i’ve used that together with panda already and it creates very nice collisions results, but you have to create the collision objects yourself.

@hypnos: i need 3d physics.
@ThomasEgi: the last time i used panda3d (i think in year 04-06) there was no ODE, so i don’t know how it works, and the Documentation about ODE here is not very good.
I’ll search for a few techdemos.
oh and i think i will not use so many particle effects (or i will make a commandline flag to disable them) because it has to run on slower machines too. i didn’t mean using particles for visualizing, but using something like particles or some force which does not only push, but also rotate the object, depending on where the planet is hit (if its hit on left side it should rotate clockwise, or to speak in 3d words: if its hit on an negative x spot, it rotates positive around y).

i found that one example with these smiley spheres falling on a plate and drawing lines on collision. but now i don’t know how to disable the main gravity (that pulls the balls towards the plate). if i can do that, and then add a few onCollide events, camera control and user controlled forces, its almost done.

OH and very important question: how shall i make that ODE space multiplayer-capable? i thought of one player starting a server (ingame) that will do all ODE calculations, and the other players would use clients which update the position of all planets, and display them in the scene every frame, without doing any ODE calculations. and if one player applies a force, the position and type of force (and the “enabled” boolean flag) would be sent to the server, which would then use it in the ODE space if the “enabled flag” is active (=the player is holding the “apply force button”)

UPDATE:
i found out howto disable the global gravity (using “OdeWorld.setGravity(0, 0, 0)”), and howto use my own “planet texture” (something that should look like stone, pre-bumpmapped in gimp). now i have flying stone balls in random colors (the planets). what i have to do now is enabling gravity forces between the planets (so that they attract each other if they are near enough), the mouse controlled camera and the mouse-controlled “3D gravity pointer”, that is used to attact or push the planets away. then ill go for multiplayer support.

i have a problem when trying to scale the planet’s NodePath (because it gets some mass on collision):
File “PlanetBox.py”, line 69, in onCollision
np.setScale(np.getScale()*1.5)
AssertionError: !is_empty() at line 1268 of panda/src/pgraph/nodePath.cxx

but what i don’t understand: after creating the sphere, i DID scale every planet once to value 1 (just to make sure i can use getScale later). but later in the onCollide event it makes an error

Anyone knows how to do that?

are you sure that “np” is valid in that scope? can you post your collision function? especially the part which assigns np?

i just saw while searching the forum that this error means my nodepath is empty. so i’ll try to explain how i get it (because i dont understand why it is empty):
i have a “prototype” of a planet:

ball = loader.loadModel("/mf/planet")

now i use 2 functions to create the transformed copy of that object, and to create a set ODE objects for it:

def mkball(x,y,z,r,g,b):
  ballNP = mkdummyball(x,y,z,r,g,b,randint(-45, 45), randint(-45, 45), randint(-45, 45))
  ballBody = OdeBody(world)
  M = OdeMass()
  M.setSphere(50, 10)
  ballBody.setMass(M)
  ballBody.setPosition(ballNP.getPos(render))
  ballBody.setQuaternion(ballNP.getQuat(render))
  ballGeom = OdeSphereGeom(space, 1)
  ballGeom.setCollideBits(BitMask32(0x00000001))
  ballGeom.setCategoryBits(BitMask32(0x00000001))
  ballGeom.setBody(ballBody)
  return (ballNP,ballGeom,ballBody)

def mkdummyball(x,y,z,r,g,b,h,p,rr):
  ballNP = ball.copyTo(render)
  ballNP.setPos(x, y, z)
  ballNP.setColor(r, g, b, 1)
  ballNP.setHpr(h, p, rr)
  ballNP.setScale(1)
  return ballNP

then i create a list of these ball objects:

balls = []
for i in range(50):
  balls.append(mkball(randint(-7, 7), randint(-7, 7), 10 + random() * 5.0,random(), random(), random()))

then i define this onCollide callback and run the simulation:

def onCollision(entry):
  global balls
  geom1 = entry.getGeom1()
  geom2 = entry.getGeom2()
  body1 = entry.getBody1()
  body2 = entry.getBody2()
  for np, geom, body in balls:
    if geom == geom1 or geom == geom2:
      if body1.getMass() > body2.getMass() and geom == geom1:
        render.setColorScale(2,2,2,1)
        M = body.getMass()
        M.add(body2.getMass())
        body.setMass(M)
        np.setScale(np,1.5)
      elif body2.getMass() > body1.getMass() and geom == geom1:
        np.remove()

what the callback should do is: adding the ODE mass of the “smaller” planet to the “bigger” one, multiplying the scale of the bigger one with 1.5, and remove the smaller one.

np.remove() will remove the node and make it empty. The next pass, you’ll get an error because you now have an empty NodePath in your list.

Perhaps you should also remove the tuple from the balls list when you remove the NodePath.

David

oh. never thought about that, but u’re right. how can i remove it from my list?
and btw: for mouse navigation i currently use base.enableMouse(). so i need only the networking code, the cursor for adding and “growing” planets, and the gravity between the planets, any idea how to do that?

ok, removing and rezizing works now:

def onCollision(entry):
  global balls
  geom1 = entry.getGeom1()
  geom2 = entry.getGeom2()
  body1 = entry.getBody1()
  body2 = entry.getBody2()
  for i in balls:
    np=i[0]
    geom=i[1]
    body=i[2]
    if geom == geom1 or geom == geom2:
      if body1.getMass() > body2.getMass() and geom == geom1:
        M = body.getMass()
        M.add(body.getMass())
        M.add(body2.getMass())
        M.add(body2.getMass())
        body.setMass(M)
        if not np.isEmpty():
          np.setScale(np,2)
          #geom.setCollideBits(BitMask32(0x00000000))
          #taskMgr.doMethodLater(3, geom.setCollideBits, "collidemelater"+np.getName(),[BitMask32(0x00000001)])
      elif body2.getMass() > body1.getMass() and geom == geom1:
        np.removeNode()
        balls.remove(i)

i used the commented lines to stop the resized planets from being kicked away in my example, where there is a cloud of planets on one spot at the beginning, so they resized into other planets…

now i have to write some code that applies a gravity field at the current camera position and in every planet (depending on the mass, where the camera would use the value 100)

could someone tell me how to make a “gravity field/area”? like an (invisible) sphere, and all ode objects inside that spherical area will get attacted by its center.

gravity code is done!

def onM2(): #eventhandler for mouse2
  global balls
  for i in balls:
    np=i[0]
    body=i[2]
    pos=VBase3(base.camera.getPos(np))
    vel=body.getLinearVel()
    nvel=VBase3(pos/5)
    nvel.addX(vel.getX()/2)
    nvel.addY(vel.getY()/2)
    nvel.addZ(vel.getZ()/2)
    body.setLinearVel(nvel)
def simulationTask(task): # ODE simulation task
  for i in balls:
    np=i[0]
    body=i[2]
    for j in balls:
      np2=j[0]
      body2=j[2]
      pos=VBase3(np.getPos(np2))
      vel=body2.getLinearVel()
      nvel=VBase3(pos/50)
      nvel.addX(vel.getX()/1.25)
      nvel.addY(vel.getY()/1.25)
      nvel.addZ(vel.getZ()/1.25)
      body2.setLinearVel(nvel)
  space.autoCollide()
 {...some collision and simulation code...}

I’m working on something similar:

code.google.com/p/stableorbit

I also would welcome the help if you wanted to team up :wink:

I do not know how ODE does move particles, but if you want to create realistic orbits with your code, I guess you have to compute the physics yourself. As long as you don’t have too many moving bodies that should not be a problem.

The method for that is the verlet integration: http://en.wikipedia.org/wiki/Verlet_integration, the velocity verlet algorithm is usually the easiest one to implement.

Maybe this leads too far into theory, but for stable orbits you need to have an integration method with a few special properties, not just a stepwise movement of objects.

Um, there is gravity in space. There is not friction, but there is gravity. What do you think keeps the planets in orbit?

I’d be curious to test the stereo-view implementation. Did you released any code? I am not interested in the game itself, but rather on a working example of stereoscopic viewing, so even a limited prototype would be useful.

i have a singleplayer version of the game, but i lost the sources, so i will try to get them from my demo release (python, so it should be easy to get my sources back). i’ll upload them when i have them. i remember there’s a very easy stereo function i made…

ok, the code i used was:

leftColor = ColorWriteAttrib.CRed
rightColor = ColorWriteAttrib.CBlue
if(len(sys.argv) > 1 and sys.argv[1]=="-aglrc"):
  rightColor = ColorWriteAttrib.CBlue | ColorWriteAttrib.CGreen
base.win.setRedBlueStereo(True, leftColor, rightColor)
oldDr = base.cam.node().getDisplayRegion(0)
oldDr.setCamera(NodePath())
dr = base.win.makeStereoDisplayRegion()
dr.setCamera(base.cam)
dr.getRightEye().setClearDepthActive(True)

if(len(sys.argv) > 1 and (sys.argv[1]=="-aglrb" or sys.argv[1]=="-aglrc")):
  dr.setStereoChannel(Lens.SCStereo)
else:
  dr.setStereoChannel(Lens.SCMono)

i did have my own function, using tinted texture rendering on half transparent planes, but that was slooooooooooooow.