diff --git a/examples/src/examples/physics/rope-bridge.example.mjs b/examples/src/examples/physics/rope-bridge.example.mjs new file mode 100644 index 00000000000..80b6185dcb7 --- /dev/null +++ b/examples/src/examples/physics/rope-bridge.example.mjs @@ -0,0 +1,267 @@ +import { deviceType, rootPath, fileImport } from 'examples/utils'; +import * as pc from 'playcanvas'; + +const { CameraControls } = await fileImport(`${rootPath}/static/scripts/camera-controls.mjs`); + +const canvas = /** @type {HTMLCanvasElement} */ (document.getElementById('application-canvas')); +window.focus(); + +pc.WasmModule.setConfig('Ammo', { + glueUrl: `${rootPath}/static/lib/ammo/ammo.wasm.js`, + wasmUrl: `${rootPath}/static/lib/ammo/ammo.wasm.wasm`, + fallbackUrl: `${rootPath}/static/lib/ammo/ammo.js` +}); +await new Promise((resolve) => { + pc.WasmModule.getInstance('Ammo', () => resolve()); +}); + +const gfxOptions = { + deviceTypes: [deviceType], + glslangUrl: `${rootPath}/static/lib/glslang/glslang.js`, + twgslUrl: `${rootPath}/static/lib/twgsl/twgsl.js` +}; + +const device = await pc.createGraphicsDevice(canvas, gfxOptions); +device.maxPixelRatio = Math.min(window.devicePixelRatio, 2); + +const createOptions = new pc.AppOptions(); +createOptions.graphicsDevice = device; +createOptions.keyboard = new pc.Keyboard(document.body); + +createOptions.componentSystems = [ + pc.CameraComponentSystem, + pc.CollisionComponentSystem, + pc.JointComponentSystem, + pc.LightComponentSystem, + pc.RenderComponentSystem, + pc.RigidBodyComponentSystem, + pc.ScriptComponentSystem +]; +createOptions.resourceHandlers = [ + pc.ScriptHandler +]; + +const app = new pc.AppBase(canvas); +app.init(createOptions); + +// Set the canvas to fill the window and automatically change resolution to be the same as the canvas size +app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW); +app.setCanvasResolution(pc.RESOLUTION_AUTO); + +// Ensure canvas is resized when window changes size +const resize = () => app.resizeCanvas(); +window.addEventListener('resize', resize); +app.on('destroy', () => { + window.removeEventListener('resize', resize); +}); + +// Start the app and create the scene +app.start(); + +// Create materials +const woodMaterial = new pc.StandardMaterial(); +woodMaterial.diffuse = new pc.Color(0.7, 0.5, 0.3); +woodMaterial.update(); + +const anchorMaterial = new pc.StandardMaterial(); +anchorMaterial.diffuse = new pc.Color(0.3, 0.3, 0.3); +anchorMaterial.update(); + +// Create the camera +const camera = new pc.Entity('Camera'); +camera.addComponent('camera', { + clearColor: new pc.Color(0.5, 0.5, 0.8) +}); +camera.addComponent('script'); +camera.setLocalPosition(0, 7, 10); +app.root.addChild(camera); + +camera.script.create(CameraControls, { + attributes: { + focusPoint: new pc.Vec3(0, 3, 0), + sceneSize: 10 + } +}); + +// Create the light +const light = new pc.Entity('Light'); +light.addComponent('light', { + type: 'directional', + castShadows: true, + shadowBias: 0.2, + shadowDistance: 40, + normalOffsetBias: 0.05, + shadowResolution: 2048 +}); +light.setEulerAngles(45, 30, 0); +app.root.addChild(light); + +function createGround() { + const ground = new pc.Entity(); + ground.addComponent('render', { type: 'plane' }); + ground.addComponent('rigidbody', { + type: 'static', + restitution: 0.5 + }); + ground.addComponent('collision', { + type: 'box', + halfExtents: new pc.Vec3(20, 0.5, 10), + linearOffset: new pc.Vec3(0, -0.5, 0) + }); + ground.setLocalScale(40, 1, 20); + app.root.addChild(ground); +} + +// Create a bridge with physics joints +function createBridge(startPos, endPos, segments) { + const plankWidth = 0.5; // Width along X axis (spacing between planks) + const plankHeight = 0.1; // Height along Y axis + const plankLength = 2; // Length along Z axis (across the bridge) + + const plankPos = startPos.clone(); + plankPos.x += plankWidth / 2; + + // Create bridge planks with minimal spacing + const planks = []; + for (let i = 0; i < segments; i++) { + // Create the physics plank with no scaling + const plank = new pc.Entity(); + plank.addComponent('rigidbody', { + type: 'dynamic', + mass: 0.5, + friction: 0.6, + restitution: 0.1 + }); + plank.addComponent('collision', { + type: 'box', + halfExtents: new pc.Vec3(plankWidth / 2, plankHeight / 2, plankLength / 2) + }); + + // Position the plank + plank.setLocalPosition(plankPos); + plankPos.x += plankWidth; + + // Create a child entity for the visual representation + const visualPlank = new pc.Entity('visual'); + visualPlank.addComponent('render', { + type: 'box', + material: woodMaterial + }); + visualPlank.setLocalScale(plankWidth, plankHeight, plankLength); + plank.addChild(visualPlank); + + app.root.addChild(plank); + planks.push(plank); + } + + // Connect planks with joints + const jointPos = startPos.clone(); + jointPos.x += plankWidth; + + for (let i = 0; i < segments - 1; i++) { + const joint = new pc.Entity(); + + // Position the joint at the center of plankA + joint.setLocalPosition(jointPos); + jointPos.x += plankWidth; + + joint.addComponent('joint', { + entityA: planks[i], + entityB: planks[i + 1], + enableCollision: false + }); + app.root.addChild(joint); + } + + // Create fixed anchor points in space + function createFixedPoint(pos, plank) { + const joint = new pc.Entity(); + joint.setLocalPosition(pos); + + joint.addComponent('joint', { + entityA: plank, // Only connect to the plank + entityB: null, // No second entity needed for fixed point + enableCollision: false, + angularMotionZ: pc.MOTION_FREE + }); + app.root.addChild(joint); + } + + // Create fixed points at start and end + createFixedPoint(startPos, planks[0]); + createFixedPoint(endPos, planks[planks.length - 1]); +} + +// Create towers at the ends of the bridge +function createTower(position) { + const towerWidth = 2; + const towerHeight = 6; + const towerDepth = 2; + + const tower = new pc.Entity('Tower'); + tower.addComponent('rigidbody', { + type: 'static' + }); + tower.addComponent('collision', { + type: 'box', + halfExtents: new pc.Vec3(towerWidth / 2, towerHeight / 2, towerDepth / 2) + }); + + const visualTower = new pc.Entity('visual'); + visualTower.addComponent('render', { + type: 'box', + material: anchorMaterial + }); + visualTower.setLocalScale(towerWidth, towerHeight, towerDepth); + tower.addChild(visualTower); + + const towerPos = new pc.Vec3(position.x, position.y - 3, position.z); + tower.setLocalPosition(towerPos); + app.root.addChild(tower); +} + +// Create bridge and towers +const towerWidth = 2; + +// Calculate bridge connection points at tower sides and plank edges +const bridgeStart = new pc.Vec3(-5, 5, 0); // Right side of left tower + half plank +const bridgeEnd = new pc.Vec3(5, 5, 0); // Left side of right tower - half plank + +createGround(); +createTower(new pc.Vec3(-5 - towerWidth / 2, 5, 0)); +createTower(new pc.Vec3(5 + towerWidth / 2, 5, 0)); +createBridge(bridgeStart, bridgeEnd, 20); + +// Create a test sphere to drop on the bridge +function createTestSphere(position) { + const sphere = new pc.Entity(); + sphere.addComponent('render', { + type: 'sphere' + }); + sphere.addComponent('rigidbody', { + type: 'dynamic', + mass: 5, + friction: 0.5, + restitution: 0.3 + }); + sphere.addComponent('collision', { + type: 'sphere', + radius: 0.5 + }); + sphere.setLocalPosition(position); + app.root.addChild(sphere); + return sphere; +} + +// Add a sphere that can be dropped with spacebar +let testSphere = null; +app.keyboard.on('keydown', (event) => { + if (event.key === pc.KEY_SPACE) { + if (testSphere) { + testSphere.destroy(); + } + testSphere = createTestSphere(new pc.Vec3(0, 10, 0)); + } +}); + +export { app }; diff --git a/src/framework/components/joint/component.js b/src/framework/components/joint/component.js index c556ac60a07..1453a0997cd 100644 --- a/src/framework/components/joint/component.js +++ b/src/framework/components/joint/component.js @@ -28,198 +28,830 @@ const properties = [ ]; /** - * The JointComponent adds a physics joint constraint linking two rigid bodies. + * The JointComponent adds a physics joint constraint between two rigid body components. + * A joint connects two rigid bodies and restricts their relative movement in various ways. + * It supports both linear and angular constraints along all three axes, with options for: * - * @ignore + * - Locked motion (no movement) + * - Free motion (unrestricted movement) + * - Limited motion (movement within specified limits) + * - Spring behavior (with configurable stiffness, damping, and equilibrium points) + * + * This can be used to create a variety of joint types like hinges, sliders, ball-and-socket joints, etc. + * Each degree of freedom (linear and angular) for each axis (X, Y, Z) can be configured independently. + * + * @beta */ class JointComponent extends Component { + /** @private */ + _constraint = null; + + /** @private */ + _entityA = null; + + /** @private */ + _entityB = null; + + /** @private */ + _breakForce = 3.4e+38; + + /** @private */ + _enableCollision = true; + + // Linear X degree of freedom + /** @private */ + _linearMotionX = MOTION_LOCKED; + + /** @private */ + _linearLimitsX = new Vec2(0, 0); + + /** @private */ + _linearSpringX = false; + + /** @private */ + _linearStiffnessX = 0; + + /** @private */ + _linearDampingX = 1; + + /** @private */ + _linearEquilibriumX = 0; + + // Linear Y degree of freedom + /** @private */ + _linearMotionY = MOTION_LOCKED; + + /** @private */ + _linearLimitsY = new Vec2(0, 0); + + /** @private */ + _linearSpringY = false; + + /** @private */ + _linearStiffnessY = 0; + + /** @private */ + _linearDampingY = 1; + + /** @private */ + _linearEquilibriumY = 0; + + // Linear Z degree of freedom + /** @private */ + _linearMotionZ = MOTION_LOCKED; + + /** @private */ + _linearLimitsZ = new Vec2(0, 0); + + /** @private */ + _linearSpringZ = false; + + /** @private */ + _linearStiffnessZ = 0; + + /** @private */ + _linearDampingZ = 1; + + /** @private */ + _linearEquilibriumZ = 0; + + // Angular X degree of freedom + /** @private */ + _angularMotionX = MOTION_LOCKED; + + /** @private */ + _angularLimitsX = new Vec2(0, 0); + + /** @private */ + _angularSpringX = false; + + /** @private */ + _angularStiffnessX = 0; + + /** @private */ + _angularDampingX = 1; + + /** @private */ + _angularEquilibriumX = 0; + + // Angular Y degree of freedom + /** @private */ + _angularMotionY = MOTION_LOCKED; + + /** @private */ + _angularLimitsY = new Vec2(0, 0); + + /** @private */ + _angularSpringY = false; + + /** @private */ + _angularStiffnessY = 0; + + /** @private */ + _angularDampingY = 1; + + /** @private */ + _angularEquilibriumY = 0; + + // Angular Z degree of freedom + /** @private */ + _angularMotionZ = MOTION_LOCKED; + + /** @private */ + _angularLimitsZ = new Vec2(0, 0); + + /** @private */ + _angularSpringZ = false; + + /** @private */ + _angularEquilibriumZ = 0; + + /** @private */ + _angularDampingZ = 1; + + /** @private */ + _angularStiffnessZ = 0; + /** * Create a new JointComponent instance. * - * @param {JointComponentSystem} system - The ComponentSystem that created this Component. - * @param {Entity} entity - The Entity that this Component is attached to. + * @param {JointComponentSystem} system - The ComponentSystem that created this Component. + * @param {Entity} entity - The Entity that this Component is attached to. + */ + constructor(system, entity) { + super(system, entity); + + Debug.assert(typeof Ammo !== 'undefined', 'ERROR: Attempting to create a pc.JointComponent but Ammo.js is not loaded'); + } + + /** + * Sets the angular damping for the X-axis. This reduces the angular velocity over time. + * A value of 0 means no damping, while higher values cause more damping. + * + * @type {number} + */ + set angularDampingX(value) { + if (this._angularDampingX !== value) { + this._angularDampingX = value; + if (this._constraint) this._constraint.setDamping(3, value); + } + } + + /** + * Gets the angular damping for the X-axis. + * + * @type {number} + */ + get angularDampingX() { + return this._angularDampingX; + } + + /** + * Sets the angular damping for the Y-axis. This reduces the angular velocity over time. + * A value of 0 means no damping, while higher values cause more damping. + * + * @type {number} + */ + set angularDampingY(value) { + if (this._angularDampingY !== value) { + this._angularDampingY = value; + if (this._constraint) this._constraint.setDamping(4, value); + } + } + + /** + * Gets the angular damping for the Y-axis. + * + * @type {number} + */ + get angularDampingY() { + return this._angularDampingY; + } + + /** + * Sets the angular damping for the Z-axis. This reduces the angular velocity over time. + * A value of 0 means no damping, while higher values cause more damping. + * + * @type {number} + */ + set angularDampingZ(value) { + if (this._angularDampingZ !== value) { + this._angularDampingZ = value; + if (this._constraint) this._constraint.setDamping(5, value); + } + } + + /** + * Gets the angular damping for the Z-axis. + * + * @type {number} + */ + get angularDampingZ() { + return this._angularDampingZ; + } + + /** + * Sets the angular equilibrium point for the X-axis. The spring will attempt to reach this angle. + * + * @type {number} + */ + set angularEquilibriumX(value) { + if (this._angularEquilibriumX !== value) { + this._angularEquilibriumX = value; + if (this._constraint) this._constraint.setEquilibriumPoint(3, value); + } + } + + /** + * Gets the angular equilibrium point for the X-axis. + * + * @type {number} + */ + get angularEquilibriumX() { + return this._angularEquilibriumX; + } + + /** + * Sets the angular equilibrium point for the Y-axis. The spring will attempt to reach this angle. + * + * @type {number} + */ + set angularEquilibriumY(value) { + if (this._angularEquilibriumY !== value) { + this._angularEquilibriumY = value; + if (this._constraint) this._constraint.setEquilibriumPoint(4, value); + } + } + + /** + * Gets the angular equilibrium point for the Y-axis. + * + * @type {number} + */ + get angularEquilibriumY() { + return this._angularEquilibriumY; + } + + /** + * Sets the angular equilibrium point for the Z-axis. The spring will attempt to reach this angle. + * + * @type {number} + */ + set angularEquilibriumZ(value) { + if (this._angularEquilibriumZ !== value) { + this._angularEquilibriumZ = value; + if (this._constraint) this._constraint.setEquilibriumPoint(5, value); + } + } + + /** + * Gets the angular equilibrium point for the Z-axis. + * + * @type {number} + */ + get angularEquilibriumZ() { + return this._angularEquilibriumZ; + } + + /** + * Sets the angular limits for the X-axis rotation in degrees. + * + * @type {Vec2} + */ + set angularLimitsX(limits) { + if (!this._angularLimitsX.equals(limits)) { + this._angularLimitsX.copy(limits); + this._updateAngularLimits(); + } + } + + /** + * Gets the angular limits for the X-axis rotation. + * + * @type {Vec2} + */ + get angularLimitsX() { + return this._angularLimitsX; + } + + /** + * Sets the angular limits for the Y-axis rotation in degrees. + * + * @type {Vec2} + */ + set angularLimitsY(limits) { + if (!this._angularLimitsY.equals(limits)) { + this._angularLimitsY.copy(limits); + this._updateAngularLimits(); + } + } + + /** + * Gets the angular limits for the Y-axis rotation. + * + * @type {Vec2} + */ + get angularLimitsY() { + return this._angularLimitsY; + } + + /** + * Sets the angular limits for the Z-axis rotation in degrees. + * + * @type {Vec2} + */ + set angularLimitsZ(limits) { + if (!this._angularLimitsZ.equals(limits)) { + this._angularLimitsZ.copy(limits); + this._updateAngularLimits(); + } + } + + /** + * Gets the angular limits for the Z-axis rotation. + * + * @type {Vec2} + */ + get angularLimitsZ() { + return this._angularLimitsZ; + } + + /** + * Sets the type of motion allowed for rotation around the X-axis. Can be: + * - MOTION_LOCKED: No rotation allowed + * - MOTION_FREE: Unlimited rotation allowed + * - MOTION_LIMITED: Rotation limited by angularLimitsX + * + * @type {string} + */ + set angularMotionX(value) { + if (this._angularMotionX !== value) { + this._angularMotionX = value; + this._updateAngularLimits(); + } + } + + /** + * Gets the type of motion allowed for rotation around the X-axis. + * + * @type {string} + */ + get angularMotionX() { + return this._angularMotionX; + } + + /** + * Sets the type of motion allowed for rotation around the Y-axis. Can be: + * - MOTION_LOCKED: No rotation allowed + * - MOTION_FREE: Unlimited rotation allowed + * - MOTION_LIMITED: Rotation limited by angularLimitsY + * + * @type {string} + */ + set angularMotionY(value) { + if (this._angularMotionY !== value) { + this._angularMotionY = value; + this._updateAngularLimits(); + } + } + + /** + * Gets the type of motion allowed for rotation around the Y-axis. + * + * @type {string} + */ + get angularMotionY() { + return this._angularMotionY; + } + + /** + * Sets the type of motion allowed for rotation around the Z-axis. Can be: + * - MOTION_LOCKED: No rotation allowed + * - MOTION_FREE: Unlimited rotation allowed + * - MOTION_LIMITED: Rotation limited by angularLimitsZ + * + * @type {string} + */ + set angularMotionZ(value) { + if (this._angularMotionZ !== value) { + this._angularMotionZ = value; + this._updateAngularLimits(); + } + } + + /** + * Gets the type of motion allowed for rotation around the Z-axis. + * + * @type {string} + */ + get angularMotionZ() { + return this._angularMotionZ; + } + + /** + * Enables or disables the spring behavior for rotation around the X-axis. + * + * @type {boolean} + */ + set angularSpringX(value) { + if (this._angularSpringX !== value) { + this._angularSpringX = value; + if (this._constraint) this._constraint.enableSpring(3, value); + } + } + + /** + * Gets whether the spring behavior is enabled for rotation around the X-axis. + * + * @type {boolean} + */ + get angularSpringX() { + return this._angularSpringX; + } + + /** + * Enables or disables the spring behavior for rotation around the Y-axis. + * + * @type {boolean} + */ + set angularSpringY(value) { + if (this._angularSpringY !== value) { + this._angularSpringY = value; + if (this._constraint) this._constraint.enableSpring(4, value); + } + } + + /** + * Gets whether the spring behavior is enabled for rotation around the Y-axis. + * + * @type {boolean} + */ + get angularSpringY() { + return this._angularSpringY; + } + + /** + * Enables or disables the spring behavior for rotation around the Z-axis. + * + * @type {boolean} + */ + set angularSpringZ(value) { + if (this._angularSpringZ !== value) { + this._angularSpringZ = value; + if (this._constraint) this._constraint.enableSpring(5, value); + } + } + + /** + * Gets whether the spring behavior is enabled for rotation around the Z-axis. + * + * @type {boolean} + */ + get angularSpringZ() { + return this._angularSpringZ; + } + + /** + * Sets the spring stiffness for rotation around the X-axis. + * + * @type {number} + */ + set angularStiffnessX(value) { + if (this._angularStiffnessX !== value) { + this._angularStiffnessX = value; + if (this._constraint) this._constraint.setStiffness(3, value); + } + } + + /** + * Gets the spring stiffness for rotation around the X-axis. + * + * @type {number} + */ + get angularStiffnessX() { + return this._angularStiffnessX; + } + + /** + * Sets the spring stiffness for rotation around the Y-axis. + * + * @type {number} + */ + set angularStiffnessY(value) { + if (this._angularStiffnessY !== value) { + this._angularStiffnessY = value; + if (this._constraint) this._constraint.setStiffness(4, value); + } + } + + /** + * Gets the spring stiffness for rotation around the Y-axis. + * + * @type {number} + */ + get angularStiffnessY() { + return this._angularStiffnessY; + } + + /** + * Sets the spring stiffness for rotation around the Z-axis. + * + * @type {number} + */ + set angularStiffnessZ(value) { + if (this._angularStiffnessZ !== value) { + this._angularStiffnessZ = value; + if (this._constraint) this._constraint.setStiffness(5, value); + } + } + + /** + * Gets the spring stiffness for rotation around the Z-axis. + * + * @type {number} + */ + get angularStiffnessZ() { + return this._angularStiffnessZ; + } + + /** + * Sets the breaking force threshold for the constraint. + * + * @type {number} + */ + set breakForce(force) { + if (this._constraint && this._breakForce !== force) { + this._constraint.setBreakingImpulseThreshold(force); + this._breakForce = force; + } + } + + /** + * Gets the breaking force threshold for the constraint. + * + * @type {number} + */ + get breakForce() { + return this._breakForce; + } + + /** + * Enables or disables collision between the constrained entities. + * + * @type {boolean} + */ + set enableCollision(enableCollision) { + this._destroyConstraint(); + this._enableCollision = enableCollision; + this._createConstraint(); + } + + /** + * Gets whether collision is enabled between the constrained entities. + * + * @type {boolean} */ - constructor(system, entity) { - super(system, entity); - - Debug.assert(typeof Ammo !== 'undefined', 'ERROR: Attempting to create a pc.JointComponent but Ammo.js is not loaded'); - - this._constraint = null; - - this._entityA = null; - this._entityB = null; - this._breakForce = 3.4e+38; - this._enableCollision = true; - - // Linear X degree of freedom - this._linearMotionX = MOTION_LOCKED; - this._linearLimitsX = new Vec2(0, 0); - this._linearSpringX = false; - this._linearStiffnessX = 0; - this._linearDampingX = 1; - this._linearEquilibriumX = 0; - - // Linear Y degree of freedom - this._linearMotionY = MOTION_LOCKED; - this._linearLimitsY = new Vec2(0, 0); - this._linearSpringY = false; - this._linearStiffnessY = 0; - this._linearDampingY = 1; - this._linearEquilibriumY = 0; - - // Linear Z degree of freedom - this._linearMotionZ = MOTION_LOCKED; - this._linearLimitsZ = new Vec2(0, 0); - this._linearSpringZ = false; - this._linearStiffnessZ = 0; - this._linearDampingZ = 1; - this._linearEquilibriumZ = 0; - - // Angular X degree of freedom - this._angularMotionX = MOTION_LOCKED; - this._angularLimitsX = new Vec2(0, 0); - this._angularSpringX = false; - this._angularStiffnessX = 0; - this._angularDampingX = 1; - this._angularEquilibriumX = 0; - - // Angular Y degree of freedom - this._angularMotionY = MOTION_LOCKED; - this._angularLimitsY = new Vec2(0, 0); - this._angularSpringY = false; - this._angularStiffnessY = 0; - this._angularDampingY = 1; - this._angularEquilibriumY = 0; - - // Angular Z degree of freedom - this._angularMotionZ = MOTION_LOCKED; - this._angularLimitsZ = new Vec2(0, 0); - this._angularSpringZ = false; - this._angularEquilibriumZ = 0; - this._angularDampingZ = 1; - this._angularStiffnessZ = 0; - - this.on('set_enabled', this._onSetEnabled, this); + get enableCollision() { + return this._enableCollision; } + /** + * Sets the first entity in the constraint. + * + * @type {Entity} + */ set entityA(body) { this._destroyConstraint(); this._entityA = body; this._createConstraint(); } + /** + * Gets the first entity in the constraint. + * + * @type {Entity} + */ get entityA() { return this._entityA; } + /** + * Sets the second entity in the constraint. + * + * @type {Entity} + */ set entityB(body) { this._destroyConstraint(); this._entityB = body; this._createConstraint(); } + /** + * Gets the second entity in the constraint. + * + * @type {Entity} + */ get entityB() { return this._entityB; } - set breakForce(force) { - if (this._constraint && this._breakForce !== force) { - this._constraint.setBreakingImpulseThreshold(force); - this._breakForce = force; + /** + * Sets the linear damping for movement along the X-axis. + * + * @type {number} + */ + set linearDampingX(value) { + if (this._linearDampingX !== value) { + this._linearDampingX = value; + if (this._constraint) this._constraint.setDamping(0, value); } } - get breakForce() { - return this._breakForce; + /** + * Gets the linear damping for movement along the X-axis. + * + * @type {number} + */ + get linearDampingX() { + return this._linearDampingX; } - set enableCollision(enableCollision) { - this._destroyConstraint(); - this._enableCollision = enableCollision; - this._createConstraint(); + /** + * Sets the linear damping for movement along the Y-axis. + * + * @type {number} + */ + set linearDampingY(value) { + if (this._linearDampingY !== value) { + this._linearDampingY = value; + if (this._constraint) this._constraint.setDamping(1, value); + } } - get enableCollision() { - return this._enableCollision; + /** + * Gets the linear damping for movement along the Y-axis. + * + * @type {number} + */ + get linearDampingY() { + return this._linearDampingY; } - set angularLimitsX(limits) { - if (!this._angularLimitsX.equals(limits)) { - this._angularLimitsX.copy(limits); - this._updateAngularLimits(); + /** + * Sets the linear damping for movement along the Z-axis. + * + * @type {number} + */ + set linearDampingZ(value) { + if (this._linearDampingZ !== value) { + this._linearDampingZ = value; + if (this._constraint) this._constraint.setDamping(2, value); } } - get angularLimitsX() { - return this._angularLimitsX; + /** + * Gets the linear damping for movement along the Z-axis. + * + * @type {number} + */ + get linearDampingZ() { + return this._linearDampingZ; } - set angularMotionX(value) { - if (this._angularMotionX !== value) { - this._angularMotionX = value; - this._updateAngularLimits(); + /** + * Sets the linear equilibrium point for movement along the X-axis. + * + * @type {number} + */ + set linearEquilibriumX(value) { + if (this._linearEquilibriumX !== value) { + this._linearEquilibriumX = value; + if (this._constraint) this._constraint.setEquilibriumPoint(0, value); } } - get angularMotionX() { - return this._angularMotionX; + /** + * Gets the linear equilibrium point for movement along the X-axis. + * + * @type {number} + */ + get linearEquilibriumX() { + return this._linearEquilibriumX; } - set angularLimitsY(limits) { - if (!this._angularLimitsY.equals(limits)) { - this._angularLimitsY.copy(limits); - this._updateAngularLimits(); + /** + * Sets the linear equilibrium point for movement along the Y-axis. + * + * @type {number} + */ + set linearEquilibriumY(value) { + if (this._linearEquilibriumY !== value) { + this._linearEquilibriumY = value; + if (this._constraint) this._constraint.setEquilibriumPoint(1, value); } } - get angularLimitsY() { - return this._angularLimitsY; + /** + * Gets the linear equilibrium point for movement along the Y-axis. + * + * @type {number} + */ + get linearEquilibriumY() { + return this._linearEquilibriumY; } - set angularMotionY(value) { - if (this._angularMotionY !== value) { - this._angularMotionY = value; - this._updateAngularLimits(); + /** + * Sets the linear equilibrium point for movement along the Z-axis. + * + * @type {number} + */ + set linearEquilibriumZ(value) { + if (this._linearEquilibriumZ !== value) { + this._linearEquilibriumZ = value; + if (this._constraint) this._constraint.setEquilibriumPoint(2, value); } } - get angularMotionY() { - return this._angularMotionY; + /** + * Gets the linear equilibrium point for movement along the Z-axis. + * + * @type {number} + */ + get linearEquilibriumZ() { + return this._linearEquilibriumZ; } - set angularLimitsZ(limits) { - if (!this._angularLimitsZ.equals(limits)) { - this._angularLimitsZ.copy(limits); - this._updateAngularLimits(); + /** + * Sets the linear limits for movement along the X-axis. + * + * @type {Vec2} + */ + set linearLimitsX(limits) { + if (!this._linearLimitsX.equals(limits)) { + this._linearLimitsX.copy(limits); + this._updateLinearLimits(); } } - get angularLimitsZ() { - return this._angularLimitsZ; + /** + * Gets the linear limits for movement along the X-axis. + * + * @type {Vec2} + */ + get linearLimitsX() { + return this._linearLimitsX; } - set angularMotionZ(value) { - if (this._angularMotionZ !== value) { - this._angularMotionZ = value; - this._updateAngularLimits(); + /** + * Sets the linear limits for movement along the Y-axis. + * + * @type {Vec2} + */ + set linearLimitsY(limits) { + if (!this._linearLimitsY.equals(limits)) { + this._linearLimitsY.copy(limits); + this._updateLinearLimits(); } } - get angularMotionZ() { - return this._angularMotionZ; + /** + * Gets the linear limits for movement along the Y-axis. + * + * @type {Vec2} + */ + get linearLimitsY() { + return this._linearLimitsY; } - set linearLimitsX(limits) { - if (!this._linearLimitsX.equals(limits)) { - this._linearLimitsX.copy(limits); + /** + * Sets the linear limits for movement along the Z-axis. + * + * @type {Vec2} + */ + set linearLimitsZ(limits) { + if (!this._linearLimitsZ.equals(limits)) { + this._linearLimitsZ.copy(limits); this._updateLinearLimits(); } } - get linearLimitsX() { - return this._linearLimitsX; + /** + * Gets the linear limits for movement along the Z-axis. + * + * @type {Vec2} + */ + get linearLimitsZ() { + return this._linearLimitsZ; } + /** + * Sets the type of motion allowed for movement along the X-axis. Can be: + * - MOTION_LOCKED: No movement allowed + * - MOTION_FREE: Unlimited movement allowed + * - MOTION_LIMITED: Movement limited by linearLimitsX + * + * @type {string} + */ set linearMotionX(value) { if (this._linearMotionX !== value) { this._linearMotionX = value; @@ -227,21 +859,23 @@ class JointComponent extends Component { } } + /** + * Gets the type of motion allowed for movement along the X-axis. + * + * @type {string} + */ get linearMotionX() { return this._linearMotionX; } - set linearLimitsY(limits) { - if (!this._linearLimitsY.equals(limits)) { - this._linearLimitsY.copy(limits); - this._updateLinearLimits(); - } - } - - get linearLimitsY() { - return this._linearLimitsY; - } - + /** + * Sets the type of motion allowed for movement along the Y-axis. Can be: + * - MOTION_LOCKED: No movement allowed + * - MOTION_FREE: Unlimited movement allowed + * - MOTION_LIMITED: Movement limited by linearLimitsY + * + * @type {string} + */ set linearMotionY(value) { if (this._linearMotionY !== value) { this._linearMotionY = value; @@ -249,21 +883,23 @@ class JointComponent extends Component { } } + /** + * Gets the type of motion allowed for movement along the Y-axis. + * + * @type {string} + */ get linearMotionY() { return this._linearMotionY; } - set linearLimitsZ(limits) { - if (!this._linearLimitsZ.equals(limits)) { - this._linearLimitsZ.copy(limits); - this._updateLinearLimits(); - } - } - - get linearLimitsZ() { - return this._linearLimitsZ; - } - + /** + * Sets the type of motion allowed for movement along the Z-axis. Can be: + * - MOTION_LOCKED: No movement allowed + * - MOTION_FREE: Unlimited movement allowed + * - MOTION_LIMITED: Movement limited by linearLimitsZ + * + * @type {string} + */ set linearMotionZ(value) { if (this._linearMotionZ !== value) { this._linearMotionZ = value; @@ -271,10 +907,141 @@ class JointComponent extends Component { } } + /** + * Gets the type of motion allowed for movement along the Z-axis. + * + * @type {string} + */ get linearMotionZ() { return this._linearMotionZ; } + /** + * Enables or disables the spring behavior for movement along the X-axis. + * + * @type {boolean} + */ + set linearSpringX(value) { + if (this._linearSpringX !== value) { + this._linearSpringX = value; + if (this._constraint) this._constraint.enableSpring(0, value); + } + } + + /** + * Gets whether the spring behavior is enabled for movement along the X-axis. + * + * @type {boolean} + */ + get linearSpringX() { + return this._linearSpringX; + } + + /** + * Enables or disables the spring behavior for movement along the Y-axis. + * + * @type {boolean} + */ + set linearSpringY(value) { + if (this._linearSpringY !== value) { + this._linearSpringY = value; + if (this._constraint) this._constraint.enableSpring(1, value); + } + } + + /** + * Gets whether the spring behavior is enabled for movement along the Y-axis. + * + * @type {boolean} + */ + get linearSpringY() { + return this._linearSpringY; + } + + /** + * Enables or disables the spring behavior for movement along the Z-axis. + * + * @type {boolean} + */ + set linearSpringZ(value) { + if (this._linearSpringZ !== value) { + this._linearSpringZ = value; + if (this._constraint) this._constraint.enableSpring(2, value); + } + } + + /** + * Gets whether the spring behavior is enabled for movement along the Z-axis. + * + * @type {boolean} + */ + get linearSpringZ() { + return this._linearSpringZ; + } + + /** + * Sets the spring stiffness for movement along the X-axis. + * + * @type {number} + */ + set linearStiffnessX(value) { + if (this._linearStiffnessX !== value) { + this._linearStiffnessX = value; + if (this._constraint) this._constraint.setStiffness(0, value); + } + } + + /** + * Gets the spring stiffness for movement along the X-axis. + * + * @type {number} + */ + get linearStiffnessX() { + return this._linearStiffnessX; + } + + /** + * Sets the spring stiffness for movement along the Y-axis. + * + * @type {number} + */ + set linearStiffnessY(value) { + if (this._linearStiffnessY !== value) { + this._linearStiffnessY = value; + if (this._constraint) this._constraint.setStiffness(1, value); + } + } + + /** + * Gets the spring stiffness for movement along the Y-axis. + * + * @type {number} + */ + get linearStiffnessY() { + return this._linearStiffnessY; + } + + /** + * Sets the spring stiffness for movement along the Z-axis. + * + * @type {number} + */ + set linearStiffnessZ(value) { + if (this._linearStiffnessZ !== value) { + this._linearStiffnessZ = value; + if (this._constraint) this._constraint.setStiffness(2, value); + } + } + + /** + * Gets the spring stiffness for movement along the Z-axis. + * + * @type {number} + */ + get linearStiffnessZ() { + return this._linearStiffnessZ; + } + _convertTransform(pcTransform, ammoTransform) { const pos = pcTransform.getTranslation(); const rot = new Quat(); @@ -468,46 +1235,9 @@ class JointComponent extends Component { this._destroyConstraint(); } - _onSetEnabled(prop, old, value) { - } - _onBeforeRemove() { this.fire('remove'); } } -const functionMap = { - Damping: 'setDamping', - Equilibrium: 'setEquilibriumPoint', - Spring: 'enableSpring', - Stiffness: 'setStiffness' -}; - -// Define additional properties for each degree of freedom -['linear', 'angular'].forEach((type) => { - ['Damping', 'Equilibrium', 'Spring', 'Stiffness'].forEach((name) => { - ['X', 'Y', 'Z'].forEach((axis) => { - const prop = type + name + axis; - const propInternal = `_${prop}`; - - let index = (type === 'linear') ? 0 : 3; - if (axis === 'Y') index += 1; - if (axis === 'Z') index += 2; - - Object.defineProperty(JointComponent.prototype, prop, { - get: function () { - return this[propInternal]; - }, - - set: function (value) { - if (this[propInternal] !== value) { - this[propInternal] = value; - this._constraint[functionMap[name]](index, value); - } - } - }); - }); - }); -}); - export { JointComponent }; diff --git a/src/framework/components/joint/constants.js b/src/framework/components/joint/constants.js index c3fd0dee0df..5bc9be4423d 100644 --- a/src/framework/components/joint/constants.js +++ b/src/framework/components/joint/constants.js @@ -2,7 +2,6 @@ * Specified degree of freedom has free movement. * * @type {string} - * @ignore */ export const MOTION_FREE = 'free'; @@ -10,7 +9,6 @@ export const MOTION_FREE = 'free'; * Specified degree of freedom has limited movement. * * @type {string} - * @ignore */ export const MOTION_LIMITED = 'limited'; @@ -18,6 +16,5 @@ export const MOTION_LIMITED = 'limited'; * Specified degree of freedom is locked and allows no movement. * * @type {string} - * @ignore */ export const MOTION_LOCKED = 'locked'; diff --git a/src/framework/components/joint/system.js b/src/framework/components/joint/system.js index 5e8f0e7850e..61fc28c11bb 100644 --- a/src/framework/components/joint/system.js +++ b/src/framework/components/joint/system.js @@ -12,7 +12,7 @@ const _schema = ['enabled']; /** * Creates and manages physics joint components. * - * @ignore + * @beta */ class JointComponentSystem extends ComponentSystem { /**