Simple Physics + Octree

Some days ago I posted an Octree implementation, some was waiting to see what we could do with it, so here it is:



Simple Physics

  • This is a jme extension that implements simple (somehow fake) physics reaction in a scene.
  • It uses the Octree to separate the triangles to check collision
  • The collision algorithm is heavily basead on the one from http://www.peroxide.dk/papers/collision/collision.pdf (thanks to Ruab who converted to java)
  • The physic reaction and simulation I did myself (it's my fault)
  • To use, create a collision scene attaching StaticColliders (meshes) and DynamicColliders (ellipsoids only, for now), add forces and call the update method.
  • There is bouncing reaction, resistance (friction), gravity and mass
  • There is also an "gripping" feature, so you can simulate climbing
  • If you need a very realistic phyisics simulation, try ODE or something else.



    Octree
  • Fixed some problems, now it’s working correctly



    http://www.goraieb.com.br/jme/simplephysics_octree-v04.zip



    There is a test for the Octree and for the SimplePhysics in the zip.





    Please, test it, use it, and add it to de extension library if it makes sense.

    Feedbacks are welcome.



    Lucas.

cool!

nice :slight_smile:



You did not write it as a jME Physics 2 implementation, did you?  :smiley:

This looks amazing and extremely useful, runs nicely for me :slight_smile:



I had a few questions, sorry if they are obvious, I'm going to look through the code to see if I can do anything myself but I thought if I set out what I was aiming for first it might be something you were planning to do anyway.



Have you considered capsule collision? There's some code here:



http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm#dist3D_Segment_to_Segment()

http://opensg.vrsource.org/trac/doxygen/html/OSGVolumeFunctions_8cpp-source.html



I'm too dumb to work out your collision code so far, but I plan to look at it more :slight_smile: I was wondering whether it does interpolated collisions - if it doesn't, capsules are a nice way to do simple interpolated collision of spheres, since you can extrude spheres along the track they trace out each frame, and check collisions with that. I use a very simple version of that in my steering code, but it would be great to use your code for steering as well. It would also be excellent to have a very fast, accurate (interpolated) sphere collision system for implementing bullets, missiles etc. with no physics, just fast collision detection to see what they hit.



When I was using the SimplePhysicsTest, the interaction of the "player" with the ground seems a little bumpy - is it possible to get smoother, less realistic interaction? I noticed there is "sliding" code in the collision solver, it would be great to have a very simple non-penetration system like in older games (quake etc.) that just prevent you walking into stuff by sliding along the normal, but in a smooth way.



If those two things (fast interpolated sphere collision and smooth sliding) can be done, and the speed is ok, then it would be absolutely ideal for my game (and I guess a lot of others), thanks again for the excellent work :slight_smile: I'll try to have a look through and understand more of it today so I can maybe answer my own stupid questions :slight_smile:

I have updated the .zip because I added a terrain in the OctreeIntersectionTest to do some testing with it and forgot to remove and clean up.

Now the OctreeIntersectionTest is fixed.



@MrCoder: Thanks



@Irrisor: No I didn't, should it be? Sorry about my lazyness, I didn't even looked at your code for Jme-Physics2.  :roll:



@shingoki:

The EllipseCollider is and implementation of a DynamicCollider, so we can add a CapsuleCollider quite easely.

I don't want to do this right now because I wan't to move on to other things (scene editor tool), and the ellipse is just enough.

But I'll be happy to help. You will need a capsule->triangle, capsule->sphere and sphere->capsule intersections, returning the position and normal of the contact.

If you decide to do it, I'll prepare the CapsuleCollider class for you.



About the smooth sliding, be sure your not walking on the "bridge" object, or any other "obstacle", because the bounce is enabled by default, causing the collider to … bounce :slight_smile:

For the terrain, I disabled bouncing using this:

collisionScene.attachStatic(new StaticCollider(terrain,100)).getPhysicMaterial().set(.01f, 0, false);



Walking on the terrain here looks very smooth to me. If the problem isn't that, give your specs and framerate so I can try to figure it out.


That's it, thanks for the feedback!
lucas g. said:

@Irrisor: No I didn't, should it be? Sorry about my lazyness, I didn't even looked at your code for Jme-Physics2. 

hehe, I didn't have a look at your's either :P - so I'm not sure if it should. If your api is simpler than the one of jME Physics 2 it probably should not. If someone currently uses jME Physics 2 with ODE and would like to switch to simpler physics without natives it would be really great to have your physics as an implementation. Or vice versa :). Or if someone is already used to the physics api...
But it would be quite some work to do, of course... probably SimplePhysics should rather be kept simple ;)

I set the same physics material the terrain has on everything, it is all very smooth and behaves exactly how I wanted  :smiley:



I spent a while playing around with it, and I've found there are a few points on the terrain where you can fall through, as far as I can tell they are all on the edges of the terrain, at random places, except that I think you can always fall through the corners of the terrain that are made from one tri (that is, NOT the corners where three edges meet, the ones where two edges meet). So the corners are probably the easiest place to test :slight_smile:



I just wanted to ask another dumb question - do the DynamicCollider intersectsXXXX methods accept a velocity, and return the time until the first collision? If so that already does the "interpolated" collision I was jabbering about, and it would be perfect for fast moving bullets. So the capsule thing isn't really important, ellipses are pretty good as you say :slight_smile:



One final thing I was wondering about - at the moment for complicated dynamic meshes (not using SimplePhysics yet, just in jme), I use about 5-10 spheres to mark out the shape, since an aeroplane can't be approximated very well by one sphere/ellipse. I was trying to think of a way of translating this, it seems like it would need something like joints, not sure if that was something you were planning on doing :slight_smile: Full joints might be a bit tricky, but just something that allowed a set of EllipseColliders to move and rotate as one rigid body would be great, since then almost any shape could be represented by a few ellipses stuck together. I realise that's probably not top of your priorities at the moment :slight_smile:

@Irrisor: The Simple Physics API might be much simpler/featureless the Jme-Physics. And the idea IMO is to keep like that, I can't see a reason to reinvent the wheel with so many physic engines around.



@shingoki:

I set the same physics material the terrain has on everything, it is all very smooth and behaves exactly how I wanted

Good to know :)

I'll check the "fall through" problem ASAP.

About fast moving, I tested with high speeds and it seemed to work. The problem is that the amount of triangles to check increases with the collider speed, because it increases the radius of the bounding sphere that intersects with the octree. For this case we could work something else, selecting the min numbers of triangles to check, as the collider is moving like a ray. Right now I think you can use the way it is, or create a ray, or even use the current intersection methods in jme.
A fast bullet simulation may come handy for a lot of people :)

Making static joints may not be that hard, a simple joint feature would be nice indeed. Right now the Ellipse is not axis aligned, this is something else to add.

Another thing to add, is the collision of the Collider with current JME bounding shapes.

I'll add this when I find some time, until then its open, anyone can help (the magic of open source).

Any chance for this stuff to be added to JME extension? Or should I make it separated?

I've been playing around with this stuff, created a cut down version of the SimplePhysicsTest and encountered a problem.

When setting/adding a force to a collider the graphical representation doesnt appaear to update in sync with the physicsState positioning.



package test.simplephysics;

import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingBox;
import com.jme.input.KeyInput;
import com.jme.light.PointLight;
import com.jme.math.FastMath;
import com.jme.math.Matrix3f;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Node;
import com.jme.scene.shape.Box;
import com.jme.scene.state.CullState;
import com.jmex.simplephysics.Collider;
import com.jmex.simplephysics.CollisionListener;
import com.jmex.simplephysics.CollisionScene;
import com.jmex.simplephysics.DynamicCollider;
import com.jmex.simplephysics.EllipseCollider;
import com.jmex.simplephysics.StaticCollider;

public class TestBoxMovement extends SimpleGame {
   
   Node pickNode;
   private CollisionScene collisionScene;
   private DynamicCollider collider1, collider2;
   private Collider lastCollided = null;
   
   public static void main(String[] args) {
      TestBoxMovement app = new TestBoxMovement();
      app.start();
   }
   
   protected void simpleInitGame() {
      
      key = KeyInput.get();
      
      CullState cs = display.getRenderer().createCullState();
      cs.setCullMode(CullState.CS_BACK);
      cs.setEnabled(true);
      
      PointLight pl = new PointLight();
      pl.setDiffuse(new ColorRGBA(0.4f, 0.4f, 0.4f, 1));
      pl.setEnabled(true);
      pl.setLocation(new Vector3f(500,1500,500));
      lightState.attach(pl);
      
      Box b = new Box("b", new Vector3f(0,0,0), 2, 2 ,2);
      
      collider1 = new EllipseCollider(new Vector3f(0, 0, 0), new Vector3f(2,2,2), b);
      collider1.getPhysicMaterial().set(0.5f, 1, 0.5f, 0.5f, true);
      
      collisionScene = new CollisionScene();
      collisionScene.attachDynamic(collider1);
      collisionScene.build();
      
      rootNode.attachChild(b);
   }
   
   private KeyInput key;
   private Vector3f axis = new Vector3f(0,1,0);
   private Vector3f force = new Vector3f();
   private float forceFactor = 0.01f;
   
   protected void simpleUpdate() {
      //force.zero();
      if(key.isKeyDown(KeyInput.KEY_U)) {
         force.x += forceFactor*10;
      }
      if(key.isKeyDown(KeyInput.KEY_J)) {
         force.x -= forceFactor*10;
      }
      if(key.isKeyDown(KeyInput.KEY_H)) {
         force.y += forceFactor*10;
      }
      if(key.isKeyDown(KeyInput.KEY_K)) {
         force.y -= forceFactor*10;
      }
      
      System.out.println(force.toString());
      collider1.getPhysicState().setForce(force);
      
      collisionScene.update(tpf);
   }
}



Hold U,J,H, or K. Symptoms exhibit themselves more offten when two keys are held.

Right I get it. I was wondering what the resistance was for!

Yes i changed setForce to addForce to imitate a car for example - when you take your foot off the accelerator you dont just stop, you must brake.

Suppose I could add a force in the opposite direction.



By the way that was supposed to be forceFactor*tpf (guess I undone a bit too much).



How can u limit speed of a collider?



Are you actively developing this system, or will it only incorporate bug fixes from this point forth?

ok… to imitate a car, try using a higher mass, more force and less resistance:


collider1.getPhysicMaterial().set(0.0001f, 20, 0.5f, 0.5f, true);


...


   private Vector3f force = new Vector3f(0,0,0);

   private float forceFactor = 100f;

   protected void simpleUpdate() {
      force.zero();
      if (key.isKeyDown(KeyInput.KEY_U)) {
         force.x = forceFactor;
      }
      if (key.isKeyDown(KeyInput.KEY_J)) {
         force.x = -forceFactor;
      }
      if (key.isKeyDown(KeyInput.KEY_H)) {
         force.y = forceFactor;
      }
      if (key.isKeyDown(KeyInput.KEY_K)) {
         force.y = -forceFactor;
      }

      collider1.getPhysicState().setForce(force);

      collisionScene.update(tpf);
   }



you limit the speed by the resistance.
Remeber that the force here is in Newton unit.

if you check the code you will see the acceleration math:
accel = force / mass
velocity += accel * time

if you need your own acceleration math, calculate the velocity and then set:
collider1.getPhysicState().velocity.set(your-calculated-velocity);

Are you actively developing this system, or will it only incorporate bug fixes from this point forth?

This is a fake/simple physics system, some games only need this so we don't have to bother any further.
In my opinion, if you need more features use a physics engine (ODE , Newton, PhysX etc).
So.. answering your question, I'll probably only work on otimizations and bug fixes.

I'm curious was looking at  the simple physics test for this but the camera just seems to be floating around and there seems to be no collisions or atleast what I expected to heppen considering its supposed to be like TestObjectWalk in jme any clues as to what the problem might be.

well I use it, but I am not a very good coder , so the solutions I use to get it working isn't optimal, the failing collisions is caused by a change in the Plane class in jme math package didn't have a clue how to start fixing so to move forward with my on/off :wink: project I simple referenced a copy of the Plane class it worked with, solved the floating issue by increasing the gravity vector, a better solution 'might' be to play with the mass of dynamic colliders.



at the time it was introduced all worked as expected jme .10 or .11 can't recall which



i pretty much um… "common sensed" stuff out from the change logs though  ://

I use a very recent jME 1.x from cvs.  Care to post the Plane class that works?  I can wire that directly to the simplephysics and use it outside of the jME packages.

when i get home

I just checked this out and I get the same floating camera effect… I think it has something to do with the gravity implementation.  But let me tell you, flying the camera around and aiming towards stuff on the ground to get a collision to happen was kinda fun!  Anyway, I'm looking for a good implementation of wall sliding to use in my project and thought this library (in perhaps combination with classic terrain following) might be helpful in gracefully navigating walls and objects. I haven't gone too far into it yet but I'll re-bump the thread if I get something useful together.  Of course, pointers to more direct implementations of wall avoidance/sliding (aside of the usual stop player or move player to previous location) would be appreciated as well.

here you go




/*
 * Copyright (c) 2003-2007 jMonkeyEngine
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.jmex.simplephysics;

import java.io.IOException;
import java.io.Serializable;
import java.util.logging.Logger;

import com.jme.math.Triangle;
import com.jme.math.Vector3f;
import com.jme.util.export.InputCapsule;
import com.jme.util.export.JMEExporter;
import com.jme.util.export.JMEImporter;
import com.jme.util.export.OutputCapsule;
import com.jme.util.export.Savable;

/**
 * <code>Plane</code> defines a plane where Normal dot (x,y,z) = Constant.
 * This provides methods for calculating a "distance" of a point from this
 * plane. The distance is pseudo due to the fact that it can be negative if the
 * point is on the non-normal side of the plane.
 *
 * @author Mark Powell
 * @version $Id: Plane.java,v 1.15 2007/09/21 15:45:26 nca Exp $
 */
public class Plane implements Serializable, Savable {
    private static final Logger logger = Logger
            .getLogger(Plane.class.getName());

    private static final long serialVersionUID = 1L;

    /**
     * NO_SIDE represents the plane itself.
     */
    public static final int NO_SIDE = 0;
    /**
     * POSITIVE_SIDE represents a point on the side the normal points.
     */
    public static final int POSITIVE_SIDE = 1;
    /**
     * NEGATIVE_SIDE represents a point on the opposite side the normal points.
     */
    public static final int NEGATIVE_SIDE = 2;

    /** Vector normal to the plane. */
    public Vector3f normal;
    /** Constant of the plane. See formula in class definition. */
    public float constant;

    /**
     * Constructor instantiates a new <code>Plane</code> object. This is the
     * default object and contains a normal of (0,0,0) and a constant of 0.
     */
    public Plane() {
        normal = new Vector3f();
    }

    /**
     * Constructor instantiates a new <code>Plane</code> object. The normal
     * and constant values are set at creation.
     *
     * @param normal
     *            the normal of the plane.
     * @param constant
     *            the constant of the plane.
     */
    public Plane(Vector3f normal, float constant) {
        if (normal == null) {
            logger.warning("Normal was null, created default normal.");
            normal = new Vector3f();
        }
        this.normal = normal;
        this.constant = constant;
    }

    /**
     * <code>setNormal</code> sets the normal of the plane.
     *
     * @param normal
     *            the new normal of the plane.
     */
    public void setNormal(Vector3f normal) {
        if (normal == null) {
            logger.warning("Normal was null, created default normal.");
            normal = new Vector3f();
        }
        this.normal = normal;
    }

    /**
     * <code>getNormal</code> retrieves the normal of the plane.
     *
     * @return the normal of the plane.
     */
    public Vector3f getNormal() {
        return normal;
    }

    /**
     * <code>setConstant</code> sets the constant value that helps define the
     * plane.
     *
     * @param constant
     *            the new constant value.
     */
    public void setConstant(float constant) {
        this.constant = constant;
    }

    /**
     * <code>getConstant</code> returns the constant of the plane.
     *
     * @return the constant of the plane.
     */
    public float getConstant() {
        return constant;
    }

    /**
     * <code>pseudoDistance</code> calculates the distance from this plane to
     * a provided point. If the point is on the negative side of the plane the
     * distance returned is negative, otherwise it is positive. If the point is
     * on the plane, it is zero.
     *
     * @param point
     *            the point to check.
     * @return the signed distance from the plane to a point.
     */
    public float pseudoDistance(Vector3f point) {
        return normal.dot(point) - constant;
    }

    /**
     * <code>whichSide</code> returns the side at which a point lies on the
     * plane. The positive values returned are: NEGATIVE_SIDE, POSITIVE_SIDE and
     * NO_SIDE.
     *
     * @param point
     *            the point to check.
     * @return the side at which the point lies.
     */
    public int whichSide(Vector3f point) {
        float dis = pseudoDistance(point);
        if (dis < 0) {
            return NEGATIVE_SIDE;
        } else if (dis > 0) {
            return POSITIVE_SIDE;
        } else {
            return NO_SIDE;
        }
    }

    /**
     * Initialize this plane using the three points of the given triangle.
     *
     * @param t
     *            the triangle
     */
    public void setPlanePoints(Triangle t) {
        setPlanePoints(t.get(0), t.get(1), t.get(2));
    }

    /**
     * Initialize the Plane using the given 3 points as coplanar.
     *
     * @param v1
     *            the first point
     * @param v2
     *            the second point
     * @param v3
     *            the third point
     */
    public void setPlanePoints(Vector3f v1, Vector3f v2, Vector3f v3) {
        normal.set(v2).subtractLocal(v1);
        normal.crossLocal(v3.x - v1.x, v3.y - v1.y, v3.z - v1.z).normalizeLocal();
                  constant = -(normal.x * v1.x + normal.y * v1.y + normal.z * v1.z);
        //normal.crossLocal(v3.x - v1.x, v3.y - v1.y, v3.z - v1.z).normalizeLocal();
        //constant = -(normal.x * v1.x + normal.y * v1.y + normal.z * v1.z);
    }

    /**
     * <code>toString</code> returns a string thta represents the string
     * representation of this plane. It represents the normal as a
     * <code>Vector3f</code> object, so the format is the following:
     * com.jme.math.Plane [Normal: org.jme.math.Vector3f [X=XX.XXXX, Y=YY.YYYY,
     * Z=ZZ.ZZZZ] - Constant: CC.CCCCC]
     *
     * @return the string representation of this plane.
     */
    public String toString() {
        return "com.jme.math.Plane [Normal: " + normal + " - Constant: "
                + constant + "]";
    }

    public void write(JMEExporter e) throws IOException {
        OutputCapsule capsule = e.getCapsule(this);
        capsule.write(normal, "normal", Vector3f.ZERO);
        capsule.write(constant, "constant", 0);
    }

    public void read(JMEImporter e) throws IOException {
        InputCapsule capsule = e.getCapsule(this);
        normal = (Vector3f) capsule.readSavable("normal", Vector3f.ZERO.clone());
        constant = capsule.readFloat("constant", 0);
    }

    public Class getClassTag() {
        return this.getClass();
    }
}


Thank you.  I'll give it a try :wink:

This is cool.

Has anyone experience using this with mesh converted from a .obj file?



Reason I ask is, I can get collision detection working with the mesh, just there are some parts I can slip through, and it only seems to work on one side of each "wall", but I need to do some more tests with this as only one side of the walls are visible at the moment so hard to see where I'm testing.



Maybe I'm going about getting the mesh data the wrong way -


public TriMesh getTrimesh(){
            return t;      //t being the trimesh that is made from the .obj file                 
         }



backDrop backdrop = new backDrop(display);
          backdrop.setModelBound(new BoundingBox());
          backdrop.updateModelBound();

          SharedMesh backdropmesh = new SharedMesh("backDropMesh", backdrop.getTrimesh());
          backdropmesh.setModelBound(new BoundingBox());
          backdropmesh.updateModelBound();
          backdropmesh.setLocalTranslation(backdrop.getLocalTranslation());



collisionScene.attachStatic(new StaticCollider(backdropmesh, 25)).getPhysicMaterial().set(0.1f, 0, false);



If I do end up having to create invisible walls inside of the scence seperately from the .obj files data then I will, I mean shapes from JME have no trouble at all with slipping though that I have found so far.  But it would save alot of time getting the game levels togeather if it worked just detecting the mesh data better.


Edit -
problem seems to be solved by bumping up the StaticCollider's trianglePerNode, I set it to 250 as a test and no slipping through happens

new StaticCollider(backdropmesh, 250)



Probably way to high now, but I'll play about with it to see how low I can set it with no slipping though. I really haven't a clue how octrees work, so I dpon't know how this affects performance.
I mean the terrain in th test is set to 25 triangles per node, and I'm sure that it has way more triangle in it.

Anyways, I forgot to thank lucas g for providing this, Thanks :D