Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions packages/core/src/physics/DynamicCollider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,15 @@ export class DynamicCollider extends Collider {
this._phasedActiveInScene && (<IDynamicCollider>this._nativeCollider).addForce(force);
}

/**
* Apply a force to the DynamicCollider at a given position in world space.
* @param force - The force to apply, in world space
* @param position - The position where the force is applied, in world space
*/
applyForceAtPosition(force: Vector3, position: Vector3): void {
this._phasedActiveInScene && (<IDynamicCollider>this._nativeCollider).addForceAtPosition(force, position);
}

/**
* Apply a torque to the DynamicCollider.
* @param torque - The force make the collider rotate
Expand Down
7 changes: 7 additions & 0 deletions packages/design/src/physics/IDynamicCollider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,13 @@ export interface IDynamicCollider extends ICollider {
*/
addForce(force: Vector3): void;

/**
* Apply a force to the dynamic collider at a world-space position.
* @param force - The force make the collider move
* @param position - The world-space position where the force is applied
*/
addForceAtPosition(force: Vector3, position: Vector3): void;

/**
* Apply a torque to the dynamic collider.
* @param torque - The force make the collider rotate
Expand Down
7 changes: 7 additions & 0 deletions packages/physics-lite/src/LiteDynamicCollider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ export class LiteDynamicCollider extends LiteCollider implements IDynamicCollide
throw "Physics-lite don't support addForce. Use Physics-PhysX instead!";
}

/**
* {@inheritDoc IDynamicCollider.addForceAtPosition }
*/
addForceAtPosition(force: Vector3, position: Vector3): void {
throw "Physics-lite don't support addForceAtPosition. Use Physics-PhysX instead!";
}

/**
* {@inheritDoc IDynamicCollider.addTorque }
*/
Expand Down
Binary file modified packages/physics-physx/libs/physx.release.simd.wasm
Binary file not shown.
Binary file modified packages/physics-physx/libs/physx.release.wasm
Binary file not shown.
11 changes: 9 additions & 2 deletions packages/physics-physx/src/PhysXDynamicCollider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,14 +215,21 @@ export class PhysXDynamicCollider extends PhysXCollider implements IDynamicColli
* {@inheritDoc IDynamicCollider.addForce }
*/
addForce(force: Vector3) {
this._pxActor.addForce({ x: force.x, y: force.y, z: force.z });
this._pxActor.addForce(force);
}

/**
* {@inheritDoc IDynamicCollider.addForceAtPosition }
*/
addForceAtPosition(force: Vector3, position: Vector3) {
this._pxActor.addForceAtPos(force, position);
}

/**
* {@inheritDoc IDynamicCollider.addTorque }
*/
addTorque(torque: Vector3) {
this._pxActor.addTorque({ x: torque.x, y: torque.y, z: torque.z });
this._pxActor.addTorque(torque);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions packages/physics-physx/src/PhysXPhysics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ export class PhysXPhysics implements IPhysics {
this._runTimeMode = runtimeMode;
this._wasmSIMDModeUrl =
runtimeUrls?.wasmSIMDModeUrl ??
"https://mdn.alipayobjects.com/rms/afts/file/A*iHrYQKBrgTAAAAAAQ4AAAAgAehQnAQ/physx.release.simd.js";
"https://mdn.alipayobjects.com/rms/uri/file/as/apwallet/1781696156399/suyi/physx.release.simd.js";
this._wasmModeUrl =
runtimeUrls?.wasmModeUrl ??
"https://mdn.alipayobjects.com/rms/afts/file/A*DFuvR6Mv5C0AAAAAQ4AAAAgAehQnAQ/physx.release.js";
"https://mdn.alipayobjects.com/rms/uri/file/as/apwallet/1781696156399/suyi/physx.release.js";
}

/**
Expand Down
65 changes: 63 additions & 2 deletions tests/src/core/physics/DynamicCollider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@ import {
PlaneColliderShape
} from "@galacean/engine-core";
import { WebGLEngine } from "@galacean/engine";
import { PhysXPhysics } from "@galacean/engine-physics-physx";
import { PhysXPhysics, PhysXRuntimeMode } from "@galacean/engine-physics-physx";
import { Vector3 } from "@galacean/engine-math";
import { vi, describe, beforeAll, beforeEach, expect, it } from "vitest";

const physXWasmModeUrl = new URL("../../../../packages/physics-physx/libs/physx.release.js", import.meta.url).href;
const physXWasmSIMDModeUrl = new URL("../../../../packages/physics-physx/libs/physx.release.simd.js", import.meta.url)
.href;

describe("DynamicCollider", function () {
let engine: Engine;
let rootEntity: Entity;
Expand Down Expand Up @@ -53,7 +57,13 @@ describe("DynamicCollider", function () {
}

beforeAll(async function () {
engine = await WebGLEngine.create({ canvas: document.createElement("canvas"), physics: new PhysXPhysics() });
engine = await WebGLEngine.create({
canvas: document.createElement("canvas"),
physics: new PhysXPhysics(PhysXRuntimeMode.Auto, {
wasmModeUrl: physXWasmModeUrl,
wasmSIMDModeUrl: physXWasmSIMDModeUrl
})
});

rootEntity = engine.sceneManager.activeScene.createRootEntity("root");
});
Expand Down Expand Up @@ -290,6 +300,57 @@ describe("DynamicCollider", function () {
expect(formatValue(boxCollider.angularVelocity.y)).eq(200);
});

it("applyForceAtPosition delegates world-space force and position to the native collider", function () {
const box = addBox(new Vector3(2, 2, 2), DynamicCollider, new Vector3(0, 0, 0));
const boxCollider = box.getComponent(DynamicCollider);
const nativeCollider = (boxCollider as any)._nativeCollider;
const force = new Vector3(1, 2, 3);
const position = new Vector3(4, 5, 6);
const addForceAtPositionSpy = vi.spyOn(nativeCollider, "addForceAtPosition").mockImplementation(() => {});

boxCollider.applyForceAtPosition(force, position);

expect(addForceAtPositionSpy).toHaveBeenCalledOnce();
expect(addForceAtPositionSpy).toHaveBeenCalledWith(force, position);
});

it("applyForceAtPosition produces the same motion as an equivalent force and torque", function () {
const setupBox = () => {
const box = addBox(new Vector3(2, 2, 2), DynamicCollider, new Vector3(0, 0, 0));
const collider = box.getComponent(DynamicCollider);
collider.mass = 1;
collider.useGravity = false;
collider.centerOfMass = new Vector3(0, 0, 0);
collider.inertiaTensor = new Vector3(1, 1, 1);
return collider;
};

const force = new Vector3(1, 0, 0);
const position = new Vector3(0, 1, 0);

const reference = setupBox();
reference.applyForce(force);
reference.applyTorque(new Vector3(0, 0, -1));
// @ts-ignore
engine.sceneManager.activeScene.physics._update(1);
const referenceLinearVelocity = reference.linearVelocity.clone();
const referenceAngularVelocity = reference.angularVelocity.clone();

rootEntity.clearChildren();

const target = setupBox();
target.applyForceAtPosition(force, position);
// @ts-ignore
engine.sceneManager.activeScene.physics._update(1);

expect(formatValue(target.linearVelocity.x)).eq(formatValue(referenceLinearVelocity.x));
expect(formatValue(target.linearVelocity.y)).eq(formatValue(referenceLinearVelocity.y));
expect(formatValue(target.linearVelocity.z)).eq(formatValue(referenceLinearVelocity.z));
expect(formatValue(target.angularVelocity.x)).eq(formatValue(referenceAngularVelocity.x));
expect(formatValue(target.angularVelocity.y)).eq(formatValue(referenceAngularVelocity.y));
expect(formatValue(target.angularVelocity.z)).eq(formatValue(referenceAngularVelocity.z));
});

it("maxDepenetrationVelocity", function () {
const box = addBox(new Vector3(2, 2, 2), DynamicCollider, new Vector3(0, 0, 0));
const box2 = addBox(new Vector3(2, 2, 2), DynamicCollider, new Vector3(0, 0, 0));
Expand Down
28 changes: 28 additions & 0 deletions tests/src/core/physics/PhysicsScene.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,34 @@ describe("Physics Test", () => {
engineLite.run();
});

it("dynamic force APIs report unsupported behavior", () => {
const root = engineLite.sceneManager.activeScene.createRootEntity("root");
const collider = root.addComponent(DynamicCollider);
const force = new Vector3(1, 0, 0);
const unsupportedOperations: Array<[string, () => unknown]> = [
["addForce", () => collider.applyForce(force)],
["addForceAtPosition", () => collider.applyForceAtPosition(force, new Vector3(0, 1, 0))],
["addTorque", () => collider.applyTorque(force)],
["putToSleep", () => collider.sleep()],
["wakeUp", () => collider.wakeUp()],
["isSleeping", () => collider.isSleeping()]
];

for (const [method, operation] of unsupportedOperations) {
let error: unknown;

try {
operation();
} catch (caughtError) {
error = caughtError;
}

expect(error).eq(`Physics-lite don't support ${method}. Use Physics-PhysX instead!`);
}

root.destroy();
});

it("removeShape", () => {
const scene = engineLite.sceneManager.activeScene;
const root = scene.createRootEntity("root");
Expand Down
Loading