From bc2b360368450690800ad66cb9a928e087acfa0e Mon Sep 17 00:00:00 2001 From: Aries Powvalla Date: Wed, 6 Aug 2025 10:30:18 -0700 Subject: [PATCH 1/3] Implement adjustable center of gravity --- fission/src/mirabuf/MirabufSceneObject.ts | 179 +++++++++++++- .../assembly-config/ConfigTypes.ts | 1 + .../assembly-config/ConfigurePanel.tsx | 11 +- .../interfaces/CenterOfGravityInterface.tsx | 222 ++++++++++++++++++ 4 files changed, 409 insertions(+), 4 deletions(-) create mode 100644 fission/src/ui/panels/configuring/assembly-config/interfaces/CenterOfGravityInterface.tsx diff --git a/fission/src/mirabuf/MirabufSceneObject.ts b/fission/src/mirabuf/MirabufSceneObject.ts index fc108a4955..7f56b4483c 100644 --- a/fission/src/mirabuf/MirabufSceneObject.ts +++ b/fission/src/mirabuf/MirabufSceneObject.ts @@ -88,6 +88,9 @@ class MirabufSceneObject extends SceneObject implements ContextSupplier { private _nameTag: SceneOverlayTag | undefined private _centerOfMassIndicator: THREE.Mesh | undefined + private _modifiedCenterOfGravity: THREE.Vector3 | undefined + private _cogEffectStrength: number = 1.0 + private _intakeActive = false private _ejectorActive = false @@ -180,6 +183,47 @@ class MirabufSceneObject extends SceneObject implements ContextSupplier { this._station = station } + public get modifiedCenterOfGravity(): THREE.Vector3 | undefined { + return this._modifiedCenterOfGravity + } + + public set modifiedCenterOfGravity(position: THREE.Vector3 | undefined) { + this._modifiedCenterOfGravity = position + } + + public get cogEffectStrength(): number { + return this._cogEffectStrength + } + + public set cogEffectStrength(strength: number) { + this._cogEffectStrength = Math.max(0, Math.min(2, strength)) + } + + public get currentCenterOfGravity(): THREE.Vector3 { + if (this._modifiedCenterOfGravity) { + const rootNodeId = this.getRootNodeId() + if (rootNodeId) { + const robotTransform = convertJoltMat44ToThreeMatrix4( + World.physicsSystem.getBody(rootNodeId).GetWorldTransform() + ) + const robotWorldPos = new THREE.Vector3() + const robotWorldQuat = new THREE.Quaternion() + const robotWorldScale = new THREE.Vector3() + robotTransform.decompose(robotWorldPos, robotWorldQuat, robotWorldScale) + + const worldCoG = this._modifiedCenterOfGravity.clone() + worldCoG.applyQuaternion(robotWorldQuat) + worldCoG.add(robotWorldPos) + return worldCoG + } + return this._modifiedCenterOfGravity.clone() + } + if (this._centerOfMassIndicator) { + return this._centerOfMassIndicator.position.clone() + } + return new THREE.Vector3(0, 0, 0) + } + public get cacheId() { return this._cacheId } @@ -323,6 +367,10 @@ class MirabufSceneObject extends SceneObject implements ContextSupplier { this.eject() } + if (this._modifiedCenterOfGravity && this.miraType === MiraType.ROBOT) { + this.applyCenterOfGravityPhysics() + } + this.updateMeshTransforms() this.updateBatches() this.updateNameTag() @@ -458,8 +506,28 @@ class MirabufSceneObject extends SceneObject implements ContextSupplier { } }) if (this._centerOfMassIndicator) { - const netCoM = totalMass > 0 ? weightedCOM.Div(totalMass) : weightedCOM - this._centerOfMassIndicator.position.set(netCoM.GetX(), netCoM.GetY(), netCoM.GetZ()) + if (this._modifiedCenterOfGravity) { + const rootNodeId = this.getRootNodeId() + if (rootNodeId) { + const robotTransform = convertJoltMat44ToThreeMatrix4( + World.physicsSystem.getBody(rootNodeId).GetWorldTransform() + ) + const robotWorldPos = new THREE.Vector3() + const robotWorldQuat = new THREE.Quaternion() + const robotWorldScale = new THREE.Vector3() + robotTransform.decompose(robotWorldPos, robotWorldQuat, robotWorldScale) + + const worldCoG = this._modifiedCenterOfGravity.clone() + worldCoG.applyQuaternion(robotWorldQuat) + worldCoG.add(robotWorldPos) + this._centerOfMassIndicator.position.copy(worldCoG) + } else { + this._centerOfMassIndicator.position.copy(this._modifiedCenterOfGravity) + } + } else { + const netCoM = totalMass > 0 ? weightedCOM.Div(totalMass) : weightedCOM + this._centerOfMassIndicator.position.set(netCoM.GetX(), netCoM.GetY(), netCoM.GetZ()) + } this._centerOfMassIndicator.visible = PreferencesSystem.getGlobalPreference("ShowCenterOfMassIndicators") } } @@ -650,7 +718,11 @@ class MirabufSceneObject extends SceneObject implements ContextSupplier { * * @returns the object containing the width (x), height (y), and depth (z) dimensions in meters. */ - public getDimensionsWithoutRotation(): { width: number; height: number; depth: number } { + public getDimensionsWithoutRotation(): { + width: number + height: number + depth: number + } { const rootNodeId = this.getRootNodeId() if (!rootNodeId) { console.warn("No root node found for robot, using regular dimensions") @@ -903,6 +975,107 @@ class MirabufSceneObject extends SceneObject implements ContextSupplier { objectCollidedWith.robotLastInContactWith = this } } + + /** + * Aries' CoG Simulation + * This method applies torque to the robot's root body to simulate the effect of a modified center of gravity. + */ + private applyCenterOfGravityPhysics(): void { + if (!this._modifiedCenterOfGravity) return + + const rootNodeId = this.getRootNodeId() + if (!rootNodeId) return + + const rootBody = World.physicsSystem.getBody(rootNodeId) + if (!rootBody || rootBody.IsStatic()) return + + const robotTransform = convertJoltMat44ToThreeMatrix4(rootBody.GetWorldTransform()) + const robotWorldPos = new THREE.Vector3() + const robotWorldQuat = new THREE.Quaternion() + const robotWorldScale = new THREE.Vector3() + robotTransform.decompose(robotWorldPos, robotWorldQuat, robotWorldScale) + + const modifiedCoGWorld = this._modifiedCenterOfGravity.clone() + modifiedCoGWorld.applyQuaternion(robotWorldQuat) + modifiedCoGWorld.add(robotWorldPos) + + let actualCoMWorld = new JOLT.RVec3(0, 0, 0) + let totalMass = 0 + + this._mirabufInstance.parser.rigidNodes.forEach(rn => { + const bodyId = this._mechanism.getBodyByNodeId(rn.id) + if (!bodyId) return + + const body = World.physicsSystem.getBody(bodyId) + const inverseMass = body.GetMotionProperties().GetInverseMass() + + if (inverseMass > 0) { + const mass = 1 / inverseMass + actualCoMWorld = actualCoMWorld.AddRVec3(body.GetCenterOfMassPosition().Mul(mass)) + totalMass += mass + } + }) + + if (totalMass === 0) return + + const actualCoM = actualCoMWorld.Div(totalMass) + const actualCoMVec3 = new THREE.Vector3(actualCoM.GetX(), actualCoM.GetY(), actualCoM.GetZ()) + + const offset = modifiedCoGWorld.clone().sub(actualCoMVec3) + + // The torque needed is: τ = r × F + // where r is the offset and F is the gravitational force + const gravityForce = new THREE.Vector3(0, -9.81 * totalMass, 0) + const torque = new THREE.Vector3().crossVectors(offset, gravityForce) + + torque.multiplyScalar(this._cogEffectStrength) + + const joltTorque = new JOLT.Vec3(torque.x, torque.y, torque.z) + rootBody.AddTorque(joltTorque) + JOLT.destroy(joltTorque) + + const velocity = rootBody.GetLinearVelocity() + const speed = Math.sqrt(velocity.GetX() ** 2 + velocity.GetY() ** 2 + velocity.GetZ() ** 2) + + if (speed > 0.1) { + const angularVel = rootBody.GetAngularVelocity() + const dampingFactor = 0.5 * this._cogEffectStrength + const dampingTorque = new JOLT.Vec3( + -angularVel.GetX() * dampingFactor * totalMass, + -angularVel.GetY() * dampingFactor * totalMass, + -angularVel.GetZ() * dampingFactor * totalMass + ) + rootBody.AddTorque(dampingTorque) + JOLT.destroy(dampingTorque) + } + + this._mirabufInstance.parser.rigidNodes.forEach(rn => { + if (rn.id === this._mechanism.rootBody) return + + const bodyId = this._mechanism.getBodyByNodeId(rn.id) + if (!bodyId) return + + const body = World.physicsSystem.getBody(bodyId) + if (body.IsStatic()) return + + const inverseMass = body.GetMotionProperties().GetInverseMass() + if (inverseMass <= 0) return + + const mass = 1 / inverseMass + + const correctionFactor = (mass / totalMass) * this._cogEffectStrength + const bodyTorque = new JOLT.Vec3( + torque.x * correctionFactor * 0.1, + torque.y * correctionFactor * 0.1, + torque.z * correctionFactor * 0.1 + ) + body.AddTorque(bodyTorque) + JOLT.destroy(bodyTorque) + }) + + JOLT.destroy(actualCoM) + JOLT.destroy(actualCoMWorld) + } } export async function createMirabuf( diff --git a/fission/src/ui/panels/configuring/assembly-config/ConfigTypes.ts b/fission/src/ui/panels/configuring/assembly-config/ConfigTypes.ts index 4319bb1bfe..af9e769142 100644 --- a/fission/src/ui/panels/configuring/assembly-config/ConfigTypes.ts +++ b/fission/src/ui/panels/configuring/assembly-config/ConfigTypes.ts @@ -14,4 +14,5 @@ export enum ConfigMode { BRAIN, DRIVETRAIN, ALLIANCE, + CENTER_OF_GRAVITY, } diff --git a/fission/src/ui/panels/configuring/assembly-config/ConfigurePanel.tsx b/fission/src/ui/panels/configuring/assembly-config/ConfigurePanel.tsx index ea2222bedc..df8849cef2 100644 --- a/fission/src/ui/panels/configuring/assembly-config/ConfigurePanel.tsx +++ b/fission/src/ui/panels/configuring/assembly-config/ConfigurePanel.tsx @@ -21,6 +21,7 @@ import AssemblySelection, { type AssemblySelectionOption } from "./configure/Ass import ConfigModeSelection, { ConfigModeSelectionOption } from "./configure/ConfigModeSelection" import AllianceSelectionInterface from "./interfaces/AllianceSelectionInterface" import BrainSelectionInterface from "./interfaces/BrainSelectionInterface" +import CenterOfGravityInterface from "./interfaces/CenterOfGravityInterface" import ConfigureGamepiecePickupInterface from "./interfaces/ConfigureGamepiecePickupInterface" import ConfigureShotTrajectoryInterface from "./interfaces/ConfigureShotTrajectoryInterface" import ConfigureSubsystemsInterface from "./interfaces/ConfigureSubsystemsInterface" @@ -109,6 +110,8 @@ const ConfigInterface: React.FC case ConfigMode.DRIVETRAIN: return + case ConfigMode.CENTER_OF_GRAVITY: + return default: throw new Error(`Config mode ${configMode} has no associated interface`) } @@ -219,10 +222,16 @@ const ConfigurePanel: React.FC> new ConfigModeSelectionOption("Drivetrain", ConfigMode.DRIVETRAIN, "Sets the drivetrain type."), + new ConfigModeSelectionOption( + "Center of Gravity", + ConfigMode.CENTER_OF_GRAVITY, + "Adjust the robot's center of gravity to modify its balance and physics behavior." + ), + new ConfigModeSelectionOption( "Intake", ConfigMode.INTAKE, - "Configure the robot’s intake position and parent node for picking up game pieces." + "Configure the robot's intake position and parent node for picking up game pieces." ), new ConfigModeSelectionOption( diff --git a/fission/src/ui/panels/configuring/assembly-config/interfaces/CenterOfGravityInterface.tsx b/fission/src/ui/panels/configuring/assembly-config/interfaces/CenterOfGravityInterface.tsx new file mode 100644 index 0000000000..04e65336b5 --- /dev/null +++ b/fission/src/ui/panels/configuring/assembly-config/interfaces/CenterOfGravityInterface.tsx @@ -0,0 +1,222 @@ +import { Stack } from "@mui/material" +import { useCallback, useEffect, useMemo, useRef, useState } from "react" +import * as THREE from "three" +import { ConfigurationSavedEvent } from "@/events/ConfigurationSavedEvent" +import type MirabufSceneObject from "@/mirabuf/MirabufSceneObject" +import PreferencesSystem from "@/systems/preferences/PreferencesSystem" +import type GizmoSceneObject from "@/systems/scene/GizmoSceneObject" +import World from "@/systems/World" +import Label from "@/ui/components/Label" +import StatefulSlider from "@/ui/components/StatefulSlider" +import { Spacer } from "@/ui/components/StyledComponents" +import TransformGizmoControl from "@/ui/components/TransformGizmoControl" +import { convertJoltMat44ToThreeMatrix4 } from "@/util/TypeConversions" + +interface CenterOfGravityInterfaceProps { + selectedRobot: MirabufSceneObject +} + +/** + * Saves the center of gravity configuration to the selected robot. + * The position is stored relative to the robot's root node so it moves with the robot. + * + * @param gizmo Reference to the transform gizmo object. + * @param selectedRobot Selected robot to save data to. + * @param effectStrength The strength of the CoG effect (0-2, where 1 is normal). + */ +function saveCenterOfGravity(gizmo: GizmoSceneObject, selectedRobot: MirabufSceneObject, effectStrength: number) { + if (!gizmo || !selectedRobot) { + return + } + + const rootNodeId = selectedRobot.getRootNodeId() + if (!rootNodeId) { + return + } + + const gizmoWorldPos = new THREE.Vector3() + gizmo.obj.getWorldPosition(gizmoWorldPos) + + const robotTransform = convertJoltMat44ToThreeMatrix4(World.physicsSystem.getBody(rootNodeId).GetWorldTransform()) + + const robotWorldPos = new THREE.Vector3() + const robotWorldQuat = new THREE.Quaternion() + const robotWorldScale = new THREE.Vector3() + robotTransform.decompose(robotWorldPos, robotWorldQuat, robotWorldScale) + + const relativePos = gizmoWorldPos.clone().sub(robotWorldPos) + relativePos.applyQuaternion(robotWorldQuat.clone().invert()) + + selectedRobot.modifiedCenterOfGravity = relativePos + selectedRobot.cogEffectStrength = effectStrength + selectedRobot.updateMeshTransforms() +} + +const CenterOfGravityInterface: React.FC = ({ selectedRobot }) => { + const gizmoRef = useRef(undefined) + const [cogPosition, setCogPosition] = useState(new THREE.Vector3(0, 0, 0)) + const [effectStrength, setEffectStrength] = useState(selectedRobot.cogEffectStrength ?? 1.0) + + // Create the center of gravity sphere mesh + const cogSphereMesh = useMemo(() => { + const material = new THREE.MeshBasicMaterial({ + color: 0xff00ff, + opacity: 0.8, + transparent: true, + depthTest: false, + depthWrite: false, + }) + const sphere = new THREE.Mesh(new THREE.SphereGeometry(0.04), material) // larger than the visual sphere + return sphere + }, []) + + const saveEvent = useCallback(() => { + if (gizmoRef.current && selectedRobot) { + saveCenterOfGravity(gizmoRef.current, selectedRobot, effectStrength) + } + }, [selectedRobot, effectStrength]) + + useEffect(() => { + ConfigurationSavedEvent.listen(saveEvent) + + return () => { + ConfigurationSavedEvent.removeListener(saveEvent) + } + }, [saveEvent]) + + useEffect(() => { + const updateInterval = setInterval(() => { + if (gizmoRef.current) { + const newPosition = new THREE.Vector3() + gizmoRef.current.obj.getWorldPosition(newPosition) + setCogPosition(newPosition) + } + }, 100) + + return () => clearInterval(updateInterval) + }, []) + + useEffect(() => { + const previousVisibility = PreferencesSystem.getGlobalPreference("ShowCenterOfMassIndicators") + PreferencesSystem.setGlobalPreference("ShowCenterOfMassIndicators", true) + selectedRobot.updateMeshTransforms() + + return () => { + PreferencesSystem.setGlobalPreference("ShowCenterOfMassIndicators", previousVisibility) + selectedRobot.updateMeshTransforms() + } + }, [selectedRobot]) + + const postGizmoCreation = useCallback( + (gizmo: GizmoSceneObject) => { + const material = (gizmo.obj as THREE.Mesh).material as THREE.Material + material.depthTest = false + + const rootNodeId = selectedRobot.getRootNodeId() + if (!rootNodeId) { + return + } + + const robotTransform = convertJoltMat44ToThreeMatrix4( + World.physicsSystem.getBody(rootNodeId).GetWorldTransform() + ) + + if (selectedRobot.modifiedCenterOfGravity) { + const robotWorldPos = new THREE.Vector3() + const robotWorldQuat = new THREE.Quaternion() + const robotWorldScale = new THREE.Vector3() + robotTransform.decompose(robotWorldPos, robotWorldQuat, robotWorldScale) + + const worldPos = selectedRobot.modifiedCenterOfGravity.clone() + worldPos.applyQuaternion(robotWorldQuat) + worldPos.add(robotWorldPos) + + gizmo.obj.position.copy(worldPos) + } else { + gizmo.obj.position.copy(selectedRobot.currentCenterOfGravity) + } + }, + [selectedRobot] + ) + + const handleReset = useCallback(() => { + selectedRobot.modifiedCenterOfGravity = undefined + selectedRobot.cogEffectStrength = 1.0 // reset + setEffectStrength(1.0) + + if (gizmoRef.current) { + const actualCoG = selectedRobot.currentCenterOfGravity + gizmoRef.current.obj.position.copy(actualCoG) + setCogPosition(actualCoG) + } + + selectedRobot.updateMeshTransforms() + }, [selectedRobot]) + + const gizmoComponent = useMemo(() => { + return ( + + ) + }, [cogSphereMesh, postGizmoCreation]) + + return ( + + + + {Spacer(8)} + + {gizmoComponent} + + {Spacer(8)} + + + + + + + {Spacer(8)} + + { + setEffectStrength(value) + selectedRobot.cogEffectStrength = value + }} + tooltip="Adjusts how strongly the modified center of gravity affects the robot's physics (0 = no effect, 1 = normal, 2 = exaggerated)" + /> + + {Spacer(8)} + + + + ) +} + +export default CenterOfGravityInterface From a4e518ee5fd3e77174c28796099f340170d5387d Mon Sep 17 00:00:00 2001 From: Aries Powvalla Date: Mon, 18 Aug 2025 02:13:52 -0700 Subject: [PATCH 2/3] remove coef --- fission/src/mirabuf/MirabufSceneObject.ts | 15 ++---------- .../interfaces/CenterOfGravityInterface.tsx | 24 ++++--------------- 2 files changed, 6 insertions(+), 33 deletions(-) diff --git a/fission/src/mirabuf/MirabufSceneObject.ts b/fission/src/mirabuf/MirabufSceneObject.ts index 0414fc3055..a3610dfa36 100644 --- a/fission/src/mirabuf/MirabufSceneObject.ts +++ b/fission/src/mirabuf/MirabufSceneObject.ts @@ -100,7 +100,6 @@ class MirabufSceneObject extends SceneObject implements ContextSupplier { private _centerOfMassIndicator: THREE.Mesh | undefined private _modifiedCenterOfGravity: THREE.Vector3 | undefined - private _cogEffectStrength: number = 1.0 private _basePositionTransform: THREE.Vector3 | undefined private _intakeActive = false @@ -206,14 +205,6 @@ class MirabufSceneObject extends SceneObject implements ContextSupplier { this._modifiedCenterOfGravity = position } - public get cogEffectStrength(): number { - return this._cogEffectStrength - } - - public set cogEffectStrength(strength: number) { - this._cogEffectStrength = Math.max(0, Math.min(2, strength)) - } - public get currentCenterOfGravity(): THREE.Vector3 { if (this._modifiedCenterOfGravity) { const rootNodeId = this.getRootNodeId() @@ -1071,8 +1062,6 @@ class MirabufSceneObject extends SceneObject implements ContextSupplier { const gravityForce = new THREE.Vector3(0, -9.81 * totalMass, 0) const torque = new THREE.Vector3().crossVectors(offset, gravityForce) - torque.multiplyScalar(this._cogEffectStrength) - const joltTorque = new JOLT.Vec3(torque.x, torque.y, torque.z) rootBody.AddTorque(joltTorque) JOLT.destroy(joltTorque) @@ -1082,7 +1071,7 @@ class MirabufSceneObject extends SceneObject implements ContextSupplier { if (speed > 0.1) { const angularVel = rootBody.GetAngularVelocity() - const dampingFactor = 0.5 * this._cogEffectStrength + const dampingFactor = 0.5 const dampingTorque = new JOLT.Vec3( -angularVel.GetX() * dampingFactor * totalMass, -angularVel.GetY() * dampingFactor * totalMass, @@ -1106,7 +1095,7 @@ class MirabufSceneObject extends SceneObject implements ContextSupplier { const mass = 1 / inverseMass - const correctionFactor = (mass / totalMass) * this._cogEffectStrength + const correctionFactor = mass / totalMass const bodyTorque = new JOLT.Vec3( torque.x * correctionFactor * 0.1, torque.y * correctionFactor * 0.1, diff --git a/fission/src/ui/panels/configuring/assembly-config/interfaces/CenterOfGravityInterface.tsx b/fission/src/ui/panels/configuring/assembly-config/interfaces/CenterOfGravityInterface.tsx index 04e65336b5..336ef443b7 100644 --- a/fission/src/ui/panels/configuring/assembly-config/interfaces/CenterOfGravityInterface.tsx +++ b/fission/src/ui/panels/configuring/assembly-config/interfaces/CenterOfGravityInterface.tsx @@ -7,7 +7,6 @@ import PreferencesSystem from "@/systems/preferences/PreferencesSystem" import type GizmoSceneObject from "@/systems/scene/GizmoSceneObject" import World from "@/systems/World" import Label from "@/ui/components/Label" -import StatefulSlider from "@/ui/components/StatefulSlider" import { Spacer } from "@/ui/components/StyledComponents" import TransformGizmoControl from "@/ui/components/TransformGizmoControl" import { convertJoltMat44ToThreeMatrix4 } from "@/util/TypeConversions" @@ -22,9 +21,8 @@ interface CenterOfGravityInterfaceProps { * * @param gizmo Reference to the transform gizmo object. * @param selectedRobot Selected robot to save data to. - * @param effectStrength The strength of the CoG effect (0-2, where 1 is normal). */ -function saveCenterOfGravity(gizmo: GizmoSceneObject, selectedRobot: MirabufSceneObject, effectStrength: number) { +function saveCenterOfGravity(gizmo: GizmoSceneObject, selectedRobot: MirabufSceneObject) { if (!gizmo || !selectedRobot) { return } @@ -48,14 +46,12 @@ function saveCenterOfGravity(gizmo: GizmoSceneObject, selectedRobot: MirabufScen relativePos.applyQuaternion(robotWorldQuat.clone().invert()) selectedRobot.modifiedCenterOfGravity = relativePos - selectedRobot.cogEffectStrength = effectStrength selectedRobot.updateMeshTransforms() } const CenterOfGravityInterface: React.FC = ({ selectedRobot }) => { const gizmoRef = useRef(undefined) const [cogPosition, setCogPosition] = useState(new THREE.Vector3(0, 0, 0)) - const [effectStrength, setEffectStrength] = useState(selectedRobot.cogEffectStrength ?? 1.0) // Create the center of gravity sphere mesh const cogSphereMesh = useMemo(() => { @@ -72,9 +68,9 @@ const CenterOfGravityInterface: React.FC = ({ sel const saveEvent = useCallback(() => { if (gizmoRef.current && selectedRobot) { - saveCenterOfGravity(gizmoRef.current, selectedRobot, effectStrength) + saveCenterOfGravity(gizmoRef.current, selectedRobot) } - }, [selectedRobot, effectStrength]) + }, [selectedRobot]) useEffect(() => { ConfigurationSavedEvent.listen(saveEvent) @@ -141,8 +137,6 @@ const CenterOfGravityInterface: React.FC = ({ sel const handleReset = useCallback(() => { selectedRobot.modifiedCenterOfGravity = undefined - selectedRobot.cogEffectStrength = 1.0 // reset - setEffectStrength(1.0) if (gizmoRef.current) { const actualCoG = selectedRobot.currentCenterOfGravity @@ -187,17 +181,7 @@ const CenterOfGravityInterface: React.FC = ({ sel {Spacer(8)} - { - setEffectStrength(value) - selectedRobot.cogEffectStrength = value - }} - tooltip="Adjusts how strongly the modified center of gravity affects the robot's physics (0 = no effect, 1 = normal, 2 = exaggerated)" - /> + {/* Removed CoG effect strength control */} {Spacer(8)} From 752bf9ee2655d36d3bf8f1c954143aaefe928b24 Mon Sep 17 00:00:00 2001 From: Aries Powvalla Date: Thu, 21 Aug 2025 16:16:38 -0700 Subject: [PATCH 3/3] physics halt, code cleanup --- fission/src/mirabuf/MirabufSceneObject.ts | 96 +++++++++++-------- .../interfaces/CenterOfGravityInterface.tsx | 10 ++ 2 files changed, 66 insertions(+), 40 deletions(-) diff --git a/fission/src/mirabuf/MirabufSceneObject.ts b/fission/src/mirabuf/MirabufSceneObject.ts index a3610dfa36..03bbe0c4cd 100644 --- a/fission/src/mirabuf/MirabufSceneObject.ts +++ b/fission/src/mirabuf/MirabufSceneObject.ts @@ -101,6 +101,7 @@ class MirabufSceneObject extends SceneObject implements ContextSupplier { private _modifiedCenterOfGravity: THREE.Vector3 | undefined private _basePositionTransform: THREE.Vector3 | undefined + private _cogPhysicsCooldownFrames = 0 private _intakeActive = false private _ejectorActive = false @@ -205,24 +206,30 @@ class MirabufSceneObject extends SceneObject implements ContextSupplier { this._modifiedCenterOfGravity = position } + private computeModifiedCenterOfGravityInWorld(): THREE.Vector3 | undefined { + if (!this._modifiedCenterOfGravity) return undefined + const rootNodeId = this.getRootNodeId() + if (rootNodeId) { + const robotTransform = convertJoltMat44ToThreeMatrix4( + World.physicsSystem.getBody(rootNodeId).GetWorldTransform() + ) + const robotWorldPos = new THREE.Vector3() + const robotWorldQuat = new THREE.Quaternion() + const robotWorldScale = new THREE.Vector3() + robotTransform.decompose(robotWorldPos, robotWorldQuat, robotWorldScale) + + const worldCoG = this._modifiedCenterOfGravity.clone() + worldCoG.applyQuaternion(robotWorldQuat) + worldCoG.add(robotWorldPos) + return worldCoG + } + return this._modifiedCenterOfGravity.clone() + } + public get currentCenterOfGravity(): THREE.Vector3 { if (this._modifiedCenterOfGravity) { - const rootNodeId = this.getRootNodeId() - if (rootNodeId) { - const robotTransform = convertJoltMat44ToThreeMatrix4( - World.physicsSystem.getBody(rootNodeId).GetWorldTransform() - ) - const robotWorldPos = new THREE.Vector3() - const robotWorldQuat = new THREE.Quaternion() - const robotWorldScale = new THREE.Vector3() - robotTransform.decompose(robotWorldPos, robotWorldQuat, robotWorldScale) - - const worldCoG = this._modifiedCenterOfGravity.clone() - worldCoG.applyQuaternion(robotWorldQuat) - worldCoG.add(robotWorldPos) - return worldCoG - } - return this._modifiedCenterOfGravity.clone() + const worldCoG = this.computeModifiedCenterOfGravityInWorld() + if (worldCoG) return worldCoG } if (this._centerOfMassIndicator) { return this._centerOfMassIndicator.position.clone() @@ -423,7 +430,16 @@ class MirabufSceneObject extends SceneObject implements ContextSupplier { this.eject() } - if (this._modifiedCenterOfGravity && this.miraType === MiraType.ROBOT) { + if (this._cogPhysicsCooldownFrames > 0) { + this._cogPhysicsCooldownFrames -= 1 + } + + if ( + this._modifiedCenterOfGravity && + this.miraType === MiraType.ROBOT && + !World.physicsSystem.isPaused && + this._cogPhysicsCooldownFrames === 0 + ) { this.applyCenterOfGravityPhysics() } @@ -543,21 +559,10 @@ class MirabufSceneObject extends SceneObject implements ContextSupplier { }) if (this._centerOfMassIndicator) { if (this._modifiedCenterOfGravity) { - const rootNodeId = this.getRootNodeId() - if (rootNodeId) { - const robotTransform = convertJoltMat44ToThreeMatrix4( - World.physicsSystem.getBody(rootNodeId).GetWorldTransform() - ) - const robotWorldPos = new THREE.Vector3() - const robotWorldQuat = new THREE.Quaternion() - const robotWorldScale = new THREE.Vector3() - robotTransform.decompose(robotWorldPos, robotWorldQuat, robotWorldScale) - - const worldCoG = this._modifiedCenterOfGravity.clone() - worldCoG.applyQuaternion(robotWorldQuat) - worldCoG.add(robotWorldPos) + const worldCoG = this.computeModifiedCenterOfGravityInWorld() + if (worldCoG) { this._centerOfMassIndicator.position.copy(worldCoG) - } else { + } else if (this._modifiedCenterOfGravity) { this._centerOfMassIndicator.position.copy(this._modifiedCenterOfGravity) } } else { @@ -1016,6 +1021,7 @@ class MirabufSceneObject extends SceneObject implements ContextSupplier { */ private applyCenterOfGravityPhysics(): void { if (!this._modifiedCenterOfGravity) return + if (World.physicsSystem.isPaused) return const rootNodeId = this.getRootNodeId() if (!rootNodeId) return @@ -1023,15 +1029,8 @@ class MirabufSceneObject extends SceneObject implements ContextSupplier { const rootBody = World.physicsSystem.getBody(rootNodeId) if (!rootBody || rootBody.IsStatic()) return - const robotTransform = convertJoltMat44ToThreeMatrix4(rootBody.GetWorldTransform()) - const robotWorldPos = new THREE.Vector3() - const robotWorldQuat = new THREE.Quaternion() - const robotWorldScale = new THREE.Vector3() - robotTransform.decompose(robotWorldPos, robotWorldQuat, robotWorldScale) - - const modifiedCoGWorld = this._modifiedCenterOfGravity.clone() - modifiedCoGWorld.applyQuaternion(robotWorldQuat) - modifiedCoGWorld.add(robotWorldPos) + const modifiedCoGWorld = this.computeModifiedCenterOfGravityInWorld() + if (!modifiedCoGWorld) return let actualCoMWorld = new JOLT.RVec3(0, 0, 0) let totalMass = 0 @@ -1108,6 +1107,23 @@ class MirabufSceneObject extends SceneObject implements ContextSupplier { JOLT.destroy(actualCoM) JOLT.destroy(actualCoMWorld) } + + public stabilizeAfterCenterOfGravityEdit(): void { + this._mirabufInstance.parser.rigidNodes.forEach(rn => { + const bodyId = this._mechanism.getBodyByNodeId(rn.id) + if (!bodyId) return + if (!World.physicsSystem.isBodyAdded(bodyId)) return + + const body = World.physicsSystem.getBody(bodyId) + if (!body || body.IsStatic()) return + + const zero = new JOLT.Vec3(0, 0, 0) + body.SetLinearVelocity(zero) + body.SetAngularVelocity(zero) + JOLT.destroy(zero) + }) + this._cogPhysicsCooldownFrames = 3 + } } export async function createMirabuf( diff --git a/fission/src/ui/panels/configuring/assembly-config/interfaces/CenterOfGravityInterface.tsx b/fission/src/ui/panels/configuring/assembly-config/interfaces/CenterOfGravityInterface.tsx index 336ef443b7..ecb1d1ec6b 100644 --- a/fission/src/ui/panels/configuring/assembly-config/interfaces/CenterOfGravityInterface.tsx +++ b/fission/src/ui/panels/configuring/assembly-config/interfaces/CenterOfGravityInterface.tsx @@ -3,6 +3,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react" import * as THREE from "three" import { ConfigurationSavedEvent } from "@/events/ConfigurationSavedEvent" import type MirabufSceneObject from "@/mirabuf/MirabufSceneObject" +import { PAUSE_REF_ASSEMBLY_CONFIG } from "@/systems/physics/PhysicsTypes" import PreferencesSystem from "@/systems/preferences/PreferencesSystem" import type GizmoSceneObject from "@/systems/scene/GizmoSceneObject" import World from "@/systems/World" @@ -72,6 +73,15 @@ const CenterOfGravityInterface: React.FC = ({ sel } }, [selectedRobot]) + useEffect(() => { + World.physicsSystem.holdPause(PAUSE_REF_ASSEMBLY_CONFIG) + + return () => { + selectedRobot.stabilizeAfterCenterOfGravityEdit() + World.physicsSystem.releasePause(PAUSE_REF_ASSEMBLY_CONFIG) + } + }, []) + useEffect(() => { ConfigurationSavedEvent.listen(saveEvent)