Explorar o código

Initial work on physics raycasting

Borut %!s(int64=6) %!d(string=hai) anos
pai
achega
e566309a96

+ 10 - 0
src/Physics/IPhysicsEngine.ts

@@ -3,6 +3,8 @@ import { Vector3, Quaternion } from "../Maths/math";
 import { AbstractMesh } from "../Meshes/abstractMesh";
 import { PhysicsImpostor, IPhysicsEnabledObject } from "./physicsImpostor";
 import { PhysicsJoint, IMotorEnabledJoint } from "./physicsJoint";
+import { PhysicsRaycastResult } from "./physicsRaycastResult";
+
 /**
  * Interface used to describe a physics joint
  */
@@ -44,6 +46,7 @@ export interface IPhysicsEnginePlugin {
     setBodyRestitution(impostor: PhysicsImpostor, restitution: number): void;
     sleepBody(impostor: PhysicsImpostor): void;
     wakeUpBody(impostor: PhysicsImpostor): void;
+    raycast(from: Vector3, to: Vector3): PhysicsRaycastResult;
     //Joint Update
     updateDistanceJoint(joint: PhysicsJoint, maxDistance: number, minDistance?: number): void;
     setMotor(joint: IMotorEnabledJoint, speed: number, maxForce?: number, motorIndex?: number): void;
@@ -153,6 +156,13 @@ export interface IPhysicsEngine {
     getImpostorWithPhysicsBody(body: any): Nullable<PhysicsImpostor>;
 
     /**
+     * Does a raycast in the physics world
+     * @param from defines physics body used by the impostor
+     * @returns PhysicsRaycastResult
+     */
+    raycast(from: Vector3, to: Vector3): PhysicsRaycastResult;
+
+    /**
      * Called by the scene. No need to call it.
      * @param delta defines the timespam between frames
      */

+ 42 - 0
src/Physics/Plugins/ammoJSPlugin.ts

@@ -6,6 +6,7 @@ import { PhysicsJoint, IMotorEnabledJoint, DistanceJointData } from "../../Physi
 import { VertexBuffer } from "../../Meshes/buffer";
 import { Nullable } from "../../types";
 import { AbstractMesh } from "../../Meshes/abstractMesh";
+import { PhysicsRaycastResult } from "../physicsRaycastResult";
 
 declare var Ammo: any;
 
@@ -43,6 +44,9 @@ export class AmmoJSPlugin implements IPhysicsEnginePlugin {
     private _tmpAmmoVectorB: any;
     private _tmpAmmoVectorC: any;
     private _tmpContactCallbackResult = false;
+    private _tmpAmmoVectorRCA: any;
+    private _tmpAmmoVectorRCB: any;
+    private _raycastResult: PhysicsRaycastResult;
 
     private static readonly DISABLE_COLLISION_FLAG = 4;
     private static readonly KINEMATIC_FLAG = 2;
@@ -787,4 +791,42 @@ export class AmmoJSPlugin implements IPhysicsEnginePlugin {
 
         this.world = null;
     }
+
+    /**
+     * @param from when should the ray start?
+     * @param to when should the ray end?
+     */
+    public raycast(from: Vector3, to: Vector3) {
+        this._tmpAmmoVectorRCA = new this.bjsAMMO.btVector3(from.x, from.y, from.z);
+        this._tmpAmmoVectorRCB = new this.bjsAMMO.btVector3(to.x, to.y, to.z);
+
+        var rayCallback = new this.bjsAMMO.ClosestRayResultCallback(this._tmpAmmoVectorRCA, this._tmpAmmoVectorRCB);
+        this.world.rayTest(this._tmpAmmoVectorRCA, this._tmpAmmoVectorRCB, rayCallback);
+
+        this._raycastResult.reset(from, to);
+        if (rayCallback.hasHit()) {
+            // TODO: do we want/need the body? If so, set all the data
+            /*
+            var rigidBody = this.bjsAMMO.btRigidBody.prototype.upcast(
+                rayCallback.get_m_collisionObject()
+            );
+            var body = {};
+            */
+            this._raycastResult.setHitData(
+                {
+                    x: rayCallback.get_m_hitNormalWorld().x(),
+                    y: rayCallback.get_m_hitNormalWorld().y(),
+                    z: rayCallback.get_m_hitNormalWorld().z(),
+                },
+                {
+                    x: rayCallback.get_m_hitPointWorld().x(),
+                    y: rayCallback.get_m_hitPointWorld().y(),
+                    z: rayCallback.get_m_hitPointWorld().z(),
+                }
+            );
+            this._raycastResult.calculateHitDistance();
+        }
+
+        return this._raycastResult;
+    }
 }

+ 34 - 0
src/Physics/Plugins/cannonJSPlugin.ts

@@ -7,6 +7,7 @@ import { IPhysicsEnginePlugin, PhysicsImpostorJoint } from "../../Physics/IPhysi
 import { PhysicsImpostor, IPhysicsEnabledObject } from "../../Physics/physicsImpostor";
 import { PhysicsJoint, IMotorEnabledJoint, DistanceJointData, SpringJointData } from "../../Physics/physicsJoint";
 import { PhysicsEngine } from "../../Physics/physicsEngine";
+import { PhysicsRaycastResult } from "../physicsRaycastResult";
 
 //declare var require: any;
 declare var CANNON: any;
@@ -18,6 +19,8 @@ export class CannonJSPlugin implements IPhysicsEnginePlugin {
     public name: string = "CannonJSPlugin";
     private _physicsMaterials = new Array();
     private _fixedTimeStep: number = 1 / 60;
+    private _cannonRaycastResult: any;
+    private _raycastResult: PhysicsRaycastResult;
     //See https://github.com/schteppe/CANNON.js/blob/gh-pages/demos/collisionFilter.html
     public BJSCANNON: any;
 
@@ -33,6 +36,8 @@ export class CannonJSPlugin implements IPhysicsEnginePlugin {
         this.world = new this.BJSCANNON.World();
         this.world.broadphase = new this.BJSCANNON.NaiveBroadphase();
         this.world.solver.iterations = iterations;
+        this._cannonRaycastResult = new this.BJSCANNON.RaycastResult();
+        this._raycastResult = new PhysicsRaycastResult();
     }
 
     public setGravity(gravity: Vector3): void {
@@ -674,6 +679,35 @@ export class CannonJSPlugin implements IPhysicsEnginePlugin {
             }
         };
     }
+
+    /**
+     * @param from when should the ray start?
+     * @param to when should the ray end?
+     */
+    public raycast(from: Vector3, to: Vector3) {
+        this._cannonRaycastResult.reset();
+        this.world.raycastClosest(from, to, {}, this._cannonRaycastResult);
+
+        this._raycastResult.reset(from, to);
+        if (this._cannonRaycastResult.hasHit) {
+            // TODO: do we also want to get the body it hit?
+            this._raycastResult.setHitData(
+                {
+                    x: this._cannonRaycastResult.hitNormalWorld.x,
+                    y: this._cannonRaycastResult.hitNormalWorld.y,
+                    z: this._cannonRaycastResult.hitNormalWorld.z,
+                },
+                {
+                    x: this._cannonRaycastResult.hitPointWorld.x,
+                    y: this._cannonRaycastResult.hitPointWorld.y,
+                    z: this._cannonRaycastResult.hitPointWorld.z,
+                }
+            );
+            this._raycastResult.setHitDistance(this._cannonRaycastResult.distance);
+        }
+
+        return this._raycastResult;
+    }
 }
 
 PhysicsEngine.DefaultPluginFactory = () => { return new CannonJSPlugin(); };

+ 8 - 0
src/Physics/Plugins/oimoJSPlugin.ts

@@ -474,4 +474,12 @@ export class OimoJSPlugin implements IPhysicsEnginePlugin {
     public dispose() {
         this.world.clear();
     }
+
+    /**
+     * @param from when should the ray start?
+     * @param to when should the ray end?
+     */
+    public raycast(from: Vector3, to: Vector3) {
+        Logger.Warn("raycast is not currently supported by the Oimo physics plugin");
+    }
 }

+ 9 - 0
src/Physics/physicsEngine.ts

@@ -3,6 +3,7 @@ import { Vector3 } from "../Maths/math";
 import { IPhysicsEngine, PhysicsImpostorJoint, IPhysicsEnginePlugin } from "./IPhysicsEngine";
 import { PhysicsImpostor, IPhysicsEnabledObject } from "./physicsImpostor";
 import { PhysicsJoint } from "./physicsJoint";
+import { PhysicsRaycastResult } from "./physicsRaycastResult";
 import { _DevTools } from '../Misc/devTools';
 
 /**
@@ -225,4 +226,12 @@ export class PhysicsEngine implements IPhysicsEngine {
 
         return null;
     }
+
+    /**
+     * @param from when should the ray start?
+     * @param to when should the ray end?
+     */
+    public raycast(from: Vector3, to: Vector3): PhysicsRaycastResult {
+        return this._physicsPlugin.raycast(from, to);
+    }
 }

+ 118 - 0
src/Physics/physicsRaycastResult.ts

@@ -0,0 +1,118 @@
+import { Vector3 } from "../Maths/math";
+
+/**
+ * Holds the data for the raycast result
+ * @see https://doc.babylonjs.com/how_to/using_the_physics_engine
+ */
+export class PhysicsRaycastResult {
+
+    private _hasHit: boolean = false;
+
+    private _hitDistance: number = 0;
+    private _hitNormalWorld: Vector3 = Vector3.Zero();
+    private _hitPointWorld: Vector3 = Vector3.Zero();
+    private _rayFromWorld: Vector3 = Vector3.Zero();
+    private _rayToWorld: Vector3 = Vector3.Zero();
+
+    /**
+     * Gets if there was a hit
+     */
+    get hasHit(): boolean {
+        return this._hasHit;
+    }
+
+    /**
+     * Gets the distance from the hit
+     */
+    get hitDistance(): number {
+        return this._hitDistance;
+    }
+
+    /**
+     * Gets the hit normal/direction in the world
+     */
+    get hitNormalWorld(): Vector3 {
+        return this._hitNormalWorld;
+    }
+
+    /**
+     * Gets the hit point in the world
+     */
+    get hitPointWorld(): Vector3 {
+        return this._hitPointWorld;
+    }
+
+    /**
+     * Gets the ray "start point" of the ray in the world
+     */
+    get rayFromWorld(): Vector3 {
+        return this._rayFromWorld;
+    }
+
+    /**
+     * Gets the ray "end point" of the ray in the world
+     */
+    get rayToWorld(): Vector3 {
+        return this._rayToWorld;
+    }
+
+    /**
+     * @param hitNormalWorld
+     * @param hitPointWorld
+     */
+    public setHitData(hitNormalWorld: IXYZ, hitPointWorld: IXYZ) {
+        this._hasHit = true;
+        this._hitNormalWorld = new Vector3(hitNormalWorld.x, hitNormalWorld.y, hitNormalWorld.z);
+        this._hitPointWorld = new Vector3(hitPointWorld.x, hitPointWorld.y, hitPointWorld.z);
+    }
+
+    /**
+     * @param distance
+     */
+    public setHitDistance(distance: number) {
+        this._hitDistance = distance;
+    }
+
+    /**
+     * Calculates the distance
+     */
+    public calculateHitDistance() {
+        this._hitDistance = Vector3.Distance(this._rayFromWorld, this._hitPointWorld);
+    }
+
+    /**
+     * @param from The from point on world space
+     * @param to The to point on world space
+     */
+    public reset(from: Vector3 = Vector3.Zero(), to: Vector3 = Vector3.Zero()) {
+        this._rayFromWorld = from;
+        this._rayToWorld = to;
+
+        this._hasHit = false;
+        this._hitDistance = 0;
+
+        this._hitNormalWorld = Vector3.Zero();
+        this._hitPointWorld = Vector3.Zero();
+    }
+
+}
+
+/**
+ * Interface for the size containing width and height
+ */
+interface IXYZ {
+    /**
+     * X
+     */
+    x: number;
+
+    /**
+     * Y
+     */
+    y: number;
+
+    /**
+     * Z
+     */
+    z: number;
+}