From 39ca2f76240c3fa151d3235cc4be21a6c3f260be Mon Sep 17 00:00:00 2001 From: Edouard DUPIN Date: Wed, 22 Dec 2021 00:39:18 +0100 Subject: [PATCH] [DEV] firc collision that is really good ==> bad implement but this is the first steop ?.... --- .classpath | 16 +- .project | 1 - .../CollisionTestApplication.java | 2 +- .../ege/loxelEngine/LoxelApplication.java | 2 +- .../LoxelApplicationPerso.java | 89 ++- src/module-info.java | 4 +- src/org/atriasoft/ege/Environement.java | 21 +- .../ege/components/ComponentPhysics.java | 65 +- .../ege/components/ComponentPhysicsPerso.java | 510 ++++++++-------- .../ege/components/ComponentPosition.java | 7 +- .../atriasoft/ege/engines/EnginePhysics.java | 9 +- .../ege/engines/EnginePhysicsPerso.java | 231 ++++---- .../atriasoft/ege/geometry/Geometry3D.java | 2 +- .../geometry/{Plane.java => Plane____.java} | 6 +- src/org/atriasoft/ege/geometry/Triangle.java | 30 +- .../ege/{samples => }/todo_sample.md | 0 ...ColisionPoints.java => ColisionPoint.java} | 4 +- src/org/atriasoft/phyligram/Collision.java | 8 +- src/org/atriasoft/phyligram/DebugDisplay.java | 312 ++-------- src/org/atriasoft/phyligram/PhysicBox.java | 141 ++--- .../phyligram/PhysicCollisionAABB.java | 77 --- .../phyligram/PhysicHeightMapChunk.java | 63 ++ .../atriasoft/phyligram/PhysicMapVoxel.java | 39 +- src/org/atriasoft/phyligram/PhysicShape.java | 98 ++- .../atriasoft/phyligram/PhysicShapeType.java | 13 - src/org/atriasoft/phyligram/PhysicSphere.java | 45 +- .../atriasoft/phyligram/PhysicTriangle.java | 61 ++ .../phyligram/ToolCollisionOBBWithOBB.java | 24 +- ...ToolCollisionSphereWithHeightMapChunk.java | 63 ++ .../ToolCollisionSphereWithSphere.java | 30 + .../ToolCollisionSphereWithTriangle.java | 155 +++++ src/org/atriasoft/phyligram/math/Ray.java | 53 ++ src/org/atriasoft/phyligram/shape/AABB.java | 228 +++++++ .../{ege/physics => phyligram}/shape/Box.java | 2 +- .../physics => phyligram}/shape/Capsule.java | 2 +- .../physics => phyligram}/shape/Concave.java | 2 +- .../physics => phyligram}/shape/Cone.java | 2 +- .../shape/ConvexHull.java | 2 +- .../physics => phyligram}/shape/Cylinder.java | 2 +- .../phyligram/shape/HeightMapChunk.java | 16 + .../physics => phyligram}/shape/Shape.java | 30 +- .../physics => phyligram}/shape/Sphere.java | 2 +- .../phyligram/tree/CallbackOverlapping.java | 5 + .../phyligram/tree/CallbackRaycast.java | 7 + src/org/atriasoft/phyligram/tree/DTree.java | 28 + .../phyligram/tree/DTreeLeafData.java | 15 + .../phyligram/tree/DTreeLeafInt.java | 16 + .../atriasoft/phyligram/tree/DTreeNode.java | 6 + .../phyligram/tree/DynamicAABBTree.java | 560 ++++++++++++++++++ .../atriasoft/phyligram/tree/PairDTree.java | 37 ++ .../atriasoft/ege/TestTransformation3D.java | 4 +- .../TestCollisionSphereTriangle.java | 77 +++ 52 files changed, 2206 insertions(+), 1018 deletions(-) rename src/org/atriasoft/ege/geometry/{Plane.java => Plane____.java} (78%) rename src/org/atriasoft/ege/{samples => }/todo_sample.md (100%) rename src/org/atriasoft/phyligram/{ColisionPoints.java => ColisionPoint.java} (66%) delete mode 100644 src/org/atriasoft/phyligram/PhysicCollisionAABB.java create mode 100644 src/org/atriasoft/phyligram/PhysicHeightMapChunk.java delete mode 100644 src/org/atriasoft/phyligram/PhysicShapeType.java create mode 100644 src/org/atriasoft/phyligram/PhysicTriangle.java create mode 100644 src/org/atriasoft/phyligram/ToolCollisionSphereWithHeightMapChunk.java create mode 100644 src/org/atriasoft/phyligram/ToolCollisionSphereWithSphere.java create mode 100644 src/org/atriasoft/phyligram/ToolCollisionSphereWithTriangle.java create mode 100644 src/org/atriasoft/phyligram/math/Ray.java create mode 100644 src/org/atriasoft/phyligram/shape/AABB.java rename src/org/atriasoft/{ege/physics => phyligram}/shape/Box.java (94%) rename src/org/atriasoft/{ege/physics => phyligram}/shape/Capsule.java (95%) rename src/org/atriasoft/{ege/physics => phyligram}/shape/Concave.java (96%) rename src/org/atriasoft/{ege/physics => phyligram}/shape/Cone.java (95%) rename src/org/atriasoft/{ege/physics => phyligram}/shape/ConvexHull.java (97%) rename src/org/atriasoft/{ege/physics => phyligram}/shape/Cylinder.java (94%) create mode 100644 src/org/atriasoft/phyligram/shape/HeightMapChunk.java rename src/org/atriasoft/{ege/physics => phyligram}/shape/Shape.java (84%) rename src/org/atriasoft/{ege/physics => phyligram}/shape/Sphere.java (94%) create mode 100644 src/org/atriasoft/phyligram/tree/CallbackOverlapping.java create mode 100644 src/org/atriasoft/phyligram/tree/CallbackRaycast.java create mode 100644 src/org/atriasoft/phyligram/tree/DTree.java create mode 100644 src/org/atriasoft/phyligram/tree/DTreeLeafData.java create mode 100644 src/org/atriasoft/phyligram/tree/DTreeLeafInt.java create mode 100644 src/org/atriasoft/phyligram/tree/DTreeNode.java create mode 100644 src/org/atriasoft/phyligram/tree/DynamicAABBTree.java create mode 100644 src/org/atriasoft/phyligram/tree/PairDTree.java create mode 100644 test/src/test/atriasoft/phyligram/TestCollisionSphereTriangle.java diff --git a/.classpath b/.classpath index 27d3de9..49e9f61 100644 --- a/.classpath +++ b/.classpath @@ -12,16 +12,6 @@ - - - - - - - - - - @@ -37,5 +27,11 @@ + + + + + + diff --git a/.project b/.project index 1902c26..045956e 100644 --- a/.project +++ b/.project @@ -19,6 +19,5 @@ org.eclipse.jdt.core.javanature - org.python.pydev.pythonNature diff --git a/samples/src/sample/atriasoft/ege/collisiontest/CollisionTestApplication.java b/samples/src/sample/atriasoft/ege/collisiontest/CollisionTestApplication.java index 06911e3..dd9b287 100644 --- a/samples/src/sample/atriasoft/ege/collisiontest/CollisionTestApplication.java +++ b/samples/src/sample/atriasoft/ege/collisiontest/CollisionTestApplication.java @@ -27,7 +27,6 @@ import org.atriasoft.ege.components.PhysicBodyType; import org.atriasoft.ege.engines.EngineLight; import org.atriasoft.ege.engines.EnginePhysics; import org.atriasoft.ege.map.MapVoxel; -import org.atriasoft.ege.physics.shape.Box; import org.atriasoft.ege.tools.MeshGenerator; import org.atriasoft.etk.Color; import org.atriasoft.etk.Uri; @@ -46,6 +45,7 @@ import org.atriasoft.gale.key.KeySpecial; import org.atriasoft.gale.key.KeyStatus; import org.atriasoft.gale.key.KeyType; import org.atriasoft.gale.resource.ResourceColored3DObject; +import org.atriasoft.phyligram.shape.Box; public class CollisionTestApplication extends GaleApplication { public static Vector3f box1HalfSize; diff --git a/samples/src/sample/atriasoft/ege/loxelEngine/LoxelApplication.java b/samples/src/sample/atriasoft/ege/loxelEngine/LoxelApplication.java index a0a0ccc..4d9a544 100644 --- a/samples/src/sample/atriasoft/ege/loxelEngine/LoxelApplication.java +++ b/samples/src/sample/atriasoft/ege/loxelEngine/LoxelApplication.java @@ -28,7 +28,6 @@ import org.atriasoft.ege.components.PhysicBodyType; import org.atriasoft.ege.engines.EngineLight; import org.atriasoft.ege.engines.EnginePhysics; import org.atriasoft.ege.map.MapVoxel; -import org.atriasoft.ege.physics.shape.Box; import org.atriasoft.ege.tools.MeshGenerator; import org.atriasoft.etk.Color; import org.atriasoft.etk.Uri; @@ -47,6 +46,7 @@ import org.atriasoft.gale.key.KeySpecial; import org.atriasoft.gale.key.KeyStatus; import org.atriasoft.gale.key.KeyType; import org.atriasoft.gale.resource.ResourceColored3DObject; +import org.atriasoft.phyligram.shape.Box; public class LoxelApplication extends GaleApplication { public static Vector3f box1HalfSize; diff --git a/samples/src/sample/atriasoft/ege/loxelEnginePerso/LoxelApplicationPerso.java b/samples/src/sample/atriasoft/ege/loxelEnginePerso/LoxelApplicationPerso.java index 15acab3..c7019c2 100644 --- a/samples/src/sample/atriasoft/ege/loxelEnginePerso/LoxelApplicationPerso.java +++ b/samples/src/sample/atriasoft/ege/loxelEnginePerso/LoxelApplicationPerso.java @@ -4,7 +4,6 @@ import java.util.ArrayList; import java.util.List; import org.atriasoft.ege.ControlCameraPlayer; -import org.atriasoft.ege.Engine; import org.atriasoft.ege.Entity; import org.atriasoft.ege.Environement; import org.atriasoft.ege.GameStatus; @@ -26,9 +25,7 @@ import org.atriasoft.ege.components.ComponentStaticMesh; import org.atriasoft.ege.components.ComponentTexture; import org.atriasoft.ege.components.PhysicBodyType; import org.atriasoft.ege.engines.EngineLight; -import org.atriasoft.ege.engines.EnginePhysics; import org.atriasoft.ege.map.MapVoxel; -import org.atriasoft.phyligram.PhysicBox; import org.atriasoft.ege.tools.MeshGenerator; import org.atriasoft.etk.Color; import org.atriasoft.etk.Uri; @@ -47,6 +44,9 @@ import org.atriasoft.gale.key.KeySpecial; import org.atriasoft.gale.key.KeyStatus; import org.atriasoft.gale.key.KeyType; import org.atriasoft.gale.resource.ResourceColored3DObject; +import org.atriasoft.phyligram.PhysicBox; +import org.atriasoft.phyligram.PhysicSphere; +import org.atriasoft.phyligram.PhysicTriangle; public class LoxelApplicationPerso extends GaleApplication { public static Vector3f box1HalfSize; @@ -58,6 +58,7 @@ public class LoxelApplicationPerso extends GaleApplication { public static List testPointsCollide = new ArrayList<>(); public static Quaternion testQTransfert; public static Vector3f testRpos; + public boolean disable = false; private float angleLight = 0; private Quaternion basicRotation = Quaternion.IDENTITY; private Quaternion basicRotation2 = Quaternion.IDENTITY; @@ -107,7 +108,7 @@ public class LoxelApplicationPerso extends GaleApplication { // new Uri("DATA", "basic.vert"), // new Uri("DATA", "basic.frag"))); // env.addEntity(localLight); - { + 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"))); @@ -117,40 +118,40 @@ public class LoxelApplicationPerso extends GaleApplication { localBox.addComponent(new ComponentPosition(new Transform3D(new Vector3f(0, 0, 5)))); final ComponentPhysicsPerso physics2 = new ComponentPhysicsPerso(this.env); final PhysicBox box2 = new PhysicBox(); - box2.setSize(new Vector3f(0.5f, 0.5f, 0.5f)); + box2.setSize(new Vector3f(1.001f, 1.001f, 1.001f)); box2.setOrigin(new Vector3f(0, 0, 0)); box2.setMass(1); physics2.addShape(box2); localBox.addComponent(physics2); this.env.addEntity(localBox); } - { + 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 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, 12.5f)))); + localBox.addComponent(new ComponentPosition(new Transform3D(new Vector3f(0, 4, 2.5f)))); final ComponentPhysicsPerso physics2 = new ComponentPhysicsPerso(this.env); final PhysicBox box2 = new PhysicBox(); - box2.setSize(new Vector3f(0.5f, 0.5f, 0.5f)); + box2.setSize(new Vector3f(2.0f, 2.0f, 2.0f)); box2.setOrigin(new Vector3f(0, 0, 0)); box2.setMass(1); physics2.addShape(box2); localBox.addComponent(physics2); this.env.addEntity(localBox); } - { + 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 ComponentRenderTexturedStaticMesh(new Uri("DATA", "basic.vert", "loxelEngine"), new Uri("DATA", "basic.frag", "loxelEngine"))); - localBox.addComponent(new ComponentPosition(new Transform3D(new Vector3f(-2, 2, 14.5f)))); + localBox.addComponent(new ComponentPosition(new Transform3D(new Vector3f(-2, 2, 1.5f)))); final ComponentPhysicsPerso physics2 = new ComponentPhysicsPerso(this.env); final PhysicBox box2 = new PhysicBox(); - box2.setSize(new Vector3f(0.5f, 0.5f, 0.5f)); + box2.setSize(new Vector3f(3.0f, 3.0f, 3.0f)); box2.setOrigin(new Vector3f(0, 0, 0)); box2.setMass(1); physics2.addShape(box2); @@ -158,13 +159,13 @@ public class LoxelApplicationPerso extends GaleApplication { this.env.addEntity(localBox); } - { + 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 ComponentRenderTexturedStaticMesh(new Uri("DATA", "basic.vert", "loxelEngine"), new Uri("DATA", "basic.frag", "loxelEngine"))); - localBox.addComponent(new ComponentPosition(new Transform3D(new Vector3f(-5, -5, 14)))); + localBox.addComponent(new ComponentPosition(new Transform3D(new Vector3f(-5, -5, 0)))); final ComponentPhysicsPerso physics2 = new ComponentPhysicsPerso(this.env); final PhysicBox box2 = new PhysicBox(); box2.setSize(new Vector3f(2, 2, 2)); @@ -174,7 +175,7 @@ public class LoxelApplicationPerso extends GaleApplication { localBox.addComponent(physics2); this.env.addEntity(localBox); } - { + if (this.disable) { // add a cube to test collision ... final Entity localBox = new Entity(this.env); Quaternion orientation = new Quaternion(0.5f, 0.2f, 0.4f, 1); @@ -182,7 +183,7 @@ public class LoxelApplicationPerso extends GaleApplication { localBox.addComponent(new ComponentStaticMesh(new Uri("RES", "cube-one.obj"))); localBox.addComponent(new ComponentTexture(new Uri("DATA", "blocks/clay.png", "loxelEngine"))); localBox.addComponent(new ComponentRenderTexturedStaticMesh(new Uri("DATA", "basic.vert", "loxelEngine"), new Uri("DATA", "basic.frag", "loxelEngine"))); - localBox.addComponent(new ComponentPosition(new Transform3D(new Vector3f(15, 15, 14), orientation))); + localBox.addComponent(new ComponentPosition(new Transform3D(new Vector3f(15, 15, 0), orientation))); final ComponentPhysicsPerso physics2 = new ComponentPhysicsPerso(this.env); final PhysicBox box2 = new PhysicBox(); box2.setSize(new Vector3f(4, 4, 4)); @@ -192,7 +193,7 @@ public class LoxelApplicationPerso extends GaleApplication { localBox.addComponent(physics2); this.env.addEntity(localBox); } - { + if (this.disable) { // add a cube to test collision ... final Entity localBox = new Entity(this.env); final Quaternion orientation = new Quaternion(0.3f, 1.3f, 0.4f, 1); @@ -200,7 +201,7 @@ public class LoxelApplicationPerso extends GaleApplication { localBox.addComponent(new ComponentStaticMesh(new Uri("RES", "cube-one.obj"))); localBox.addComponent(new ComponentTexture(new Uri("DATA", "blocks/clay.png", "loxelEngine"))); localBox.addComponent(new ComponentRenderTexturedStaticMesh(new Uri("DATA", "basic.vert", "loxelEngine"), new Uri("DATA", "basic.frag", "loxelEngine"))); - localBox.addComponent(new ComponentPosition(new Transform3D(new Vector3f(2, -2, 14.2f), orientation))); + localBox.addComponent(new ComponentPosition(new Transform3D(new Vector3f(2, -2, 0.2f), orientation))); final ComponentPhysicsPerso physics2 = new ComponentPhysicsPerso(this.env); // TODO: physics2.setAngularReactionEnable(false); final PhysicBox box2 = new PhysicBox(); @@ -211,7 +212,7 @@ public class LoxelApplicationPerso extends GaleApplication { localBox.addComponent(physics2); this.env.addEntity(localBox); } - { + if (this.disable) { // this is the floor final Entity localBox = new Entity(this.env); Quaternion orientation = new Quaternion(0, 0, 0, 1); @@ -261,6 +262,41 @@ public class LoxelApplicationPerso extends GaleApplication { // 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(0, 4, 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)); + 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)))); + final ComponentPhysicsPerso physics2 = new ComponentPhysicsPerso(this.env); + final PhysicSphere box2 = new PhysicSphere(); + box2.setSize(1.0f); + box2.setOrigin(new Vector3f(0, 0, 0)); + box2.setMass(1); + physics2.addShape(box2); + localBox.addComponent(physics2); + this.env.addEntity(localBox); + } + final Entity gird = new Entity(this.env); gird.addComponent(new ComponentPosition(new Transform3D(new Vector3f(0, 0, 0)))); gird.addComponent(new ComponentStaticMesh(MeshGenerator.createGrid(5))); @@ -304,6 +340,14 @@ public class LoxelApplicationPerso extends GaleApplication { player.addComponent(new ComponentTexture(new Uri("RES", "playerTexture.png"))); player.addComponent(new ComponentRenderTexturedMaterialsStaticMesh(new Uri("DATA", "basicMaterial.vert", "loxelEngine"), new Uri("DATA", "basicMaterial.frag", "loxelEngine"), (EngineLight) this.env.getEngine(EngineLight.ENGINE_NAME))); + final ComponentPhysicsPerso physics = new ComponentPhysicsPerso(this.env); + physics.setBodyType(PhysicBodyType.BODY_DYNAMIC); + final PhysicBox box = new PhysicBox(); + box.setSize(new Vector3f(0.6f, 0.6f, 1.8f)); + box.setOrigin(new Vector3f(0, 0, 0.9f)); + box.setMass(0); + physics.addShape(box); + player.addComponent(physics); this.env.addEntity(player); } final Camera mainView = new Camera(); @@ -320,13 +364,6 @@ public class LoxelApplicationPerso extends GaleApplication { this.basicRotation = Quaternion.fromEulerAngles(new Vector3f(0.005f, 0.005f, 0.01f)); this.basicRotation2 = Quaternion.fromEulerAngles(new Vector3f(0.003f, 0.01f, 0.001f)); - final Engine tmpEngine = this.env.getEngine("physics"); - if (tmpEngine != null) { - final EnginePhysics physicsEngine = (EnginePhysics) tmpEngine; - //Disable gravity for test ... - physicsEngine.setGravity(new Vector3f(0.0f, 0.0f, -9.0f)); - } - // ready to let Gale & Ege manage the display Log.info("==> Init APPL (END)"); } diff --git a/src/module-info.java b/src/module-info.java index 341c3ed..5696f0f 100644 --- a/src/module-info.java +++ b/src/module-info.java @@ -9,9 +9,11 @@ open module org.atriasoft.ege { exports org.atriasoft.ege.engines; exports org.atriasoft.ege.geometry; exports org.atriasoft.ege.map; - exports org.atriasoft.ege.physics.shape; exports org.atriasoft.ege.tools; exports org.atriasoft.phyligram; + exports org.atriasoft.phyligram.shape; + exports org.atriasoft.phyligram.math; + exports org.atriasoft.phyligram.tree; exports entities; exports guis; exports models; diff --git a/src/org/atriasoft/ege/Environement.java b/src/org/atriasoft/ege/Environement.java index 5feee6f..187fdad 100644 --- a/src/org/atriasoft/ege/Environement.java +++ b/src/org/atriasoft/ege/Environement.java @@ -8,13 +8,11 @@ import java.util.List; import java.util.Map; import org.atriasoft.ege.camera.Camera; -import org.atriasoft.ege.components.ComponentPhysicsPerso; import org.atriasoft.ege.engines.EngineAI; import org.atriasoft.ege.engines.EngineDynamicMeshs; import org.atriasoft.ege.engines.EngineGravity; import org.atriasoft.ege.engines.EngineLight; import org.atriasoft.ege.engines.EngineParticle; -import org.atriasoft.ege.engines.EnginePhysics; import org.atriasoft.ege.engines.EnginePhysicsPerso; import org.atriasoft.ege.engines.EnginePlayer; import org.atriasoft.ege.engines.EngineRender; @@ -62,7 +60,7 @@ public class Environement { protected Map listCamera = new HashMap<>(); protected long gameTime = 0; // !< time of the game running - + long startTime = 0; Clock startClock = null; @@ -73,11 +71,11 @@ public class Environement { addEngine(new EngineAI(this)); addEngine(new EngineDynamicMeshs(this)); addEngine(new EngineRender(this)); - addEngine(new EnginePhysics(this)); + //addEngine(new EnginePhysics(this)); addEngine(new EnginePhysicsPerso(this)); addEngine(new EngineParticle(this)); addEngine(new EngineLight(this)); - startClock = Clock.systemUTC(); + this.startClock = Clock.systemUTC(); } /** @@ -221,9 +219,9 @@ public class Environement { // EGEVERBOSE(" Emit Signal"); // signalPlayTimeChange.emit(mgameTime*0.000001f); // } - // + // // //EWOLDEBUG("Time: mlastCallTime=" + mlastCallTime + " deltaTime=" + deltaTime); - // + // // // update camera positions: // for (auto it : mlistCamera) { // if (it.second != null) { @@ -239,7 +237,7 @@ public class Environement { // EGEVERBOSE(" update: " + it.getType()); // it.update(echrono::Duration(double(curentDelta))); // } - // + // // //EGE.debug("stepSimulation (start)"); // ///step the simulation // // TODO mphysicEngine.update(curentDelta); @@ -338,14 +336,15 @@ public class Environement { final long lastUpdate = this.lastCallTime; this.lastCallTime = System.nanoTime(); Clock currentClock = Clock.systemUTC(); - final EventTime event = new EventTime(currentClock, this.startClock, this.lastCallTime , this.startTime, Duration.ofNanos(this.lastCallTime - lastUpdate), Duration.ofNanos(this.lastCallTime - lastUpdate)); + 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) { elem.periodicCall(event); } for (final Engine engine : this.engines) { - engine.update((long)((this.lastCallTime - lastUpdate)/1000000)); + engine.update((this.lastCallTime - lastUpdate) / 1000000); } - }; + } public void removeControlInterface(final ControlInterface ref) { this.controls.remove(ref); diff --git a/src/org/atriasoft/ege/components/ComponentPhysics.java b/src/org/atriasoft/ege/components/ComponentPhysics.java index 716922f..bf3bdbe 100644 --- a/src/org/atriasoft/ege/components/ComponentPhysics.java +++ b/src/org/atriasoft/ege/components/ComponentPhysics.java @@ -10,20 +10,11 @@ import org.atriasoft.ege.camera.Camera; import org.atriasoft.ege.components.part.PositionningInterface; import org.atriasoft.ege.engines.EnginePhysics; import org.atriasoft.ege.internal.Log; -import org.atriasoft.ege.physics.shape.Box; -import org.atriasoft.ege.physics.shape.Capsule; -import org.atriasoft.ege.physics.shape.Concave; -import org.atriasoft.ege.physics.shape.Cone; -import org.atriasoft.ege.physics.shape.ConvexHull; -import org.atriasoft.ege.physics.shape.Cylinder; -import org.atriasoft.ege.physics.shape.Shape; -import org.atriasoft.ege.physics.shape.Sphere; import org.atriasoft.ephysics.body.BodyType; import org.atriasoft.ephysics.body.RigidBody; import org.atriasoft.ephysics.collision.ProxyShape; import org.atriasoft.ephysics.collision.TriangleMesh; import org.atriasoft.ephysics.collision.TriangleVertexArray; -import org.atriasoft.ephysics.collision.shapes.AABB; import org.atriasoft.ephysics.collision.shapes.BoxShape; import org.atriasoft.ephysics.collision.shapes.CapsuleShape; import org.atriasoft.ephysics.collision.shapes.CollisionShape; @@ -38,6 +29,14 @@ import org.atriasoft.etk.math.Quaternion; import org.atriasoft.etk.math.Transform3D; import org.atriasoft.etk.math.Vector3f; import org.atriasoft.gale.resource.ResourceColored3DObject; +import org.atriasoft.phyligram.shape.Box; +import org.atriasoft.phyligram.shape.Capsule; +import org.atriasoft.phyligram.shape.Concave; +import org.atriasoft.phyligram.shape.Cone; +import org.atriasoft.phyligram.shape.ConvexHull; +import org.atriasoft.phyligram.shape.Cylinder; +import org.atriasoft.phyligram.shape.Shape; +import org.atriasoft.phyligram.shape.Sphere; public class ComponentPhysics extends Component implements PositionningInterface { public Signal signalPosition = new Signal<>(); @@ -220,62 +219,56 @@ public class ComponentPhysics extends Component implements PositionningInterface public void drawShape(final ResourceColored3DObject _draw, final Camera _camera) { final Transform3D transform = getTransform(); //final float[] mmm = new float[16]; - // Get the OpenGL matrix array of the transform + // Get the OpenGL matrix array of the transform final Matrix4f mmm = transform.getOpenGLMatrix(); final Matrix4f transformationMatrix = mmm.transpose(); final Color tmpColor = new Color(1.0f, 0.0f, 0.0f, 0.3f); for (final Shape it : this.shape) { - if (it.isBox()) { + if (it instanceof Box tmpElement) { Log.debug(" Box"); - final Box tmpElement = (Box) it; final Transform3D transformLocal = new Transform3D(it.getOrigin(), it.getOrientation()); Matrix4f transformationMatrixLocal = transformLocal.getOpenGLMatrix(); transformationMatrixLocal = transformationMatrixLocal.transpose(); transformationMatrixLocal = transformationMatrix.multiply(transformationMatrixLocal); _draw.drawSquare(tmpElement.getSize(), transformationMatrixLocal, tmpColor); - } else if (it.isCylinder()) { + } else if (it instanceof Cylinder tmpElement) { Log.debug(" Cylinder"); - final Cylinder tmpElement = (Cylinder) it; final Transform3D transformLocal = new Transform3D(it.getOrigin(), it.getOrientation()); Matrix4f transformationMatrixLocal = transformLocal.getOpenGLMatrix(); transformationMatrixLocal = transformationMatrixLocal.transpose(); transformationMatrixLocal = transformationMatrix.multiply(transformationMatrixLocal); _draw.drawCylinder(tmpElement.getRadius(), tmpElement.getSize(), 10, 10, transformationMatrixLocal, tmpColor); - } else if (it.isCapsule()) { + } else if (it instanceof Capsule tmpElement) { Log.debug(" Capsule"); - final Capsule tmpElement = (Capsule) it; final Transform3D transformLocal = new Transform3D(it.getOrigin(), it.getOrientation()); Matrix4f transformationMatrixLocal = transformLocal.getOpenGLMatrix(); transformationMatrixLocal = transformationMatrixLocal.transpose(); transformationMatrixLocal = transformationMatrix.multiply(transformationMatrixLocal); _draw.drawCapsule(tmpElement.getRadius(), tmpElement.getSize(), 10, 10, transformationMatrixLocal, tmpColor); - } else if (it.isCone()) { + } else if (it instanceof Cone tmpElement) { Log.debug(" Cone"); - final Cone tmpElement = (Cone) it; final Transform3D transformLocal = new Transform3D(it.getOrigin(), it.getOrientation()); Matrix4f transformationMatrixLocal = transformLocal.getOpenGLMatrix(); transformationMatrixLocal = transformationMatrixLocal.transpose(); transformationMatrixLocal = transformationMatrix.multiply(transformationMatrixLocal); _draw.drawCone(tmpElement.getRadius(), tmpElement.getSize(), 10, 10, transformationMatrixLocal, tmpColor); - } else if (it.isSphere()) { + } else if (it instanceof Sphere tmpElement) { Log.debug(" Sphere"); - final Sphere tmpElement = (Sphere) it; final Transform3D transformLocal = new Transform3D(it.getOrigin(), it.getOrientation()); Matrix4f transformationMatrixLocal = transformLocal.getOpenGLMatrix(); transformationMatrixLocal = transformationMatrixLocal.transpose(); transformationMatrixLocal = transformationMatrix.multiply(transformationMatrixLocal); _draw.drawSphere(tmpElement.getRadius(), 10, 10, transformationMatrixLocal, tmpColor); - } else if (it.isConcave()) { + } else if (it instanceof Concave tmpElement) { Log.debug(" concave"); - final Concave tmpElement = (Concave) it; final Transform3D transformLocal = new Transform3D(it.getOrigin(), it.getOrientation()); Matrix4f transformationMatrixLocal = transformLocal.getOpenGLMatrix(); @@ -283,9 +276,8 @@ public class ComponentPhysics extends Component implements PositionningInterface transformationMatrixLocal = transformationMatrixLocal.multiply(transformationMatrixLocal); _draw.drawTriangles(tmpElement.getVertex(), tmpElement.getIndices(), transformationMatrixLocal, tmpColor); - } else if (it.isConvexHull()) { + } else if (it instanceof ConvexHull tmpElement) { Log.debug(" convexHull"); - final ConvexHull tmpElement = (ConvexHull) it; break; } } @@ -312,9 +304,8 @@ public class ComponentPhysics extends Component implements PositionningInterface if (it == null) { continue; } - if (it.isBox()) { + if (it instanceof Box tmpElement) { Log.debug(" Box"); - final Box tmpElement = (Box) it; // Half extents of the box in the x, y and z directions final Vector3f halfExtents = new Vector3f(tmpElement.getSize().x(), tmpElement.getSize().y(), tmpElement.getSize().z()); // Create the box shape @@ -326,9 +317,8 @@ public class ComponentPhysics extends Component implements PositionningInterface final ProxyShape proxyShape = this.rigidBody.addCollisionShape(shape, transform, it.getMass()); proxyShape.setUserData(this); this.listProxyShape.add(proxyShape); - } else if (it.isCylinder()) { + } else if (it instanceof Cylinder tmpElement) { Log.debug(" Cylinder"); - final Cylinder tmpElement = (Cylinder) it; // Create the Cylinder shape // Create the Cylinder shape final CylinderShape shape = new CylinderShape(tmpElement.getRadius(), tmpElement.getSize()); @@ -338,9 +328,8 @@ public class ComponentPhysics extends Component implements PositionningInterface final ProxyShape proxyShape = this.rigidBody.addCollisionShape(shape, transform, it.getMass()); proxyShape.setUserData(this); this.listProxyShape.add(proxyShape); - } else if (it.isCapsule()) { + } else if (it instanceof Capsule tmpElement) { Log.debug(" Capsule"); - final Capsule tmpElement = (Capsule) it; // Create the Capsule shape final CapsuleShape shape = new CapsuleShape(tmpElement.getRadius(), tmpElement.getSize()); // The ephysic use Y as UP ==> ege use Z as UP @@ -349,9 +338,8 @@ public class ComponentPhysics extends Component implements PositionningInterface final ProxyShape proxyShape = this.rigidBody.addCollisionShape(shape, transform, it.getMass()); proxyShape.setUserData(this); this.listProxyShape.add(proxyShape); - } else if (it.isCone()) { + } else if (it instanceof Cone tmpElement) { Log.debug(" Cone"); - final Cone tmpElement = (Cone) it; // Create the Cone shape final ConeShape shape = new ConeShape(tmpElement.getRadius(), tmpElement.getSize()); // The ephysic use Y as UP ==> ege use Z as UP @@ -360,9 +348,8 @@ public class ComponentPhysics extends Component implements PositionningInterface final ProxyShape proxyShape = this.rigidBody.addCollisionShape(shape, transform, it.getMass()); proxyShape.setUserData(this); this.listProxyShape.add(proxyShape); - } else if (it.isSphere()) { + } else if (it instanceof Sphere tmpElement) { Log.debug(" Sphere"); - final Sphere tmpElement = (Sphere) it; // Create the box shape final SphereShape shape = new SphereShape(tmpElement.getRadius()); // The ephysic use Y as UP ==> ege use Z as UP @@ -371,9 +358,8 @@ public class ComponentPhysics extends Component implements PositionningInterface final ProxyShape proxyShape = this.rigidBody.addCollisionShape(shape, transform, it.getMass()); proxyShape.setUserData(this); this.listProxyShape.add(proxyShape); - } else if (it.isConcave()) { + } else if (it instanceof Concave tmpElement) { Log.debug(" Concave"); - final Concave tmpElement = (Concave) it; //static etk::Vector vertices = {Vector3f(-100.0f,-100.0f,-50.0f),Vector3f(100.0f,-100.0f,-50.0f),Vector3f(100.0f,100.0f,-50.0f)}; //static etk::Vector indices = {0,1,2}; @@ -480,14 +466,13 @@ public class ComponentPhysics extends Component implements PositionningInterface } public void renderDebug(final ResourceColored3DObject _draw, final Camera _camera) { - if (this.rigidBody == null) { - return; - } + if (this.rigidBody == null) {} + /* final Matrix4f transformationMatrix = Matrix4f.IDENTITY; final Color tmpColor = new Color(0.0f, 1.0f, 0.0f, 0.8f); final AABB value = this.rigidBody.getAABB(); _draw.drawCubeLine(value.getMin(), value.getMax(), tmpColor, transformationMatrix, true, true); - + */ } public void setAngularReactionEnable(final boolean value) { diff --git a/src/org/atriasoft/ege/components/ComponentPhysicsPerso.java b/src/org/atriasoft/ege/components/ComponentPhysicsPerso.java index 4df62e6..1cb3045 100644 --- a/src/org/atriasoft/ege/components/ComponentPhysicsPerso.java +++ b/src/org/atriasoft/ege/components/ComponentPhysicsPerso.java @@ -3,50 +3,49 @@ package org.atriasoft.ege.components; import java.util.ArrayList; import java.util.List; +import org.atriasoft.ege.Component; +import org.atriasoft.ege.Environement; +import org.atriasoft.ege.engines.EngineGravity; +import org.atriasoft.ege.engines.EnginePhysicsPerso; +import org.atriasoft.ege.internal.Log; import org.atriasoft.etk.Color; import org.atriasoft.etk.math.Matrix4f; import org.atriasoft.etk.math.Vector3f; import org.atriasoft.gale.resource.ResourceColored3DObject; -import org.atriasoft.ege.internal.Log; -import org.atriasoft.ege.Component; -import org.atriasoft.ege.Environement; -import org.atriasoft.ege.engines.EngineGravity; -import org.atriasoft.ege.engines.EnginePhysics; -import org.atriasoft.ege.engines.EnginePhysicsPerso; +import org.atriasoft.phyligram.ColisionPoint; import org.atriasoft.phyligram.PhysicBox; -import org.atriasoft.phyligram.PhysicCollisionAABB; +import org.atriasoft.phyligram.PhysicHeightMapChunk; import org.atriasoft.phyligram.PhysicMapVoxel; import org.atriasoft.phyligram.PhysicShape; import org.atriasoft.phyligram.PhysicSphere; +import org.atriasoft.phyligram.PhysicTriangle; import org.atriasoft.phyligram.ToolCollisionOBBWithOBB; - +import org.atriasoft.phyligram.ToolCollisionSphereWithHeightMapChunk; +import org.atriasoft.phyligram.ToolCollisionSphereWithSphere; +import org.atriasoft.phyligram.ToolCollisionSphereWithTriangle; +import org.atriasoft.phyligram.shape.AABB; public class ComponentPhysicsPerso extends Component { - private PhysicCollisionAABB aabb; - private List aabbIntersection = new ArrayList(); - private List narrowIntersection = new ArrayList(); - private List shapes = new ArrayList(); + 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 List shapes = new ArrayList<>(); private ComponentPosition position; private boolean staticObject = false; - private boolean manageGravity = false; - public static float globalMaxSpeed = Float.MAX_VALUE; + private boolean manageGravity = true; private float maxSpeed = globalMaxSpeed; // current speed of the object - private Vector3f speed = new Vector3f(0,0,0); + private Vector3f speed = new Vector3f(0, 0, 0); // current acceleration of the object - private Vector3f acceleration = new Vector3f(0,0,0); + private Vector3f acceleration = new Vector3f(0, 0, 0); // Applied static force on it - private Vector3f staticForce = new Vector3f(0,0,0); + private Vector3f staticForce = new Vector3f(0, 0, 0); // Apply dynamic force on it - private Vector3f dynamicForce = new Vector3f(0,0,0); + private Vector3f dynamicForce = new Vector3f(0, 0, 0); private EnginePhysicsPerso engine; - private PhysicBodyType bodyType; - - - @Override - public String getType() { - return EnginePhysicsPerso.ENGINE_NAME; - } + private PhysicBodyType bodyType; public ComponentPhysicsPerso(final Environement _env) { this.engine = (EnginePhysicsPerso) _env.getEngine(getType()); @@ -56,123 +55,114 @@ public class ComponentPhysicsPerso extends Component { public void addFriendComponent(Component component) { if (component.getType().contentEquals("position")) { if (component instanceof ComponentPosition tmp) { - position = tmp; + this.position = tmp; } else { Log.error("Not manage position model..."); } } } - @Override - public void removeFriendComponent(Component component) { - // nothing to do. + + public void addIntersection(ComponentPhysicsPerso component) { + // do not add multiple times + if (this.aabbIntersection.contains(component)) { + return; + } + this.aabbIntersection.add(component); } - public void updateAABB() { - if (position == null) { - Log.info("No position in Entity "); - return; - } - // TODO Add a flag to check if it is needed to update the AABB... - PhysicCollisionAABB aabbNew = PhysicCollisionAABB.beforeCalculated(); - for (PhysicShape shape : shapes) { - shape.updateAABB(position.getTransform(), aabbNew); - } - aabb = aabbNew; + public void addShape(PhysicShape shape) { + this.shapes.add(shape); } - public PhysicCollisionAABB getAABB() { - return aabb; + public void applyColisionForce() { + for (ColisionPoint impact : this.collisionPoints) { + this.position.applyForce(impact.force); + } + } - public void updateForNarrowCollision() { - narrowIntersection.clear(); - if (aabbIntersection.size() == 0) { - return; + public void applyForces(float timeStep, EngineGravity gravity) { + // get the gravity at the specific position... + Vector3f gravityForce; + if (this.manageGravity) { + gravityForce = gravity.getGravityAtPosition(this.position.getTransform().getPosition()).multiply(timeStep); + } else { + gravityForce = new Vector3f(0, 0, 0); } - if (position == null) { - Log.info("No position in Entity "); - return; - } - for (PhysicShape shape : shapes) { - shape.updateForNarrowCollision(position.getTransform()); + // apply this force on the Object + Log.info("apply gravity: " + gravityForce); + // relative to the object + Vector3f staticForce = this.staticForce.multiply(timeStep); + 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); + 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)))); } - public boolean isNarrowCollide() { - if (narrowIntersection.size() == 0) { - return false; - } - return true; - } - public boolean checkNarrowCollision() { - if (this.staticObject == true) { - return false; - } - for (ComponentPhysicsPerso elem : aabbIntersection) { - boolean collide = false; - for (PhysicShape shapeCurrent : shapes) { - if (elem.checkCollide(shapeCurrent) == true) { - collide = true; - break; - } - } - if (collide == true) { - narrowIntersection.add(elem); - elem.narrowIntersection.add(this); - } - } - return isNarrowCollide(); - } - public void narrowCollisionCreateContactAndForce() { - if (narrowIntersection.size() == 0) { - return; - } - for (ComponentPhysicsPerso elem : narrowIntersection) { - for (PhysicShape shapeCurrent : this.shapes) { - //TODO Do a better method we do this many times ... - if (elem.checkCollide(shapeCurrent) == false) { - continue; - } - elem.getCollidePoints(shapeCurrent, this.staticObject); - } - } - } - + private boolean checkCollide(PhysicShape shapeCurrent) { - if (shapeCurrent instanceof PhysicBox) { - PhysicBox shape111 = (PhysicBox)shapeCurrent; - for (PhysicShape shape : shapes) { - if (shape instanceof PhysicBox) { - PhysicBox shape222 = (PhysicBox)shape; - if (ToolCollisionOBBWithOBB.testCollide(shape111, shape222) == true) { + if (shapeCurrent instanceof PhysicBox shape111) { + for (PhysicShape shape : this.shapes) { + if (shape instanceof PhysicHeightMapChunk shape222) { + // detect collision from cube on height-map !!! + + } else if (shape instanceof PhysicBox shape222) { + // detect collision between 2 cubes + if (ToolCollisionOBBWithOBB.testCollide(shape111, shape222)) { return true; } - } else if (shape instanceof PhysicSphere) { + } else if (shape instanceof PhysicSphere shape222) { - } else if (shape instanceof PhysicMapVoxel) { + } else if (shape instanceof PhysicMapVoxel shape222) { } else { Log.error("Not manage collision model... " + shape); } } - } else if (shapeCurrent instanceof PhysicSphere) { - for (PhysicShape shape : shapes) { - if (shape instanceof PhysicBox) { + } else if (shapeCurrent instanceof PhysicSphere shape111) { + for (PhysicShape shape : this.shapes) { + if (shape instanceof PhysicHeightMapChunk shape222) { + // detect collision from sphere on height-map !!! + if (ToolCollisionSphereWithHeightMapChunk.testCollide(shape111, shape222)) { + return true; + } - } else if (shape instanceof PhysicSphere) { + } else if (shape instanceof PhysicTriangle shape222) { + // detect collision from sphere on height-map !!! + if (ToolCollisionSphereWithTriangle.testCollide(shape111, shape222)) { + return true; + } - } else if (shape instanceof PhysicMapVoxel) { + } else if (shape instanceof PhysicBox shape222) { + // detect collision from sphere on cube !!! + + } else if (shape instanceof PhysicSphere shape222) { + // detect collision from sphere on sphere !!! + if (ToolCollisionSphereWithSphere.testCollide(shape111, shape222)) { + return true; + } + + } else if (shape instanceof PhysicMapVoxel shape222) { } else { Log.error("Not manage collision model... " + shape); } } - } else if (shapeCurrent instanceof PhysicMapVoxel) { - for (PhysicShape shape : shapes) { - if (shape instanceof PhysicBox) { + } else if (shapeCurrent instanceof PhysicMapVoxel shape111) { + for (PhysicShape shape : this.shapes) { + if (shape instanceof PhysicBox shape222) { - } else if (shape instanceof PhysicSphere) { + } else if (shape instanceof PhysicSphere shape222) { - } else if (shape instanceof PhysicMapVoxel) { + } else if (shape instanceof PhysicMapVoxel shape222) { } else { Log.error("Not manage collision model... " + shape); @@ -183,152 +173,204 @@ public class ComponentPhysicsPerso extends Component { } return false; } - private void getCollidePoints(PhysicShape shapeCurrent, boolean isStatic) { - if (shapeCurrent instanceof PhysicBox) { - PhysicBox shape111 = (PhysicBox)shapeCurrent; - for (PhysicShape shape : this.shapes) { - if (shape instanceof PhysicBox) { - PhysicBox shape222 = (PhysicBox)shape; - ToolCollisionOBBWithOBB.getCollidePoints(shape111, isStatic, shape222, this.staticObject); - } else if (shape instanceof PhysicSphere) { - - } else if (shape instanceof PhysicMapVoxel) { - - } else { - Log.error("Not manage collision model... " + shape); - } - } - } else if (shapeCurrent instanceof PhysicSphere) { - for (PhysicShape shape : this.shapes) { - if (shape instanceof PhysicBox) { - - } else if (shape instanceof PhysicSphere) { - - } else if (shape instanceof PhysicMapVoxel) { - - } else { - Log.error("Not manage collision model... " + shape); - } - } - } else if (shapeCurrent instanceof PhysicMapVoxel) { - for (PhysicShape shape : this.shapes) { - if (shape instanceof PhysicBox) { - - } else if (shape instanceof PhysicSphere) { - - } else if (shape instanceof PhysicMapVoxel) { - - } else { - Log.error("Not manage collision model... " + shape); - } - } - } else { - Log.error("Not manage collision model... " + shapeCurrent); + + public boolean checkNarrowCollision() { + if (this.staticObject) { + return false; } - return; + for (ComponentPhysicsPerso elem : this.aabbIntersection) { + boolean collide = false; + for (PhysicShape shapeCurrent : this.shapes) { + if (elem.checkCollide(shapeCurrent)) { + collide = true; + break; + } + } + if (collide) { + this.narrowIntersection.add(elem); + elem.narrowIntersection.add(this); + } + } + return isNarrowCollide(); } - - public void applyForces(float timeStep, EngineGravity gravity) { - // get the gravity at the specific position... - Vector3f gravityForce; - if (manageGravity == true) { - gravityForce = gravity.getGravityAtPosition(position.getTransform().getPosition()).multiply(timeStep); + + public void clearAABBIntersection() { + this.aabbIntersection.clear(); + } + + public void clearPreviousCollision() { + this.collisionPoints.clear(); + this.aabbIntersection.clear(); + this.narrowIntersection.clear(); + } + + public void clearShape() { + this.shapes.clear(); + } + + public AABB getAABB() { + return this.aabb; + } + + public List getAabbIntersection() { + return this.aabbIntersection; + } + + public PhysicBodyType getBodyType() { + return this.bodyType; + } + + private List getCollidePoints(PhysicShape shapeRemote) { + List out = new ArrayList<>(); + if (shapeRemote instanceof PhysicSphere remoteShere) { + for (PhysicShape shape : this.shapes) { + if (shape instanceof PhysicSphere localShape) { + ColisionPoint point = ToolCollisionSphereWithSphere.getCollisionPoint(remoteShere, localShape); + if (point != null) { + out.add(point); + } + } else if (shape instanceof PhysicTriangle localShape) { + ColisionPoint point = ToolCollisionSphereWithTriangle.getCollisionPoint(remoteShere, localShape); + if (point != null) { + out.add(point); + } + + } + } + } else if (shapeRemote instanceof PhysicTriangle) { + // nothing can happens ... } else { - gravityForce = new Vector3f(0,0,0); + Log.error("Not manage collision model... " + shapeRemote); } - // apply this force on the Object - Log.info("apply gravity: " + gravityForce); - // relative to the object - Vector3f staticForce = this.staticForce.multiply(timeStep); - float globalMass = 0; - for (PhysicShape shape : shapes) { - globalMass += shape.getMass(); + return out; + } + + public float getMaxSpeed() { + return this.maxSpeed; + } + + @Override + public String getType() { + return EnginePhysicsPerso.ENGINE_NAME; + } + + public boolean isManageGravity() { + return this.manageGravity; + } + + public boolean isNarrowCollide() { + if (this.narrowIntersection.size() == 0) { + return false; } - // 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(dynamicForce)).multiply(globalMass); - this.dynamicForce = new Vector3f(0,0,0); - this.speed.add(this.acceleration); - limitWithMaxSpeed(); - Log.info("apply acceleration: " + this.acceleration); - Log.info("apply speed: " + this.speed); - this.position.getTransform().getPosition().add(this.speed.multiply(timeStep)); + 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); + } + } + + public void narrowCollisionCreateContactAndForce() { + + if (this.staticObject) { + return; + } + if (this.narrowIntersection.size() == 0) { + return; + } + for (ComponentPhysicsPerso elem : this.narrowIntersection) { + for (PhysicShape shapeCurrent : this.shapes) { + //TODO Do a better method we do this many times ... + /* + if (!elem.checkCollide(shapeCurrent)) { + continue; + } + */ + List points = elem.getCollidePoints(shapeCurrent); + this.collisionPoints.addAll(points); + } + } + } + + @Override + public void removeFriendComponent(Component component) { + // nothing to do. } 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); + } if (this.aabbIntersection.size() == 0) { - displayColor = new Color(1,1,1,1); + displayColor = new Color(1.0f, 1.0f, 1.0f, 1.0f); } else { if (this.narrowIntersection.size() == 0) { - displayColor = new Color(1,1,0,1); + displayColor = new Color(0.0f, 1.0f, 0.0f, 1.0f); } else { - displayColor = new Color(1,0,0,1); + displayColor = new Color(1.0f, 0.0f, 0.0f, 1.0f); } } - if (aabb != null) { - debugDrawProperty.drawCubeLine(aabb.getMin(), aabb.getMax(), displayColor, Matrix4f.IDENTITY, true, true); + if (this.aabb != null) { + debugDrawProperty.drawCubeLine(this.aabb.getMin(), this.aabb.getMax(), displayColor, Matrix4f.IDENTITY, true, true); //debugDrawProperty.drawCubeLine(new Vector3f(0,0,0), new Vector3f(32,32,32), new Color(1,0,1,1), Matrix4f.identity(), true, true); } else { Log.error("no AABB"); } - for (PhysicShape shape : shapes) { - shape.renderDebug(position.getTransform(), debugDrawProperty); + + for (PhysicShape shape : this.shapes) { + shape.renderDebug(this.position.getTransform(), debugDrawProperty); } + } - public void addShape(PhysicShape shape) { - shapes.add(shape); - } - public void clearShape() { - shapes.clear(); - } - public boolean isManageGravity() { - return manageGravity; - } - public void setManageGravity(boolean manageGravity) { - this.manageGravity = manageGravity; - } - private void limitWithMaxSpeed() { - if (this.speed.length2() > this.maxSpeed*this.maxSpeed) { - this.speed.safeNormalize().multiply(this.maxSpeed); - } - } - public float getMaxSpeed() { - return maxSpeed; - } - - public void setMaxSpeed(float maxSpeed) { - this.maxSpeed = maxSpeed; - } - - public void clearAABBIntersection() { - this.aabbIntersection.clear(); - } - public void addIntersection(ComponentPhysicsPerso component) { - // do not add multiple times - for (ComponentPhysicsPerso elem : this.aabbIntersection) { - if (elem == component) { - return; - } - } - this.aabbIntersection.add(component); - } - public List getAabbIntersection() { - return aabbIntersection; - } - - public boolean isStaticObject() { - return staticObject; - } - - public void setStaticObject(boolean staticObject) { - this.staticObject = staticObject; - } - - public PhysicBodyType getBodyType() { - return bodyType; - } - + public void setBodyType(PhysicBodyType bodyType) { this.bodyType = bodyType; } + + public void setManageGravity(boolean manageGravity) { + this.manageGravity = manageGravity; + } + + public void setMaxSpeed(float maxSpeed) { + this.maxSpeed = maxSpeed; + } + + public void setStaticObject(boolean staticObject) { + this.staticObject = staticObject; + } + + public void updateAABB() { + + if (this.position == null) { + Log.info("No position in Entity "); + return; + } + // TODO Add a flag to check if it is needed to update the AABB... + AABB aabbNew = AABB.createInvertedEmpty(); + for (PhysicShape shape : this.shapes) { + shape.updateAABB(this.position.getTransform(), aabbNew); + } + this.aabb = aabbNew; + } + + public void updateForNarrowCollision() { + this.narrowIntersection.clear(); + if (this.aabbIntersection.size() == 0) { + return; + } + if (this.position == null) { + Log.info("No position in Entity "); + return; + } + for (PhysicShape shape : this.shapes) { + shape.updateForNarrowCollision(this.position.getTransform()); + } + } } diff --git a/src/org/atriasoft/ege/components/ComponentPosition.java b/src/org/atriasoft/ege/components/ComponentPosition.java index 2c9fa08..eb9ce77 100644 --- a/src/org/atriasoft/ege/components/ComponentPosition.java +++ b/src/org/atriasoft/ege/components/ComponentPosition.java @@ -5,9 +5,10 @@ import org.atriasoft.ege.Signal; import org.atriasoft.ege.components.part.PositionningInterface; import org.atriasoft.ege.internal.Log; import org.atriasoft.etk.math.Transform3D; +import org.atriasoft.etk.math.Vector3f; public class ComponentPosition extends Component implements PositionningInterface { - public final Signal signalPosition = new Signal(); + public final Signal signalPosition = new Signal<>(); protected Transform3D transform; /** @@ -32,6 +33,10 @@ public class ComponentPosition extends Component implements PositionningInterfac } } + public void applyForce(Vector3f force) { + this.transform = this.transform.withPosition(this.transform.getPosition().add(force)); + } + /** * set a new transformation * @return Transformation of the position diff --git a/src/org/atriasoft/ege/engines/EnginePhysics.java b/src/org/atriasoft/ege/engines/EnginePhysics.java index 9e0f5fc..a07b2b4 100644 --- a/src/org/atriasoft/ege/engines/EnginePhysics.java +++ b/src/org/atriasoft/ege/engines/EnginePhysics.java @@ -87,10 +87,9 @@ public class EnginePhysics extends Engine implements EventListener { @Override public void componentAdd(final Component ref) { - if (!(ref instanceof ComponentPhysics)) { + if (!(ref instanceof ComponentPhysics elem)) { return; } - final ComponentPhysics elem = (ComponentPhysics) ref; this.components.add(elem); elem.generate(); } @@ -180,10 +179,8 @@ public class EnginePhysics extends Engine implements EventListener { for (final ContactManifold it : listContact) { for (int iii = 0; iii < it.getNbContactPoints(); iii++) { final ContactPoint contact = it.getContactPoint(iii); - this.debugDrawProperty.drawSquare(new Vector3f(0.05f, 0.05f, 0.05f), Matrix4f.IDENTITY.multiply(Matrix4f.createMatrixTranslate(contact.getWorldPointOnBody1())), - new Color(0, 1, 0, 1)); - this.debugDrawProperty.drawSquare(new Vector3f(0.05f, 0.05f, 0.05f), Matrix4f.IDENTITY.multiply(Matrix4f.createMatrixTranslate(contact.getWorldPointOnBody2())), - new Color(0, 1, 0, 1)); + this.debugDrawProperty.drawSquare(new Vector3f(0.05f, 0.05f, 0.05f), Matrix4f.IDENTITY.multiply(Matrix4f.createMatrixTranslate(contact.getWorldPointOnBody1())), new Color(0, 1, 0, 1)); + this.debugDrawProperty.drawSquare(new Vector3f(0.05f, 0.05f, 0.05f), Matrix4f.IDENTITY.multiply(Matrix4f.createMatrixTranslate(contact.getWorldPointOnBody2())), new Color(0, 1, 0, 1)); } } diff --git a/src/org/atriasoft/ege/engines/EnginePhysicsPerso.java b/src/org/atriasoft/ege/engines/EnginePhysicsPerso.java index ceae58f..974c241 100644 --- a/src/org/atriasoft/ege/engines/EnginePhysicsPerso.java +++ b/src/org/atriasoft/ege/engines/EnginePhysicsPerso.java @@ -2,151 +2,184 @@ package org.atriasoft.ege.engines; import java.util.Vector; -import org.atriasoft.gale.resource.ResourceColored3DObject; -import org.atriasoft.ege.internal.Log; import org.atriasoft.ege.Component; 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.phyligram.PhysicCollisionAABB; +import org.atriasoft.ege.internal.Log; +import org.atriasoft.gale.resource.ResourceColored3DObject; +import org.atriasoft.phyligram.DebugDisplay; +import org.atriasoft.phyligram.shape.AABB; public class EnginePhysicsPerso extends Engine { public static final String ENGINE_NAME = "physicsPerso"; - private float accumulator = 0; private static final float TIME_STEP = 0.005f; + private float accumulator = 0; private EngineGravity gravity; protected EnginePhysicsPerso engine; - private Vector components = new Vector(); + private Vector components = new Vector<>(); + private Vector componentsWithCollision = new Vector<>(); private ResourceColored3DObject debugDrawProperty = ResourceColored3DObject.create(); public EnginePhysicsPerso(Environement env) { super(env); - this.gravity = (EngineGravity)env.getEngine("gravity"); + this.gravity = (EngineGravity) env.getEngine("gravity"); if (this.gravity == null) { Log.critical("Must initialyse Gravity before physics..."); } } - - @Override - public void componentRemove(Component ref) { - components.remove(ref); + + private void addIncomponentWithCollision(ComponentPhysicsPerso elem) { + if (this.componentsWithCollision.contains(elem)) { + return; + } + this.componentsWithCollision.add(elem); } - + + private void applyForces(float timeStep) { + for (ComponentPhysicsPerso it : this.components) { + it.applyForces(TIME_STEP, this.gravity); + } + } + + /** + * Clear the previous data of collision. + */ + private void clearPreviousCycle() { + for (ComponentPhysicsPerso it : this.components) { + it.clearPreviousCollision(); + } + } + @Override public void componentAdd(Component ref) { if (ref instanceof ComponentPhysicsPerso == false) { return; } - components.add((ComponentPhysicsPerso)ref); + this.components.add((ComponentPhysicsPerso) 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.info("update physic ... " + accumulator); - //applyForces(TIME_STEP); - updateAABB(TIME_STEP); - updateCollisionsAABB(TIME_STEP); - updateCollisionsNarrowPhase(TIME_STEP); - generateResultCollisionsForces(TIME_STEP); - // Decrease the accumulated time - accumulator -= TIME_STEP; - } - - } - - private void applyForces(float timeStep) { - for (ComponentPhysicsPerso it: components) { - it.applyForces(TIME_STEP, gravity); - } + public void componentRemove(Component ref) { + this.components.remove(ref); } + /** - * Collision detection STEP 1: Upadte the AABB positioning of each elements - * @param timeStep Delta time since the last check - */ - private void updateAABB(float timeStep) { - for (ComponentPhysicsPerso it: components) { - it.updateAABB(); - } - } - /** - * Collision Detection STEP 2: update the list of each element that collide together in the AABB Boxs (update is done between each boxes) - * @param timeStep Delta time since the last check - */ - private void updateCollisionsAABB(float timeStep) { - // clear all object intersection - for (ComponentPhysicsPerso it: components) { - it.clearAABBIntersection(); - } - // update the current object intersection... - for (int iii=0; iii< components.size(); iii++) { - ComponentPhysicsPerso current = components.get(iii); - PhysicCollisionAABB currentAABB = current.getAABB(); - for (int jjj=iii+1; jjj< components.size(); jjj++) { - ComponentPhysicsPerso remote = components.get(jjj); - if (currentAABB.intersect(components.get(jjj).getAABB()) == true) { - current.addIntersection(remote); - remote.addIntersection(current); - } - } - } - } - /** - * Collision Detection STEP 3: Narrow phase: process the collision between every OBB boxes (or other..) - * @param timeStep Delta time since the last check - */ - private void updateCollisionsNarrowPhase(float timeStep) { - // clear all object intersection - for (ComponentPhysicsPerso it: components) { - it.updateForNarrowCollision(); - } - // check for every component if the narrow collision is available. - for (int iii=0; iii< components.size(); iii++) { - ComponentPhysicsPerso current = components.get(iii); - boolean collide = current.checkNarrowCollision(); - - } - // update the force of collision available. - for (int iii=0; iii< components.size(); iii++) { - ComponentPhysicsPerso current = components.get(iii); - current.narrowCollisionCreateContactAndForce(); - } - } - /** - * Collision Detection STEP 4: apply all calculated forces (with containts) - * @param timeStep + * Collision Detection STEP 4: apply all calculated forces (with containts) + * @param timeStep */ private void generateResultCollisionsForces(float timeStep) { - + for (ComponentPhysicsPerso it : this.componentsWithCollision) { + it.applyColisionForce(); + } } - + + @Override + public String getType() { + // TODO Auto-generated method stub + return ENGINE_NAME; + } + @Override public void render(long deltaMili, Camera camera) { // TODO Auto-generated method stub - for (ComponentPhysicsPerso it: this.components) { + for (ComponentPhysicsPerso it : this.components) { //Log.info("Render " + it); - it.renderDebug(debugDrawProperty); + it.renderDebug(this.debugDrawProperty); } //debugDrawProperty.drawCone(2, 5, 9, 12, Matrix4f.identity(), new Color(1,1,0,1)); //debugDrawProperty.drawSquare(new Vector3f(1,1,1), Matrix4f.identity(), new Color(1,1,0,1)); //debugDrawProperty.drawCubeLine(new Vector3f(1,1,1), new Vector3f(5,5,5), new Color(1,0,1,1), Matrix4f.identity(), true, true); //debugDrawProperty.drawCubeLine(new Vector3f(0,0,0), new Vector3f(32,32,32), new Color(1,0,1,1), Matrix4f.identity(), true, true); } - + @Override public void renderDebug(long deltaMili, Camera camera) { // TODO Auto-generated method stub + DebugDisplay.onDraw(); + DebugDisplay.clear(); + } + + @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.info("update physic ... " + this.accumulator); + clearPreviousCycle(); + applyForces(TIME_STEP); + // update AABB after because in rotation force, the Bounding box change... + updateAABB(TIME_STEP); + // update the colision tree between each object in the room + updateCollisionsAABB(TIME_STEP); + updateCollisionsNarrowPhase(TIME_STEP); + generateResultCollisionsForces(TIME_STEP); + // Decrease the accumulated time + this.accumulator -= TIME_STEP; + } } - - @Override - public String getType() { - // TODO Auto-generated method stub - return ENGINE_NAME; + + /** + * Collision detection STEP 1: Upadte the AABB positioning of each elements + * @param timeStep Delta time since the last check + */ + private void updateAABB(float timeStep) { + for (ComponentPhysicsPerso it : this.components) { + it.updateAABB(); + } } - + + /** + * Collision Detection STEP 2: update the list of each element that collide together in the AABB Boxs (update is done between each boxes) + * @param timeStep Delta time since the last check + */ + // TODO : generate a B-TREE to manage collision, it is faster, but now, this is not the purpose ... + private void updateCollisionsAABB(float timeStep) { + this.componentsWithCollision.clear(); + // clear all object intersection + for (ComponentPhysicsPerso it : this.components) { + it.clearAABBIntersection(); + } + // update the current object intersection... + for (int iii = 0; iii < this.components.size(); iii++) { + ComponentPhysicsPerso current = this.components.get(iii); + AABB currentAABB = current.getAABB(); + for (int jjj = iii + 1; jjj < this.components.size(); jjj++) { + ComponentPhysicsPerso remote = this.components.get(jjj); + // 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); + remote.addIntersection(current); + addIncomponentWithCollision(remote); + addIncomponentWithCollision(current); + } + } + } + } + + /** + * Collision Detection STEP 3: Narrow phase: process the collision between every OBB boxes (or other..) + * @param timeStep Delta time since the last check + */ + private void updateCollisionsNarrowPhase(float timeStep) { + // clear all object intersection + for (ComponentPhysicsPerso it : this.componentsWithCollision) { + it.updateForNarrowCollision(); + } + // check for every component if the narrow collision is available. + for (int iii = 0; iii < this.componentsWithCollision.size(); iii++) { + ComponentPhysicsPerso current = this.componentsWithCollision.get(iii); + boolean collide = current.checkNarrowCollision(); + + } + // update the force of collision available. + for (int iii = 0; iii < this.components.size(); iii++) { + ComponentPhysicsPerso current = this.components.get(iii); + current.narrowCollisionCreateContactAndForce(); + } + } + } diff --git a/src/org/atriasoft/ege/geometry/Geometry3D.java b/src/org/atriasoft/ege/geometry/Geometry3D.java index ebe2e69..c20b6da 100644 --- a/src/org/atriasoft/ege/geometry/Geometry3D.java +++ b/src/org/atriasoft/ege/geometry/Geometry3D.java @@ -52,7 +52,7 @@ public class Geometry3D { return true; } - public static boolean pointInPlane(final Vector3f point, final Plane plane) { + public static boolean pointInPlane(final Vector3f point, final Plane____ plane) { // This should probably use an epsilon! //return Dot(point, plane.normal) - plane.distance == 0.0f; return CMP(point.dot(plane.normal) - plane.distance, 0.0f); diff --git a/src/org/atriasoft/ege/geometry/Plane.java b/src/org/atriasoft/ege/geometry/Plane____.java similarity index 78% rename from src/org/atriasoft/ege/geometry/Plane.java rename to src/org/atriasoft/ege/geometry/Plane____.java index 9ad0dcc..e295577 100644 --- a/src/org/atriasoft/ege/geometry/Plane.java +++ b/src/org/atriasoft/ege/geometry/Plane____.java @@ -2,15 +2,15 @@ package org.atriasoft.ege.geometry; import org.atriasoft.etk.math.Vector3f; -public class Plane { +public class Plane____ { public Vector3f normal; public float distance; - public Plane(Vector3f normal, float distance) { + public Plane____(Vector3f normal, float distance) { this.normal = normal; this.distance = distance; } - public Plane() { + public Plane____() { this.normal = new Vector3f(1.0f, 0.0f, 0.0f); this.distance = 0; } diff --git a/src/org/atriasoft/ege/geometry/Triangle.java b/src/org/atriasoft/ege/geometry/Triangle.java index df80fbf..ab68c4a 100644 --- a/src/org/atriasoft/ege/geometry/Triangle.java +++ b/src/org/atriasoft/ege/geometry/Triangle.java @@ -3,8 +3,19 @@ package org.atriasoft.ege.geometry; import org.atriasoft.etk.math.Vector3f; public class Triangle { + public static Vector3f getCenter(Vector3f p1, Vector3f p2, Vector3f p3) { + return p1.add(p2).add(p3).multiply(0.33333333333f); + } + + public static Vector3f getNormal(Vector3f p1, Vector3f p2, Vector3f p3) { + Vector3f dir = p2.less(p1).cross(p3.less(p1)); + return dir.normalize(); + } + public Vector3f p1; + public Vector3f p2; + public Vector3f p3; public Triangle() { @@ -19,9 +30,26 @@ public class Triangle { this.p3 = p3; } + @Override + public Triangle clone() { + return new Triangle(this.p1, this.p2, this.p3); + } + + public Vector3f getCenter() { + return this.p1.add(this.p2).add(this.p3).multiply(0.33333333333f); + } + + public Vector3f getNormal() { + Vector3f dir = this.p2.less(this.p1).cross(this.p3.less(this.p1)); + return dir.normalize(); + } + + public Plane getPlane() { + return new Plane(getNormal(), getCenter()); + } + @Override public String toString() { return "Triangle [p1=" + this.p1 + ", p2=" + this.p2 + ", p3=" + this.p3 + "]"; } - } diff --git a/src/org/atriasoft/ege/samples/todo_sample.md b/src/org/atriasoft/ege/todo_sample.md similarity index 100% rename from src/org/atriasoft/ege/samples/todo_sample.md rename to src/org/atriasoft/ege/todo_sample.md diff --git a/src/org/atriasoft/phyligram/ColisionPoints.java b/src/org/atriasoft/phyligram/ColisionPoint.java similarity index 66% rename from src/org/atriasoft/phyligram/ColisionPoints.java rename to src/org/atriasoft/phyligram/ColisionPoint.java index 7c97d06..7b0f091 100644 --- a/src/org/atriasoft/phyligram/ColisionPoints.java +++ b/src/org/atriasoft/phyligram/ColisionPoint.java @@ -2,11 +2,11 @@ package org.atriasoft.phyligram; import org.atriasoft.etk.math.Vector3f; -public class ColisionPoints { +public class ColisionPoint { public Vector3f position; public Vector3f force; - public ColisionPoints(Vector3f position, Vector3f force) { + public ColisionPoint(Vector3f position, Vector3f force) { this.position = position; this.force = force; } diff --git a/src/org/atriasoft/phyligram/Collision.java b/src/org/atriasoft/phyligram/Collision.java index 6248da5..b76d386 100644 --- a/src/org/atriasoft/phyligram/Collision.java +++ b/src/org/atriasoft/phyligram/Collision.java @@ -1,12 +1,12 @@ package org.atriasoft.phyligram; public class Collision { - public final ColisionPoints[] colisionPointLocal; + public final ColisionPoint[] colisionPointLocal; public final PhysicShape shapeRemote; - public final ColisionPoints[] colisionPointRemote; + public final ColisionPoint[] colisionPointRemote; public final boolean staticRemote; - public Collision(ColisionPoints[] colisionPointLocal, PhysicShape shapeRemote, - ColisionPoints[] colisionPointRemote, boolean staticRemote) { + public Collision(ColisionPoint[] colisionPointLocal, PhysicShape shapeRemote, + ColisionPoint[] colisionPointRemote, boolean staticRemote) { super(); this.colisionPointLocal = colisionPointLocal; this.shapeRemote = shapeRemote; diff --git a/src/org/atriasoft/phyligram/DebugDisplay.java b/src/org/atriasoft/phyligram/DebugDisplay.java index abda111..b55caf1 100644 --- a/src/org/atriasoft/phyligram/DebugDisplay.java +++ b/src/org/atriasoft/phyligram/DebugDisplay.java @@ -8,303 +8,62 @@ import org.atriasoft.etk.math.Matrix4f; import org.atriasoft.etk.math.Quaternion; import org.atriasoft.etk.math.Vector3f; import org.atriasoft.gale.backend3d.OpenGL; -import org.atriasoft.gale.context.GaleContext; import org.atriasoft.gale.resource.ResourceColored3DObject; public class DebugDisplay { -// public static ComponentPosition relativeTestPos; -// public static PhysicBox boxTest; - public static List testPoints = new ArrayList(); - public static List testPointsBox = new ArrayList(); - public static List testPointsCollide = new ArrayList(); + // public static ComponentPosition relativeTestPos; + // public static PhysicBox boxTest; + public static List testPoints = new ArrayList<>(); + public static List testPointsBox = new ArrayList<>(); + public static List testPointsCollide = new ArrayList<>(); public static Vector3f testRpos; public static Quaternion testQTransfert; public static Vector3f box1HalfSize; public static Vector3f box2HalfSize; - private ResourceColored3DObject debugDrawProperty; - public DebugDisplay(){ + private static ResourceColored3DObject debugDrawProperty; + + static { + if (debugDrawProperty == null) { + debugDrawProperty = ResourceColored3DObject.create(); + } } - /* - @Override - public void onCreate(Context context) { - // set the system global max speed - ComponentPhysics.globalMaxSpeed = 3; - Gale.getContext().grabPointerEvents(true, new Vector2f(0,0)); - env = new Environement(); - this.canDraw = true; - setSize(new Vector2f(1500, 1500)); - setTitle("Loxel sample"); - map = new MapVoxel(this.env); -// this.env.addEngine(map); -// map.init(); - - // simple sun to have a global light ... - Entity globalGravity = new Entity(this.env); - globalGravity.addComponent(new ComponentGravityStatic(new Vector3f(0,0,-1))); - env.addEntity(globalGravity); + + public static void clear() { + testPoints.clear(); + testPointsBox.clear(); + testPointsCollide.clear(); - // simple sun to have a global light ... - Entity sun = new Entity(this.env); - sun.addComponent(new ComponentPosition(new Transform3D(new Vector3f(1000,1000,1000)))); - sun.addComponent(new ComponentLightSun(new Light(new Vector3f(0.4f,0.4f,0.4f), new Vector3f(0,0,0), new Vector3f(0.8f,0,0)))); - env.addEntity(sun); - - // add a cube to show where in the light ... - Entity localLight = new Entity(this.env); - lightPosition = new ComponentPosition(new Transform3D(new Vector3f(-10,-10,17))); -// localLight.addComponent(lightPosition); -// localLight.addComponent(new ComponentStaticMesh(new Uri("RES", "cube.obj"))); -// localLight.addComponent(new ComponentTexture(new Uri("RES", "grass.png"))); -// localLight.addComponent(new ComponentLight(new Light(new Vector3f(0,1,0), new Vector3f(0,0,0), new Vector3f(0.8f,0.03f,0.002f)))); -// localLight.addComponent(new ComponentRenderTexturedStaticMesh( -// new Uri("DATA", "basic.vert"), -// new Uri("DATA", "basic.frag"))); -// env.addEntity(localLight); - { - // add a cube to test collision ... - Entity localBox = new Entity(this.env); - localBox.addComponent(new ComponentPosition(new Transform3D(new Vector3f(-2,-2,14)))); - localBox.addComponent(new ComponentStaticMesh(new Uri("RES", "cube-one.obj"))); - localBox.addComponent(new ComponentTexture(new Uri("DATA", "blocks/clay.png"))); - localBox.addComponent(new ComponentLight(new Light(new Vector3f(0,1,0), new Vector3f(0,0,0), new Vector3f(0.8f,0.03f,0.002f)))); - localBox.addComponent(new ComponentRenderTexturedStaticMesh( - new Uri("DATA", "basic.vert"), - new Uri("DATA", "basic.frag"))); - ComponentPhysics physics2 = new ComponentPhysics(true); - PhysicBox box2 = new PhysicBox(); - box2.setSize(new Vector3f(1,1,1)); - box2.setOrigin(new Vector3f(0,0,0)); - box2.setMass(1); - physics2.addShape(box2); - localBox.addComponent(physics2); - env.addEntity(localBox); - } - { - // add a cube to test collision ... - Entity localBox = new Entity(this.env); - localBox.addComponent(new ComponentPosition(new Transform3D(new Vector3f(0,4,12.5f)))); - localBox.addComponent(new ComponentStaticMesh(new Uri("RES", "cube-one.obj"))); - localBox.addComponent(new ComponentTexture(new Uri("DATA", "blocks/clay.png"))); - localBox.addComponent(new ComponentLight(new Light(new Vector3f(0,1,0), new Vector3f(0,0,0), new Vector3f(0.8f,0.03f,0.002f)))); - localBox.addComponent(new ComponentRenderTexturedStaticMesh( - new Uri("DATA", "basic.vert"), - new Uri("DATA", "basic.frag"))); - ComponentPhysics physics2 = new ComponentPhysics(true); - PhysicBox box2 = new PhysicBox(); - box2.setSize(new Vector3f(1,1,1)); - box2.setOrigin(new Vector3f(0,0,0)); - box2.setMass(1); - physics2.addShape(box2); - localBox.addComponent(physics2); - env.addEntity(localBox); - } - { - // add a cube to test collision ... - Entity localBox = new Entity(this.env); - localBox.addComponent(new ComponentPosition(new Transform3D(new Vector3f(-2,2,14.5f)))); - localBox.addComponent(new ComponentStaticMesh(new Uri("RES", "cube-one.obj"))); - localBox.addComponent(new ComponentTexture(new Uri("DATA", "blocks/clay.png"))); - localBox.addComponent(new ComponentRenderTexturedStaticMesh( - new Uri("DATA", "basic.vert"), - new Uri("DATA", "basic.frag"))); - ComponentPhysics physics2 = new ComponentPhysics(true); - PhysicBox box2 = new PhysicBox(); - box2.setSize(new Vector3f(1,1,1)); - box2.setOrigin(new Vector3f(0,0,0)); - box2.setMass(1); - physics2.addShape(box2); - localBox.addComponent(physics2); - env.addEntity(localBox); - } - - { - // add a cube to test collision ... - Entity localBox = new Entity(this.env); - localBox.addComponent(new ComponentPosition(new Transform3D(new Vector3f(-5,-5,14)))); - localBox.addComponent(new ComponentStaticMesh(new Uri("RES", "cube-one.obj"))); - localBox.addComponent(new ComponentTexture(new Uri("DATA", "blocks/clay.png"))); - localBox.addComponent(new ComponentRenderTexturedStaticMesh( - new Uri("DATA", "basic.vert"), - new Uri("DATA", "basic.frag"))); - ComponentPhysics physics2 = new ComponentPhysics(true); - PhysicBox box2 = new PhysicBox(); - box2.setSize(new Vector3f(4,4,4)); - box2.setOrigin(new Vector3f(0,0,0)); - box2.setMass(1); - physics2.addShape(box2); - localBox.addComponent(physics2); - env.addEntity(localBox); - } - { - // add a cube to test collision ... - Entity localBox = new Entity(this.env); - Quaternion transform = new Quaternion(0.5f,0.2f,0.4f,1); - transform.normalize(); - localBox.addComponent(new ComponentPosition(new Transform3D(new Vector3f(15,15,14), transform))); - localBox.addComponent(new ComponentStaticMesh(new Uri("RES", "cube-one.obj"))); - localBox.addComponent(new ComponentTexture(new Uri("DATA", "blocks/clay.png"))); - localBox.addComponent(new ComponentRenderTexturedStaticMesh( - new Uri("DATA", "basic.vert"), - new Uri("DATA", "basic.frag"))); - ComponentPhysics physics2 = new ComponentPhysics(true); - PhysicBox box2 = new PhysicBox(); - box2.setSize(new Vector3f(8,8,8)); - box2.setOrigin(new Vector3f(0,0,0)); - box2.setMass(1); - physics2.addShape(box2); - localBox.addComponent(physics2); - env.addEntity(localBox); - } - { - // add a cube to test collision ... - Entity localBox = new Entity(this.env); - Quaternion transform = new Quaternion(0.3f,0.3f,0.4f,1); - transform.normalize(); - localBox.addComponent(new ComponentPosition(new Transform3D(new Vector3f(2,-2,14.2f),transform))); - localBox.addComponent(new ComponentStaticMesh(new Uri("RES", "cube-one.obj"))); - localBox.addComponent(new ComponentTexture(new Uri("DATA", "blocks/clay.png"))); - localBox.addComponent(new ComponentRenderTexturedStaticMesh( - new Uri("DATA", "basic.vert"), - new Uri("DATA", "basic.frag"))); - ComponentPhysics physics2 = new ComponentPhysics(true); - PhysicBox box2 = new PhysicBox(); - box2.setSize(new Vector3f(1,1,1)); - box2.setOrigin(new Vector3f(0,0,0)); - box2.setMass(1); - physics2.addShape(box2); - localBox.addComponent(physics2); - env.addEntity(localBox); - } - { - // add a cube to test collision ... - Entity localBox = new Entity(this.env); - Quaternion transform = new Quaternion(0,0,1,1); - transform.normalize(); - localBox.addComponent(new ComponentPosition(new Transform3D(new Vector3f(2,2,14.2f),transform))); - localBox.addComponent(new ComponentStaticMesh(new Uri("RES", "cube-one.obj"))); - localBox.addComponent(new ComponentTexture(new Uri("DATA", "blocks/clay.png"))); - localBox.addComponent(new ComponentRenderTexturedStaticMesh( - new Uri("DATA", "basic.vert"), - new Uri("DATA", "basic.frag"))); - ComponentPhysics physics2 = new ComponentPhysics(true); - PhysicBox box2 = new PhysicBox(); - box2.setSize(new Vector3f(1,1,1)); - box2.setOrigin(new Vector3f(0,0,0)); - box2.setMass(1); - physics2.addShape(box2); - localBox.addComponent(physics2); - env.addEntity(localBox); - } -// { -// // add a cube to test collision ... -// Entity localBox = new Entity(this.env); -// relativeTestPos = new ComponentPosition(new Transform3D(new Vector3f(0,0,14),new Quaternion(0.5f,0.2f,0.4f,1))); -// localBox.addComponent(relativeTestPos); -//// localBox.addComponent(new ComponentStaticMesh(new Uri("RES", "cube-one.obj"))); -//// localBox.addComponent(new ComponentTexture(new Uri("DATA", "blocks/clay.png"))); -//// localBox.addComponent(new ComponentLight(new Light(new Vector3f(0,1,0), new Vector3f(0,0,0), new Vector3f(0.8f,0.03f,0.002f)))); -//// localBox.addComponent(new ComponentRenderTexturedStaticMesh( -//// new Uri("DATA", "basic.vert"), -//// new Uri("DATA", "basic.frag"))); -// ComponentPhysics physics2 = new ComponentPhysics(true); -// boxTest = new PhysicBox(); -// boxTest.setSize(new Vector3f(1,1,1)); -// boxTest.setOrigin(new Vector3f(0,0,0)); -// boxTest.setMass(1); -// physics2.addShape(boxTest); -// localBox.addComponent(physics2); -// env.addEntity(localBox); -// } -// { -// Entity localBox = new Entity(this.env); -// localBox.addComponent(new ComponentPosition(new Transform3D(new Vector3f(0,0,14)))); -// localBox.addComponent(new ComponentStaticMesh(new Uri("RES", "cube-one.obj"))); -// localBox.addComponent(new ComponentTexture(new Uri("DATA", "blocks/clay.png"))); -// localBox.addComponent(new ComponentRenderTexturedStaticMesh( -// new Uri("DATA", "basic.vert"), -// new Uri("DATA", "basic.frag"))); -// env.addEntity(localBox); -// } - - Entity gird = new Entity(this.env); - gird.addComponent(new ComponentPosition(new Transform3D(new Vector3f(0,0,13)))); - gird.addComponent(new ComponentStaticMesh(MeshGenerator.createGrid(5))); - gird.addComponent(new ComponentRenderColoredStaticMesh( - new Uri("DATA_EGE", "wireColor.vert"), - new Uri("DATA_EGE", "wireColor.frag"))); - env.addEntity(gird); - - Entity player = new Entity(this.env); - objectPosition = new ComponentPositionPlayer(new Transform3D(new Vector3f(5,5,13))); - player.addComponent(objectPosition); - objectPlayer = new ComponentPlayer(); - player.addComponent(objectPlayer); - player.addComponent(new ComponentMaterial(new Material())); - //player.addComponent(new ComponentStaticMesh(new Uri("RES", "person.obj"))); - player.addComponent(new ComponentStaticMesh(new Uri("RES", "person_-yfw_zup.obj"))); - player.addComponent(new ComponentTexture(new Uri("RES", "playerTexture.png"))); - player.addComponent(new ComponentRenderTexturedMaterialsStaticMesh( - new Uri("DATA", "basicMaterial.vert"), - new Uri("DATA", "basicMaterial.frag"), - (EngineLight)env.getEngine(EngineLight.ENGINE_NAME))); - ComponentPhysics physics = new ComponentPhysics(true); - PhysicBox box = new PhysicBox(); - box.setSize(new Vector3f(0.6f,0.6f,1.8f)); - box.setOrigin(new Vector3f(0,0,0.9f)); - box.setMass(1); - physics.addShape(box); - player.addComponent(physics); - env.addEntity(player); - - - Camera mainView = new Camera(); - env.addCamera("default", mainView); - mainView.setPitch((float)Math.PI*-0.25f); - mainView.setPosition(new Vector3f(0,-5,5)); - - this.simpleControl = new ControlCameraPlayer(mainView, player); - env.addControlInterface(simpleControl); - - // start the engine. - env.setPropertyStatus(GameStatus.gameStart); - - basicRotation.setEulerAngles(new Vector3f(0.005f,0.005f,0.01f)); - basicRotation2.setEulerAngles(new Vector3f(0.003f,0.01f,0.001f)); - // ready to let Gale & Ege manage the display - Log.info("==> Init APPL (END)"); - creationDone = true; } - */ - - public void onDraw(GaleContext context) { - if (this.debugDrawProperty == null) { + + public static void onDraw() { + if (debugDrawProperty == null) { debugDrawProperty = ResourceColored3DObject.create(); } // now render the point test collision ... - for (int iii=0; iii tmp = new ArrayList<>(); + tmp.add(new Vector3f(0, 0, 0)); + tmp.add(force); + debugDrawProperty.drawLine(tmp, new Color(1, 0, 0, 1), Matrix4f.createMatrixTranslate(subPosition), true, false); + } + public void setSize(Vector3f size) { this.size = size; } - + @Override - public void updateAABB(Transform3D transformGlobal, PhysicCollisionAABB aabb) { + public void updateAABB(Transform3D transformGlobal, AABB aabb) { // store it, many time usefull... this.transformGlobal = transformGlobal; this.colisionPoints.clear(); @@ -41,14 +102,7 @@ public class PhysicBox extends PhysicShape { aabb.update(transformGlobal.multiply(this.transform.multiply(new Vector3f(-this.size.x() * 0.5f, -this.size.y() * 0.5f, -this.size.z() * 0.5f)))); aabb.update(transformGlobal.multiply(this.transform.multiply(new Vector3f(this.size.x() * 0.5f, -this.size.y() * 0.5f, -this.size.z() * 0.5f)))); } - - // only needed for the narrow phase calculation ... - public Vector3f narrowPhaseGlobalPos; - public Vector3f narrowPhaseAxisX = new Vector3f(1, 0, 0); - public Vector3f narrowPhaseAxisY = new Vector3f(1, 0, 0); - public Vector3f narrowPhaseAxisZ = new Vector3f(1, 0, 0); - public Vector3f narrowPhaseHalfSize; - + @Override public void updateForNarrowCollision(Transform3D transformGlobal) { this.narrowPhaseGlobalPos = transformGlobal.multiply(this.transform.multiply(new Vector3f(0, 0, 0))); @@ -57,56 +111,5 @@ public class PhysicBox extends PhysicShape { this.narrowPhaseAxisZ = transformGlobal.multiply(this.transform.multiply(new Vector3f(0, 0, 1))).less(this.narrowPhaseGlobalPos); this.narrowPhaseHalfSize = this.size.multiply(0.5f); } - - private void renderPoint(Vector3f subPosition, Transform3D transformGlobal, ResourceColored3DObject debugDrawProperty) { - Matrix4f transformation = transformGlobal.getOpenGLMatrix().multiply(this.transform.getOpenGLMatrix()).multiply(Matrix4f.createMatrixTranslate(subPosition)); - - debugDrawProperty.drawSquare(new Vector3f(0.08f, 0.08f, 0.08f), transformation, new Color(0, 0, 1, 1)); - } - - private void renderPoint2(Vector3f subPosition, Transform3D transformGlobal, ResourceColored3DObject debugDrawProperty) { - Matrix4f transformation = transformGlobal.getOpenGLMatrix().multiply(this.transform.getOpenGLMatrix()).multiply(Matrix4f.createMatrixTranslate(subPosition)); - debugDrawProperty.drawSquare(new Vector3f(0.05f, 0.05f, 0.05f), transformation, new Color(0, 1, 0, 1)); - - } - - private void renderPoint3(Vector3f subPosition, Transform3D transformGlobal, ResourceColored3DObject debugDrawProperty) { - Matrix4f transformation = transformGlobal.getOpenGLMatrix().multiply(this.transform.getOpenGLMatrix()).multiply(Matrix4f.createMatrixTranslate(subPosition)); - debugDrawProperty.drawSquare(new Vector3f(0.05f, 0.05f, 0.05f), transformation, new Color(1, 1, 0, 1)); - - } - - private void renderPoint4(Vector3f subPosition, Vector3f force, ResourceColored3DObject debugDrawProperty) { - debugDrawProperty.drawSquare(new Vector3f(0.1f, 0.1f, 0.1f), Matrix4f.createMatrixTranslate(subPosition), new Color(1, 0, 0, 1)); - List tmp = new ArrayList<>(); - tmp.add(new Vector3f(0,0,0)); - tmp.add(force); - debugDrawProperty.drawLine(tmp, new Color(1, 0, 0, 1), Matrix4f.createMatrixTranslate(subPosition), true, false); - } - - @Override - public void renderDebug(Transform3D transformGlobal, ResourceColored3DObject debugDrawProperty) { - debugDrawProperty.drawSquare(this.size.multiply(0.5f), this.transform.getOpenGLMatrix().multiply(transformGlobal.getOpenGLMatrix()), new Color(0, 1, 0, 0.25f)); - Vector3f dimention = this.size.multiply(0.5f); - renderPoint2(new Vector3f(+dimention.x(), +dimention.y(), +dimention.z()), transformGlobal, debugDrawProperty); - renderPoint(new Vector3f(-dimention.x(), +dimention.y(), +dimention.z()), transformGlobal, debugDrawProperty); - renderPoint(new Vector3f(+dimention.x(), -dimention.y(), +dimention.z()), transformGlobal, debugDrawProperty); - renderPoint(new Vector3f(-dimention.x(), -dimention.y(), +dimention.z()), transformGlobal, debugDrawProperty); - renderPoint(new Vector3f(+dimention.x(), +dimention.y(), -dimention.z()), transformGlobal, debugDrawProperty); - renderPoint(new Vector3f(-dimention.x(), +dimention.y(), -dimention.z()), transformGlobal, debugDrawProperty); - renderPoint(new Vector3f(+dimention.x(), -dimention.y(), -dimention.z()), transformGlobal, debugDrawProperty); - renderPoint3(new Vector3f(-dimention.x(), -dimention.y(), -dimention.z()), transformGlobal, debugDrawProperty); - for (Collision elem: this.colisionPoints) { - if (elem != null) { - if (elem.colisionPointLocal == null) { - Log.error("colision point must be set !!!"); - continue; - } - for (int iii = 0; iii < elem.colisionPointLocal.length; iii++) { - renderPoint4(elem.colisionPointLocal[iii].position, elem.colisionPointLocal[iii].force, debugDrawProperty); - } - } - } - } - + } diff --git a/src/org/atriasoft/phyligram/PhysicCollisionAABB.java b/src/org/atriasoft/phyligram/PhysicCollisionAABB.java deleted file mode 100644 index 1348025..0000000 --- a/src/org/atriasoft/phyligram/PhysicCollisionAABB.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.atriasoft.phyligram; - -import org.atriasoft.etk.math.Vector3f; - -public class PhysicCollisionAABB { - public float minX; - public float minY; - public float minZ; - public float maxX; - public float maxY; - public float maxZ; - public PhysicCollisionAABB(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { - super(); - this.minX = minX; - this.minY = minY; - this.minZ = minZ; - this.maxX = maxX; - this.maxY = maxY; - this.maxZ = maxZ; - } - public boolean intersect(PhysicCollisionAABB other) { - if (this == other) { - return false; - } - if (minX > other.maxX) { - return false; - } - if (maxX < other.minX) { - return false; - } - if (minY > other.maxY) { - return false; - } - if (maxY < other.minY) { - return false; - } - if (minZ > other.maxZ) { - return false; - } - if (maxZ < other.minZ) { - return false; - } - return true; - } - public void update(Vector3f point) { - if (minX > point.x()) { - minX = point.x(); - } - if (maxX < point.x()) { - maxX = point.x(); - } - if (minY > point.y()) { - minY = point.y(); - } - if (maxY < point.y()) { - maxY = point.y(); - } - if (minZ > point.z()) { - minZ = point.z(); - } - if (maxZ < point.z()) { - maxZ = point.z(); - } - } - public Vector3f getMin() { - return new Vector3f(minX, minY, minZ); - } - public Vector3f getMax() { - return new Vector3f(maxX, maxY, maxZ); - } - public static PhysicCollisionAABB beforeCalculated() { - // TODO Auto-generated method stub - return new PhysicCollisionAABB(999999,999999,999999,-999999,-999999,-999999); - } -} - - diff --git a/src/org/atriasoft/phyligram/PhysicHeightMapChunk.java b/src/org/atriasoft/phyligram/PhysicHeightMapChunk.java new file mode 100644 index 0000000..f8621c6 --- /dev/null +++ b/src/org/atriasoft/phyligram/PhysicHeightMapChunk.java @@ -0,0 +1,63 @@ +package org.atriasoft.phyligram; + +import org.atriasoft.ege.geometry.Triangle; +import org.atriasoft.etk.math.Transform3D; +import org.atriasoft.etk.math.Vector3f; +import org.atriasoft.gale.resource.ResourceColored3DObject; +import org.atriasoft.phyligram.shape.AABB; + +public class PhysicHeightMapChunk extends PhysicShape { + // Box size property in X, Y and Z + private Triangle[] triangles = null; + public Triangle[] trianglesGlobalPos = null; + + public PhysicHeightMapChunk() { + + } + + public PhysicHeightMapChunk(Triangle[] triangles) { + this.triangles = triangles; + } + + public Triangle[] getTriangles() { + return this.triangles; + } + + @Override + public void renderDebug(Transform3D transformGlobal, ResourceColored3DObject debugDrawProperty) { + + } + + public void setTriangles(Triangle[] triangles) { + this.triangles = triangles; + this.trianglesGlobalPos = new Triangle[triangles.length]; + for (int iii = 0; iii < this.triangles.length; iii++) { + Vector3f data1 = this.transformGlobal.multiply(this.transform.multiply(this.triangles[iii].p1)); + Vector3f data2 = this.transformGlobal.multiply(this.transform.multiply(this.triangles[iii].p2)); + Vector3f data3 = this.transformGlobal.multiply(this.transform.multiply(this.triangles[iii].p3)); + this.trianglesGlobalPos[iii] = new Triangle(data1, data2, data3); + } + } + + @Override + public void updateAABB(Transform3D transformGlobal, AABB aabb) { + // store it, many time usefull... + this.transformGlobal = transformGlobal; + this.colisionPoints.clear(); + for (int iii = 0; iii < this.triangles.length; iii++) { + aabb.update(transformGlobal.multiply(this.transform.multiply(this.triangles[iii].p1))); + aabb.update(transformGlobal.multiply(this.transform.multiply(this.triangles[iii].p2))); + aabb.update(transformGlobal.multiply(this.transform.multiply(this.triangles[iii].p3))); + } + } + + @Override + public void updateForNarrowCollision(Transform3D transformGlobal) { + for (int iii = 0; iii < this.triangles.length; iii++) { + this.trianglesGlobalPos[iii].p1 = this.transformGlobal.multiply(this.transform.multiply(this.triangles[iii].p1)); + this.trianglesGlobalPos[iii].p2 = this.transformGlobal.multiply(this.transform.multiply(this.triangles[iii].p2)); + this.trianglesGlobalPos[iii].p3 = this.transformGlobal.multiply(this.transform.multiply(this.triangles[iii].p3)); + } + } + +} diff --git a/src/org/atriasoft/phyligram/PhysicMapVoxel.java b/src/org/atriasoft/phyligram/PhysicMapVoxel.java index ec96f57..f13f9cb 100644 --- a/src/org/atriasoft/phyligram/PhysicMapVoxel.java +++ b/src/org/atriasoft/phyligram/PhysicMapVoxel.java @@ -1,42 +1,43 @@ package org.atriasoft.phyligram; +import org.atriasoft.ege.map.VoxelChunk; import org.atriasoft.etk.math.Transform3D; import org.atriasoft.etk.math.Vector3f; import org.atriasoft.gale.resource.ResourceColored3DObject; -import org.atriasoft.ege.map.VoxelChunk; +import org.atriasoft.phyligram.shape.AABB; public class PhysicMapVoxel extends PhysicShape { // Box size property in X, Y and Z private VoxelChunk chunk; + // only needed for the narrow phase calculation ... + private Vector3f narrowPhaseGlobalPos; + private Vector3f narrowPhaseAxisX = new Vector3f(1, 0, 0); + private Vector3f narrowPhaseAxisY = new Vector3f(1, 0, 0); + private Vector3f narrowPhaseAxisZ = new Vector3f(1, 0, 0); + private Vector3f narrowPhaseHalfSize = new Vector3f(0.5f, 0.5f, 0.5f); + public PhysicMapVoxel(VoxelChunk chunk) { - super(PhysicShapeType.MAP_VOXEL); this.chunk = chunk; } + @Override - public void updateAABB(Transform3D transform, PhysicCollisionAABB aabb) { + public void renderDebug(Transform3D transform, ResourceColored3DObject debugDrawProperty) { + + } + + @Override + public void updateAABB(Transform3D transform, AABB aabb) { if (this.chunk == null) { return; } this.colisionPoints.clear(); - aabb.update(new Vector3f(this.chunk.getPosition().x(),this.chunk.getPosition().y(),this.chunk.getPosition().z())); - aabb.update(new Vector3f( - this.chunk.getPosition().x() + VoxelChunk.VOXEL_CHUNK_SIZE, - this.chunk.getPosition().y() + VoxelChunk.VOXEL_CHUNK_SIZE, + aabb.update(new Vector3f(this.chunk.getPosition().x(), this.chunk.getPosition().y(), this.chunk.getPosition().z())); + aabb.update(new Vector3f(this.chunk.getPosition().x() + VoxelChunk.VOXEL_CHUNK_SIZE, this.chunk.getPosition().y() + VoxelChunk.VOXEL_CHUNK_SIZE, this.chunk.getPosition().z() + VoxelChunk.VOXEL_CHUNK_SIZE)); } - // only needed for the narrow phase calculation ... - private Vector3f narrowPhaseGlobalPos; - private Vector3f narrowPhaseAxisX = new Vector3f(1,0,0); - private Vector3f narrowPhaseAxisY = new Vector3f(1,0,0); - private Vector3f narrowPhaseAxisZ = new Vector3f(1,0,0); - private Vector3f narrowPhaseHalfSize = new Vector3f(0.5f,0.5f,0.5f); + @Override public void updateForNarrowCollision(Transform3D transform) { - this.narrowPhaseGlobalPos = transform.multiply(this.transform.multiply(new Vector3f(0,0,0))); - } - @Override - public void renderDebug(Transform3D transform, ResourceColored3DObject debugDrawProperty) { - - + this.narrowPhaseGlobalPos = transform.multiply(this.transform.multiply(new Vector3f(0, 0, 0))); } } diff --git a/src/org/atriasoft/phyligram/PhysicShape.java b/src/org/atriasoft/phyligram/PhysicShape.java index 3c90611..9999843 100644 --- a/src/org/atriasoft/phyligram/PhysicShape.java +++ b/src/org/atriasoft/phyligram/PhysicShape.java @@ -7,13 +7,9 @@ import org.atriasoft.etk.math.Quaternion; import org.atriasoft.etk.math.Transform3D; import org.atriasoft.etk.math.Vector3f; import org.atriasoft.gale.resource.ResourceColored3DObject; - - - +import org.atriasoft.phyligram.shape.AABB; public abstract class PhysicShape { - - protected List colisionPoints = new ArrayList<>(); // protected Quaternion quaternion; @@ -21,81 +17,73 @@ public abstract class PhysicShape { protected Transform3D transform; protected Transform3D transformGlobal; protected float mass = 0; - protected final PhysicShapeType type; - - public PhysicShape(PhysicShapeType type) { - this.type = type; + + public PhysicShape() { this.transform = Transform3D.IDENTITY; // this.quaternion = Quaternion.identity(); // this.origin = Vector3f.zero(); this.mass = 0; } - - public PhysicShape(PhysicShapeType type, Quaternion quaternion, Vector3f origin, float mass) { - this.type = type; + + public PhysicShape(Quaternion quaternion, Vector3f origin, float mass) { this.transform = new Transform3D(origin, quaternion); // this.quaternion = quaternion; // this.origin = origin; this.mass = mass; } - - public Quaternion getQuaternionFull() { - return transformGlobal.getOrientation().multiply(transform.getOrientation()); + + public void addColision(Collision colision) { + this.colisionPoints.add(colision); } - - public Quaternion getQuaternion() { - return transform.getOrientation(); + + public float getMass() { + return this.mass; } - - public void setQuaternion(Quaternion quaternion) { - this.transform = this.transform.withOrientation(quaternion); - } - + public Vector3f getOrigin() { return this.transform.getPosition(); } - - public void setOrigin(Vector3f origin) { - this.transform = this.transform.withPosition(origin); + + public Quaternion getQuaternion() { + return this.transform.getOrientation(); } - + + public Quaternion getQuaternionFull() { + return this.transformGlobal.getOrientation().multiply(this.transform.getOrientation()); + } + public Transform3D getTransform() { - return transform; + return this.transform; } - - public void setTransform(Transform3D transform) { - this.transform = transform; - } - + public Transform3D getTransformGlobal() { - return transformGlobal; + return this.transformGlobal; } - - public void setTransformGlobal(Transform3D transform) { - this.transformGlobal = transform; - } - - public float getMass() { - return mass; - } - + + public abstract void renderDebug(Transform3D transform, ResourceColored3DObject debugDrawProperty); + public void setMass(float mass) { this.mass = mass; } - - public PhysicShapeType getType() { - return type; - } - - public void addColision(Collision colision) { - colisionPoints.add(colision); + + public void setOrigin(Vector3f origin) { + this.transform = this.transform.withPosition(origin); } + public void setQuaternion(Quaternion quaternion) { + this.transform = this.transform.withOrientation(quaternion); + } + + public void setTransform(Transform3D transform) { + this.transform = transform; + } + + public void setTransformGlobal(Transform3D transform) { + this.transformGlobal = transform; + } + + public abstract void updateAABB(Transform3D transform, AABB aabb); - public abstract void updateAABB(Transform3D transform, PhysicCollisionAABB aabb); - public abstract void updateForNarrowCollision(Transform3D transform); - - public abstract void renderDebug(Transform3D transform, ResourceColored3DObject debugDrawProperty); - + } diff --git a/src/org/atriasoft/phyligram/PhysicShapeType.java b/src/org/atriasoft/phyligram/PhysicShapeType.java deleted file mode 100644 index 8f162cd..0000000 --- a/src/org/atriasoft/phyligram/PhysicShapeType.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.atriasoft.phyligram; - -public enum PhysicShapeType { - UNKNOWN, - BOX, - CAPSULE, - CONE, - CONVEXHULL, - CYLINDER, - SPHERE, - CONCAVE, - MAP_VOXEL -} diff --git a/src/org/atriasoft/phyligram/PhysicSphere.java b/src/org/atriasoft/phyligram/PhysicSphere.java index 8a6b8af..00473fe 100644 --- a/src/org/atriasoft/phyligram/PhysicSphere.java +++ b/src/org/atriasoft/phyligram/PhysicSphere.java @@ -4,35 +4,46 @@ import org.atriasoft.etk.Color; import org.atriasoft.etk.math.Transform3D; import org.atriasoft.etk.math.Vector3f; import org.atriasoft.gale.resource.ResourceColored3DObject; +import org.atriasoft.phyligram.shape.AABB; public class PhysicSphere extends PhysicShape { // Box size property in X, Y and Z private float size; - public PhysicSphere() { - super(PhysicShapeType.SPHERE); - } + + // only needed for the narrow phase calculation ... + public Vector3f narrowPhaseGlobalPos; + + public PhysicSphere() {} + public float getSize() { - return size; + return this.size; } + + @Override + public void renderDebug(Transform3D transform, ResourceColored3DObject debugDrawProperty) { + debugDrawProperty.drawSphere(this.size, 9, 9, this.transform.getOpenGLMatrix().multiply(transform.getOpenGLMatrix()), new Color(0, 1, 0, 1)); + + } + public void setSize(float size) { this.size = size; } + @Override - public void updateAABB(Transform3D transform, PhysicCollisionAABB aabb) { - aabb.update(transform.multiply(this.transform.getPosition()).add(this.size,0,0)); - aabb.update(transform.multiply(this.transform.getPosition()).add(-this.size,0,0)); - aabb.update(transform.multiply(this.transform.getPosition()).add(0,this.size,0)); - aabb.update(transform.multiply(this.transform.getPosition()).add(0,-this.size,0)); - aabb.update(transform.multiply(this.transform.getPosition()).add(0,0,this.size)); - aabb.update(transform.multiply(this.transform.getPosition()).add(0,0,-this.size)); + public void updateAABB(Transform3D transformGlobal, AABB aabb) { + // store it, many time usefull... + this.transformGlobal = transformGlobal; + Vector3f basePosition = transformGlobal.multiply(this.transform.getPosition()); + aabb.update(basePosition.add(this.size, 0, 0)); + aabb.update(basePosition.add(-this.size, 0, 0)); + aabb.update(basePosition.add(0, this.size, 0)); + aabb.update(basePosition.add(0, -this.size, 0)); + aabb.update(basePosition.add(0, 0, this.size)); + aabb.update(basePosition.add(0, 0, -this.size)); } + @Override public void updateForNarrowCollision(Transform3D transform) { - - } - @Override - public void renderDebug(Transform3D transform, ResourceColored3DObject debugDrawProperty) { - debugDrawProperty.drawSphere(this.size, 9, 9, this.transform.getOpenGLMatrix().multiply(transform.getOpenGLMatrix()), new Color(0,1,0,1)); - + this.narrowPhaseGlobalPos = this.transformGlobal.multiply(this.transform.multiply(new Vector3f(0, 0, 0))); } } \ No newline at end of file diff --git a/src/org/atriasoft/phyligram/PhysicTriangle.java b/src/org/atriasoft/phyligram/PhysicTriangle.java new file mode 100644 index 0000000..5ab6e98 --- /dev/null +++ b/src/org/atriasoft/phyligram/PhysicTriangle.java @@ -0,0 +1,61 @@ +package org.atriasoft.phyligram; + +import org.atriasoft.ege.geometry.Triangle; +import org.atriasoft.etk.Color; +import org.atriasoft.etk.math.Transform3D; +import org.atriasoft.etk.math.Vector3f; +import org.atriasoft.gale.resource.ResourceColored3DObject; +import org.atriasoft.phyligram.shape.AABB; + +public class PhysicTriangle extends PhysicShape { + // Box size property in X, Y and Z + private Triangle triangle; + + // only needed for the narrow phase calculation ... + public Triangle narrowPhaseGlobalTriangle; + + public PhysicTriangle() {} + + public Triangle getTriangle() { + return this.triangle; + } + + public Triangle getTriangleGlobalPos() { + return this.narrowPhaseGlobalTriangle; + } + + @Override + public void renderDebug(Transform3D transform, ResourceColored3DObject debugDrawProperty) { + debugDrawProperty.drawTriangle(this.triangle.p1, this.triangle.p2, this.triangle.p3, this.transform.getOpenGLMatrix().multiply(transform.getOpenGLMatrix()), new Color(0, 1, 0, 1)); + + } + + public void setPoints(Vector3f p1, Vector3f p2, Vector3f p3) { + this.triangle = new Triangle(p1, p2, p3); + } + + public void setTriangle(Triangle data) { + this.triangle = data.clone(); + } + + @Override + public void updateAABB(Transform3D transformGlobal, AABB aabb) { + // store it, many time usefull... + this.transformGlobal = transformGlobal; + Vector3f basePositionP1 = transformGlobal.multiply(this.transform.getPosition().add(this.triangle.p1)); + Vector3f basePositionP2 = transformGlobal.multiply(this.transform.getPosition().add(this.triangle.p2)); + Vector3f basePositionP3 = transformGlobal.multiply(this.transform.getPosition().add(this.triangle.p3)); + this.narrowPhaseGlobalTriangle = new Triangle(basePositionP1, basePositionP2, basePositionP3); + aabb.update(basePositionP1); + aabb.update(basePositionP2); + aabb.update(basePositionP3); + } + + @Override + public void updateForNarrowCollision(Transform3D transform) { + Vector3f basePositionP1 = this.transformGlobal.multiply(this.transform.multiply(this.triangle.p1)); + Vector3f basePositionP2 = this.transformGlobal.multiply(this.transform.multiply(this.triangle.p2)); + Vector3f basePositionP3 = this.transformGlobal.multiply(this.transform.multiply(this.triangle.p3)); + this.narrowPhaseGlobalTriangle = new Triangle(basePositionP1, basePositionP2, basePositionP3); + } +} \ No newline at end of file diff --git a/src/org/atriasoft/phyligram/ToolCollisionOBBWithOBB.java b/src/org/atriasoft/phyligram/ToolCollisionOBBWithOBB.java index e7286eb..f4262f2 100644 --- a/src/org/atriasoft/phyligram/ToolCollisionOBBWithOBB.java +++ b/src/org/atriasoft/phyligram/ToolCollisionOBBWithOBB.java @@ -141,7 +141,7 @@ public class ToolCollisionOBBWithOBB { // getCollidePointsAABBCenteredWithOBB(box2.narrowPhaseHalfSize, box1.narrowPhaseHalfSize, quatTransfer2, rPos1); // fonctionne quand le block est trourner de 90% petit pb de positionnement en hauteur.... // getCollidePointsAABBCenteredWithOBB(box2.narrowPhaseHalfSize, box1.narrowPhaseHalfSize, quatTransfer2, quat2.multiply(rPos2)); - ColisionPoints[] collide1 = getCollidePointsAABBCenteredWithOBB(box2.narrowPhaseHalfSize, box1.narrowPhaseHalfSize, quatTransfer2, quat2.inverse().getMatrix4().multiply(rPos1)); + ColisionPoint[] collide1 = getCollidePointsAABBCenteredWithOBB(box2.narrowPhaseHalfSize, box1.narrowPhaseHalfSize, quatTransfer2, quat2.inverse().getMatrix4().multiply(rPos1)); // transfer detection point collision in global environement: if (collide1 != null) { for (int iii = 0; iii < collide1.length; iii++) { @@ -155,7 +155,7 @@ public class ToolCollisionOBBWithOBB { DebugDisplay.testQTransfert = quatTransfer1; DebugDisplay.box1HalfSize = box1.narrowPhaseHalfSize; DebugDisplay.box2HalfSize = box2.narrowPhaseHalfSize; - ColisionPoints[] collide2 = getCollidePointsAABBCenteredWithOBB(box1.narrowPhaseHalfSize, box2.narrowPhaseHalfSize, quatTransfer1, quat1.inverse().getMatrix4().multiply(rPos2)); + ColisionPoint[] collide2 = getCollidePointsAABBCenteredWithOBB(box1.narrowPhaseHalfSize, box2.narrowPhaseHalfSize, quatTransfer1, quat1.inverse().getMatrix4().multiply(rPos2)); if (collide2 != null) { for (int iii = 0; iii < collide2.length; iii++) { collide2[iii].position = quat2.multiply(collide2[iii].position).add(box2.narrowPhaseGlobalPos); @@ -174,7 +174,7 @@ public class ToolCollisionOBBWithOBB { } - public static ColisionPoints[] getCollidePointsAABBCenteredWithOBB(Vector3f box1HalfSize, Vector3f box2HalfSize, Quaternion box2Orientation, Vector3f box2Position) { + public static ColisionPoint[] getCollidePointsAABBCenteredWithOBB(Vector3f box1HalfSize, Vector3f box2HalfSize, Quaternion box2Orientation, Vector3f box2Position) { // point in AABB Vector3f topBackRight = box2Orientation.multiply(new Vector3f(+box2HalfSize.x(), +box2HalfSize.y(), +box2HalfSize.z())).add(box2Position); @@ -245,38 +245,38 @@ public class ToolCollisionOBBWithOBB { if (insideBottomFrontLeft != null) { count++; } - ColisionPoints[] out = new ColisionPoints[count]; + ColisionPoint[] out = new ColisionPoint[count]; count = 0; if (insideTopBackRight != null) { - out[count] = new ColisionPoints(new Vector3f(+box2HalfSize.x(), +box2HalfSize.y(), +box2HalfSize.z()), insideTopBackRight); + out[count] = new ColisionPoint(new Vector3f(+box2HalfSize.x(), +box2HalfSize.y(), +box2HalfSize.z()), insideTopBackRight); count++; } if (insideTopBackLeft != null) { - out[count] = new ColisionPoints(new Vector3f(-box2HalfSize.x(), +box2HalfSize.y(), +box2HalfSize.z()), insideTopBackLeft); + out[count] = new ColisionPoint(new Vector3f(-box2HalfSize.x(), +box2HalfSize.y(), +box2HalfSize.z()), insideTopBackLeft); count++; } if (insideTopFrontRight != null) { - out[count] = new ColisionPoints(new Vector3f(+box2HalfSize.x(), -box2HalfSize.y(), +box2HalfSize.z()), insideTopFrontRight); + out[count] = new ColisionPoint(new Vector3f(+box2HalfSize.x(), -box2HalfSize.y(), +box2HalfSize.z()), insideTopFrontRight); count++; } if (insideTopFrontLeft != null) { - out[count] = new ColisionPoints(new Vector3f(-box2HalfSize.x(), -box2HalfSize.y(), +box2HalfSize.z()), insideTopFrontLeft); + out[count] = new ColisionPoint(new Vector3f(-box2HalfSize.x(), -box2HalfSize.y(), +box2HalfSize.z()), insideTopFrontLeft); count++; } if (insideBottomBackRight != null) { - out[count] = new ColisionPoints(new Vector3f(+box2HalfSize.x(), +box2HalfSize.y(), -box2HalfSize.z()), insideBottomBackRight); + out[count] = new ColisionPoint(new Vector3f(+box2HalfSize.x(), +box2HalfSize.y(), -box2HalfSize.z()), insideBottomBackRight); count++; } if (insideBottomBackLeft != null) { - out[count] = new ColisionPoints(new Vector3f(-box2HalfSize.x(), +box2HalfSize.y(), -box2HalfSize.z()), insideBottomBackLeft); + out[count] = new ColisionPoint(new Vector3f(-box2HalfSize.x(), +box2HalfSize.y(), -box2HalfSize.z()), insideBottomBackLeft); count++; } if (insideBottomFrontRight != null) { - out[count] = new ColisionPoints(new Vector3f(+box2HalfSize.x(), -box2HalfSize.y(), -box2HalfSize.z()), insideBottomFrontRight); + out[count] = new ColisionPoint(new Vector3f(+box2HalfSize.x(), -box2HalfSize.y(), -box2HalfSize.z()), insideBottomFrontRight); count++; } if (insideBottomFrontLeft != null) { - out[count] = new ColisionPoints(new Vector3f(-box2HalfSize.x(), -box2HalfSize.y(), -box2HalfSize.z()), insideBottomFrontLeft); + out[count] = new ColisionPoint(new Vector3f(-box2HalfSize.x(), -box2HalfSize.y(), -box2HalfSize.z()), insideBottomFrontLeft); count++; } if (count != 0) { diff --git a/src/org/atriasoft/phyligram/ToolCollisionSphereWithHeightMapChunk.java b/src/org/atriasoft/phyligram/ToolCollisionSphereWithHeightMapChunk.java new file mode 100644 index 0000000..e506061 --- /dev/null +++ b/src/org/atriasoft/phyligram/ToolCollisionSphereWithHeightMapChunk.java @@ -0,0 +1,63 @@ +package org.atriasoft.phyligram; + +import org.atriasoft.ege.geometry.Triangle; +import org.atriasoft.etk.math.Vector3f; + +public class ToolCollisionSphereWithHeightMapChunk { + //intersection entre 2 droite (en 2d avec estimation en 3D + public static Vector3f lineIntersect(Vector3f p1, Vector3f p2, Vector3f p3, Vector3f p4) { + float denom = (p4.y() - p3.y()) * (p2.x() - p1.x()) - (p4.x() - p3.x()) * (p2.y() - p1.y()); + if (denom == 0.0f) { // Lines are parallel. + return null; + } + float ua = ((p4.x() - p3.x()) * (p1.y() - p3.y()) - (p4.y() - p3.y()) * (p1.x() - p3.x())) / denom; + float ub = ((p2.x() - p1.x()) * (p1.y() - p3.y()) - (p2.y() - p1.y()) * (p1.x() - p3.x())) / denom; + if (ua >= 0.0f && ua <= 1.0f && ub >= 0.0f && ub <= 1.0f) { + // Get the intersection point. + return new Vector3f(p1.x() + ua * (p2.x() - p1.x()), p1.y() + ua * (p2.y() - p1.y()), p1.z() + ua * (p2.z() - p1.z())); + } + return null; + } + + public static Vector3f middlePoint(Vector3f p1, Vector3f p2, Vector3f p3) { + float distance1 = p1.distance(p2); + float distance2 = p1.distance(p3); + float ratio = distance1 / distance2; + return p3.add(p3.less(p1).multiply(ratio)); + } + + private static boolean pointInTriangle(Vector3f pt, Triangle triangle) { + float d1, d2, d3; + boolean has_neg, has_pos; + + d1 = sign(pt, triangle.p1, triangle.p2); + d2 = sign(pt, triangle.p2, triangle.p3); + d3 = sign(pt, triangle.p3, triangle.p1); + + has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0); + has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0); + + return !(has_neg && has_pos); + } + + private static float sign(Vector3f p1, Vector3f p2, Vector3f p3) { + return (p1.x() - p3.x()) * (p2.y() - p3.y()) - (p2.x() - p3.x()) * (p1.y() - p3.y()); + } + + public static boolean testCollide(PhysicSphere sphere1, PhysicHeightMapChunk map) { + for (Triangle elem : map.trianglesGlobalPos) { + if (pointInTriangle(sphere1.narrowPhaseGlobalPos, elem)) { + // calculate linear extrapolation of the z height at this position: + Vector3f position = lineIntersect(elem.p1, sphere1.narrowPhaseGlobalPos, elem.p2, elem.p3); + Vector3f middlePoint = middlePoint(elem.p1, sphere1.narrowPhaseGlobalPos, position); + float distanceSquare = middlePoint.distance2(sphere1.narrowPhaseGlobalPos); + if (sphere1.getSize() * sphere1.getSize() < distanceSquare) { + return true; + } + } + } + return false; + } + + private ToolCollisionSphereWithHeightMapChunk() {} +} diff --git a/src/org/atriasoft/phyligram/ToolCollisionSphereWithSphere.java b/src/org/atriasoft/phyligram/ToolCollisionSphereWithSphere.java new file mode 100644 index 0000000..584c825 --- /dev/null +++ b/src/org/atriasoft/phyligram/ToolCollisionSphereWithSphere.java @@ -0,0 +1,30 @@ +package org.atriasoft.phyligram; + +import org.atriasoft.etk.math.Vector3f; +import org.atriasoft.phyligram.internal.Log; + +public class ToolCollisionSphereWithSphere { + // Note sphere 2 is the reference ... + public static ColisionPoint getCollisionPoint(PhysicSphere sphere1, PhysicSphere shapeReference) { + if (sphere1.getSize() > shapeReference.getSize()) { + Log.todo("implement then reference is smaller than moving"); + } + Vector3f force = sphere1.narrowPhaseGlobalPos.less(shapeReference.narrowPhaseGlobalPos); + float distance = shapeReference.getSize() + sphere1.getSize() - force.length(); + force = force.safeNormalize(); + Vector3f impact = force.multiply(shapeReference.getSize()); + force = force.multiply(distance); + force = force.multiply(sphere1.getSize() + distance); + return new ColisionPoint(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(); + distance2 = distance2 * distance2; + return distance1 <= distance2; + } + + private ToolCollisionSphereWithSphere() {} +} diff --git a/src/org/atriasoft/phyligram/ToolCollisionSphereWithTriangle.java b/src/org/atriasoft/phyligram/ToolCollisionSphereWithTriangle.java new file mode 100644 index 0000000..87b34f1 --- /dev/null +++ b/src/org/atriasoft/phyligram/ToolCollisionSphereWithTriangle.java @@ -0,0 +1,155 @@ +package org.atriasoft.phyligram; + +import org.atriasoft.ege.geometry.Plane; +import org.atriasoft.etk.math.FMath; +import org.atriasoft.etk.math.Vector3f; + +// https://realtimecollisiondetection.net/blog/?p=103 +public class ToolCollisionSphereWithTriangle { + public static float clamp(float val, float min, float max) { + return Math.max(min, Math.min(max, val)); + } + + public static Vector3f getClosestPointOnFiniteLine(Vector3f point, Vector3f lineStart, Vector3f lineEnd) { + Vector3f lineDirection = lineEnd.less(lineStart); + float lineLength = lineDirection.length(); + lineDirection = lineDirection.normalize(); + float position = point.less(lineStart).dot(lineDirection); + float ProjectionLength = clamp(position, 0, lineLength); + return lineStart.add(lineDirection.multiply(ProjectionLength)); + } + + public static ColisionPoint getCollisionPoint(PhysicSphere sphere1, PhysicTriangle shapeReference) { + Plane plane = new Plane(shapeReference.getTriangleGlobalPos()); + float distance = plane.distance(sphere1.narrowPhaseGlobalPos); + float distance1 = distance * distance; + float distance2 = sphere1.getSize() * sphere1.getSize(); + if (distance1 > distance2) { + return null; + } + /* + if (distance < 0.0f) { + System.out.println("Distance : (BOTTOM) " + distance); + } else { + System.out.println("Distance : (TOP) " + distance); + } + */ + boolean inCenter = false; + // check the position with the perpendicular plan of each side of the triangle + Plane plane1 = new Plane(shapeReference.getTriangleGlobalPos().p1, shapeReference.getTriangleGlobalPos().p2, shapeReference.getTriangleGlobalPos().p1.add(plane.normal())); + float distanceBorder = plane1.distance(sphere1.narrowPhaseGlobalPos); + inCenter = distanceBorder < 0.0f; + if (inCenter == true) { + Plane plane2 = new Plane(shapeReference.getTriangleGlobalPos().p2, shapeReference.getTriangleGlobalPos().p3, shapeReference.getTriangleGlobalPos().p2.add(plane.normal())); + distanceBorder = plane2.distance(sphere1.narrowPhaseGlobalPos); + inCenter = distanceBorder < 0.0f; + } + if (inCenter == true) { + Plane plane3 = new Plane(shapeReference.getTriangleGlobalPos().p3, shapeReference.getTriangleGlobalPos().p1, shapeReference.getTriangleGlobalPos().p3.add(plane.normal())); + distanceBorder = plane3.distance(sphere1.narrowPhaseGlobalPos); + inCenter = distanceBorder < 0.0f; + } + if (inCenter == true) { + Vector3f impact = plane.minimalIntersectionPoint(sphere1.narrowPhaseGlobalPos); + Vector3f force = plane.normal(); + if (distance < 0) { + force = force.multiply(-(sphere1.getSize() + distance)); + } else { + force = force.multiply(-(sphere1.getSize() + distance)); + } + return new ColisionPoint(impact, force); + } + + System.out.println("Not in center"); + + // now we need to check if we have a collision with the border. + Vector3f nearestPointP1 = getClosestPointOnFiniteLine(sphere1.narrowPhaseGlobalPos, shapeReference.getTriangleGlobalPos().p1, shapeReference.getTriangleGlobalPos().p2); + float distanceP1Square = Vector3f.length2(nearestPointP1, sphere1.narrowPhaseGlobalPos); + System.out.println("distanceP1Square=" + distanceP1Square); + Vector3f nearestPointP2 = getClosestPointOnFiniteLine(sphere1.narrowPhaseGlobalPos, shapeReference.getTriangleGlobalPos().p2, shapeReference.getTriangleGlobalPos().p3); + float distanceP2Square = Vector3f.length2(nearestPointP2, sphere1.narrowPhaseGlobalPos); + System.out.println("distanceP2Square=" + distanceP2Square); + Vector3f nearestPointP3 = getClosestPointOnFiniteLine(sphere1.narrowPhaseGlobalPos, shapeReference.getTriangleGlobalPos().p3, shapeReference.getTriangleGlobalPos().p1); + float distanceP3Square = Vector3f.length2(nearestPointP3, sphere1.narrowPhaseGlobalPos); + System.out.println("distanceP3Square=" + distanceP3Square); + float distanceFinal; + Vector3f impact; + if (distanceP1Square < distanceP2Square && distanceP1Square < distanceP3Square) { + // P1 is the smallest + distanceFinal = FMath.sqrt(distanceP1Square); + impact = nearestPointP1; + } else if (distanceP2Square < distanceP3Square) { + // P2 is the smallest + distanceFinal = FMath.sqrt(distanceP2Square); + impact = nearestPointP2; + } else { + // P3 is the smallest + distanceFinal = FMath.sqrt(distanceP3Square); + impact = nearestPointP3; + } + + Vector3f force = sphere1.narrowPhaseGlobalPos.less(impact).safeNormalize(); + force = force.multiply(sphere1.getSize() - distanceFinal); + return new ColisionPoint(impact, force); + } + + public static boolean testCollide(PhysicSphere sphere1, PhysicTriangle shapeReference) { + Plane plane = new Plane(shapeReference.getTriangleGlobalPos()); + float distance = plane.distance(sphere1.narrowPhaseGlobalPos); + float distance1 = distance * distance; + float distance2 = sphere1.getSize() * sphere1.getSize(); + if (distance1 > distance2) { + return false; + } + /* + if (distance < 0.0f) { + System.out.println("Distance : (BOTTOM) " + distance); + } else { + System.out.println("Distance : (TOP) " + distance); + } + */ + boolean inCenter = false; + // check the position with the perpendicular plan of each side of the triangle + Plane plane1 = new Plane(shapeReference.getTriangleGlobalPos().p1, shapeReference.getTriangleGlobalPos().p2, shapeReference.getTriangleGlobalPos().p1.add(plane.normal())); + float distanceBorder = plane1.distance(sphere1.narrowPhaseGlobalPos); + inCenter = distanceBorder < 0.0f; + if (inCenter == true) { + Plane plane2 = new Plane(shapeReference.getTriangleGlobalPos().p2, shapeReference.getTriangleGlobalPos().p3, shapeReference.getTriangleGlobalPos().p2.add(plane.normal())); + distanceBorder = plane2.distance(sphere1.narrowPhaseGlobalPos); + inCenter = distanceBorder < 0.0f; + } + if (inCenter == true) { + Plane plane3 = new Plane(shapeReference.getTriangleGlobalPos().p3, shapeReference.getTriangleGlobalPos().p1, shapeReference.getTriangleGlobalPos().p3.add(plane.normal())); + distanceBorder = plane3.distance(sphere1.narrowPhaseGlobalPos); + inCenter = distanceBorder < 0.0f; + } + if (inCenter == true) { + return true; + } + + System.out.println("Not in center"); + + // now we need to check if we have a collision with the border. + Vector3f nearestPointP1 = getClosestPointOnFiniteLine(sphere1.narrowPhaseGlobalPos, shapeReference.getTriangleGlobalPos().p1, shapeReference.getTriangleGlobalPos().p2); + float distanceP1Square = Vector3f.length2(nearestPointP1, sphere1.narrowPhaseGlobalPos); + System.out.println("distanceP1Square=" + distanceP1Square); + if (distanceP1Square < distance2) { + return true; + } + Vector3f nearestPointP2 = getClosestPointOnFiniteLine(sphere1.narrowPhaseGlobalPos, shapeReference.getTriangleGlobalPos().p2, shapeReference.getTriangleGlobalPos().p3); + float distanceP2Square = Vector3f.length2(nearestPointP2, sphere1.narrowPhaseGlobalPos); + System.out.println("distanceP2Square=" + distanceP2Square); + if (distanceP2Square < distance2) { + return true; + } + Vector3f nearestPointP3 = getClosestPointOnFiniteLine(sphere1.narrowPhaseGlobalPos, shapeReference.getTriangleGlobalPos().p3, shapeReference.getTriangleGlobalPos().p1); + float distanceP3Square = Vector3f.length2(nearestPointP3, sphere1.narrowPhaseGlobalPos); + System.out.println("distanceP3Square=" + distanceP3Square); + if (distanceP3Square < distance2) { + return true; + } + return false; + } + + private ToolCollisionSphereWithTriangle() {} +} diff --git a/src/org/atriasoft/phyligram/math/Ray.java b/src/org/atriasoft/phyligram/math/Ray.java new file mode 100644 index 0000000..d904bed --- /dev/null +++ b/src/org/atriasoft/phyligram/math/Ray.java @@ -0,0 +1,53 @@ +package org.atriasoft.phyligram.math; + +import org.atriasoft.etk.math.Vector3f; + +public class Ray { + public float maxFraction; //!< Maximum fraction value + public Vector3f point1; //! aabb.minX) { + return false; + } + if (this.minY > aabb.minY) { + return false; + } + if (this.minZ > aabb.minZ) { + return false; + } + if (this.maxX < aabb.maxX) { + return false; + } + if (this.maxY < aabb.maxY) { + return false; + } + if (this.maxZ < aabb.maxZ) { + return false; + } + return true; + } + + public Vector3f getMax() { + return new Vector3f(this.maxX, this.maxY, this.maxZ); + } + + public Vector3f getMin() { + return new Vector3f(this.minX, this.minY, this.minZ); + } + + /** + * Get the cube total volume. + * @return + */ + public float getVolume() { + return ((this.maxX - this.minX) * (this.maxY - this.minY) * (this.maxZ - this.minZ)); + } + + public boolean intersect(AABB other) { + if (this == other) { + return false; + } + if (null == other) { + return false; + } + if (this.minX > other.maxX) { + return false; + } + if (this.maxX < other.minX) { + return false; + } + if (this.minY > other.maxY) { + return false; + } + if (this.maxY < other.minY) { + return false; + } + if (this.minZ > other.maxZ) { + return false; + } + if (this.maxZ < other.minZ) { + return false; + } + return true; + } + + public void lessMax(float deltaX, float deltaY, float deltaZ) { + this.maxX -= deltaX; + this.maxY -= deltaY; + this.maxZ -= deltaZ; + } + + public void lessMin(float deltaX, float deltaY, float deltaZ) { + this.minX -= deltaX; + this.minY -= deltaY; + this.minZ -= deltaZ; + } + + public void mergeTwoAABBs(AABB aaa, AABB bbb) { + this.minX = Math.min(aaa.minX, bbb.minX); + this.minY = Math.min(aaa.minY, bbb.minY); + this.minZ = Math.min(aaa.minZ, bbb.minZ); + this.minX = Math.max(aaa.maxX, bbb.maxX); + this.minY = Math.max(aaa.maxY, bbb.maxY); + this.minZ = Math.max(aaa.maxZ, bbb.maxZ); + } + + public void set(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + this.minX = minX; + this.minY = minY; + this.minZ = minZ; + this.maxX = maxX; + this.maxY = maxY; + this.maxZ = maxZ; + } + + /* + * check if the ray intersects the AABB + * This method use the line vs AABB raycasting technique described in + * Real-time Collision Detection by Christer Ericson. + * @param ray Ray to test + * @return true The raytest intersect the AABB box + */ + public boolean testRayIntersect(final Ray ray) { + final Vector3f point2 = ray.point2.less(ray.point1).multiply(ray.maxFraction).add(ray.point1); + final float eeeX = this.maxX - this.minX; + final float eeeY = this.maxY - this.minY; + final float eeeZ = this.maxZ - this.minZ; + final Vector3f d = point2.less(ray.point1); + final float mmmX = ray.point1.x() - ray.point2.x() - this.minX - this.maxX; + final float mmmY = ray.point1.y() - ray.point2.y() - this.minY - this.maxY; + final float mmmZ = ray.point1.z() - ray.point2.z() - this.minZ - this.maxZ; + // Test if the AABB face normals are separating axis + float adx = FMath.abs(d.x()); + if (FMath.abs(mmmX) > eeeX + adx) { + return false; + } + float ady = FMath.abs(d.y()); + if (FMath.abs(mmmY) > eeeY + ady) { + return false; + } + float adz = FMath.abs(d.z()); + if (FMath.abs(mmmZ) > eeeZ + adz) { + return false; + } + // Add in an epsilon term to counteract arithmetic errors when segment is + // (near) parallel to a coordinate axis (see text for detail) + final float epsilon = 0.00001f; + adx += epsilon; + ady += epsilon; + adz += epsilon; + // Test if the cross products between face normals and ray direction are + // separating axis + if (FMath.abs(mmmY * d.z() - mmmZ * d.y()) > eeeY * adz + eeeZ * ady) { + return false; + } + if (FMath.abs(mmmZ * d.x() - mmmX * d.z()) > eeeX * adz + eeeZ * adx) { + return false; + } + if (FMath.abs(mmmX * d.y() - mmmY * d.x()) > eeeX * ady + eeeY * adx) { + return false; + } + // No separating axis has been found + return true; + } + + public void update(Vector3f point) { + if (this.minX > point.x()) { + this.minX = point.x(); + } + if (this.maxX < point.x()) { + this.maxX = point.x(); + } + if (this.minY > point.y()) { + this.minY = point.y(); + } + if (this.maxY < point.y()) { + this.maxY = point.y(); + } + if (this.minZ > point.z()) { + this.minZ = point.z(); + } + if (this.maxZ < point.z()) { + this.maxZ = point.z(); + } + } +} diff --git a/src/org/atriasoft/ege/physics/shape/Box.java b/src/org/atriasoft/phyligram/shape/Box.java similarity index 94% rename from src/org/atriasoft/ege/physics/shape/Box.java rename to src/org/atriasoft/phyligram/shape/Box.java index 625132f..29425fc 100644 --- a/src/org/atriasoft/ege/physics/shape/Box.java +++ b/src/org/atriasoft/phyligram/shape/Box.java @@ -3,7 +3,7 @@ * @copyright 2011, Edouard DUPIN, all right reserved * @license MPL v2.0 (see license file) */ -package org.atriasoft.ege.physics.shape; +package org.atriasoft.phyligram.shape; import org.atriasoft.etk.math.Vector3f; diff --git a/src/org/atriasoft/ege/physics/shape/Capsule.java b/src/org/atriasoft/phyligram/shape/Capsule.java similarity index 95% rename from src/org/atriasoft/ege/physics/shape/Capsule.java rename to src/org/atriasoft/phyligram/shape/Capsule.java index 401a7de..f213d45 100644 --- a/src/org/atriasoft/ege/physics/shape/Capsule.java +++ b/src/org/atriasoft/phyligram/shape/Capsule.java @@ -3,7 +3,7 @@ * @copyright 2011, Edouard DUPIN, all right reserved * @license MPL v2.0 (see license file) */ -package org.atriasoft.ege.physics.shape; +package org.atriasoft.phyligram.shape; import org.atriasoft.etk.math.Vector3f; diff --git a/src/org/atriasoft/ege/physics/shape/Concave.java b/src/org/atriasoft/phyligram/shape/Concave.java similarity index 96% rename from src/org/atriasoft/ege/physics/shape/Concave.java rename to src/org/atriasoft/phyligram/shape/Concave.java index fb79dab..1f1d365 100644 --- a/src/org/atriasoft/ege/physics/shape/Concave.java +++ b/src/org/atriasoft/phyligram/shape/Concave.java @@ -3,7 +3,7 @@ * @copyright 2011, Edouard DUPIN, all right reserved * @license MPL v2.0 (see license file) */ -package org.atriasoft.ege.physics.shape; +package org.atriasoft.phyligram.shape; import java.util.ArrayList; import java.util.List; diff --git a/src/org/atriasoft/ege/physics/shape/Cone.java b/src/org/atriasoft/phyligram/shape/Cone.java similarity index 95% rename from src/org/atriasoft/ege/physics/shape/Cone.java rename to src/org/atriasoft/phyligram/shape/Cone.java index 390ad53..66d8842 100644 --- a/src/org/atriasoft/ege/physics/shape/Cone.java +++ b/src/org/atriasoft/phyligram/shape/Cone.java @@ -3,7 +3,7 @@ * @copyright 2011, Edouard DUPIN, all right reserved * @license MPL v2.0 (see license file) */ -package org.atriasoft.ege.physics.shape; +package org.atriasoft.phyligram.shape; import org.atriasoft.etk.math.Vector3f; diff --git a/src/org/atriasoft/ege/physics/shape/ConvexHull.java b/src/org/atriasoft/phyligram/shape/ConvexHull.java similarity index 97% rename from src/org/atriasoft/ege/physics/shape/ConvexHull.java rename to src/org/atriasoft/phyligram/shape/ConvexHull.java index 8f884d7..f197567 100644 --- a/src/org/atriasoft/ege/physics/shape/ConvexHull.java +++ b/src/org/atriasoft/phyligram/shape/ConvexHull.java @@ -3,7 +3,7 @@ * @copyright 2011, Edouard DUPIN, all right reserved * @license MPL v2.0 (see license file) */ -package org.atriasoft.ege.physics.shape; +package org.atriasoft.phyligram.shape; import java.util.ArrayList; import java.util.List; diff --git a/src/org/atriasoft/ege/physics/shape/Cylinder.java b/src/org/atriasoft/phyligram/shape/Cylinder.java similarity index 94% rename from src/org/atriasoft/ege/physics/shape/Cylinder.java rename to src/org/atriasoft/phyligram/shape/Cylinder.java index acb169a..aae7ea4 100644 --- a/src/org/atriasoft/ege/physics/shape/Cylinder.java +++ b/src/org/atriasoft/phyligram/shape/Cylinder.java @@ -3,7 +3,7 @@ * @copyright 2011, Edouard DUPIN, all right reserved * @license MPL v2.0 (see license file) */ -package org.atriasoft.ege.physics.shape; +package org.atriasoft.phyligram.shape; import org.atriasoft.etk.math.Vector3f; diff --git a/src/org/atriasoft/phyligram/shape/HeightMapChunk.java b/src/org/atriasoft/phyligram/shape/HeightMapChunk.java new file mode 100644 index 0000000..80be6b4 --- /dev/null +++ b/src/org/atriasoft/phyligram/shape/HeightMapChunk.java @@ -0,0 +1,16 @@ +package org.atriasoft.phyligram.shape; + +import org.atriasoft.ephysics.collision.Triangle; + +public class HeightMapChunk extends Shape { + private Triangle[] triangles; + public HeightMapChunk(Triangle[] triangles) { + this.triangles = triangles; + } + @Override + public void display() { + // TODO Auto-generated method stub + super.display(); + } + +} diff --git a/src/org/atriasoft/ege/physics/shape/Shape.java b/src/org/atriasoft/phyligram/shape/Shape.java similarity index 84% rename from src/org/atriasoft/ege/physics/shape/Shape.java rename to src/org/atriasoft/phyligram/shape/Shape.java index 6f33506..a1280b0 100644 --- a/src/org/atriasoft/ege/physics/shape/Shape.java +++ b/src/org/atriasoft/phyligram/shape/Shape.java @@ -3,7 +3,7 @@ * @copyright 2011, Edouard DUPIN, all right reserved * @license MPL v2.0 (see license file) */ -package org.atriasoft.ege.physics.shape; +package org.atriasoft.phyligram.shape; import org.atriasoft.ege.internal.Log; import org.atriasoft.etk.math.Quaternion; @@ -30,34 +30,6 @@ public class Shape { return this.origin; } - public boolean isBox() { - return this instanceof Box; - }; - - public boolean isCapsule() { - return this instanceof Capsule; - } - - public boolean isConcave() { - return this instanceof Concave; - } - - public boolean isCone() { - return this instanceof Cone; - } - - public boolean isConvexHull() { - return this instanceof ConvexHull; - }; - - public boolean isCylinder() { - return this instanceof Cylinder; - }; - - public boolean isSphere() { - return this instanceof Sphere; - }; - public boolean parse(final String _line) { Log.error("dfgdfg"); /* diff --git a/src/org/atriasoft/ege/physics/shape/Sphere.java b/src/org/atriasoft/phyligram/shape/Sphere.java similarity index 94% rename from src/org/atriasoft/ege/physics/shape/Sphere.java rename to src/org/atriasoft/phyligram/shape/Sphere.java index a0d2bcd..c430300 100644 --- a/src/org/atriasoft/ege/physics/shape/Sphere.java +++ b/src/org/atriasoft/phyligram/shape/Sphere.java @@ -3,7 +3,7 @@ * @copyright 2011, Edouard DUPIN, all right reserved * @license MPL v2.0 (see license file) */ -package org.atriasoft.ege.physics.shape; +package org.atriasoft.phyligram.shape; import org.atriasoft.etk.math.Vector3f; diff --git a/src/org/atriasoft/phyligram/tree/CallbackOverlapping.java b/src/org/atriasoft/phyligram/tree/CallbackOverlapping.java new file mode 100644 index 0000000..04280f6 --- /dev/null +++ b/src/org/atriasoft/phyligram/tree/CallbackOverlapping.java @@ -0,0 +1,5 @@ +package org.atriasoft.phyligram.tree; + +public interface CallbackOverlapping { + public void callback(DTree nodeId); +} \ No newline at end of file diff --git a/src/org/atriasoft/phyligram/tree/CallbackRaycast.java b/src/org/atriasoft/phyligram/tree/CallbackRaycast.java new file mode 100644 index 0000000..72eedb2 --- /dev/null +++ b/src/org/atriasoft/phyligram/tree/CallbackRaycast.java @@ -0,0 +1,7 @@ +package org.atriasoft.phyligram.tree; + +import org.atriasoft.phyligram.math.Ray; + +public interface CallbackRaycast { + public float callback(DTree node, Ray ray); +} \ No newline at end of file diff --git a/src/org/atriasoft/phyligram/tree/DTree.java b/src/org/atriasoft/phyligram/tree/DTree.java new file mode 100644 index 0000000..d77f753 --- /dev/null +++ b/src/org/atriasoft/phyligram/tree/DTree.java @@ -0,0 +1,28 @@ +package org.atriasoft.phyligram.tree; + +import java.lang.ref.WeakReference; + +import org.atriasoft.phyligram.shape.AABB; + +/** + * It represents a node of the dynamic AABB tree. + */ +public class DTree { + private static int simpleCounter = 0; + public final int uid; + /** + * A node is either in the tree (has a parent) or in the free nodes list (has a next node) + */ + WeakReference parent = null; //!< Parent node ID (0 is ROOT) + int height = -1; //!< Height of the node in the tree TODO check the need... + AABB aabb = AABB.createInvertedEmpty(); //!< Fat axis aligned bounding box (AABB) corresponding to the node + + public DTree() { + this.uid = simpleCounter++; + } + + boolean isLeaf() { + return false; + } + +} diff --git a/src/org/atriasoft/phyligram/tree/DTreeLeafData.java b/src/org/atriasoft/phyligram/tree/DTreeLeafData.java new file mode 100644 index 0000000..1bf3096 --- /dev/null +++ b/src/org/atriasoft/phyligram/tree/DTreeLeafData.java @@ -0,0 +1,15 @@ +package org.atriasoft.phyligram.tree; + +class DTreeLeafData extends DTree { + Object dataPointer = null; + + public DTreeLeafData(final INTERNAL_DATA_TYPE dataPointer) { + super(); + this.dataPointer = dataPointer; + } + + @Override + boolean isLeaf() { + return true; + } +} \ No newline at end of file diff --git a/src/org/atriasoft/phyligram/tree/DTreeLeafInt.java b/src/org/atriasoft/phyligram/tree/DTreeLeafInt.java new file mode 100644 index 0000000..ab601f9 --- /dev/null +++ b/src/org/atriasoft/phyligram/tree/DTreeLeafInt.java @@ -0,0 +1,16 @@ +package org.atriasoft.phyligram.tree; + +class DTreeLeafInt extends DTree { + int dataInt0 = 0; + int dataInt1 = 0; + + public DTreeLeafInt(final int dataInt0, final int dataInt1) { + this.dataInt0 = dataInt0; + this.dataInt1 = dataInt1; + } + + @Override + boolean isLeaf() { + return true; + } +} diff --git a/src/org/atriasoft/phyligram/tree/DTreeNode.java b/src/org/atriasoft/phyligram/tree/DTreeNode.java new file mode 100644 index 0000000..ccbe43b --- /dev/null +++ b/src/org/atriasoft/phyligram/tree/DTreeNode.java @@ -0,0 +1,6 @@ +package org.atriasoft.phyligram.tree; + +class DTreeNode extends DTree { + DTree childrenleft = null; //!< Left child of the node + DTree childrenright = null; //!< Right child of the node +} \ No newline at end of file diff --git a/src/org/atriasoft/phyligram/tree/DynamicAABBTree.java b/src/org/atriasoft/phyligram/tree/DynamicAABBTree.java new file mode 100644 index 0000000..00beaf7 --- /dev/null +++ b/src/org/atriasoft/phyligram/tree/DynamicAABBTree.java @@ -0,0 +1,560 @@ +package org.atriasoft.phyligram.tree; + +import java.lang.ref.WeakReference; +import java.util.Stack; + +import org.atriasoft.phyligram.shape.AABB; +import org.atriasoft.phyligram.internal.Log; +import org.atriasoft.phyligram.math.Ray; +import org.atriasoft.etk.math.FMath; +import org.atriasoft.etk.math.Vector3f; + +/** + * It implements a dynamic AABB tree that is used for broad-phase + * collision detection. This data structure is inspired by Nathanael Presson's + * dynamic tree implementation in BulletPhysics. The following implementation is + * based on the one from Erin Catto in Box2D as described in the book + * "Introduction to Game Physics with Box2D" by Ian Parberry. + */ +public class DynamicAABBTree { + private DTree rootNode; //!< Pointer to the memory location of the nodes of the tree + + /// Constructor + public DynamicAABBTree() { + init(); + } + + /// Add an object into the tree (where node data is a pointer) + public DTree addObject(final AABB aabb, final INTERNAL_DATA_TYPE data) { + final DTreeLeafData node = new DTreeLeafData(data); + addObjectInternal(aabb, node); + return node; + } + + /// Internally add an object into the tree + private void addObjectInternal(final AABB aabb, final DTree leafNode) { + // Create the fat aabb to use in the tree + leafNode.aabb.clone(); + // Set the height of the node in the tree + leafNode.height = 0; + // Insert the new leaf node in the tree + insertLeafNode(leafNode); + assert (leafNode.isLeaf()); + } + + /// Balance the sub-tree of a given node using left or right rotations. + private DTree balanceSubTreeAtNode(final DTree node) { + assert (node != null); + // If the node is a leaf or the height of A's sub-tree is less than 2 + if (node.isLeaf() || node.height < 2) { + // Do not perform any rotation + return node; + } + final DTreeNode nodeA = (DTreeNode) node; + // Get the two children nodes + final DTree nodeB = nodeA.childrenleft; + final DTree nodeC = nodeA.childrenright; + // Compute the factor of the left and right sub-trees + final int balanceFactor = nodeC.height - nodeB.height; + // If the right node C is 2 higher than left node B + if (balanceFactor > 1) { + assert (!nodeC.isLeaf()); + final DTreeNode nodeCTree = (DTreeNode) nodeC; + final DTree nodeF = nodeCTree.childrenleft; + final DTree nodeG = nodeCTree.childrenright; + nodeCTree.childrenleft = node; + nodeCTree.parent = nodeA.parent; + nodeA.parent = new WeakReference<>(nodeC); + if (nodeC.parent != null) { + final DTreeNode nodeCParent = (DTreeNode) nodeC.parent.get(); + if (nodeCParent.childrenleft == node) { + nodeCParent.childrenleft = nodeC; + } else { + assert (nodeCParent.childrenright == node); + nodeCParent.childrenright = nodeC; + } + } else { + this.rootNode = nodeC; + } + assert (!nodeC.isLeaf()); + assert (!nodeA.isLeaf()); + // If the right node C was higher than left node B because of the F node + if (nodeF.height > nodeG.height) { + nodeCTree.childrenright = nodeF; + nodeA.childrenright = nodeG; + nodeG.parent = new WeakReference<>(node); + // Recompute the AABB of node A and C + nodeA.aabb.mergeTwoAABBs(nodeB.aabb, nodeG.aabb); + nodeC.aabb.mergeTwoAABBs(nodeA.aabb, nodeF.aabb); + // Recompute the height of node A and C + nodeA.height = FMath.max(nodeB.height, nodeG.height) + 1; + nodeC.height = FMath.max(nodeA.height, nodeF.height) + 1; + assert (nodeA.height > 0); + assert (nodeC.height > 0); + } else { + // If the right node C was higher than left node B because of node G + nodeCTree.childrenright = nodeG; + nodeA.childrenright = nodeF; + nodeF.parent = new WeakReference<>(node); + // Recompute the AABB of node A and C + nodeA.aabb.mergeTwoAABBs(nodeB.aabb, nodeF.aabb); + nodeC.aabb.mergeTwoAABBs(nodeA.aabb, nodeG.aabb); + // Recompute the height of node A and C + nodeA.height = FMath.max(nodeB.height, nodeF.height) + 1; + nodeC.height = FMath.max(nodeA.height, nodeG.height) + 1; + assert (nodeA.height > 0); + assert (nodeC.height > 0); + } + // Return the new root of the sub-tree + return nodeC; + } + // If the left node B is 2 higher than right node C + if (balanceFactor < -1) { + assert (!nodeB.isLeaf()); + final DTreeNode nodeBTree = (DTreeNode) nodeB; + final DTree nodeF = nodeBTree.childrenleft; + final DTree nodeG = nodeBTree.childrenright; + nodeBTree.childrenleft = node; + nodeB.parent = nodeA.parent; + nodeA.parent = new WeakReference<>(nodeB); + if (nodeB.parent != null) { + final DTreeNode nodeBParent = (DTreeNode) nodeB.parent.get(); + if (nodeBParent.childrenleft == node) { + nodeBParent.childrenleft = nodeB; + } else { + assert (nodeBParent.childrenright == node); + nodeBParent.childrenright = nodeB; + } + } else { + this.rootNode = nodeB; + } + assert (!nodeB.isLeaf()); + assert (!nodeA.isLeaf()); + // If the left node B was higher than right node C because of the F node + if (nodeF.height > nodeG.height) { + nodeBTree.childrenright = nodeF; + nodeA.childrenleft = nodeG; + nodeG.parent = new WeakReference<>(node); + // Recompute the AABB of node A and B + nodeA.aabb.mergeTwoAABBs(nodeC.aabb, nodeG.aabb); + nodeB.aabb.mergeTwoAABBs(nodeA.aabb, nodeF.aabb); + // Recompute the height of node A and B + nodeA.height = FMath.max(nodeC.height, nodeG.height) + 1; + nodeB.height = FMath.max(nodeA.height, nodeF.height) + 1; + assert (nodeA.height > 0); + assert (nodeB.height > 0); + } else { + // If the left node B was higher than right node C because of node G + nodeBTree.childrenright = nodeG; + nodeA.childrenleft = nodeF; + nodeF.parent = new WeakReference<>(node); + // Recompute the AABB of node A and B + nodeA.aabb.mergeTwoAABBs(nodeC.aabb, nodeF.aabb); + nodeB.aabb.mergeTwoAABBs(nodeA.aabb, nodeG.aabb); + // Recompute the height of node A and B + nodeA.height = FMath.max(nodeC.height, nodeF.height) + 1; + nodeB.height = FMath.max(nodeA.height, nodeG.height) + 1; + assert (nodeA.height > 0); + assert (nodeB.height > 0); + } + // Return the new root of the sub-tree + return nodeB; + } + // If the sub-tree is balanced, return the current root node + return node; + } + + /// Compute the height of the tree + public int computeHeight() { + return computeHeight(this.rootNode); + } + + /// Compute the height of a given node in the tree + private int computeHeight(final DTree node) { + // If the node is a leaf, its height is zero + if (node.isLeaf()) { + return 0; + } + final DTreeNode nodeTree = (DTreeNode) node; + + // Compute the height of the left and right sub-tree + final int leftHeight = computeHeight(nodeTree.childrenleft); + final int rightHeight = computeHeight(nodeTree.childrenright); + // Return the height of the node + return 1 + Math.max(leftHeight, rightHeight); + } + + /// Return the fat AABB corresponding to a given node ID + public AABB getFatAABB(final DTree node) { + return node.aabb; + } + + public int getNodeDataInt0(final DTree node) { + assert (node.isLeaf()); + return ((DTreeLeafInt) node).dataInt0; + } + + public int getNodeDataInt1(final DTree node) { + assert (node.isLeaf()); + return ((DTreeLeafInt) node).dataInt1; + } + + /// Return the data pointer of a given leaf node of the tree + public Object getNodeDataPointer(final DTree node) { + assert (node.isLeaf()); + return ((DTreeLeafData) node).dataPointer; + } + + /// Return the root AABB of the tree + public AABB getRootAABB() { + return getFatAABB(this.rootNode); + } + + /// Initialize the tree + private void init() { + this.rootNode = null; + } + + /// Insert a leaf node in the tree + private void insertLeafNode(final DTree inNode) { + // If the tree is empty + if (this.rootNode == null) { + this.rootNode = inNode; + return; + } + // Find the best sibling node for the new node + final AABB newNodeAABB = inNode.aabb; + DTree currentNode = this.rootNode; + while (!currentNode.isLeaf()) { + final DTreeNode node = (DTreeNode) currentNode; + final DTree leftChild = node.childrenleft; + final DTree rightChild = node.childrenright; + // Compute the merged AABB + final float volumeAABB = currentNode.aabb.getVolume(); + final AABB mergedAABBs = AABB.mergeAABB(currentNode.aabb, newNodeAABB); + final float mergedVolume = mergedAABBs.getVolume(); + // Compute the cost of making the current node the sibbling of the new node + final float costS = 2.0f * mergedVolume; + // Compute the minimum cost of pushing the new node further down the tree (inheritance cost) + final float costI = 2.0f * (mergedVolume - volumeAABB); + // Compute the cost of descending into the left child + float costLeft; + final AABB currentAndLeftAABB = new AABB(); + currentAndLeftAABB.mergeTwoAABBs(newNodeAABB, leftChild.aabb); + if (leftChild.isLeaf()) { // If the left child is a leaf + costLeft = currentAndLeftAABB.getVolume() + costI; + } else { + final float leftChildVolume = leftChild.aabb.getVolume(); + costLeft = costI + currentAndLeftAABB.getVolume() - leftChildVolume; + } + // Compute the cost of descending into the right child + float costRight; + final AABB currentAndRightAABB = new AABB(); + currentAndRightAABB.mergeTwoAABBs(newNodeAABB, rightChild.aabb); + if (rightChild.isLeaf()) { // If the right child is a leaf + costRight = currentAndRightAABB.getVolume() + costI; + } else { + final float rightChildVolume = rightChild.aabb.getVolume(); + costRight = costI + currentAndRightAABB.getVolume() - rightChildVolume; + } + // If the cost of making the current node a sibbling of the new node is smaller than + // the cost of going down into the left or right child + if (costS < costLeft && costS < costRight) { + break; + } + // It is cheaper to go down into a child of the current node, choose the best child + if (costLeft < costRight) { + currentNode = leftChild; + } else { + currentNode = rightChild; + } + } + final DTree siblingNode = currentNode; + // Create a new parent for the new node and the sibling node + final WeakReference oldParentNode = siblingNode.parent; + final DTreeNode newParentNode = new DTreeNode(); + newParentNode.parent = currentNode.parent; + newParentNode.aabb.mergeTwoAABBs(currentNode.aabb, newNodeAABB); + newParentNode.height = currentNode.height + 1; + // If the sibling node was not the root node + if (oldParentNode != null) { + // replace in parent the child with the new child + final DTreeNode parentNode = (DTreeNode) oldParentNode.get(); + if (parentNode.childrenleft == siblingNode) { + parentNode.childrenleft = newParentNode; + } else { + parentNode.childrenright = newParentNode; + } + } else { + // If the sibling node was the root node + this.rootNode = newParentNode; + } + // setup the children + newParentNode.childrenleft = siblingNode; + newParentNode.childrenright = inNode; + siblingNode.parent = new WeakReference<>(newParentNode); + inNode.parent = new WeakReference<>(newParentNode); + + // Move up in the tree to change the AABBs that have changed + currentNode = newParentNode; + assert (!currentNode.isLeaf()); + while (currentNode != null) { + // Balance the sub-tree of the current node if it is not balanced + currentNode = balanceSubTreeAtNode(currentNode); + assert (inNode.isLeaf()); + assert (!currentNode.isLeaf()); + final DTreeNode nodeDouble = (DTreeNode) currentNode; + final DTree leftChild = nodeDouble.childrenleft; + final DTree rightChild = nodeDouble.childrenright; + assert (leftChild != null); + assert (rightChild != null); + // Recompute the height of the node in the tree + currentNode.height = FMath.max(leftChild.height, rightChild.height) + 1; + assert (currentNode.height > 0); + // Recompute the AABB of the node + currentNode.aabb.mergeTwoAABBs(leftChild.aabb, rightChild.aabb); + if (currentNode.parent != null) { + currentNode = currentNode.parent.get(); + } else { + currentNode = null; + } + } + assert (inNode.isLeaf()); + } + + /// Ray casting method + public void raycast(final Ray ray, final CallbackRaycast callback) { + if (callback == null) { + Log.error("call with null callback"); + return; + } + float maxFraction = ray.maxFraction; + // 128 max + final Stack stack = new Stack<>(); + stack.push(this.rootNode); + // Walk through the tree from the root looking for proxy shapes + // that overlap with the ray AABB + while (stack.size() > 0) { + // Get the next node in the stack + final DTree node = stack.pop(); + // If it is a null node, skip it + if (node == null) { + continue; + } + final Ray rayTemp = new Ray(ray.point1, ray.point2, maxFraction); + // Test if the ray intersects with the current node AABB + if (!node.aabb.testRayIntersect(rayTemp)) { + continue; + } + // If the node is a leaf of the tree + if (node.isLeaf()) { + // Call the callback that will raycast again the broad-phase shape + final float hitFraction = callback.callback(node, rayTemp); + // If the user returned a hitFraction of zero, it means that + // the raycasting should stop here + if (hitFraction == 0.0f) { + return; + } + // If the user returned a positive fraction + if (hitFraction > 0.0f) { + // We update the maxFraction value and the ray + // AABB using the new maximum fraction + if (hitFraction < maxFraction) { + maxFraction = hitFraction; + } + } + // If the user returned a negative fraction, we continue + // the raycasting as if the proxy shape did not exist + } else { // If the node has children + final DTreeNode tmpNode = (DTreeNode) node; + // Push its children in the stack of nodes to explore + stack.push(tmpNode.childrenleft); + stack.push(tmpNode.childrenright); + } + } + + } + + /// Release a node + private void releaseNode(final DTree node) { + //this.numberNodes--; + } + + /// Remove a leaf node from the tree + private void removeLeafNode(final DTree node) { + assert (node.isLeaf()); + // If we are removing the root node (root node is a leaf in this case) + if (this.rootNode == node) { + this.rootNode = null; + return; + } + // parent can not be null. + final DTreeNode parentNode = (DTreeNode) node.parent.get(); + final WeakReference grandParentNodeWeak = parentNode.parent; + DTree siblingNode; + if (parentNode.childrenleft == node) { + siblingNode = parentNode.childrenright; + } else { + siblingNode = parentNode.childrenleft; + } + // If the parent of the node to remove is not the root node + if (grandParentNodeWeak == null) { + // If the parent of the node to remove is the root node + // The sibling node becomes the new root node + this.rootNode = siblingNode; + siblingNode.parent = null; + releaseNode(parentNode); + } else { + final DTreeNode grandParentNode = (DTreeNode) grandParentNodeWeak.get(); + // Destroy the parent node + if (grandParentNode.childrenleft == parentNode) { + grandParentNode.childrenleft = siblingNode; + } else { + grandParentNode.childrenright = siblingNode; + } + siblingNode.parent = parentNode.parent; + releaseNode(parentNode); + // Now, we need to recompute the AABBs of the node on the path back to the root + // and make sure that the tree is still balanced + DTree currentNode = grandParentNode; + while (currentNode != null) { + // Balance the current sub-tree if necessary + currentNode = balanceSubTreeAtNode(currentNode); + assert (!currentNode.isLeaf()); + final DTreeNode currentTreeNode = (DTreeNode) currentNode; + // Get the two children of the current node + final DTree leftChild = currentTreeNode.childrenleft; + final DTree rightChild = currentTreeNode.childrenright; + // Recompute the AABB and the height of the current node + currentNode.aabb.mergeTwoAABBs(leftChild.aabb, rightChild.aabb); + currentNode.height = FMath.max(leftChild.height, rightChild.height) + 1; + assert (currentNode.height > 0); + if (currentNode.parent == null) { + currentNode = null; + } else { + currentNode = currentNode.parent.get(); + } + } + } + } + + /// Remove an object from the tree + public void removeObject(final DTree node) { + assert (node.isLeaf()); + // Remove the node from the tree + removeLeafNode(node); + releaseNode(node); + } + + /// Report all shapes overlapping with the AABB given in parameter. + public void reportAllShapesOverlappingWithAABB(final AABB aabb, final CallbackOverlapping callback) { + if (callback == null) { + Log.error("call with null callback"); + return; + } + //Log.error("reportAllShapesOverlappingWithAABB"); + // Create a stack with the nodes to visit + final Stack stack = new Stack<>(); + // 64 max + stack.push(this.rootNode); + //Log.error(" add stack: " + this.rootNode); + // While there are still nodes to visit + while (stack.size() > 0) { + // Get the next node ID to visit + final DTree nodeIDToVisit = stack.pop(); + // Skip it if it is a null node + if (nodeIDToVisit == null) { + continue; + } + // Get the corresponding node + final DTree nodeToVisit = nodeIDToVisit; + //Log.error(" check colision: " + nodeIDToVisit); + // If the AABB in parameter overlaps with the AABB of the node to visit + if (aabb.intersect(nodeToVisit.aabb)) { + // If the node is a leaf + if (nodeToVisit.isLeaf()) { + /* + if (aabb != nodeToVisit.aabb) { + Log.error(" ======> Real collision ..."); + } + */ + // Notify the broad-phase about a new potential overlapping pair + callback.callback(nodeIDToVisit); + } else { + final DTreeNode tmp = (DTreeNode) nodeToVisit; + // If the node is not a leaf + // We need to visit its children + stack.push(tmp.childrenleft); + stack.push(tmp.childrenright); + //Log.error(" add stack: " + tmp.childrenleft); + //Log.error(" add stack: " + tmp.childrenright); + } + } + } + } + + /// Clear all the nodes and reset the tree + public void reset() { + // Initialize the tree + init(); + } + + /// Update the dynamic tree after an object has moved. + /// If the new AABB of the object that has moved is still inside its fat AABB, then + /// nothing is done. Otherwise, the corresponding node is removed and reinserted into the tree. + /// The method returns true if the object has been reinserted into the tree. The "displacement" + /// argument is the linear velocity of the AABB multiplied by the elapsed time between two + /// frames. If the "forceReinsert" parameter is true, we force a removal and reinsertion of the node + /// (this can be useful if the shape AABB has become much smaller than the previous one for instance). + public boolean updateObject(final DTree node, final AABB newAABB, final Vector3f displacement) { + return updateObject(node, newAABB, displacement, false); + } + + public boolean updateObject(final DTree node, final AABB newAABB, final Vector3f displacement, final boolean forceReinsert) { + assert (node.isLeaf()); + assert (node.height >= 0); + //Log.verbose(" compare : " + node.aabb.getMin() + " " + node.aabb.getMax()); + //Log.verbose(" : " + newAABB.getMin() + " " + newAABB.getMax()); + // If the new AABB is still inside the fat AABB of the node + if (!forceReinsert && node.aabb.contains(newAABB)) { + return false; + } + // If the new AABB is outside the fat AABB, we remove the corresponding node + removeLeafNode(node); + // Compute the fat AABB by inflating the AABB with a ant gap + node.aabb = newAABB.clone(); + // Inflate the fat AABB in direction of the linear motion of the AABB + float xmin = node.aabb.getMin().x(); + float ymin = node.aabb.getMin().y(); + float zmin = node.aabb.getMin().z(); + float xmax = node.aabb.getMax().x(); + float ymax = node.aabb.getMax().y(); + float zmax = node.aabb.getMax().z(); + if (displacement.x() < 0.0f) { + xmin = node.aabb.getMin().x() + displacement.x(); + } else { + xmax = node.aabb.getMax().x() + displacement.x(); + } + if (displacement.y() < 0.0f) { + ymin = node.aabb.getMin().y() + displacement.y(); + } else { + ymax = node.aabb.getMax().y() + displacement.y(); + } + if (displacement.z() < 0.0f) { + zmin = node.aabb.getMin().z() + displacement.z(); + } else { + zmax = node.aabb.getMax().z() + displacement.z(); + } + node.aabb.set(xmin, ymin, zmin, xmax, ymax, zmax); + //Log.error(" compare : " + node.aabb.getMin() + " " + node.aabb.getMax()); + //Log.error(" : " + newAABB.getMin() + " " + newAABB.getMax()); + if (!node.aabb.contains(newAABB)) { + //Log.critical("ERROR"); + } + assert (node.aabb.contains(newAABB)); + // Reinsert the node into the tree + insertLeafNode(node); + return true; + } + +} diff --git a/src/org/atriasoft/phyligram/tree/PairDTree.java b/src/org/atriasoft/phyligram/tree/PairDTree.java new file mode 100644 index 0000000..ff78cb0 --- /dev/null +++ b/src/org/atriasoft/phyligram/tree/PairDTree.java @@ -0,0 +1,37 @@ +package org.atriasoft.phyligram.tree; + +import java.util.Set; + +public record PairDTree( + DTree first, + DTree second) { + public static int countInSet(final Set values, final PairDTree sample) { + int count = 0; + for (final PairDTree elem : values) { + if (elem.first != sample.first) { + continue; + } + if (elem.second != sample.second) { + continue; + } + count++; + } + return count; + } + + public PairDTree(final DTree first, final DTree second) { + if (first.uid < second.uid) { + this.first = first; + this.second = second; + } else { + this.first = second; + this.second = first; + + } + } + + @Override + public String toString() { + return "PairDTree [first=" + this.first.uid + ", second=" + this.second.uid + "]"; + } +} diff --git a/test/src/test/atriasoft/ege/TestTransformation3D.java b/test/src/test/atriasoft/ege/TestTransformation3D.java index ee79095..5a4841c 100644 --- a/test/src/test/atriasoft/ege/TestTransformation3D.java +++ b/test/src/test/atriasoft/ege/TestTransformation3D.java @@ -3,7 +3,7 @@ package test.atriasoft.ege; import org.atriasoft.ege.geometry.AABB; import org.atriasoft.ege.geometry.Geometry3D; import org.atriasoft.ege.geometry.OBB; -import org.atriasoft.ege.geometry.Plane; +import org.atriasoft.ege.geometry.Plane____; import org.atriasoft.ege.geometry.Sphere; import org.atriasoft.ege.geometry.Triangle; import org.atriasoft.etk.math.Matrix3f; @@ -53,7 +53,7 @@ public class TestTransformation3D { @Test void testPointInPlane() { - final Plane shape = new Plane((new Vector3f(4, 4, 4)).normalize(), (float) Math.sqrt(1 * 1 + 1 * 1)); + final Plane____ shape = new Plane____((new Vector3f(4, 4, 4)).normalize(), (float) Math.sqrt(1 * 1 + 1 * 1)); Assert.assertFalse(Geometry3D.pointInPlane(new Vector3f(0, 0, 0), shape)); Assert.assertFalse(Geometry3D.pointInPlane(new Vector3f(6, 6, 6), shape)); Assert.assertTrue(Geometry3D.pointInPlane(new Vector3f(3, 3, 3), shape)); diff --git a/test/src/test/atriasoft/phyligram/TestCollisionSphereTriangle.java b/test/src/test/atriasoft/phyligram/TestCollisionSphereTriangle.java new file mode 100644 index 0000000..4d74a04 --- /dev/null +++ b/test/src/test/atriasoft/phyligram/TestCollisionSphereTriangle.java @@ -0,0 +1,77 @@ +package test.atriasoft.phyligram; + +import java.util.stream.Stream; + +import org.atriasoft.etk.math.Transform3D; +import org.atriasoft.etk.math.Vector3f; +import org.atriasoft.phyligram.PhysicSphere; +import org.atriasoft.phyligram.PhysicTriangle; +import org.atriasoft.phyligram.ToolCollisionSphereWithTriangle; +import org.atriasoft.phyligram.shape.AABB; +import org.junit.Assert; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class TestCollisionSphereTriangle { + + static Stream generateData() { + return Stream.of( // ... + Arguments.of(1, new Vector3f(0.0f, 0.0f, 1.001f), false), // test just on top Z + Arguments.of(2, new Vector3f(0.0f, 0.0f, -1.001f), false), // test just on bottom Z + Arguments.of(2, new Vector3f(2.001f, 0.0f, 0.0f), false), // test on the top X + Arguments.of(2, new Vector3f(-2.001f, 0.0f, 0.0f), false), // test on the bottom X + Arguments.of(2, new Vector3f(0.0f, 1.7f, 0.0f), false), // test on the top Y + Arguments.of(2, new Vector3f(0.0f, -1.7f, 0.0f), false), // test on the bottom Y + // test collision + Arguments.of(2, new Vector3f(0.0f, 0.0f, 0.9f), true), // in center + Arguments.of(1, new Vector3f(0.0f, 0.0f, 0.999f), true), // test just on top Z + Arguments.of(2, new Vector3f(0.0f, 0.0f, -0.999f), true), // test just on bottom Z + Arguments.of(2, new Vector3f(1.999f, 0.0f, 0.0f), true), // test on the top X + Arguments.of(2, new Vector3f(-1.999f, 0.0f, 0.0f), true), // test on the bottom X + Arguments.of(2, new Vector3f(0.0f, 1.5f, 0.0f), true), // test on the top Y + Arguments.of(2, new Vector3f(0.0f, -1.5f, 0.0f), true), // test on the bottom Y + Arguments.of(2, new Vector3f(1.5f, 1.5f, 0.0f), true), // test on corner A + Arguments.of(2, new Vector3f(1.5f, -1.5f, 0.0f), true) // test on corner B + ); + } + + @ParameterizedTest + @MethodSource("generateData") + void testsphereOut(int testId, Vector3f position, boolean resultTheoricValue) { + System.out.println("AAAAA "); + float testCoefficient = 1.0f; + PhysicSphere sphere = new PhysicSphere(); + sphere.setSize(testCoefficient); + PhysicTriangle triangle = new PhysicTriangle(); + triangle.setPoints(new Vector3f(testCoefficient, testCoefficient, 0.0f), new Vector3f(testCoefficient, -testCoefficient, 0.0f), new Vector3f(-testCoefficient, 0.0f, 0.0f)); + Transform3D transformGlobalTriangle = Transform3D.IDENTITY; + Transform3D transformGlobalsphere = new Transform3D(position.multiply(testCoefficient)); + AABB aabb = new AABB(); + sphere.updateAABB(transformGlobalsphere, aabb); + sphere.updateForNarrowCollision(transformGlobalsphere); + triangle.updateAABB(transformGlobalTriangle, aabb); + triangle.updateForNarrowCollision(transformGlobalTriangle); + boolean result = ToolCollisionSphereWithTriangle.testCollide(sphere, triangle); + Assert.assertEquals(resultTheoricValue, result); + } + + @Test + void testsphereOutTop() { + float testCoefficient = 2.0f; + PhysicSphere sphere = new PhysicSphere(); + sphere.setSize(testCoefficient); + PhysicTriangle triangle = new PhysicTriangle(); + triangle.setPoints(new Vector3f(testCoefficient, testCoefficient, 0.0f), new Vector3f(testCoefficient, -testCoefficient, 0.0f), new Vector3f(-testCoefficient, 0.0f, 0.0f)); + Transform3D transformGlobalTriangle = Transform3D.IDENTITY; + Transform3D transformGlobalsphere = new Transform3D(new Vector3f(0.0f, 0.0f, testCoefficient + 0.001f)); + AABB aabb = new AABB(); + sphere.updateAABB(transformGlobalsphere, aabb); + sphere.updateForNarrowCollision(transformGlobalsphere); + triangle.updateAABB(transformGlobalTriangle, aabb); + triangle.updateForNarrowCollision(transformGlobalTriangle); + boolean result = ToolCollisionSphereWithTriangle.testCollide(sphere, triangle); + Assert.assertFalse(result); + } +}