Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support get contact points and get closest point on shape from a point #2458

Merged
merged 15 commits into from
Dec 18, 2024
3 changes: 2 additions & 1 deletion packages/core/src/physics/CharacterController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Collider } from "./Collider";
import { PhysicsScene } from "./PhysicsScene";
import { ControllerNonWalkableMode } from "./enums/ControllerNonWalkableMode";
import { ColliderShape } from "./shape";
import { deepClone } from "../clone/CloneManager";
import { deepClone, ignoreClone } from "../clone/CloneManager";

/**
* The character controllers.
Expand Down Expand Up @@ -166,6 +166,7 @@ export class CharacterController extends Collider {
(<ICharacterController>this._nativeCollider).getWorldPosition(this.entity.transform.worldPosition);
}

@ignoreClone
private _setUpDirection(): void {
(<ICharacterController>this._nativeCollider).setUpDirection(this._upDirection);
}
Expand Down
41 changes: 41 additions & 0 deletions packages/core/src/physics/Collision.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,46 @@
import { ContactPoint } from "./ContactPoint";
import { ColliderShape } from "./shape";
import { ICollision } from "@galacean/engine-design";

/**
* Collision information between two shapes when they collide.
*/
export class Collision {
/** @internal */
_nativeCollision: ICollision;

/** The target shape be collided. */
shape: ColliderShape;

/**
* Count of contact points.
*/
get contactCount(): number {
return this._nativeCollision.contactCount;
}

/**
* Get contact points.
* @param outContacts - The result of contact points
* @returns The actual count of contact points
* @remarks To optimize performance, the engine does not modify the length of the array you pass.
* You need to obtain the actual number of contact points from the function's return value.
*/
getContacts(outContacts: ContactPoint[]): number {
const { shape0Id, shape1Id } = this._nativeCollision;
const factor = shape0Id < shape1Id ? 1 : -1;

const nativeContactPoints = this._nativeCollision.getContacts();
const length = nativeContactPoints.size();
for (let i = 0; i < length; i++) {
const nativeContractPoint = nativeContactPoints.get(i);

const contact = (outContacts[i] ||= new ContactPoint());
contact.position.copyFrom(nativeContractPoint.position);
contact.normal.copyFrom(nativeContractPoint.normal).scale(factor);
contact.impulse.copyFrom(nativeContractPoint.impulse).scale(factor);
contact.separation = nativeContractPoint.separation;
}
return length;
}
}
15 changes: 15 additions & 0 deletions packages/core/src/physics/ContactPoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Vector3 } from "@galacean/engine-math";

/**
* Describes a contact point where the collision occurs.
*/
export class ContactPoint {
/** The position of the contact point between the shapes, in world space. */
readonly position = new Vector3();
/** The normal of the contacting surfaces at the contact point. The normal direction points from the second shape to the first shape. */
readonly normal = new Vector3();
/** The impulse applied at the contact point, in world space. Divide by the simulation time step to get a force value. */
readonly impulse = new Vector3();
/** The separation of the shapes at the contact point. A negative separation denotes a penetration. */
separation: number;
}
35 changes: 19 additions & 16 deletions packages/core/src/physics/PhysicsScene.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ICharacterController, ICollider, IPhysics, IPhysicsScene } from "@galacean/engine-design";
import { ICharacterController, ICollider, IPhysics, IPhysicsScene, ICollision } from "@galacean/engine-design";
import { MathUtil, Ray, Vector3 } from "@galacean/engine-math";
import { Layer } from "../Layer";
import { Scene } from "../Scene";
Expand Down Expand Up @@ -28,14 +28,16 @@ export class PhysicsScene {
private _gravity: Vector3 = new Vector3(0, -9.81, 0);
private _nativePhysicsScene: IPhysicsScene;

private _onContactEnter = (obj1: number, obj2: number) => {
private _onContactEnter = (nativeCollision: ICollision) => {
const physicalObjectsMap = Engine._physicalObjectsMap;
const shape1 = physicalObjectsMap[obj1];
const shape2 = physicalObjectsMap[obj2];
const { shape0Id, shape1Id } = nativeCollision;
const shape1 = physicalObjectsMap[shape0Id];
const shape2 = physicalObjectsMap[shape1Id];
const collision = PhysicsScene._collision;
collision._nativeCollision = nativeCollision;

shape1.collider.entity._scripts.forEach(
(element: Script) => {
let collision = PhysicsScene._collision;
collision.shape = shape2;
element.onCollisionEnter(collision);
},
Expand All @@ -46,7 +48,6 @@ export class PhysicsScene {

shape2.collider.entity._scripts.forEach(
(element: Script) => {
let collision = PhysicsScene._collision;
collision.shape = shape1;
element.onCollisionEnter(collision);
},
Expand All @@ -56,14 +57,16 @@ export class PhysicsScene {
);
};

private _onContactExit = (obj1: number, obj2: number) => {
private _onContactExit = (nativeCollision: ICollision) => {
const physicalObjectsMap = Engine._physicalObjectsMap;
const shape1 = physicalObjectsMap[obj1];
const shape2 = physicalObjectsMap[obj2];
const { shape0Id, shape1Id } = nativeCollision;
const shape1 = physicalObjectsMap[shape0Id];
const shape2 = physicalObjectsMap[shape1Id];
const collision = PhysicsScene._collision;
collision._nativeCollision = nativeCollision;

shape1.collider.entity._scripts.forEach(
(element: Script) => {
let collision = PhysicsScene._collision;
collision.shape = shape2;
element.onCollisionExit(collision);
},
Expand All @@ -74,7 +77,6 @@ export class PhysicsScene {

shape2.collider.entity._scripts.forEach(
(element: Script) => {
let collision = PhysicsScene._collision;
collision.shape = shape1;
element.onCollisionExit(collision);
},
Expand All @@ -83,14 +85,16 @@ export class PhysicsScene {
}
);
};
private _onContactStay = (obj1: number, obj2: number) => {
private _onContactStay = (nativeCollision: ICollision) => {
const physicalObjectsMap = Engine._physicalObjectsMap;
const shape1 = physicalObjectsMap[obj1];
const shape2 = physicalObjectsMap[obj2];
const { shape0Id, shape1Id } = nativeCollision;
const shape1 = physicalObjectsMap[shape0Id];
const shape2 = physicalObjectsMap[shape1Id];
const collision = PhysicsScene._collision;
collision._nativeCollision = nativeCollision;

shape1.collider.entity._scripts.forEach(
(element: Script) => {
let collision = PhysicsScene._collision;
collision.shape = shape2;
element.onCollisionStay(collision);
},
Expand All @@ -101,7 +105,6 @@ export class PhysicsScene {

shape2.collider.entity._scripts.forEach(
(element: Script) => {
let collision = PhysicsScene._collision;
collision.shape = shape1;
element.onCollisionStay(collision);
},
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/physics/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export { HitResult } from "./HitResult";
export { PhysicsMaterial } from "./PhysicsMaterial";
export { PhysicsScene } from "./PhysicsScene";
export { StaticCollider } from "./StaticCollider";
export { Collision } from "./Collision";
export { ContactPoint } from "./ContactPoint";
export * from "./enums";
export * from "./joint";
export * from "./shape";
30 changes: 28 additions & 2 deletions packages/core/src/physics/shape/ColliderShape.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { IColliderShape } from "@galacean/engine-design";
import { PhysicsMaterial } from "../PhysicsMaterial";
import { Vector3 } from "@galacean/engine-math";
import { MathUtil, Matrix, Quaternion, Vector3 } from "@galacean/engine-math";
import { Collider } from "../Collider";
import { deepClone, ignoreClone } from "../../clone/CloneManager";
import { ICustomClone } from "../../clone/ComponentCloner";
Expand All @@ -11,6 +11,8 @@ import { Engine } from "../../Engine";
*/
export abstract class ColliderShape implements ICustomClone {
private static _idGenerator: number = 0;
private static _tempWorldPos: Vector3 = new Vector3();
private static _tempWorldRot: Quaternion = new Quaternion();

/** @internal */
@ignoreClone
Expand Down Expand Up @@ -80,7 +82,7 @@ export abstract class ColliderShape implements ICustomClone {
}

/**
* The local rotation of this ColliderShape.
* The local rotation of this ColliderShape, in radians.
*/
get rotation(): Vector3 {
return this._rotation;
Expand Down Expand Up @@ -133,6 +135,30 @@ export abstract class ColliderShape implements ICustomClone {
Engine._physicalObjectsMap[this._id] = this;
}

/**
* Get the distance and the closest point on the shape from a point.
* @param point - Location in world space you want to find the closest point to
* @param outClosestPoint - The closest point on the shape in world space
* @returns The distance between the point and the shape
*/
getClosestPoint(point: Vector3, outClosestPoint: Vector3): number {
const collider = this._collider;
if (collider.enabled === false || collider.entity._isActiveInHierarchy === false) {
console.warn("The collider is not active in scene.");
return -1;
}

const res = this._nativeShape.pointDistance(point);
const distance = res.w;
if (distance > 0) {
outClosestPoint.set(res.x, res.y, res.z);
} else {
outClosestPoint.copyFrom(point);
}

return Math.sqrt(distance);
}

/**
* @internal
*/
Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/physics/shape/PlaneColliderShape.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Vector3 } from "@galacean/engine-math";
import { PhysicsScene } from "../PhysicsScene";
import { ColliderShape } from "./ColliderShape";

Expand All @@ -9,4 +10,9 @@ export class PlaneColliderShape extends ColliderShape {
super();
this._nativeShape = PhysicsScene._nativePhysics.createPlaneColliderShape(this._id, this._material._nativeMaterial);
}

override getClosestPoint(point: Vector3, closestPoint: Vector3): number {
console.error("PlaneColliderShape is not support getClosestPoint");
return -1;
}
}
37 changes: 37 additions & 0 deletions packages/design/src/physics/ICollision.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Interface of collision.
*/
export interface ICollision {
/** The unique id of the first collider. */
shape0Id: number;
/** The unique id of the second collider. */
shape1Id: number;
/** Count of contact points. */
contactCount: number;
/** Get contact points. */
getContacts(): VectorContactPairPoint;
}

interface VectorContactPairPoint {
size(): number;
get(index: number): IContactPoint;
}

interface IContactPoint {
position: {
x: number;
y: number;
z: number;
};
normal: {
x: number;
y: number;
z: number;
};
impulse: {
x: number;
y: number;
z: number;
};
separation: number;
}
7 changes: 4 additions & 3 deletions packages/design/src/physics/IPhysics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { IPhysicsScene } from "./IPhysicsScene";
import { IStaticCollider } from "./IStaticCollider";
import { IFixedJoint, IHingeJoint, ISpringJoint } from "./joints";
import { IBoxColliderShape, ICapsuleColliderShape, IPlaneColliderShape, ISphereColliderShape } from "./shape";
import { ICollision } from "./ICollision";

/**
* The interface of physics creation.
Expand Down Expand Up @@ -36,9 +37,9 @@ export interface IPhysics {
*/
createPhysicsScene(
physicsManager: IPhysicsManager,
onContactEnter?: (obj1: number, obj2: number) => void,
onContactExit?: (obj1: number, obj2: number) => void,
onContactStay?: (obj1: number, obj2: number) => void,
onContactEnter?: (collision: ICollision) => void,
onContactExit?: (collision: ICollision) => void,
onContactStay?: (collision: ICollision) => void,
onTriggerEnter?: (obj1: number, obj2: number) => void,
onTriggerExit?: (obj1: number, obj2: number) => void,
onTriggerStay?: (obj1: number, obj2: number) => void
Expand Down
1 change: 1 addition & 0 deletions packages/design/src/physics/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ export type { IPhysicsMaterial } from "./IPhysicsMaterial";
export type { IPhysicsScene } from "./IPhysicsScene";
export type { IPhysicsManager } from "./IPhysicsManager";
export type { IStaticCollider } from "./IStaticCollider";
export type { ICollision } from "./ICollision";
export * from "./joints/index";
export * from "./shape/index";
8 changes: 7 additions & 1 deletion packages/design/src/physics/shape/IColliderShape.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Vector3 } from "@galacean/engine-math";
import { Quaternion, Vector3, Vector4 } from "@galacean/engine-math";
import { IPhysicsMaterial } from "../IPhysicsMaterial";

/**
Expand Down Expand Up @@ -41,6 +41,12 @@ export interface IColliderShape {
*/
setIsTrigger(value: boolean): void;

/**
* Get the distance between a point and the shape.
* @param point - The point
* @returns The distance information
*/
pointDistance(point: Vector3): Vector4;
/**
* Decrements the reference count of a shape and releases it if the new reference count is zero.
*/
Expand Down
Loading
Loading