From 1ecc20a08c0e1af401a902adab332da24557dc8d Mon Sep 17 00:00:00 2001 From: Edouard DUPIN Date: Wed, 22 Dec 2021 23:22:12 +0100 Subject: [PATCH] [DEV] collide sphere with triangle and sphere with triangle is good ==> need to add friction to stop object --- .../LoxelApplicationPerso.java | 35 ++++++-- src/org/atriasoft/ege/Environement.java | 6 +- .../ege/components/ComponentPhysicsPerso.java | 86 +++++++++++-------- .../ege/components/ComponentPosition.java | 10 +++ .../ege/components/PhysicBodyType.java | 6 +- src/org/atriasoft/ege/engines/EngineAI.java | 75 ++++++++-------- .../ege/engines/EnginePhysicsPerso.java | 4 + .../ToolCollisionSphereWithSphere.java | 7 +- .../ToolCollisionSphereWithTriangle.java | 2 +- 9 files changed, 146 insertions(+), 85 deletions(-) diff --git a/samples/src/sample/atriasoft/ege/loxelEnginePerso/LoxelApplicationPerso.java b/samples/src/sample/atriasoft/ege/loxelEnginePerso/LoxelApplicationPerso.java index c7019c2..e3ec266 100644 --- a/samples/src/sample/atriasoft/ege/loxelEnginePerso/LoxelApplicationPerso.java +++ b/samples/src/sample/atriasoft/ege/loxelEnginePerso/LoxelApplicationPerso.java @@ -262,17 +262,23 @@ public class LoxelApplicationPerso extends GaleApplication { // env.addEntity(localBox); // } - if (true) { + if (!this.disable) { // add a cube to test collision ... final Entity localBox = new Entity(this.env); localBox.addComponent(new ComponentStaticMesh(new Uri("RES", "cube-one.obj"))); localBox.addComponent(new ComponentTexture(new Uri("DATA", "blocks/clay.png", "loxelEngine"))); localBox.addComponent(new ComponentLight(new Light(new Color(0.0f, 1.0f, 0.0f), new Vector3f(0, 0, 0), new Vector3f(0.8f, 0.03f, 0.002f)))); localBox.addComponent(new ComponentRenderTexturedStaticMesh(new Uri("DATA", "basic.vert", "loxelEngine"), new Uri("DATA", "basic.frag", "loxelEngine"))); - localBox.addComponent(new ComponentPosition(new Transform3D(new Vector3f(0, 4, 0)))); + localBox.addComponent(new ComponentPosition(new Transform3D(new Vector3f(0, 3, 0)))); final ComponentPhysicsPerso physics2 = new ComponentPhysicsPerso(this.env); - final PhysicTriangle box2 = new PhysicTriangle(); - box2.setPoints(new Vector3f(2, 0, 0), new Vector3f(0, 2, 0), new Vector3f(3, 3, 2)); + physics2.setBodyType(PhysicBodyType.BODY_STATIC); + PhysicTriangle box2 = new PhysicTriangle(); + box2.setPoints(new Vector3f(4, 0, 0), new Vector3f(0, 4, 0), new Vector3f(-2, -2, 1)); + box2.setOrigin(new Vector3f(0, 0, 0)); + box2.setMass(0); + physics2.addShape(box2); + box2 = new PhysicTriangle(); + box2.setPoints(new Vector3f(4, 0, 0), new Vector3f(0, 4, 0), new Vector3f(6, 6, 0)); box2.setOrigin(new Vector3f(0, 0, 0)); box2.setMass(0); physics2.addShape(box2); @@ -286,9 +292,28 @@ public class LoxelApplicationPerso extends GaleApplication { localBox.addComponent(new ComponentTexture(new Uri("DATA", "blocks/clay.png", "loxelEngine"))); localBox.addComponent(new ComponentLight(new Light(new Color(0.0f, 1.0f, 0.0f), new Vector3f(0, 0, 0), new Vector3f(0.8f, 0.03f, 0.002f)))); localBox.addComponent(new ComponentRenderTexturedStaticMesh(new Uri("DATA", "basic.vert", "loxelEngine"), new Uri("DATA", "basic.frag", "loxelEngine"))); - localBox.addComponent(new ComponentPosition(new Transform3D(new Vector3f(1, 5, 4)))); + localBox.addComponent(new ComponentPosition(new Transform3D(new Vector3f(1.1f, 5.1f, 1.0f), Quaternion.fromEulerAngles(new Vector3f(0.15f, 0.95f, 0.3f))))); final ComponentPhysicsPerso physics2 = new ComponentPhysicsPerso(this.env); final PhysicSphere box2 = new PhysicSphere(); + physics2.setBodyType(PhysicBodyType.BODY_STATIC); + box2.setSize(1.0f); + box2.setOrigin(new Vector3f(0, 0, 0)); + box2.setMass(0); + physics2.addShape(box2); + localBox.addComponent(physics2); + this.env.addEntity(localBox); + } + if (true) { + // add a cube to test collision ... + final Entity localBox = new Entity(this.env); + localBox.addComponent(new ComponentStaticMesh(new Uri("RES", "cube-one.obj"))); + localBox.addComponent(new ComponentTexture(new Uri("DATA", "blocks/clay.png", "loxelEngine"))); + localBox.addComponent(new ComponentLight(new Light(new Color(0.0f, 1.0f, 0.0f), new Vector3f(0, 0, 0), new Vector3f(0.8f, 0.03f, 0.002f)))); + localBox.addComponent(new ComponentRenderTexturedStaticMesh(new Uri("DATA", "basic.vert", "loxelEngine"), new Uri("DATA", "basic.frag", "loxelEngine"))); + localBox.addComponent(new ComponentPosition(new Transform3D(new Vector3f(1, 5, 4), Quaternion.fromEulerAngles(new Vector3f(0.15f, 0.95f, 0.3f))))); + final ComponentPhysicsPerso physics2 = new ComponentPhysicsPerso(this.env); + physics2.setBodyType(PhysicBodyType.BODY_DYNAMIC); + final PhysicSphere box2 = new PhysicSphere(); box2.setSize(1.0f); box2.setOrigin(new Vector3f(0, 0, 0)); box2.setMass(1); diff --git a/src/org/atriasoft/ege/Environement.java b/src/org/atriasoft/ege/Environement.java index 187fdad..ce717a1 100644 --- a/src/org/atriasoft/ege/Environement.java +++ b/src/org/atriasoft/ege/Environement.java @@ -333,9 +333,13 @@ public class Environement { this.startTime = System.nanoTime(); this.lastCallTime = this.startTime; } - final long lastUpdate = this.lastCallTime; + long lastUpdate = this.lastCallTime; this.lastCallTime = System.nanoTime(); Clock currentClock = Clock.systemUTC(); + // in the simulation, we need to limit the delta... + if (this.lastCallTime - lastUpdate > 1000000000) { + lastUpdate = this.lastCallTime - 5100000; + } final EventTime event = new EventTime(currentClock, this.startClock, this.lastCallTime, this.startTime, Duration.ofNanos(this.lastCallTime - lastUpdate), Duration.ofNanos(this.lastCallTime - lastUpdate)); for (final ControlInterface elem : this.controls) { diff --git a/src/org/atriasoft/ege/components/ComponentPhysicsPerso.java b/src/org/atriasoft/ege/components/ComponentPhysicsPerso.java index 1cb3045..e8eb9b6 100644 --- a/src/org/atriasoft/ege/components/ComponentPhysicsPerso.java +++ b/src/org/atriasoft/ege/components/ComponentPhysicsPerso.java @@ -1,7 +1,10 @@ package org.atriasoft.ege.components; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import org.atriasoft.ege.Component; import org.atriasoft.ege.Environement; @@ -29,11 +32,10 @@ public class ComponentPhysicsPerso extends Component { public static float globalMaxSpeed = Float.MAX_VALUE; private AABB aabb; private List aabbIntersection = new ArrayList<>(); - private List narrowIntersection = new ArrayList<>(); - List collisionPoints = new ArrayList<>(); + private Map> narrowIntersection = new HashMap<>(); + //List collisionPoints = new ArrayList<>(); private List shapes = new ArrayList<>(); private ComponentPosition position; - private boolean staticObject = false; private boolean manageGravity = true; private float maxSpeed = globalMaxSpeed; // current speed of the object @@ -44,6 +46,8 @@ public class ComponentPhysicsPerso extends Component { private Vector3f staticForce = new Vector3f(0, 0, 0); // Apply dynamic force on it private Vector3f dynamicForce = new Vector3f(0, 0, 0); + // Apply dynamic force on it + private Vector3f dynamicForceGlobal = new Vector3f(0, 0, 0); private EnginePhysicsPerso engine; private PhysicBodyType bodyType; @@ -75,37 +79,51 @@ public class ComponentPhysicsPerso extends Component { } public void applyColisionForce() { - for (ColisionPoint impact : this.collisionPoints) { - this.position.applyForce(impact.force); + Vector3f globalForce = Vector3f.ZERO; + for (Entry> elem : this.narrowIntersection.entrySet()) { + for (ColisionPoint impact : elem.getValue()) { + globalForce = globalForce.add(impact.force); + } } - + // this force is not depending on the mass... + this.speed = this.speed.add(globalForce); + this.position.applyForce(globalForce); } public void applyForces(float timeStep, EngineGravity gravity) { // get the gravity at the specific position... - Vector3f gravityForce; + Vector3f gravityAcceleration; if (this.manageGravity) { - gravityForce = gravity.getGravityAtPosition(this.position.getTransform().getPosition()).multiply(timeStep); + gravityAcceleration = gravity.getGravityAtPosition(this.position.getTransform().getPosition()); } else { - gravityForce = new Vector3f(0, 0, 0); + gravityAcceleration = new Vector3f(0, 0, 0); } // apply this force on the Object - Log.info("apply gravity: " + gravityForce); + Log.info("apply gravity: " + gravityAcceleration); // relative to the object - Vector3f staticForce = this.staticForce.multiply(timeStep); + Vector3f staticForce = this.staticForce; float globalMass = 0; for (PhysicShape shape : this.shapes) { globalMass += shape.getMass(); } // note the acceleration is not real, it depend on the current delta time... - this.acceleration = gravityForce.add(this.position.getTransform().getOrientation().multiply(staticForce)).add(this.position.getTransform().getOrientation().multiply(this.dynamicForce)) - .multiply(globalMass); - this.dynamicForce = new Vector3f(0, 0, 0); - this.speed = this.speed.add(this.acceleration); + Vector3f staticforceOriented = this.position.getTransform().getOrientation().multiply(staticForce); + Vector3f dynamicforceOriented = this.position.getTransform().getOrientation().multiply(this.dynamicForce); + Vector3f globalForce = staticforceOriented.add(dynamicforceOriented); + if (globalMass > 0.0) { + globalForce = globalForce.divide(globalMass); + } else { + gravityAcceleration = Vector3f.ZERO; + } + if (this.bodyType != PhysicBodyType.BODY_DYNAMIC) { + gravityAcceleration = Vector3f.ZERO; + } + this.acceleration = gravityAcceleration.add(globalForce); + this.speed = this.speed.add(this.acceleration.multiply(timeStep)); limitWithMaxSpeed(); Log.info("apply acceleration: " + this.acceleration); Log.info("apply speed: " + this.speed); - this.position.setTransform(this.position.getTransform().withPosition(this.position.getTransform().getPosition().add(this.speed.multiply(timeStep)))); + this.position.setTransform(this.position.getTransform().withPosition(this.position.getTransform().getPosition().add(this.speed))); } private boolean checkCollide(PhysicShape shapeCurrent) { @@ -175,7 +193,7 @@ public class ComponentPhysicsPerso extends Component { } public boolean checkNarrowCollision() { - if (this.staticObject) { + if (this.bodyType != PhysicBodyType.BODY_DYNAMIC) { return false; } for (ComponentPhysicsPerso elem : this.aabbIntersection) { @@ -187,8 +205,12 @@ public class ComponentPhysicsPerso extends Component { } } if (collide) { - this.narrowIntersection.add(elem); - elem.narrowIntersection.add(this); + if (!this.narrowIntersection.containsKey(elem)) { + this.narrowIntersection.put(elem, new ArrayList<>()); + } + if (!elem.narrowIntersection.containsKey(this)) { + elem.narrowIntersection.put(this, new ArrayList<>()); + } } } return isNarrowCollide(); @@ -199,7 +221,6 @@ public class ComponentPhysicsPerso extends Component { } public void clearPreviousCollision() { - this.collisionPoints.clear(); this.aabbIntersection.clear(); this.narrowIntersection.clear(); } @@ -259,16 +280,12 @@ public class ComponentPhysicsPerso extends Component { } public boolean isNarrowCollide() { - if (this.narrowIntersection.size() == 0) { + if (this.narrowIntersection.isEmpty()) { return false; } return true; } - public boolean isStaticObject() { - return this.staticObject; - } - private void limitWithMaxSpeed() { if (this.speed.length2() > this.maxSpeed * this.maxSpeed) { this.speed = this.speed.safeNormalize().multiply(this.maxSpeed); @@ -276,14 +293,13 @@ public class ComponentPhysicsPerso extends Component { } public void narrowCollisionCreateContactAndForce() { - - if (this.staticObject) { + if (this.bodyType != PhysicBodyType.BODY_DYNAMIC) { return; } if (this.narrowIntersection.size() == 0) { return; } - for (ComponentPhysicsPerso elem : this.narrowIntersection) { + for (Entry> elem : this.narrowIntersection.entrySet()) { for (PhysicShape shapeCurrent : this.shapes) { //TODO Do a better method we do this many times ... /* @@ -291,8 +307,8 @@ public class ComponentPhysicsPerso extends Component { continue; } */ - List points = elem.getCollidePoints(shapeCurrent); - this.collisionPoints.addAll(points); + List points = elem.getKey().getCollidePoints(shapeCurrent); + elem.getValue().addAll(points); } } } @@ -305,8 +321,10 @@ public class ComponentPhysicsPerso extends Component { public void renderDebug(ResourceColored3DObject debugDrawProperty) { Color displayColor; displayColor = new Color(1.0f, 0.0f, 0.0f, 1.0f); - for (ColisionPoint impact : this.collisionPoints) { - debugDrawProperty.drawSquare(new Vector3f(0.02f, 0.02f, 0.02f), Matrix4f.createMatrixTranslate(impact.position), displayColor); + for (Entry> elem : this.narrowIntersection.entrySet()) { + for (ColisionPoint impact : elem.getValue()) { + debugDrawProperty.drawSquare(new Vector3f(0.02f, 0.02f, 0.02f), Matrix4f.createMatrixTranslate(impact.position), displayColor); + } } if (this.aabbIntersection.size() == 0) { displayColor = new Color(1.0f, 1.0f, 1.0f, 1.0f); @@ -342,10 +360,6 @@ public class ComponentPhysicsPerso extends Component { this.maxSpeed = maxSpeed; } - public void setStaticObject(boolean staticObject) { - this.staticObject = staticObject; - } - public void updateAABB() { if (this.position == null) { diff --git a/src/org/atriasoft/ege/components/ComponentPosition.java b/src/org/atriasoft/ege/components/ComponentPosition.java index eb9ce77..ad079f2 100644 --- a/src/org/atriasoft/ege/components/ComponentPosition.java +++ b/src/org/atriasoft/ege/components/ComponentPosition.java @@ -10,12 +10,14 @@ import org.atriasoft.etk.math.Vector3f; public class ComponentPosition extends Component implements PositionningInterface { public final Signal signalPosition = new Signal<>(); protected Transform3D transform; + //protected Vector3f speed; /** * Create a basic position component (no orientation and position (0,0,0)) */ public ComponentPosition() { this.transform = Transform3D.IDENTITY; + //this.speed = Vector3f.ZERO; } /** @@ -37,6 +39,10 @@ public class ComponentPosition extends Component implements PositionningInterfac this.transform = this.transform.withPosition(this.transform.getPosition().add(force)); } + //public Vector3f getSpeed() { + // return this.speed; + //} + /** * set a new transformation * @return Transformation of the position @@ -51,6 +57,10 @@ public class ComponentPosition extends Component implements PositionningInterfac return "position"; } + //public void setSpeed(Vector3f speed) { + // this.speed = speed; + //} + /** * set a new transformation * @param transform transformation of the position diff --git a/src/org/atriasoft/ege/components/PhysicBodyType.java b/src/org/atriasoft/ege/components/PhysicBodyType.java index c3ececd..9a02802 100644 --- a/src/org/atriasoft/ege/components/PhysicBodyType.java +++ b/src/org/atriasoft/ege/components/PhysicBodyType.java @@ -1,7 +1,7 @@ package org.atriasoft.ege.components; public enum PhysicBodyType { - BODY_DYNAMIC, - BODY_STATIC, - BODY_KINEMATIC, + BODY_DYNAMIC, // mobile object that move and interact with other (42) + BODY_STATIC, // static object that does not move (map, wall) + BODY_KINEMATIC // static object that can change position by the user (like doors, lift ...) } diff --git a/src/org/atriasoft/ege/engines/EngineAI.java b/src/org/atriasoft/ege/engines/EngineAI.java index 8749436..6f9c936 100644 --- a/src/org/atriasoft/ege/engines/EngineAI.java +++ b/src/org/atriasoft/ege/engines/EngineAI.java @@ -1,9 +1,9 @@ + package org.atriasoft.ege.engines; import java.util.ArrayList; import java.util.List; -import org.atriasoft.ege.internal.Log; import org.atriasoft.ege.Component; import org.atriasoft.ege.Engine; import org.atriasoft.ege.Environement; @@ -12,58 +12,59 @@ import org.atriasoft.ege.components.ComponentAI; public class EngineAI extends Engine { public static final String ENGINE_NAME = "ia"; - private float accumulator = 0; private static final float TIME_STEP = 5.0f; - private final List components = new ArrayList(); + private float accumulator = 0; + private final List components = new ArrayList<>(); + public EngineAI(Environement env) { super(env); // TODO Auto-generated constructor stub } - - @Override - public void componentRemove(Component ref) { - components.remove(ref); - } - + @Override public void componentAdd(Component ref) { if (!(ref instanceof ComponentAI)) { return; } - components.add((ComponentAI)ref); + this.components.add((ComponentAI) ref); } - + @Override - public void update(long deltaMili) { - // Add the time difference in the accumulator - accumulator += (float)deltaMili*0.0001f; - // While there is enough accumulated time to take one or several physics steps - while (accumulator >= TIME_STEP) { - //Log.warning("AI: Generate for " + accumulator + " / " + TIME_STEP + " for:" + components.size()); - // call every object to usdate their constant forces applyed - for (ComponentAI it: components) { - it.update(TIME_STEP); - } - // Decrease the accumulated time - accumulator -= TIME_STEP; - } - + public void componentRemove(Component ref) { + this.components.remove(ref); } - - @Override - public void render(long deltaMili, Camera camera) { - // nothing to do ... - } - - @Override - public void renderDebug(long deltaMili, Camera camera) { - // nothing to do ... - } - + @Override public String getType() { // TODO Auto-generated method stub return ENGINE_NAME; } - + + @Override + public void render(long deltaMili, Camera camera) { + // nothing to do ... + } + + @Override + public void renderDebug(long deltaMili, Camera camera) { + // nothing to do ... + } + + @Override + public void update(long deltaMili) { + // Add the time difference in the accumulator + this.accumulator += deltaMili * 0.0001f; + // While there is enough accumulated time to take one or several physics steps + while (this.accumulator >= TIME_STEP) { + //Log.warning("AI: Generate for " + accumulator + " / " + TIME_STEP + " for:" + components.size()); + // call every object to usdate their constant forces applyed + for (ComponentAI it : this.components) { + it.update(TIME_STEP); + } + // Decrease the accumulated time + this.accumulator -= TIME_STEP; + } + + } + } diff --git a/src/org/atriasoft/ege/engines/EnginePhysicsPerso.java b/src/org/atriasoft/ege/engines/EnginePhysicsPerso.java index 974c241..8c3d26d 100644 --- a/src/org/atriasoft/ege/engines/EnginePhysicsPerso.java +++ b/src/org/atriasoft/ege/engines/EnginePhysicsPerso.java @@ -7,6 +7,7 @@ import org.atriasoft.ege.Engine; import org.atriasoft.ege.Environement; import org.atriasoft.ege.camera.Camera; import org.atriasoft.ege.components.ComponentPhysicsPerso; +import org.atriasoft.ege.components.PhysicBodyType; import org.atriasoft.ege.internal.Log; import org.atriasoft.gale.resource.ResourceColored3DObject; import org.atriasoft.phyligram.DebugDisplay; @@ -149,6 +150,9 @@ public class EnginePhysicsPerso extends Engine { AABB currentAABB = current.getAABB(); for (int jjj = iii + 1; jjj < this.components.size(); jjj++) { ComponentPhysicsPerso remote = this.components.get(jjj); + if (current.getBodyType() != PhysicBodyType.BODY_DYNAMIC && remote.getBodyType() != PhysicBodyType.BODY_DYNAMIC) { + continue; + } // prefer checking the collision, this a time-constant operation, check if collision already exist is a unpredictable time. if (currentAABB.intersect(this.components.get(jjj).getAABB()) == true) { current.addIntersection(remote); diff --git a/src/org/atriasoft/phyligram/ToolCollisionSphereWithSphere.java b/src/org/atriasoft/phyligram/ToolCollisionSphereWithSphere.java index 584c825..f67cea8 100644 --- a/src/org/atriasoft/phyligram/ToolCollisionSphereWithSphere.java +++ b/src/org/atriasoft/phyligram/ToolCollisionSphereWithSphere.java @@ -15,13 +15,16 @@ public class ToolCollisionSphereWithSphere { Vector3f impact = force.multiply(shapeReference.getSize()); force = force.multiply(distance); force = force.multiply(sphere1.getSize() + distance); - return new ColisionPoint(impact, force); + // set relative impact position: + //return new ColisionPoint(impact, force); + // set global position + return new ColisionPoint(shapeReference.narrowPhaseGlobalPos.add(impact), force); } // Note sphere 2 is the reference ... public static boolean testCollide(PhysicSphere sphere1, PhysicSphere shapeReference) { float distance1 = sphere1.narrowPhaseGlobalPos.distance2(shapeReference.narrowPhaseGlobalPos); - float distance2 = sphere1.getSize() * shapeReference.getSize(); + float distance2 = sphere1.getSize() + shapeReference.getSize(); distance2 = distance2 * distance2; return distance1 <= distance2; } diff --git a/src/org/atriasoft/phyligram/ToolCollisionSphereWithTriangle.java b/src/org/atriasoft/phyligram/ToolCollisionSphereWithTriangle.java index 87b34f1..86808fe 100644 --- a/src/org/atriasoft/phyligram/ToolCollisionSphereWithTriangle.java +++ b/src/org/atriasoft/phyligram/ToolCollisionSphereWithTriangle.java @@ -55,7 +55,7 @@ public class ToolCollisionSphereWithTriangle { if (distance < 0) { force = force.multiply(-(sphere1.getSize() + distance)); } else { - force = force.multiply(-(sphere1.getSize() + distance)); + force = force.multiply(sphere1.getSize() - distance); } return new ColisionPoint(impact, force); }