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
48 changes: 48 additions & 0 deletions packages/core/src/physics/Collision.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,53 @@
import { Vector3 } from "@galacean/engine-math";
import { ColliderShape } from "./shape";
import { ICollision } from "@galacean/engine-design";

/**
* Describes a contact point where the collision occurs.
*/
export interface ContactPoint {
/** The position of the contact point between the shapes, in world space. */
readonly position: 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: Vector3;
/** The impulse applied at the contact point, in world space. Divide by the simulation time step to get a force value. */
readonly impulse: Vector3;
/** The separation of the shapes at the contact point. A negative separation denotes a penetration. */
readonly separation: number;
}

export class Collision {
/** @internal */
_nativeCollision: ICollision;

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

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

Check warning on line 31 in packages/core/src/physics/Collision.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/physics/Collision.ts#L30-L31

Added lines #L30 - L31 were not covered by tests

/**
* Get contact points.
* @param outContacts - The result of contact points
* @returns The result of contact points
*/
getContacts(outContacts: ContactPoint[]): ContactPoint[] {
const nativeContactPoints = this._nativeCollision.getContacts();
for (let i = 0, n = nativeContactPoints.size(); i < n; i++) {
const nativeContractPoint = nativeContactPoints.get(i);
const { position, normal, impulse, separation } = nativeContractPoint;
const contact: ContactPoint = {
position: new Vector3(position.x, position.y, position.z),
normal: new Vector3(normal.x, normal.y, normal.z),
impulse: new Vector3(impulse.x, impulse.y, impulse.z),
separation: separation
};
outContacts.push(contact);
}
return outContacts;
}

Check warning on line 52 in packages/core/src/physics/Collision.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/physics/Collision.ts#L39-L52

Added lines #L39 - L52 were not covered by tests
}
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
25 changes: 24 additions & 1 deletion 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 { 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 @@
*/
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 @@ -133,6 +135,27 @@
Engine._physicalObjectsMap[this._id] = this;
}

/**
* Get the distance and the closest point on the shape from a point.
* @param point - The point
* @param outClosestPoint - The result of the closest point on the shape
* @returns The distance between the point and the shape
*/
getDistanceAndClosestPointFromPoint(point: Vector3, outClosestPoint: Vector3): number {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#include "PxPhysicsAPI.h"

using namespace physx;

PxVec3 getClosestPointOnShape(PxShape* shape, const PxVec3& point) {
    PxGeometryHolder geomHolder = shape->getGeometry(); // 获取 PxShape 的几何信息

    if (!geomHolder.any().isValid()) {
        throw std::runtime_error("Invalid geometry in shape.");
    }

    PxTransform pose = shape->getActor()->getGlobalPose() * shape->getLocalPose(); // 获取 PxShape 的全局变换

    PxVec3 closestPoint;
    PxReal distance = PxGeometryQuery::pointDistance(point, geomHolder.any(), pose, &closestPoint);

    return closestPoint; // 返回最近点
}

const tempQuat = ColliderShape._tempWorldRot;
const tempPos = ColliderShape._tempWorldPos;
Vector3.add(this._collider.entity.transform.position, this._position, tempPos);
Quaternion.fromAngle(this._rotation, tempQuat);
Quaternion.multiply(this._collider.entity.transform.rotationQuaternion, tempQuat, tempQuat);
const res = this._nativeShape.pointDistance(tempPos, tempQuat, point);
const distance = res.distance;
outClosestPoint.copyFrom(res.closestPoint);
if (distance > 0) {
outClosestPoint.subtract(tempPos);
}
return Math.sqrt(distance);
}

Check warning on line 157 in packages/core/src/physics/shape/ColliderShape.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/physics/shape/ColliderShape.ts#L145-L157

Added lines #L145 - L157 were not covered by tests

/**
* @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 @@
super();
this._nativeShape = PhysicsScene._nativePhysics.createPlaneColliderShape(this._id, this._material._nativeMaterial);
}

override getDistanceAndClosestPointFromPoint(point: Vector3, closestPoint: Vector3): number {
console.error("PlaneColliderShape is not support getDistanceAndClosestPointFromPoint");
return -1;
}

Check warning on line 17 in packages/core/src/physics/shape/PlaneColliderShape.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/physics/shape/PlaneColliderShape.ts#L15-L17

Added lines #L15 - L17 were not covered by tests
}
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(): PhysXVectorPxContactPairPoint;
}

interface PhysXVectorPxContactPairPoint {
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";
25 changes: 24 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 } from "@galacean/engine-math";
import { IPhysicsMaterial } from "../IPhysicsMaterial";

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

/**
* Get the distance between a point and the shape.
* @param position - The position in world space
* @param rotation - The rotation in world space
* @param point - The point
* @returns The distance information
*/
pointDistance(position: Vector3, rotation: Quaternion, point: Vector3): IPointDistanceInfo;
/**
* Decrements the reference count of a shape and releases it if the new reference count is zero.
*/
destroy(): void;
}

/**
* Distance information of a point to the shape.
*/
export interface IPointDistanceInfo {
/**
* The distance between the point and the shape.
*/
distance: number;

/**
* The closest point on the shape.
*/
closestPoint: Vector3;
}
2 changes: 1 addition & 1 deletion packages/design/src/physics/shape/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type { IColliderShape } from "./IColliderShape";
export type { IColliderShape, IPointDistanceInfo } from "./IColliderShape";
export type { IBoxColliderShape } from "./IBoxColliderShape";
export type { ICapsuleColliderShape } from "./ICapsuleColliderShape";
export type { ISphereColliderShape } from "./ISphereColliderShape";
Expand Down
10 changes: 10 additions & 0 deletions packages/math/src/Quaternion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,16 @@
out._onValueChanged && out._onValueChanged();
}

/**
* Create a quaternion from the specified angle.
* @param angle - The specified angle
* @param out - The calculated quaternion
*/
static fromAngle(angle: Vector3, out: Quaternion): void {
const angleToRadian = Math.PI / 180;
Quaternion.rotationYawPitchRoll(angle.y * angleToRadian, angle.x * angleToRadian, angle.z * angleToRadian, out);
}

Check warning on line 424 in packages/math/src/Quaternion.ts

View check run for this annotation

Codecov / codecov/patch

packages/math/src/Quaternion.ts#L422-L424

Added lines #L422 - L424 were not covered by tests

/** @internal */
_x: number;
/** @internal */
Expand Down
7 changes: 4 additions & 3 deletions packages/physics-lite/src/LitePhysics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
IBoxColliderShape,
ICapsuleColliderShape,
ICharacterController,
ICollision,
IDynamicCollider,
IFixedJoint,
IHingeJoint,
Expand Down Expand Up @@ -43,9 +44,9 @@ export class LitePhysics implements IPhysics {
*/
createPhysicsScene(
physicsManager: LitePhysicsManager,
onContactBegin?: (obj1: number, obj2: number) => void,
onContactEnd?: (obj1: number, obj2: number) => void,
onContactPersist?: (obj1: number, obj2: number) => void,
onContactBegin?: (collision: ICollision) => void,
onContactEnd?: (collision: ICollision) => void,
onContactPersist?: (collision: ICollision) => void,
onTriggerBegin?: (obj1: number, obj2: number) => void,
onTriggerEnd?: (obj1: number, obj2: number) => void,
onTriggerPersist?: (obj1: number, obj2: number) => void
Expand Down
14 changes: 7 additions & 7 deletions packages/physics-lite/src/LitePhysicsScene.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BoundingBox, BoundingSphere, CollisionUtil, DisorderedArray, Ray, Vector3 } from "@galacean/engine";
import { ICharacterController, IPhysicsScene } from "@galacean/engine-design";
import { ICharacterController, ICollision, IPhysicsScene } from "@galacean/engine-design";
import { LiteCollider } from "./LiteCollider";
import { LiteDynamicCollider } from "./LiteDynamicCollider";
import { LiteHitResult } from "./LiteHitResult";
Expand All @@ -17,9 +17,9 @@ export class LitePhysicsScene implements IPhysicsScene {
private static _currentHit: LiteHitResult = new LiteHitResult();
private static _hitResult: LiteHitResult = new LiteHitResult();

private readonly _onContactEnter?: (obj1: number, obj2: number) => void;
private readonly _onContactExit?: (obj1: number, obj2: number) => void;
private readonly _onContactStay?: (obj1: number, obj2: number) => void;
private readonly _onContactEnter?: (collision: ICollision) => void;
private readonly _onContactExit?: (collision: ICollision) => void;
private readonly _onContactStay?: (collision: ICollision) => void;
private readonly _onTriggerEnter?: (obj1: number, obj2: number) => void;
private readonly _onTriggerExit?: (obj1: number, obj2: number) => void;
private readonly _onTriggerStay?: (obj1: number, obj2: number) => void;
Expand All @@ -34,9 +34,9 @@ export class LitePhysicsScene implements IPhysicsScene {
private _eventPool: TriggerEvent[] = [];

constructor(
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
Loading
Loading