UNPKG

@polygonjs/polygonjs

Version:

node-based WebGL 3D engine https://polygonjs.com

536 lines (535 loc) 18.1 kB
"use strict"; import { TypeAssert } from "./../../poly/Assert"; import { PHYSICS_RBD_COLLIDER_TYPES, PHYSICS_RBD_TYPES, CorePhysicsAttribute, PhysicsRBDColliderType, PHYSICS_RBD_TYPE_MENU_ENTRIES, PHYSICS_RBD_COLLIDER_TYPE_MENU_ENTRIES } from "./../../../core/physics/PhysicsAttribute"; import { TypedSopNode } from "./_Base"; import { PhysicsRBDAttributesSopOperation, SIZE_COMPUTATION_METHOD_MENU_ENTRIES, SizeComputationMethod, SIZE_COMPUTATION_METHODS } from "../../operations/sop/PhysicsRBDAttributes"; import { NodeParamsConfig, ParamConfig } from "../utils/params/ParamsConfig"; import { Vector3, Box3, Sphere } from "three"; import { isBooleanTrue } from "../../../core/Type"; import { SopType } from "../../poly/registers/nodes/types/Sop"; const tmpBox = new Box3(); const tmpSphere = new Sphere(); const DEFAULT = PhysicsRBDAttributesSopOperation.DEFAULT_PARAMS; const VECTOR3_COMPONENT_NAMES = ["x", "y", "z"]; const tmpV3 = new Vector3(); const SIZE_METHOD_CUSTOM = { sizeMethod: SIZE_COMPUTATION_METHODS.indexOf(SizeComputationMethod.MANUAL) }; const VISIBLE_OPTIONS = { CAPSULE: { ...SIZE_METHOD_CUSTOM, colliderType: PHYSICS_RBD_COLLIDER_TYPES.indexOf(PhysicsRBDColliderType.CAPSULE) }, CONE: { ...SIZE_METHOD_CUSTOM, colliderType: PHYSICS_RBD_COLLIDER_TYPES.indexOf(PhysicsRBDColliderType.CONE) }, CUBOID: { ...SIZE_METHOD_CUSTOM, colliderType: PHYSICS_RBD_COLLIDER_TYPES.indexOf(PhysicsRBDColliderType.CUBOID) }, CYLINDER: { ...SIZE_METHOD_CUSTOM, colliderType: PHYSICS_RBD_COLLIDER_TYPES.indexOf(PhysicsRBDColliderType.CYLINDER) }, SPHERE: { ...SIZE_METHOD_CUSTOM, colliderType: PHYSICS_RBD_COLLIDER_TYPES.indexOf(PhysicsRBDColliderType.SPHERE) }, HEIGHT_FIELD: { colliderType: PHYSICS_RBD_COLLIDER_TYPES.indexOf(PhysicsRBDColliderType.HEIGHT_FIELD) } }; const SIZE_METHOD_AVAILABLE = [ PhysicsRBDColliderType.CAPSULE, PhysicsRBDColliderType.CONE, PhysicsRBDColliderType.CUBOID, PhysicsRBDColliderType.CYLINDER, PhysicsRBDColliderType.SPHERE ]; export const BORDER_RADIUS_AVAILABLE = [ PhysicsRBDColliderType.CONE, PhysicsRBDColliderType.CUBOID, PhysicsRBDColliderType.CYLINDER ]; class PhysicsRBDAttributesSopParamsConfig extends NodeParamsConfig { constructor() { super(...arguments); this.main = ParamConfig.FOLDER(); /** @param Rigid body type */ this.RBDType = ParamConfig.INTEGER(DEFAULT.RBDType, { menu: { entries: PHYSICS_RBD_TYPE_MENU_ENTRIES } }); /** @param collider type */ this.colliderType = ParamConfig.INTEGER(DEFAULT.colliderType, { menu: { entries: PHYSICS_RBD_COLLIDER_TYPE_MENU_ENTRIES } }); /** @param Rigid body type */ this.sizeMethod = ParamConfig.INTEGER(DEFAULT.sizeMethod, { visibleIf: SIZE_METHOD_AVAILABLE.map((colliderType) => ({ colliderType: PHYSICS_RBD_COLLIDER_TYPES.indexOf(colliderType) })), menu: { entries: SIZE_COMPUTATION_METHOD_MENU_ENTRIES } }); /** @param border radius */ this.borderRadius = ParamConfig.FLOAT(DEFAULT.borderRadius, { visibleIf: BORDER_RADIUS_AVAILABLE.map((colliderType) => ({ colliderType: PHYSICS_RBD_COLLIDER_TYPES.indexOf(colliderType) })), expression: { forEntities: true } }); /** @param sizes */ this.sizes = ParamConfig.VECTOR3(DEFAULT.sizes.toArray(), { visibleIf: VISIBLE_OPTIONS.CUBOID, expression: { forEntities: true } }); /** @param sizes */ this.size = ParamConfig.FLOAT(DEFAULT.size, { visibleIf: VISIBLE_OPTIONS.CUBOID, expression: { forEntities: true } }); /** @param radius */ this.radius = ParamConfig.FLOAT(DEFAULT.radius, { range: [0, 1], rangeLocked: [true, false], visibleIf: [VISIBLE_OPTIONS.CAPSULE, VISIBLE_OPTIONS.CONE, VISIBLE_OPTIONS.CYLINDER, VISIBLE_OPTIONS.SPHERE], expression: { forEntities: true } }); /** @param half height */ this.height = ParamConfig.FLOAT(DEFAULT.height, { range: [0, 1], rangeLocked: [true, false], visibleIf: [VISIBLE_OPTIONS.CAPSULE, VISIBLE_OPTIONS.CONE, VISIBLE_OPTIONS.CYLINDER], expression: { forEntities: true } }); /** @param heightField rows */ this.rows = ParamConfig.INTEGER(DEFAULT.rows, { range: [1, 100], rangeLocked: [true, false], visibleIf: VISIBLE_OPTIONS.HEIGHT_FIELD, expression: { forEntities: true } }); /** @param heightField cols */ this.cols = ParamConfig.INTEGER(DEFAULT.cols, { range: [1, 100], rangeLocked: [true, false], visibleIf: VISIBLE_OPTIONS.HEIGHT_FIELD, expression: { forEntities: true } }); /** @param density */ this.density = ParamConfig.FLOAT(DEFAULT.density, { range: [0, 10], rangeLocked: [true, false], expression: { forEntities: true }, separatorBefore: true }); /** @param friction */ this.friction = ParamConfig.FLOAT(DEFAULT.friction, { range: [0, 1], rangeLocked: [true, false], expression: { forEntities: true } }); /** @param restitution */ this.restitution = ParamConfig.FLOAT(DEFAULT.restitution, { range: [0, 2], rangeLocked: [true, false], expression: { forEntities: true } }); /** @param linear damping (affects velocity) */ this.linearDamping = ParamConfig.FLOAT(DEFAULT.linearDamping, { range: [0, 10], rangeLocked: [true, false], expression: { forEntities: true } }); /** @param angular damping (affects rotations) */ this.angularDamping = ParamConfig.FLOAT(DEFAULT.angularDamping, { range: [0, 10], rangeLocked: [true, false], expression: { forEntities: true } }); /** @param linear velocity */ this.linearVelocity = ParamConfig.VECTOR3(DEFAULT.linearVelocity, { expression: { forEntities: true } }); /** @param angular velocity */ this.angularVelocity = ParamConfig.VECTOR3(DEFAULT.angularVelocity, { expression: { forEntities: true } }); /** @param gravity Scale */ this.gravityScale = ParamConfig.FLOAT(DEFAULT.gravityScale, { range: [-10, 10], rangeLocked: [false, false], expression: { forEntities: true } }); /** @param can sleep */ this.canSleep = ParamConfig.BOOLEAN(DEFAULT.canSleep, { expression: { forEntities: true } }); this.details = ParamConfig.FOLDER(); /** @param add id */ this.addId = ParamConfig.BOOLEAN(1); /** @param id */ this.id = ParamConfig.STRING("`$OS`-`@objnum`", { visibleIf: { addId: true }, expression: { forEntities: true } }); } } const ParamsConfig = new PhysicsRBDAttributesSopParamsConfig(); export class PhysicsRBDAttributesSopNode extends TypedSopNode { constructor() { super(...arguments); this.paramsConfig = ParamsConfig; } static type() { return SopType.PHYSICS_RBD_ATTRIBUTES; } initializeNode() { this.io.inputs.setCount(0, 1); this.io.inputs.initInputsClonedState(PhysicsRBDAttributesSopOperation.INPUT_CLONED_STATE); } setRBDType(RBDtype) { this.p.RBDType.set(PHYSICS_RBD_TYPES.indexOf(RBDtype)); } RBDType() { return PHYSICS_RBD_TYPES[this.pv.RBDType]; } setColliderType(colliderType) { this.p.colliderType.set(PHYSICS_RBD_COLLIDER_TYPES.indexOf(colliderType)); } colliderType() { return PHYSICS_RBD_COLLIDER_TYPES[this.pv.colliderType]; } setSizeMethod(sizeMethod) { this.p.sizeMethod.set(SIZE_COMPUTATION_METHODS.indexOf(sizeMethod)); } sizeMethod() { return SIZE_COMPUTATION_METHODS[this.pv.sizeMethod]; } async cook(inputCoreGroups) { const coreGroup = inputCoreGroups[0]; const RBDType = this.RBDType(); const colliderType = this.colliderType(); const sizeMethod = this.sizeMethod(); const coreObjects = coreGroup.allCoreObjects(); for (const coreObject of coreObjects) { CorePhysicsAttribute.setRBDType(coreObject.object(), RBDType); CorePhysicsAttribute.setColliderType(coreObject.object(), colliderType); } const promises = []; this._applyColliderType(colliderType, sizeMethod, coreObjects, promises); if (BORDER_RADIUS_AVAILABLE.includes(colliderType)) { promises.push( this._computeNumberParam( this.p.borderRadius, coreObjects, CorePhysicsAttribute.setBorderRadius.bind(CorePhysicsAttribute) ) ); } promises.push( this._computeNumberParam( this.p.density, coreObjects, CorePhysicsAttribute.setDensity.bind(CorePhysicsAttribute) ) ); promises.push( this._computeNumberParam( this.p.friction, coreObjects, CorePhysicsAttribute.setFriction.bind(CorePhysicsAttribute) ) ); promises.push( this._computeNumberParam( this.p.linearDamping, coreObjects, CorePhysicsAttribute.setLinearDamping.bind(CorePhysicsAttribute) ) ); promises.push( this._computeNumberParam( this.p.angularDamping, coreObjects, CorePhysicsAttribute.setAngularDamping.bind(CorePhysicsAttribute) ) ); promises.push( this._computeVector3Param( this.p.linearVelocity, coreObjects, CorePhysicsAttribute.setLinearVelocity.bind(CorePhysicsAttribute) ) ); promises.push( this._computeVector3Param( this.p.angularVelocity, coreObjects, CorePhysicsAttribute.setAngularVelocity.bind(CorePhysicsAttribute) ) ); promises.push( this._computeNumberParam( this.p.gravityScale, coreObjects, CorePhysicsAttribute.setGravityScale.bind(CorePhysicsAttribute) ) ); promises.push( this._computeNumberParam( this.p.restitution, coreObjects, CorePhysicsAttribute.setRestitution.bind(CorePhysicsAttribute) ) ); promises.push( this._computeBooleanParam( this.p.canSleep, coreObjects, CorePhysicsAttribute.setCanSleep.bind(CorePhysicsAttribute) ) ); if (isBooleanTrue(this.pv.addId)) { promises.push( this._computeStringParam( this.p.id, coreObjects, CorePhysicsAttribute.setRBDId.bind(CorePhysicsAttribute) ) ); } await Promise.all(promises); this.setCoreGroup(coreGroup); } _applyColliderType(colliderType, sizeMethod, coreObjects, promises) { switch (colliderType) { case PhysicsRBDColliderType.CUBOID: { switch (sizeMethod) { case SizeComputationMethod.AUTO: { for (const coreObject of coreObjects) { coreObject.geometryBoundingBox(tmpBox); tmpBox.getSize(tmpV3); CorePhysicsAttribute.setCuboidSizes(coreObject.object(), tmpV3); CorePhysicsAttribute.setCuboidSize(coreObject.object(), 1); } return; } case SizeComputationMethod.MANUAL: { promises.push( this._computeVector3Param( this.p.sizes, coreObjects, CorePhysicsAttribute.setCuboidSizes.bind(CorePhysicsAttribute) ) ); promises.push( this._computeNumberParam( this.p.size, coreObjects, CorePhysicsAttribute.setCuboidSize.bind(CorePhysicsAttribute) ) ); return; } } return; } case PhysicsRBDColliderType.SPHERE: { switch (sizeMethod) { case SizeComputationMethod.AUTO: { for (const coreObject of coreObjects) { coreObject.geometryBoundingSphere(tmpSphere); const radius = tmpSphere.radius; CorePhysicsAttribute.setRadius(coreObject.object(), radius); } return; } case SizeComputationMethod.MANUAL: { promises.push( this._computeNumberParam( this.p.radius, coreObjects, CorePhysicsAttribute.setRadius.bind(CorePhysicsAttribute) ) ); return; } } return; } case PhysicsRBDColliderType.CAPSULE: { switch (sizeMethod) { case SizeComputationMethod.AUTO: { for (const coreObject of coreObjects) { coreObject.geometryBoundingBox(tmpBox); tmpBox.getSize(tmpV3); const radius = 0.5 * tmpV3.x; const height = tmpV3.y - 2 * radius; CorePhysicsAttribute.setHeight(coreObject.object(), height); CorePhysicsAttribute.setRadius(coreObject.object(), radius); } return; } case SizeComputationMethod.MANUAL: { promises.push( this._computeNumberParam( this.p.height, coreObjects, CorePhysicsAttribute.setHeight.bind(CorePhysicsAttribute) ) ); promises.push( this._computeNumberParam( this.p.radius, coreObjects, CorePhysicsAttribute.setRadius.bind(CorePhysicsAttribute) ) ); return; } } return; } case PhysicsRBDColliderType.CONE: case PhysicsRBDColliderType.CYLINDER: { switch (sizeMethod) { case SizeComputationMethod.AUTO: { for (const coreObject of coreObjects) { coreObject.geometryBoundingBox(tmpBox); tmpBox.getSize(tmpV3); CorePhysicsAttribute.setHeight(coreObject.object(), tmpV3.y); CorePhysicsAttribute.setRadius(coreObject.object(), 0.5 * tmpV3.x); } return; } case SizeComputationMethod.MANUAL: { promises.push( this._computeNumberParam( this.p.height, coreObjects, CorePhysicsAttribute.setHeight.bind(CorePhysicsAttribute) ) ); promises.push( this._computeNumberParam( this.p.radius, coreObjects, CorePhysicsAttribute.setRadius.bind(CorePhysicsAttribute) ) ); return; } } return; } case PhysicsRBDColliderType.HEIGHT_FIELD: { promises.push( this._computeNumberParam( this.p.rows, coreObjects, CorePhysicsAttribute.setHeightFieldRows.bind(CorePhysicsAttribute) ) ); promises.push( this._computeNumberParam( this.p.cols, coreObjects, CorePhysicsAttribute.setHeightFieldCols.bind(CorePhysicsAttribute) ) ); return; } case PhysicsRBDColliderType.CONVEX_HULL: case PhysicsRBDColliderType.TRIMESH: { return; } } TypeAssert.unreachable(colliderType); } async _computeStringParam(param, coreObjects, applyMethod) { if (param.expressionController && param.expressionController.entitiesDependent()) { await param.expressionController.computeExpressionForObjects(coreObjects, (coreObject, value) => { applyMethod(coreObject.object(), value); }); } else { for (const coreObject of coreObjects) { applyMethod(coreObject.object(), param.value); } } } async _computeVector3Param(vectorParam, coreObjects, applyMethod) { const components = vectorParam.components; const valuesByCoreObjectIndex = /* @__PURE__ */ new Map(); for (const coreObject of coreObjects) { valuesByCoreObjectIndex.set(coreObject.index(), new Vector3()); } for (let componentIndex = 0; componentIndex < components.length; componentIndex++) { const component_param = components[componentIndex]; const component_name = VECTOR3_COMPONENT_NAMES[componentIndex]; if (component_param.hasExpression() && component_param.expressionController && component_param.expressionController.entitiesDependent()) { await component_param.expressionController.computeExpressionForObjects( coreObjects, (coreObject, value) => { const vector = valuesByCoreObjectIndex.get(coreObject.index()); if (vector) { vector[component_name] = value; } } ); } else { for (const coreObject of coreObjects) { const vector = valuesByCoreObjectIndex.get(coreObject.index()); if (vector) { vector[component_name] = component_param.value; } } } } for (let i = 0; i < coreObjects.length; i++) { const coreObject = coreObjects[i]; const value = valuesByCoreObjectIndex.get(coreObject.index()); if (value != null) { applyMethod(coreObject.object(), value); } } } async _computeNumberParam(param, coreObjects, applyMethod) { if (param.expressionController && param.expressionController.entitiesDependent()) { await param.expressionController.computeExpressionForObjects(coreObjects, (coreObject, value) => { applyMethod(coreObject.object(), value); }); } else { for (const coreObject of coreObjects) { applyMethod(coreObject.object(), param.value); } } } async _computeBooleanParam(param, coreObjects, applyMethod) { if (param.expressionController && param.expressionController.entitiesDependent()) { await param.expressionController.computeExpressionForObjects(coreObjects, (coreObject, value) => { applyMethod(coreObject.object(), value); }); } else { for (const coreObject of coreObjects) { applyMethod(coreObject.object(), param.value); } } } }