From 90c32285d1ed025713c314a71b99f3875b0902df Mon Sep 17 00:00:00 2001 From: luzhuang <364439895@qq.com> Date: Tue, 24 Dec 2024 21:49:12 +0800 Subject: [PATCH] Add physicsMaterial loader and joint component parser and change radians to degrees in physics (#2475) * feat: add physicsMaterial loader and joint component parser and change radians to degrees in physics --- packages/core/src/asset/AssetType.ts | 4 +- .../core/src/physics/CharacterController.ts | 6 +-- packages/core/src/physics/DynamicCollider.ts | 21 ++++++++-- packages/core/src/physics/joint/Joint.ts | 37 +++++++++++------- .../core/src/physics/joint/JointLimits.ts | 16 ++++---- packages/core/src/physics/joint/JointMotor.ts | 24 ++++++++---- .../core/src/physics/shape/ColliderShape.ts | 7 +++- .../src/physics/ICharacterController.ts | 2 +- .../design/src/physics/IDynamicCollider.ts | 5 +++ packages/loader/src/PhysicsMaterialLoader.ts | 33 ++++++++++++++++ packages/loader/src/index.ts | 1 + .../resources/parser/HierarchyParser.ts | 8 +++- .../resources/parser/ParserContext.ts | 38 ++++++++++++++----- .../resources/parser/ReflectionParser.ts | 25 +++++++++--- .../resources/schema/BasicSchema.ts | 5 +++ .../physics-lite/src/LiteDynamicCollider.ts | 7 ++++ .../src/shape/LiteColliderShape.ts | 17 ++++++--- .../src/PhysXCharacterController.ts | 2 +- .../physics-physx/src/PhysXDynamicCollider.ts | 24 ++++++++++-- .../src/joint/PhysXHingeJoint.ts | 13 +++++-- .../src/shape/PhysXCapsuleColliderShape.ts | 2 +- .../src/shape/PhysXColliderShape.ts | 12 ++++-- .../core/physics/CharacterController.test.ts | 11 +++--- tests/src/core/physics/ColliderShape.test.ts | 16 ++++---- .../src/core/physics/DynamicCollider.test.ts | 25 +++++++++--- tests/src/core/physics/HingeJoint.test.ts | 34 ++++++++--------- tests/src/core/physics/Joint.test.ts | 12 +++--- 27 files changed, 290 insertions(+), 117 deletions(-) create mode 100644 packages/loader/src/PhysicsMaterialLoader.ts diff --git a/packages/core/src/asset/AssetType.ts b/packages/core/src/asset/AssetType.ts index d1e4725256..a176d32732 100644 --- a/packages/core/src/asset/AssetType.ts +++ b/packages/core/src/asset/AssetType.ts @@ -60,5 +60,7 @@ export enum AssetType { /** AudioClip, include ogg, wav and mp3. */ Audio = "Audio", /** Project asset. */ - Project = "project" + Project = "project", + /** PhysicsMaterial. */ + PhysicsMaterial = "PhysicsMaterial" } diff --git a/packages/core/src/physics/CharacterController.ts b/packages/core/src/physics/CharacterController.ts index d96eae2048..2cef370d3a 100644 --- a/packages/core/src/physics/CharacterController.ts +++ b/packages/core/src/physics/CharacterController.ts @@ -15,7 +15,7 @@ export class CharacterController extends Collider { private _nonWalkableMode: ControllerNonWalkableMode = ControllerNonWalkableMode.PreventClimbing; @deepClone private _upDirection = new Vector3(0, 1, 0); - private _slopeLimit = 0.707; + private _slopeLimit = 45; /** * The step offset for the controller, the value must be greater than or equal to 0. @@ -61,8 +61,8 @@ export class CharacterController extends Collider { } /** - * The slope limit for the controller, the value is the cosine value of the maximum slope angle. - * @defaultValue 0.707(the cosine value of 45 degrees) + * The slope limit in degrees for the controller, the value is the cosine value of the maximum slope angle. + * @defaultValue 45 degrees */ get slopeLimit(): number { return this._slopeLimit; diff --git a/packages/core/src/physics/DynamicCollider.ts b/packages/core/src/physics/DynamicCollider.ts index 26a8384529..83f6db4b46 100644 --- a/packages/core/src/physics/DynamicCollider.ts +++ b/packages/core/src/physics/DynamicCollider.ts @@ -20,9 +20,10 @@ export class DynamicCollider extends Collider { private _centerOfMass = new Vector3(); @ignoreClone private _inertiaTensor = new Vector3(1, 1, 1); - private _maxAngularVelocity = 100; + private _maxAngularVelocity = 18000 / Math.PI; private _maxDepenetrationVelocity = 1.0000000331813535e32; private _solverIterations = 4; + private _useGravity = true; private _isKinematic = false; private _constraints: DynamicColliderConstraints = 0; private _collisionDetectionMode: CollisionDetectionMode = CollisionDetectionMode.Discrete; @@ -77,7 +78,7 @@ export class DynamicCollider extends Collider { } /** - * The angular velocity vector of the dynamic collider measured in radians per second. + * The angular velocity vector of the dynamic collider measured in degrees per second. */ get angularVelocity(): Vector3 { //@ts-ignore @@ -185,7 +186,7 @@ export class DynamicCollider extends Collider { } /** - * The maximum angular velocity of the collider measured in radians per second. (Default 7) range { 0, infinity }. + * The maximum angular velocity of the collider measured in degrees per second. */ get maxAngularVelocity(): number { return this._maxAngularVelocity; @@ -240,6 +241,20 @@ export class DynamicCollider extends Collider { } } + /** + * Controls whether gravity affects the dynamic collider. + */ + get useGravity(): boolean { + return this._useGravity; + } + + set useGravity(value: boolean) { + if (this._useGravity !== value) { + this._useGravity = value; + (this._nativeCollider).setUseGravity(value); + } + } + /** * Controls whether physics affects the dynamic collider. */ diff --git a/packages/core/src/physics/joint/Joint.ts b/packages/core/src/physics/joint/Joint.ts index d86fae052c..54ab5dc983 100644 --- a/packages/core/src/physics/joint/Joint.ts +++ b/packages/core/src/physics/joint/Joint.ts @@ -6,12 +6,12 @@ import { dependentComponents, DependentMode } from "../../ComponentsDependencies import { Entity } from "../../Entity"; import { Collider } from "../Collider"; import { TransformModifyFlags } from "../../Transform"; - +import { DynamicCollider } from "../DynamicCollider"; /** * A base class providing common functionality for joints. * @decorator `@dependentComponents(Collider, DependentMode.CheckOnly)` */ -@dependentComponents(Collider, DependentMode.CheckOnly) +@dependentComponents(DynamicCollider, DependentMode.AutoAdd) export abstract class Joint extends Component { private static _tempVector3 = new Vector3(); @@ -24,6 +24,8 @@ export abstract class Joint extends Component { private _force = Infinity; private _torque = Infinity; private _automaticConnectedAnchor = true; + @ignoreClone + private _updateConnectedActualAnchor: Function; /** * The connected collider. @@ -37,7 +39,7 @@ export abstract class Joint extends Component { this._connectedColliderInfo.collider?.entity._updateFlagManager.removeListener(this._onConnectedTransformChanged); value?.entity._updateFlagManager.addListener(this._onConnectedTransformChanged); this._connectedColliderInfo.collider = value; - this._nativeJoint?.setConnectedCollider(value._nativeCollider); + this._nativeJoint?.setConnectedCollider(value?._nativeCollider); if (this._automaticConnectedAnchor) { this._calculateConnectedAnchor(); } else { @@ -47,8 +49,7 @@ export abstract class Joint extends Component { } /** - * The connected anchor position. - * @remarks If connectedCollider is set, this anchor is relative offset, or the anchor is world position. + * The anchor position. */ get anchor(): Vector3 { return this._colliderInfo.anchor; @@ -66,13 +67,22 @@ export abstract class Joint extends Component { /** * The connected anchor position. * @remarks If connectedCollider is set, this anchor is relative offset, or the anchor is world position. + * The connectedAnchor is automatically calculated, if you want to set it manually, please set automaticConnectedAnchor to false */ get connectedAnchor(): Vector3 { - return this._connectedColliderInfo.anchor; + const connectedColliderAnchor = this._connectedColliderInfo.anchor; + if (this._automaticConnectedAnchor) { + //@ts-ignore + connectedColliderAnchor._onValueChanged = null; + this._calculateConnectedAnchor(); + //@ts-ignore + connectedColliderAnchor._onValueChanged = this._updateConnectedActualAnchor; + } + return connectedColliderAnchor; } set connectedAnchor(value: Vector3) { - if (this.automaticConnectedAnchor) { + if (this._automaticConnectedAnchor) { console.warn("Cannot set connectedAnchor when automaticConnectedAnchor is true."); return; } @@ -96,7 +106,7 @@ export abstract class Joint extends Component { } /** - * The scale to apply to the inverse mass of collider 0 for resolving this constraint. + * The scale to apply to the mass of collider 0 for resolving this constraint. */ get connectedMassScale(): number { return this._connectedColliderInfo.massScale; @@ -110,7 +120,7 @@ export abstract class Joint extends Component { } /** - * The scale to apply to the inverse mass of collider 1 for resolving this constraint. + * The scale to apply to the mass of collider 1 for resolving this constraint. */ get massScale(): number { return this._colliderInfo.massScale; @@ -124,7 +134,7 @@ export abstract class Joint extends Component { } /** - * The scale to apply to the inverse inertia of collider0 for resolving this constraint. + * The scale to apply to the inertia of collider0 for resolving this constraint. */ get connectedInertiaScale(): number { return this._connectedColliderInfo.inertiaScale; @@ -138,7 +148,7 @@ export abstract class Joint extends Component { } /** - * The scale to apply to the inverse inertia of collider1 for resolving this constraint. + * The scale to apply to the inertia of collider1 for resolving this constraint. */ get inertiaScale(): number { return this._colliderInfo.inertiaScale; @@ -183,8 +193,10 @@ export abstract class Joint extends Component { super(entity); //@ts-ignore this._colliderInfo.anchor._onValueChanged = this._updateActualAnchor.bind(this, AnchorOwner.Self); + this._updateConnectedActualAnchor = this._updateActualAnchor.bind(this, AnchorOwner.Connected); //@ts-ignore - this._connectedColliderInfo.anchor._onValueChanged = this._updateActualAnchor.bind(this, AnchorOwner.Connected); + this._connectedColliderInfo.anchor._onValueChanged = this._updateConnectedActualAnchor; + this._onSelfTransformChanged = this._onSelfTransformChanged.bind(this); this._onConnectedTransformChanged = this._onConnectedTransformChanged.bind(this); // @ts-ignore @@ -261,7 +273,6 @@ export abstract class Joint extends Component { } } - @ignoreClone private _updateActualAnchor(flag: AnchorOwner): void { if (flag & AnchorOwner.Self) { const worldScale = this.entity.transform.lossyWorldScale; diff --git a/packages/core/src/physics/joint/JointLimits.ts b/packages/core/src/physics/joint/JointLimits.ts index 40758d63d1..3d7ff811be 100644 --- a/packages/core/src/physics/joint/JointLimits.ts +++ b/packages/core/src/physics/joint/JointLimits.ts @@ -16,34 +16,34 @@ export class JointLimits { private _damping = 0; /** - * The upper angular limit (in radians) of the joint. + * The upper angular limit (in degrees) of the joint. */ get max(): number { return this._max; } set max(value: number) { - if (value < this._min) { - throw new Error("Max limit must be greater than min limit"); - } if (this._max !== value) { + if (value < this._min) { + this._min = value; + } this._max = value; this._updateFlagManager.dispatch(); } } /** - * The lower angular limit (in radians) of the joint. + * The lower angular limit (in degrees) of the joint. */ get min(): number { return this._min; } set min(value: number) { - if (value > this._max) { - throw new Error("Min limit must be less than max limit"); - } if (this._min !== value) { + if (value > this._max) { + this._max = value; + } this._min = value; this._updateFlagManager.dispatch(); } diff --git a/packages/core/src/physics/joint/JointMotor.ts b/packages/core/src/physics/joint/JointMotor.ts index ef36d31f90..0f381c1a6c 100644 --- a/packages/core/src/physics/joint/JointMotor.ts +++ b/packages/core/src/physics/joint/JointMotor.ts @@ -22,8 +22,10 @@ export class JointMotor { } set targetVelocity(value: number) { - this._targetVelocity = value; - this._updateFlagManager.dispatch(); + if (this._targetVelocity !== value) { + this._targetVelocity = value; + this._updateFlagManager.dispatch(); + } } /** @@ -34,8 +36,10 @@ export class JointMotor { } set forceLimit(value: number) { - this._forceLimit = value; - this._updateFlagManager.dispatch(); + if (this._forceLimit !== value) { + this._forceLimit = value; + this._updateFlagManager.dispatch(); + } } /** @@ -46,8 +50,10 @@ export class JointMotor { } set gearRatio(value: number) { - this._gearRatio = value; - this._updateFlagManager.dispatch(); + if (this._gearRatio !== value) { + this._gearRatio = value; + this._updateFlagManager.dispatch(); + } } /** @@ -58,7 +64,9 @@ export class JointMotor { } set freeSpin(value: boolean) { - this._freeSpin = value; - this._updateFlagManager.dispatch(); + if (this._freeSpin !== value) { + this._freeSpin = value; + this._updateFlagManager.dispatch(); + } } } diff --git a/packages/core/src/physics/shape/ColliderShape.ts b/packages/core/src/physics/shape/ColliderShape.ts index 7701561635..f056670906 100644 --- a/packages/core/src/physics/shape/ColliderShape.ts +++ b/packages/core/src/physics/shape/ColliderShape.ts @@ -66,13 +66,16 @@ export abstract class ColliderShape implements ICustomClone { } /** - * Physical material. + * Physical material, material can't be null. */ get material(): PhysicsMaterial { return this._material; } set material(value: PhysicsMaterial) { + if (!value) { + throw new Error("The physics material of the shape can't be null."); + } if (this._material !== value) { this._material = value; this._nativeShape.setMaterial(value._nativeMaterial); @@ -80,7 +83,7 @@ export abstract class ColliderShape implements ICustomClone { } /** - * The local rotation of this ColliderShape, in radians. + * The local rotation of this ColliderShape, in degrees. */ get rotation(): Vector3 { return this._rotation; diff --git a/packages/design/src/physics/ICharacterController.ts b/packages/design/src/physics/ICharacterController.ts index 03aff94e0b..80cf3bae82 100644 --- a/packages/design/src/physics/ICharacterController.ts +++ b/packages/design/src/physics/ICharacterController.ts @@ -44,7 +44,7 @@ export interface ICharacterController extends ICollider { setUpDirection(up: Vector3): void; /** - * Sets the slope limit. + * Sets the slope limit in degrees. * @param slopeLimit The slope limit for the controller. */ setSlopeLimit(slopeLimit: number): void; diff --git a/packages/design/src/physics/IDynamicCollider.ts b/packages/design/src/physics/IDynamicCollider.ts index c1fbdf5068..f4c31fda6b 100644 --- a/packages/design/src/physics/IDynamicCollider.ts +++ b/packages/design/src/physics/IDynamicCollider.ts @@ -132,6 +132,11 @@ export interface IDynamicCollider extends ICollider { */ setCollisionDetectionMode(value: number): void; + /** + * Whether the collider is affected by gravity. + */ + setUseGravity(value: boolean): void; + /** * Controls whether physics affects the dynamic collider. * @param value - is or not diff --git a/packages/loader/src/PhysicsMaterialLoader.ts b/packages/loader/src/PhysicsMaterialLoader.ts new file mode 100644 index 0000000000..334e60a618 --- /dev/null +++ b/packages/loader/src/PhysicsMaterialLoader.ts @@ -0,0 +1,33 @@ +import { + resourceLoader, + Loader, + AssetPromise, + AssetType, + LoadItem, + ResourceManager, + PhysicsMaterial +} from "@galacean/engine-core"; + +@resourceLoader(AssetType.PhysicsMaterial, ["mesh"]) +class PhysicsMaterialLoader extends Loader { + load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { + return ( + resourceManager + // @ts-ignore + ._request(item.url, { + ...item, + type: "json" + }) + .then((data) => { + const physicsMaterial = new PhysicsMaterial(); + physicsMaterial.bounciness = data.bounciness; + physicsMaterial.dynamicFriction = data.dynamicFriction; + physicsMaterial.staticFriction = data.staticFriction; + physicsMaterial.bounceCombine = data.bounceCombine; + physicsMaterial.frictionCombine = data.frictionCombine; + + return physicsMaterial; + }) + ); + } +} diff --git a/packages/loader/src/index.ts b/packages/loader/src/index.ts index 9316c4dcfd..7b7ec39494 100644 --- a/packages/loader/src/index.ts +++ b/packages/loader/src/index.ts @@ -22,6 +22,7 @@ import "./AudioLoader"; import "./ktx2/KTX2Loader"; import "./ShaderLoader"; import "./ShaderChunkLoader"; +import "./PhysicsMaterialLoader"; export { GLTFLoader } from "./GLTFLoader"; export type { GLTFParams } from "./GLTFLoader"; diff --git a/packages/loader/src/resource-deserialize/resources/parser/HierarchyParser.ts b/packages/loader/src/resource-deserialize/resources/parser/HierarchyParser.ts index b427f44103..2c6bdaa241 100644 --- a/packages/loader/src/resource-deserialize/resources/parser/HierarchyParser.ts +++ b/packages/loader/src/resource-deserialize/resources/parser/HierarchyParser.ts @@ -84,7 +84,6 @@ export abstract class HierarchyParser { const entitiesConfig = this.data.entities; const entityMap = this.context.entityMap; - const components = this.context.components; const promises = []; for (let i = 0, l = entitiesConfig.length; i < l; i++) { @@ -94,11 +93,16 @@ export abstract class HierarchyParser resolve(null)); + } + return Promise.all(promises); } diff --git a/packages/loader/src/resource-deserialize/resources/parser/ParserContext.ts b/packages/loader/src/resource-deserialize/resources/parser/ParserContext.ts index a91dcc8920..70251d5b85 100644 --- a/packages/loader/src/resource-deserialize/resources/parser/ParserContext.ts +++ b/packages/loader/src/resource-deserialize/resources/parser/ParserContext.ts @@ -1,16 +1,12 @@ import { Component, Engine, EngineObject, Entity, ReferResource, ResourceManager, Scene } from "@galacean/engine-core"; -import type { IEntity, IHierarchyFile } from "../schema"; +import type { IComponentRef, IEntity, IHierarchyFile } from "../schema"; export enum ParserType { Prefab, Scene } /** - * Parser context - * @export - * @class ParserContext - * @template T - * @template I + * @internal */ export class ParserContext { entityMap: Map = new Map(); @@ -18,6 +14,7 @@ export class ParserContext { components: Map = new Map(); rootIds: string[] = []; strippedIds: string[] = []; + componentWaitingMap: Map = new Map(); readonly resourceManager: ResourceManager; @@ -29,10 +26,31 @@ export class ParserContext { this.resourceManager = engine.resourceManager; } - /** - * Destroy the context. - * @memberof ParserContext - */ + addComponent(id: string, component: Component) { + this.components.set(id, component); + const waitingList = this.componentWaitingMap.get(id); + if (waitingList?.length) { + waitingList.forEach((resolve) => resolve(component)); + this.componentWaitingMap.delete(id); + } + } + + getComponentByRef(ref: IComponentRef): Promise { + return new Promise((resolve, reject) => { + const component = this.components.get(ref.componentId); + if (component) { + resolve(component); + } else { + const resolves = this.componentWaitingMap.get(ref.componentId); + if (resolves) { + resolves.push(resolve); + } else { + this.componentWaitingMap.set(ref.componentId, [resolve]); + } + } + }); + } + clear() { this.entityMap.clear(); this.components.clear(); diff --git a/packages/loader/src/resource-deserialize/resources/parser/ReflectionParser.ts b/packages/loader/src/resource-deserialize/resources/parser/ReflectionParser.ts index 3655569f51..47ad86b98d 100644 --- a/packages/loader/src/resource-deserialize/resources/parser/ReflectionParser.ts +++ b/packages/loader/src/resource-deserialize/resources/parser/ReflectionParser.ts @@ -1,5 +1,14 @@ -import { EngineObject, Entity, Loader, ReferResource } from "@galacean/engine-core"; -import type { IAssetRef, IBasicType, IClassObject, IEntity, IEntityRef, IHierarchyFile, IRefEntity } from "../schema"; +import { EngineObject, Entity, Loader } from "@galacean/engine-core"; +import type { + IAssetRef, + IBasicType, + IClassObject, + IEntity, + IEntityRef, + IComponentRef, + IHierarchyFile, + IRefEntity +} from "../schema"; import { ParserContext, ParserType } from "./ParserContext"; export class ReflectionParser { @@ -85,6 +94,8 @@ export class ReflectionParser { } return resource; }); + } else if (ReflectionParser._isComponentRef(value)) { + return this._context.getComponentByRef(value); } else if (ReflectionParser._isEntityRef(value)) { // entity reference return Promise.resolve(this._context.entityMap.get(value.entityId)); @@ -144,14 +155,18 @@ export class ReflectionParser { } private static _isClass(value: any): value is IClassObject { - return value["class"] != undefined; + return value["class"] !== undefined; } private static _isAssetRef(value: any): value is IAssetRef { - return value["refId"] != undefined; + return value["refId"] !== undefined; } private static _isEntityRef(value: any): value is IEntityRef { - return value["entityId"] != undefined; + return value["entityId"] !== undefined; + } + + private static _isComponentRef(value: any): value is IComponentRef { + return value["ownerId"] !== undefined && value["componentId"] !== undefined; } } diff --git a/packages/loader/src/resource-deserialize/resources/schema/BasicSchema.ts b/packages/loader/src/resource-deserialize/resources/schema/BasicSchema.ts index 1cce358d62..8243c46936 100644 --- a/packages/loader/src/resource-deserialize/resources/schema/BasicSchema.ts +++ b/packages/loader/src/resource-deserialize/resources/schema/BasicSchema.ts @@ -92,3 +92,8 @@ export type IBasicType = export type IAssetRef = { key?: string; refId: string }; export type IEntityRef = { entityId: string }; + +export type IComponentRef = { + ownerId: string; + componentId: string; +}; diff --git a/packages/physics-lite/src/LiteDynamicCollider.ts b/packages/physics-lite/src/LiteDynamicCollider.ts index 0660cb18a2..68768a0af7 100644 --- a/packages/physics-lite/src/LiteDynamicCollider.ts +++ b/packages/physics-lite/src/LiteDynamicCollider.ts @@ -133,6 +133,13 @@ export class LiteDynamicCollider extends LiteCollider implements IDynamicCollide Logger.error("Physics-lite don't support setInertiaTensor. Use Physics-PhysX instead!"); } + /** + * {@inheritDoc IDynamicCollider.setUseGravity } + */ + setUseGravity(value: boolean): void { + throw "Physics-lite don't support setUseGravity. Use Physics-PhysX instead!"; + } + /** * {@inheritDoc IDynamicCollider.setIsKinematic } */ diff --git a/packages/physics-lite/src/shape/LiteColliderShape.ts b/packages/physics-lite/src/shape/LiteColliderShape.ts index a205753972..295241dfed 100644 --- a/packages/physics-lite/src/shape/LiteColliderShape.ts +++ b/packages/physics-lite/src/shape/LiteColliderShape.ts @@ -1,4 +1,4 @@ -import { Matrix, Quaternion, Ray, Vector3, Vector4 } from "@galacean/engine"; +import { MathUtil, Matrix, Quaternion, Ray, Vector3, Vector4 } from "@galacean/engine"; import { IColliderShape, IPhysicsMaterial } from "@galacean/engine-design"; import { LiteCollider } from "../LiteCollider"; import { LiteHitResult } from "../LiteHitResult"; @@ -43,10 +43,17 @@ export abstract class LiteColliderShape implements IColliderShape { * {@inheritDoc IColliderShape.setRotation } */ setRotation(rotation: Vector3): void { - if (rotation !== this._rotation) { - this._rotation.copyFrom(rotation); - Quaternion.rotationEuler(rotation.x, rotation.y, rotation.z, this._transform.rotationQuaternion); - } + const rotationInRadians = this._rotation.set( + MathUtil.degreeToRadian(rotation.x), + MathUtil.degreeToRadian(rotation.y), + MathUtil.degreeToRadian(rotation.z) + ); + Quaternion.rotationEuler( + rotationInRadians.x, + rotationInRadians.y, + rotationInRadians.z, + this._transform.rotationQuaternion + ); } /** diff --git a/packages/physics-physx/src/PhysXCharacterController.ts b/packages/physics-physx/src/PhysXCharacterController.ts index d5a266c8fa..69c1b99400 100644 --- a/packages/physics-physx/src/PhysXCharacterController.ts +++ b/packages/physics-physx/src/PhysXCharacterController.ts @@ -81,7 +81,7 @@ export class PhysXCharacterController implements ICharacterController { * {@inheritDoc ICharacterController.setSlopeLimit } */ setSlopeLimit(slopeLimit: number): void { - this._pxController?.setSlopeLimit(slopeLimit); + this._pxController?.setSlopeLimit(Math.cos((slopeLimit * Math.PI) / 180)); } /** diff --git a/packages/physics-physx/src/PhysXDynamicCollider.ts b/packages/physics-physx/src/PhysXDynamicCollider.ts index e2083e268a..89fe20e01d 100644 --- a/packages/physics-physx/src/PhysXDynamicCollider.ts +++ b/packages/physics-physx/src/PhysXDynamicCollider.ts @@ -1,5 +1,5 @@ import { IDynamicCollider } from "@galacean/engine-design"; -import { Quaternion, Vector3 } from "@galacean/engine"; +import { MathUtil, Quaternion, Vector3 } from "@galacean/engine"; import { PhysXCollider } from "./PhysXCollider"; import { PhysXPhysics } from "./PhysXPhysics"; @@ -78,14 +78,23 @@ export class PhysXDynamicCollider extends PhysXCollider implements IDynamicColli */ getAngularVelocity(out: Vector3): Vector3 { const velocity = this._pxActor.getAngularVelocity(); - return out.set(velocity.x, velocity.y, velocity.z); + return out.set( + MathUtil.radianToDegree(velocity.x), + MathUtil.radianToDegree(velocity.y), + MathUtil.radianToDegree(velocity.z) + ); } /** * {@inheritDoc IDynamicCollider.setAngularVelocity } */ setAngularVelocity(value: Vector3): void { - this._pxActor.setAngularVelocity(value, true); + PhysXDynamicCollider._tempTranslation.set( + MathUtil.degreeToRadian(value.x), + MathUtil.degreeToRadian(value.y), + MathUtil.degreeToRadian(value.z) + ); + this._pxActor.setAngularVelocity(PhysXDynamicCollider._tempTranslation, true); } /** @@ -136,7 +145,7 @@ export class PhysXDynamicCollider extends PhysXCollider implements IDynamicColli * {@inheritDoc IDynamicCollider.setMaxAngularVelocity } */ setMaxAngularVelocity(value: number): void { - this._pxActor.setMaxAngularVelocity(value); + this._pxActor.setMaxAngularVelocity(MathUtil.degreeToRadian(value)); } /** @@ -194,6 +203,13 @@ export class PhysXDynamicCollider extends PhysXCollider implements IDynamicColli } } + /** + * {@inheritDoc IDynamicCollider.setUseGravity } + */ + setUseGravity(value: boolean): void { + this._pxActor.setActorFlag(this._physXPhysics._physX.PxActorFlag.eDISABLE_GRAVITY, !value); + } + /** * {@inheritDoc IDynamicCollider.setIsKinematic } */ diff --git a/packages/physics-physx/src/joint/PhysXHingeJoint.ts b/packages/physics-physx/src/joint/PhysXHingeJoint.ts index e90b5b6870..7addf168dc 100644 --- a/packages/physics-physx/src/joint/PhysXHingeJoint.ts +++ b/packages/physics-physx/src/joint/PhysXHingeJoint.ts @@ -1,5 +1,5 @@ import { IHingeJoint } from "@galacean/engine-design"; -import { Quaternion, Vector3 } from "@galacean/engine"; +import { MathUtil, Quaternion, Vector3 } from "@galacean/engine"; import { PhysXCollider } from "../PhysXCollider"; import { PhysXPhysics } from "../PhysXPhysics"; import { PhysXJoint } from "./PhysXJoint"; @@ -56,7 +56,7 @@ export class PhysXHingeJoint extends PhysXJoint implements IHingeJoint { * {@inheritDoc IHingeJoint.getAngle } */ getAngle(): number { - return this._pxJoint.getAngle(); + return MathUtil.radianToDegree(this._pxJoint.getAngle()); } /** @@ -70,14 +70,19 @@ export class PhysXHingeJoint extends PhysXJoint implements IHingeJoint { * {@inheritDoc IHingeJoint.setHardLimitCone } */ setHardLimit(lowerLimit: number, upperLimit: number, contactDist: number): void { - this._pxJoint.setHardLimit(lowerLimit, upperLimit, contactDist); + this._pxJoint.setHardLimit(MathUtil.degreeToRadian(lowerLimit), MathUtil.degreeToRadian(upperLimit), contactDist); } /** * {@inheritDoc IHingeJoint.setHardLimitCone } */ setSoftLimit(lowerLimit: number, upperLimit: number, stiffness: number, damping: number): void { - this._pxJoint.setSoftLimit(lowerLimit, upperLimit, stiffness, damping); + this._pxJoint.setSoftLimit( + MathUtil.degreeToRadian(lowerLimit), + MathUtil.degreeToRadian(upperLimit), + stiffness, + damping + ); } /** diff --git a/packages/physics-physx/src/shape/PhysXCapsuleColliderShape.ts b/packages/physics-physx/src/shape/PhysXCapsuleColliderShape.ts index 8d1a75706c..f140cfd65e 100644 --- a/packages/physics-physx/src/shape/PhysXCapsuleColliderShape.ts +++ b/packages/physics-physx/src/shape/PhysXCapsuleColliderShape.ts @@ -104,7 +104,7 @@ export class PhysXCapsuleColliderShape extends PhysXColliderShape implements ICa break; } if (rotation) { - Quaternion.rotationYawPitchRoll(rotation.x, rotation.y, rotation.z, physXRotation); + Quaternion.rotationYawPitchRoll(rotation.y, rotation.x, rotation.z, physXRotation); Quaternion.multiply(physXRotation, axis, physXRotation); } else { physXRotation.copyFrom(axis); diff --git a/packages/physics-physx/src/shape/PhysXColliderShape.ts b/packages/physics-physx/src/shape/PhysXColliderShape.ts index a2565ade4d..676c6dc5f3 100644 --- a/packages/physics-physx/src/shape/PhysXColliderShape.ts +++ b/packages/physics-physx/src/shape/PhysXColliderShape.ts @@ -1,4 +1,4 @@ -import { Quaternion, Vector3, DisorderedArray, Vector4 } from "@galacean/engine"; +import { Quaternion, Vector3, DisorderedArray, Vector4, MathUtil } from "@galacean/engine"; import { IColliderShape } from "@galacean/engine-design"; import { PhysXCharacterController } from "../PhysXCharacterController"; import { PhysXPhysics } from "../PhysXPhysics"; @@ -35,7 +35,7 @@ export abstract class PhysXColliderShape implements IColliderShape { protected _physXPhysics: PhysXPhysics; protected _worldScale: Vector3 = new Vector3(1, 1, 1); protected _position: Vector3 = new Vector3(); - protected _rotation: Vector3 = null; + protected _rotation: Vector3 = new Vector3(); protected _axis: Quaternion = null; protected _physXRotation: Quaternion = new Quaternion(); @@ -58,8 +58,12 @@ export abstract class PhysXColliderShape implements IColliderShape { * {@inheritDoc IColliderShape.setRotation } */ setRotation(value: Vector3): void { - this._rotation = value; - Quaternion.rotationYawPitchRoll(value.y, value.x, value.z, this._physXRotation); + const rotation = this._rotation.set( + MathUtil.degreeToRadian(value.x), + MathUtil.degreeToRadian(value.y), + MathUtil.degreeToRadian(value.z) + ); + Quaternion.rotationYawPitchRoll(rotation.y, rotation.x, rotation.z, this._physXRotation); this._axis && Quaternion.multiply(this._physXRotation, this._axis, this._physXRotation); this._physXRotation.normalize(); this._setLocalPose(); diff --git a/tests/src/core/physics/CharacterController.test.ts b/tests/src/core/physics/CharacterController.test.ts index fd7870fc3a..636ed920b2 100644 --- a/tests/src/core/physics/CharacterController.test.ts +++ b/tests/src/core/physics/CharacterController.test.ts @@ -100,7 +100,7 @@ describe("CharacterController", function () { expect(controller.stepOffset).eq(0.5); expect(controller.nonWalkableMode).eq(ControllerNonWalkableMode.PreventClimbing); expect(controller.upDirection).deep.include({ x: 0, y: 1, z: 0 }); - expect(controller.slopeLimit).eq(0.707); + expect(controller.slopeLimit).eq(45); }); it("addShape and removeShape", () => { @@ -179,8 +179,8 @@ describe("CharacterController", function () { const { fixedTimeStep } = engine.sceneManager.activeScene.physics; const moveScript = roleEntity.getComponent(MoveScript); const controller = roleEntity.getComponent(CharacterController); - expect(controller.slopeLimit).eq(0.707); - controller.slopeLimit = 1; + expect(controller.slopeLimit).eq(45); + controller.slopeLimit = 0; const slope = addPlane(new Vector3(0, 0, 2), new Quaternion().rotateX(-Math.PI / 4)); moveScript.moveTo(new Vector3(0, 0, 3), 50); // @ts-ignore @@ -194,7 +194,7 @@ describe("CharacterController", function () { const { fixedTimeStep } = engine.sceneManager.activeScene.physics; const moveScript = roleEntity.getComponent(MoveScript); const controller = roleEntity.getComponent(CharacterController); - expect(controller.slopeLimit).eq(0.707); + expect(controller.slopeLimit).eq(45); const slope = addPlane(new Vector3(0, 0, 2), new Quaternion().rotateX(-Math.PI / 4)); moveScript.moveTo(new Vector3(0, 0, 3), 50); // @ts-ignore @@ -295,7 +295,7 @@ describe("CharacterController", function () { const { fixedTimeStep } = engine.sceneManager.activeScene.physics; const moveScript = newRole.getComponent(MoveScript); const controller = newRole.getComponent(CharacterController); - expect(controller.slopeLimit).eq(0.707); + expect(controller.slopeLimit).eq(45); const slope = addPlane(new Vector3(0, 0, 2), new Quaternion().rotateX(-Math.PI / 4)); moveScript.moveTo(new Vector3(0, 0, 3), 50); // @ts-ignore @@ -308,7 +308,6 @@ describe("CharacterController", function () { it("inActive modification", function () { roleEntity.isActive = false; const controller = roleEntity.getComponent(CharacterController); - controller.contactOffset = 0.1; controller.stepOffset = 1; controller.slopeLimit = 1; controller.nonWalkableMode = ControllerNonWalkableMode.PreventClimbingAndForceSliding; diff --git a/tests/src/core/physics/ColliderShape.test.ts b/tests/src/core/physics/ColliderShape.test.ts index 6ea29d627d..ce2b15a43f 100644 --- a/tests/src/core/physics/ColliderShape.test.ts +++ b/tests/src/core/physics/ColliderShape.test.ts @@ -268,10 +268,10 @@ describe("ColliderShape PhysX", () => { // @ts-ignore engine.sceneManager.activeScene.physics._update(1 / 60); let distance = boxShape.getClosestPoint(point, closestPoint); - expect(formatValue(distance)).to.eq(10.492); - expect(formatValue(closestPoint.x)).to.eq(-16.0876); - expect(formatValue(closestPoint.y)).to.eq(10.7095); - expect(formatValue(closestPoint.z)).to.eq(12.7889); + expect(formatValue(distance)).to.eq(6.897); + expect(formatValue(closestPoint.x)).to.eq(-12.3658); + expect(formatValue(closestPoint.y)).to.eq(10.107); + expect(formatValue(closestPoint.z)).to.eq(11.1562); entity.transform.setScale(1, 1, 1); entity.transform.setRotation(0, 0, 0); @@ -497,10 +497,10 @@ describe("ColliderShape Lite", () => { // @ts-ignore engine.sceneManager.activeScene.physics._update(1 / 60); let distance = boxShape.getClosestPoint(point, closestPoint); - expect(formatValue(distance)).to.eq(10.492); - expect(formatValue(closestPoint.x)).to.eq(-16.0876); - expect(formatValue(closestPoint.y)).to.eq(10.7095); - expect(formatValue(closestPoint.z)).to.eq(12.7889); + expect(formatValue(distance)).to.eq(6.897); + expect(formatValue(closestPoint.x)).to.eq(-12.3658); + expect(formatValue(closestPoint.y)).to.eq(10.107); + expect(formatValue(closestPoint.z)).to.eq(11.1562); entity.transform.setScale(1, 1, 1); entity.transform.setRotation(0, 0, 0); diff --git a/tests/src/core/physics/DynamicCollider.test.ts b/tests/src/core/physics/DynamicCollider.test.ts index b1b2b23fa1..0a49903b8a 100644 --- a/tests/src/core/physics/DynamicCollider.test.ts +++ b/tests/src/core/physics/DynamicCollider.test.ts @@ -155,12 +155,12 @@ describe("DynamicCollider", function () { expect(formatValue(boxCollider.angularVelocity.y)).eq(0); expect(formatValue(box.transform.rotation.y)).eq(0); - boxCollider.angularVelocity = new Vector3(0, 1, 0); + boxCollider.angularVelocity = new Vector3(0, 45, 0); boxCollider.angularDamping = 0; // @ts-ignore engine.sceneManager.activeScene.physics._update(1); - expect(formatValue(boxCollider.angularVelocity.y)).eq(1); - expect(formatValue(box.transform.rotation.y)).eq(57.29577); + expect(formatValue(boxCollider.angularVelocity.y)).eq(45); + expect(formatValue(box.transform.rotation.y)).closeTo(45, 0.0001); }); it("mass", function () { @@ -241,7 +241,7 @@ describe("DynamicCollider", function () { // @ts-ignore engine.sceneManager.activeScene.physics._update(1); expect(formatValue(boxCollider.inertiaTensor.y)).eq(1); - expect(formatValue(boxCollider.angularVelocity.y)).eq(0.15853); + expect(formatValue(boxCollider.angularVelocity.y)).eq(9.08338); boxCollider.inertiaTensor = new Vector3(0, 2, 0); boxCollider.angularVelocity.y = 0; @@ -249,7 +249,7 @@ describe("DynamicCollider", function () { // @ts-ignore engine.sceneManager.activeScene.physics._update(1); expect(formatValue(boxCollider.inertiaTensor.y)).eq(2); - expect(formatValue(boxCollider.angularVelocity.y)).eq(0.07927); + expect(formatValue(boxCollider.angularVelocity.y)).eq(4.54169); }); it("automaticInertiaTensor", function () { @@ -364,6 +364,21 @@ describe("DynamicCollider", function () { expect(Math.abs(formatValue(boxCollider2.linearVelocity.x))).lessThan(4); }); + it("useGravity", function () { + const box = addBox(new Vector3(2, 2, 2), DynamicCollider, new Vector3(0, 10, 0)); + const boxCollider = box.getComponent(DynamicCollider); + boxCollider.useGravity = false; + // @ts-ignore + engine.sceneManager.activeScene.physics._update(1); + expect(formatValue(box.transform.position.y)).eq(10); + + boxCollider.useGravity = true; + boxCollider.wakeUp(); + // @ts-ignore + engine.sceneManager.activeScene.physics._update(1); + expect(formatValue(box.transform.position.y)).lessThan(10); + }); + it("isKinematic", function () { const box = addBox(new Vector3(2, 2, 2), DynamicCollider, new Vector3(0, 1, 0)); const boxCollider = box.getComponent(DynamicCollider); diff --git a/tests/src/core/physics/HingeJoint.test.ts b/tests/src/core/physics/HingeJoint.test.ts index dcc4d63ada..d6f1ad12d8 100644 --- a/tests/src/core/physics/HingeJoint.test.ts +++ b/tests/src/core/physics/HingeJoint.test.ts @@ -68,7 +68,7 @@ describe("HingeJoint", function () { // @ts-ignore engine.sceneManager.activeScene.physics._update(1 / 60); expect(formatValue(joint.velocity)).eq(6.89082); - expect(formatValue(joint.angle)).eq(0.11485); + expect(formatValue(joint.angle)).eq(6.58019); }); it("hardLimit", function () { @@ -94,12 +94,12 @@ describe("HingeJoint", function () { collider2.applyTorque(new Vector3(0, 1000, 0)); // @ts-ignore engine.sceneManager.activeScene.physics._update(1); - expect(formatValue(joint.angle)).eq(1.5708); + expect(formatValue(joint.angle)).eq(1.57019); collider2.applyTorque(new Vector3(0, -1000, 0)); // @ts-ignore engine.sceneManager.activeScene.physics._update(1); - expect(formatValue(joint.angle)).eq(-1.5708); + expect(formatValue(joint.angle)).eq(-1.57019); }); it("softLimit", function () { @@ -121,8 +121,8 @@ describe("HingeJoint", function () { joint.useLimits = true; joint.useSpring = true; const limits = new JointLimits(); - limits.min = -Math.PI / 2; - limits.max = Math.PI / 2; + limits.min = -90; + limits.max = 90; limits.stiffness = 1000; limits.damping = 30; joint.limits = limits; @@ -132,7 +132,7 @@ describe("HingeJoint", function () { collider2.applyTorque(new Vector3(0, 1000, 0)); // @ts-ignore engine.sceneManager.activeScene.physics._update(1); - expect(formatValue(joint.angle)).eq(0.10957); + expect(formatValue(joint.angle)).eq(6.27762); }); it("stiffness", function () { @@ -153,8 +153,8 @@ describe("HingeJoint", function () { joint.useLimits = true; joint.useSpring = true; const limits = new JointLimits(); - limits.min = -Math.PI / 2; - limits.max = Math.PI / 2; + limits.min = -90; + limits.max = 90; limits.stiffness = 2000; limits.damping = 30; joint.limits = limits; @@ -164,7 +164,7 @@ describe("HingeJoint", function () { collider2.applyTorque(new Vector3(0, 1000, 0)); // @ts-ignore engine.sceneManager.activeScene.physics._update(1); - expect(formatValue(joint.angle)).eq(-0.22578); + expect(formatValue(joint.angle)).eq(-12.93617); }); it("damping", function () { @@ -185,8 +185,8 @@ describe("HingeJoint", function () { joint.useLimits = true; joint.useSpring = true; const limits = new JointLimits(); - limits.min = -Math.PI / 2; - limits.max = Math.PI / 2; + limits.min = -90; + limits.max = 90; limits.stiffness = 1000; limits.damping = 100; joint.limits = limits; @@ -196,7 +196,7 @@ describe("HingeJoint", function () { collider2.applyTorque(new Vector3(0, 1000, 0)); // @ts-ignore engine.sceneManager.activeScene.physics._update(1); - expect(formatValue(joint.angle)).eq(0.87375); + expect(formatValue(joint.angle)).eq(50.06221); }); it("motor", function () { @@ -219,7 +219,7 @@ describe("HingeJoint", function () { // @ts-ignore engine.sceneManager.activeScene.physics._update(1); expect(formatValue(joint.velocity)).eq(30); - expect(formatValue(joint.angle)).eq(4.86726); + expect(formatValue(joint.angle)).eq(278.87335); }); it("forceLimit", function () { @@ -304,18 +304,18 @@ describe("HingeJoint", function () { collider2.applyTorque(new Vector3(0, 1000, 0)); // @ts-ignore engine.sceneManager.activeScene.physics._update(1); - expect(formatValue(collider2.angularVelocity.y)).eq(30); + expect(formatValue(collider2.angularVelocity.y)).eq(1718.87328); motor.targetVelocity = 30; motor.freeSpin = true; // @ts-ignore engine.sceneManager.activeScene.physics._update(1); - expect(formatValue(collider2.angularVelocity.y)).eq(30); + expect(formatValue(collider2.angularVelocity.y)).eq(1718.87328); collider2.applyTorque(new Vector3(0, 1000, 0)); // @ts-ignore engine.sceneManager.activeScene.physics._update(1); - expect(formatValue(collider2.angularVelocity.y)).eq(95.20031); + expect(formatValue(collider2.angularVelocity.y)).eq(5454.57596); }); it("clone", function () { @@ -355,7 +355,7 @@ describe("HingeJoint", function () { // @ts-ignore engine.sceneManager.activeScene.physics._update(1 / 60); expect(formatValue(newJoint.velocity)).eq(6.89082); - expect(formatValue(newJoint.angle)).eq(0.11485); + expect(formatValue(newJoint.angle)).eq(6.58019); }); it("inActive modification", function () { diff --git a/tests/src/core/physics/Joint.test.ts b/tests/src/core/physics/Joint.test.ts index 90d65174a1..0d5c59c4f5 100644 --- a/tests/src/core/physics/Joint.test.ts +++ b/tests/src/core/physics/Joint.test.ts @@ -198,7 +198,7 @@ describe("Joint", function () { box2Collider.applyTorque(new Vector3(0, 1000, 0)); // @ts-ignore engine.sceneManager.activeScene.physics._update(1 / 60); - expect(formatValue(box2Collider.angularVelocity.y)).eq(8.32635); + expect(formatValue(box2Collider.angularVelocity.y)).eq(477.06462); box2.transform.rotation = new Vector3(0, 0, 0); box1Collider.angularVelocity = new Vector3(0, 0, 0); @@ -211,7 +211,7 @@ describe("Joint", function () { box2Collider.applyTorque(new Vector3(0, 1000, 0)); // @ts-ignore engine.sceneManager.activeScene.physics._update(1 / 60); - expect(formatValue(box2Collider.angularVelocity.y)).closeTo(1.513, 0.001); + expect(formatValue(box2Collider.angularVelocity.y)).closeTo(86.74174, 0.01); box2.transform.rotation = new Vector3(0, 0, 0); box2Collider.inertiaTensor.y = 1; @@ -226,7 +226,7 @@ describe("Joint", function () { box2Collider.applyTorque(new Vector3(0, 1000, 0)); // @ts-ignore engine.sceneManager.activeScene.physics._update(1 / 60); - expect(formatValue(box2Collider.angularVelocity.y)).closeTo(1.513, 0.001); + expect(formatValue(box2Collider.angularVelocity.y)).closeTo(86.74174, 0.01); }); it("connectedInertiaScale", function () { @@ -245,7 +245,7 @@ describe("Joint", function () { box2Collider.applyTorque(new Vector3(0, 1000, 0)); // @ts-ignore engine.sceneManager.activeScene.physics._update(1 / 60); - expect(formatValue(box2Collider.angularVelocity.y)).eq(8.32635); + expect(formatValue(box2Collider.angularVelocity.y)).eq(477.06451); box2.transform.rotation = new Vector3(0, 0, 0); box1Collider.angularVelocity = new Vector3(0, 0, 0); @@ -258,7 +258,7 @@ describe("Joint", function () { box2Collider.applyTorque(new Vector3(0, 1000, 0)); // @ts-ignore engine.sceneManager.activeScene.physics._update(1 / 60); - expect(formatValue(box2Collider.angularVelocity.y)).closeTo(1.513, 0.001); + expect(formatValue(box2Collider.angularVelocity.y)).closeTo(86.74174, 0.01); box2.transform.rotation = new Vector3(0, 0, 0); box2Collider.inertiaTensor.y = 1; @@ -273,7 +273,7 @@ describe("Joint", function () { box2Collider.applyTorque(new Vector3(0, 1000, 0)); // @ts-ignore engine.sceneManager.activeScene.physics._update(1 / 60); - expect(formatValue(box2Collider.angularVelocity.y)).closeTo(1.513, 0.001); + expect(formatValue(box2Collider.angularVelocity.y)).closeTo(86.74174, 0.01); }); it("breakForce", function () {