@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
536 lines (535 loc) • 18.1 kB
JavaScript
"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);
}
}
}
}