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

Add basic physics joint component include FixedJoint, SpringJoint, HingeJoint #853

Merged
merged 16 commits into from
Jul 6, 2022
11 changes: 11 additions & 0 deletions packages/core/src/physics/enums/HingeJointFlag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* Flags specific to the Hinge Joint.
*/
export enum HingeJointFlag {
yangfengzzz marked this conversation as resolved.
Show resolved Hide resolved
/** enable the limit */
LimitEnabled = 1,
/** enable the drive */
DriveEnabled = 2,
/** if the existing velocity is beyond the drive velocity, do not add force */
DriveFreeSpin = 4
}
5 changes: 5 additions & 0 deletions packages/core/src/physics/enums/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from "./PhysicsMaterialCombineMode";
export * from "./ColliderShapeUpAxis";
export * from "./ControllerCollisionFlag";
export * from "./ControllerNonWalkableMode";
export * from "./HingeJointFlag";
7 changes: 2 additions & 5 deletions packages/core/src/physics/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
export { HitResult } from "./HitResult";
export { PhysicsMaterialCombineMode } from "./enums/PhysicsMaterialCombineMode";
export { ColliderShapeUpAxis } from "./enums/ColliderShapeUpAxis";
export { ControllerCollisionFlag } from "./enums/ControllerCollisionFlag";
export { ControllerNonWalkableMode } from "./enums/ControllerNonWalkableMode";

export { PhysicsManager } from "./PhysicsManager";
export { PhysicsMaterial } from "./PhysicsMaterial";
export { CharacterController } from "./CharacterController";
export * from "./shape";
export * from "./joint";
export * from "./enums";

export { Collider } from "./Collider";
export { StaticCollider } from "./StaticCollider";
Expand Down
43 changes: 43 additions & 0 deletions packages/core/src/physics/joint/FixedJoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Joint } from "./Joint";
import { IFixedJoint } from "@oasis-engine/design";
import { PhysicsManager } from "../PhysicsManager";
import { dependentComponents } from "../../ComponentsDependencies";
import { Collider } from "../Collider";
import { Vector3 } from "@oasis-engine/math";

/*
* A fixed joint permits no relative movement between two bodies. ie the bodies are glued together.
yangfengzzz marked this conversation as resolved.
Show resolved Hide resolved
*/
export class FixedJoint extends Joint {
private static _offsetVector = new Vector3(1, 0, 0);

/**
* @override
*/
set connectedCollider(value: Collider) {
this._connectedCollider.collider = value;
this._nativeJoint.setConnectedCollider(value._nativeCollider);
const offsetVector = FixedJoint._offsetVector;
Vector3.subtract(this.entity.transform.worldPosition, value.entity.transform.worldPosition, offsetVector);
(<IFixedJoint>this._nativeJoint).setOffset(offsetVector);
}

/**
* @override
* @internal
*/
_onAwake() {
const jointCollider0 = this._connectedCollider;
const jointCollider1 = this._collider;
jointCollider0.collider = null;
jointCollider1.collider = this.entity.getComponent(Collider);
this._nativeJoint = PhysicsManager._nativePhysics.createFixedJoint(
null,
jointCollider0.localPosition,
jointCollider0.localRotation,
jointCollider1.collider._nativeCollider,
jointCollider1.localPosition,
jointCollider1.localRotation
);
}
}
167 changes: 167 additions & 0 deletions packages/core/src/physics/joint/HingeJoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import { Joint } from "./Joint";
import { IHingeJoint } from "@oasis-engine/design";
import { PhysicsManager } from "../PhysicsManager";
import { HingeJointFlag } from "../enums";
import { Collider } from "../Collider";
import { dependentComponents } from "../../ComponentsDependencies";
import { Vector3, Quaternion } from "@oasis-engine/math";
import { JointMotor } from "./JointMotor";
import { JointLimits } from "./JointLimits";

/**
* A joint which behaves in a similar way to a hinge or axle.
*/
export class HingeJoint extends Joint {
private _swingOffset: Vector3 = new Vector3();
private _axis: Vector3 = new Vector3(1, 0, 0);
private _hingeFlags: number = 0;
private _useSpring: boolean = false;
private _jointMonitor: JointMotor;
private _limits: JointLimits;

/**
* The anchor rotation.
*/
get axis(): Vector3 {
return this._axis;
}

set axis(value: Vector3) {
const axis = this._axis;
if (value !== axis) {
axis.copyFrom(value);
}
(<IHingeJoint>this._nativeJoint).setAxis(axis);
}

/**
* The swing offset.
*/
get swingOffset(): Vector3 {
return this._swingOffset;
}

set swingOffset(value: Vector3) {
const swingOffset = this._swingOffset;
if (value !== swingOffset) {
swingOffset.copyFrom(value);
}
(<IHingeJoint>this._nativeJoint).setSwingOffset(swingOffset);
}

/**
* The connected anchor position.
* @remark If connectedCollider is set, this anchor is relative offset.
yangfengzzz marked this conversation as resolved.
Show resolved Hide resolved
* Or the anchor is world anchor position.
*/
get connectedAnchor(): Vector3 {
return this._connectedCollider.localPosition;
}

set connectedAnchor(value: Vector3) {
(<IHingeJoint>this._nativeJoint).setConnectedAnchor(value);
}

/**
* The current angle in degrees of the joint relative to its rest position.
*/
get angle(): number {
return (<IHingeJoint>this._nativeJoint).getAngle();
}

/**
* The angular velocity of the joint in degrees per second.
*/
get velocity(): Readonly<Vector3> {
return (<IHingeJoint>this._nativeJoint).getVelocity();
}

/**
* Enables the joint's limits. Disabled by default.
*/
get useLimits(): boolean {
return (this._hingeFlags & HingeJointFlag.LimitEnabled) == HingeJointFlag.LimitEnabled;
}

set useLimits(value: boolean) {
if (value !== this.useLimits) {
this._hingeFlags |= HingeJointFlag.LimitEnabled;
}
(<IHingeJoint>this._nativeJoint).setRevoluteJointFlag(HingeJointFlag.LimitEnabled, value);
}

/**
* Enables the joint's motor. Disabled by default.
*/
get useMotor(): boolean {
return (this._hingeFlags & HingeJointFlag.DriveEnabled) == HingeJointFlag.DriveEnabled;
}

set useMotor(value: boolean) {
if (value !== this.useMotor) {
this._hingeFlags |= HingeJointFlag.DriveEnabled;
}
(<IHingeJoint>this._nativeJoint).setRevoluteJointFlag(HingeJointFlag.DriveEnabled, value);
}

/**
* Enables the joint's spring. Disabled by default.
*/
get useSpring(): boolean {
return this._useSpring;
}

set useSpring(value: boolean) {
this._useSpring = value;
}

/**
* The motor will apply a force up to a maximum force to achieve the target velocity in degrees per second.
*/
get motor(): JointMotor {
return this._jointMonitor;
}

set motor(value: JointMotor) {
this._jointMonitor = value;
(<IHingeJoint>this._nativeJoint).setDriveVelocity(value.targetVelocity);
(<IHingeJoint>this._nativeJoint).setDriveForceLimit(value.forceLimit);
(<IHingeJoint>this._nativeJoint).setDriveGearRatio(value.gearRation);
(<IHingeJoint>this._nativeJoint).setRevoluteJointFlag(HingeJointFlag.DriveFreeSpin, value.freeSpin);
}

/**
* Limit of angular rotation (in degrees) on the hinge joint.
*/
get limits(): JointLimits {
return this._limits;
}

set limits(value: JointLimits) {
this._limits = value;
if (this.useSpring) {
(<IHingeJoint>this._nativeJoint).setSoftLimit(value.min, value.max, value.stiffness, value.damping);
} else {
(<IHingeJoint>this._nativeJoint).setHardLimit(value.min, value.max, value.contactDistance);
}
yangfengzzz marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* @override
* @internal
*/
_onAwake() {
const jointCollider0 = this._connectedCollider;
const jointCollider1 = this._collider;
jointCollider0.collider = null;
jointCollider1.collider = this.entity.getComponent(Collider);
this._nativeJoint = PhysicsManager._nativePhysics.createHingeJoint(
null,
jointCollider0.localPosition,
jointCollider0.localRotation,
jointCollider1.collider._nativeCollider,
jointCollider1.localPosition,
jointCollider1.localRotation
);
}
}
111 changes: 111 additions & 0 deletions packages/core/src/physics/joint/Joint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { IJoint } from "@oasis-engine/design";
import { Vector3, Quaternion } from "@oasis-engine/math";
import { Component } from "../../Component";
import { Collider } from "../Collider";
import { dependentComponents } from "../../ComponentsDependencies";

/**
* A base class providing common functionality for joints.
* @decorator `@dependentComponents(Collider)`
*/
@dependentComponents(Collider)
export class Joint extends Component {
private _force: number = 0;
private _torque: number = 0;
private _flags: number = 0;
protected _connectedCollider = new JointCollider();
protected _collider = new JointCollider();
protected _nativeJoint: IJoint;

/**
* The connected collider.
*/
get connectedCollider(): Collider {
return this._connectedCollider.collider;
}

set connectedCollider(value: Collider) {
this._connectedCollider.collider = value;
this._nativeJoint.setConnectedCollider(value._nativeCollider);
}

/**
* The scale to apply to the inverse mass of collider 0 for resolving this constraint.
*/
get connectedMassScale(): number {
return this._connectedCollider.massScale;
}

set connectedMassScale(value: number) {
this._connectedCollider.massScale = value;
this._nativeJoint.setConnectedMassScale(value);
}

/**
* The scale to apply to the inverse inertia of collider0 for resolving this constraint.
*/
get connectedInertiaScale(): number {
return this._connectedCollider.inertiaScale;
}

set connectedInertiaScale(value: number) {
this._connectedCollider.inertiaScale = value;
this._nativeJoint.setConnectedInertiaScale(value);
}

/**
* The scale to apply to the inverse mass of collider 1 for resolving this constraint.
*/
get massScale(): number {
return this._collider.massScale;
}

set massScale(value: number) {
this._collider.massScale = value;
this._nativeJoint.setMassScale(value);
}

/**
* The scale to apply to the inverse inertia of collider1 for resolving this constraint.
*/
get inertiaScale(): number {
return this._collider.inertiaScale;
}

set inertiaScale(value: number) {
this._collider.inertiaScale = value;
this._nativeJoint.setInertiaScale(value);
}

/**
* The maximum force the joint can apply before breaking.
*/
get breakForce(): number {
return this._force;
}

set breakForce(value: number) {
this._force = value;
this._nativeJoint.setBreakForce(value);
}

/**
* The maximum torque the joint can apply before breaking.
*/
get breakTorque(): number {
return this._torque;
}

set breakTorque(value: number) {
this._torque = value;
this._nativeJoint.setBreakTorque(value);
}
}

class JointCollider {
collider: Collider = null;
localPosition = new Vector3();
localRotation = new Quaternion();
massScale: number = 0;
inertiaScale: number = 0;
}
16 changes: 16 additions & 0 deletions packages/core/src/physics/joint/JointLimits.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* JointLimits is used to limit the joints angle.
*/
export class JointLimits {
/** The upper angular limit (in degrees) of the joint. */
max: number = 0;
/** The lower angular limit (in degrees) of the joint. */
min: number = 0;
/** Distance inside the limit value at which the limit will be considered to be active by the solver. */
contactDistance: number = -1;

/** The spring forces used to reach the target position. */
stiffness: number = 0;
/** The damper force uses to dampen the spring. */
damping: number = 0;
}
13 changes: 13 additions & 0 deletions packages/core/src/physics/joint/JointMotor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* The JointMotor is used to motorize a joint.
*/
export class JointMotor {
/** The motor will apply a force up to force to achieve targetVelocity. */
targetVelocity: number = 0;
/** The force limit.*/
forceLimit: number = Number.MAX_VALUE;
/** Gear ration for the motor */
gearRation: number = 1.0;
/** If freeSpin is enabled the motor will only accelerate but never slow down. */
freeSpin: boolean = false;
}
Loading