diff --git a/exporter/SynthesisFusionAddin/src/Parser/SynthesisParser/JointHierarchy.py b/exporter/SynthesisFusionAddin/src/Parser/SynthesisParser/JointHierarchy.py index 8a381966d9..4ed2717f35 100644 --- a/exporter/SynthesisFusionAddin/src/Parser/SynthesisParser/JointHierarchy.py +++ b/exporter/SynthesisFusionAddin/src/Parser/SynthesisParser/JointHierarchy.py @@ -82,7 +82,9 @@ class JointRelationship(enum.Enum): class DynamicOccurrenceNode(GraphNode): - def __init__(self, occurrence: adsk.fusion.Occurrence, isGround=False, previous=None): + def __init__( + self, occurrence: adsk.fusion.Occurrence, isGround=False, previous=None + ): super().__init__(occurrence) self.isGround = isGround self.name = occurrence.name @@ -122,7 +124,9 @@ def getConnectedAxisTokens(self) -> list: class DynamicEdge(GraphEdge): - def __init__(self, relationship: OccurrenceRelationship, node: DynamicOccurrenceNode): + def __init__( + self, relationship: OccurrenceRelationship, node: DynamicOccurrenceNode + ): super().__init__(relationship, node) # should print all in this class @@ -514,7 +518,9 @@ def populateJoint(simNode: SimulationNode, joints: joint_pb2.Joints, progressDia # print(f"Configuring {proto_joint.info.name}") # construct body tree if possible - createTreeParts(simNode.data, OccurrenceRelationship.CONNECTION, root, progressDialog) + createTreeParts( + simNode.data, OccurrenceRelationship.CONNECTION, root, progressDialog + ) proto_joint.parts.nodes.append(root) @@ -533,7 +539,10 @@ def createTreeParts( raise RuntimeError("User canceled export") # if it's the next part just exit early for our own sanity - if relationship == OccurrenceRelationship.NEXT or dynNode.data.isLightBulbOn == False: + if ( + relationship == OccurrenceRelationship.NEXT + or dynNode.data.isLightBulbOn == False + ): return # set the occurrence / component id to reference the part diff --git a/exporter/SynthesisFusionAddin/src/Parser/SynthesisParser/Joints.py b/exporter/SynthesisFusionAddin/src/Parser/SynthesisParser/Joints.py index aca930d117..7e67388aa1 100644 --- a/exporter/SynthesisFusionAddin/src/Parser/SynthesisParser/Joints.py +++ b/exporter/SynthesisFusionAddin/src/Parser/SynthesisParser/Joints.py @@ -402,7 +402,8 @@ def _jointOrigin( geometryOrOrigin = ( ( fusionJoint.geometryOrOriginOne - if fusionJoint.geometryOrOriginOne.objectType == "adsk::fusion::JointGeometry" + if fusionJoint.geometryOrOriginOne.objectType + == "adsk::fusion::JointGeometry" else fusionJoint.geometryOrOriginTwo ) if fusionJoint.objectType == "adsk::fusion::Joint" diff --git a/exporter/SynthesisFusionAddin/src/Parser/SynthesisParser/PDMessage.py b/exporter/SynthesisFusionAddin/src/Parser/SynthesisParser/PDMessage.py index d284a44f93..d5057709da 100644 --- a/exporter/SynthesisFusionAddin/src/Parser/SynthesisParser/PDMessage.py +++ b/exporter/SynthesisFusionAddin/src/Parser/SynthesisParser/PDMessage.py @@ -43,9 +43,13 @@ def _format(self): # TABS DO NOTHING HALP out = f"{self.assemblyName} parsing:\n" out += f"\t Components: \t[ {self.currentCompCount} / {self.componentCount} ]\n" - out += f"\t Occurrences: \t[ {self.currentOccCount} / {self.occurrenceCount} ]\n" + out += ( + f"\t Occurrences: \t[ {self.currentOccCount} / {self.occurrenceCount} ]\n" + ) out += f"\t Materials: \t[ {self.currentMatCount} / {self.materialCount} ]\n" - out += f"\t Appearances: \t[ {self.currentAppCount} / {self.appearanceCount} ]\n" + out += ( + f"\t Appearances: \t[ {self.currentAppCount} / {self.appearanceCount} ]\n" + ) out += f"{self.currentMessage}" return out diff --git a/exporter/SynthesisFusionAddin/src/Parser/SynthesisParser/Parser.py b/exporter/SynthesisFusionAddin/src/Parser/SynthesisParser/Parser.py index 396fc28010..923fb00243 100644 --- a/exporter/SynthesisFusionAddin/src/Parser/SynthesisParser/Parser.py +++ b/exporter/SynthesisFusionAddin/src/Parser/SynthesisParser/Parser.py @@ -212,14 +212,18 @@ def export(self) -> bool: for child in node.children: if child.value == "ground": - joint_hierarchy_out = f"{joint_hierarchy_out} |---> ground\n" + joint_hierarchy_out = ( + f"{joint_hierarchy_out} |---> ground\n" + ) else: newnode = assembly_out.data.joints.joint_instances[ child.value ] - jointdefinition = assembly_out.data.joints.joint_definitions[ - newnode.joint_reference - ] + jointdefinition = ( + assembly_out.data.joints.joint_definitions[ + newnode.joint_reference + ] + ) wheel_ = ( " wheel : true" if (jointdefinition.user_data.data["wheel"] != "") diff --git a/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py b/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py index 44cadb8eb0..e776ee3b10 100644 --- a/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py +++ b/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py @@ -186,8 +186,12 @@ def notify(self, args): cmd.isAutoExecute = False cmd.isExecutedWhenPreEmpted = False cmd.okButtonText = "Export" # replace default OK text with "export" - cmd.setDialogInitialSize(400, 350) # these aren't working for some reason... - cmd.setDialogMinimumSize(400, 350) # these aren't working for some reason... + cmd.setDialogInitialSize( + 400, 350 + ) # these aren't working for some reason... + cmd.setDialogMinimumSize( + 400, 350 + ) # these aren't working for some reason... global INPUTS_ROOT # Global CommandInputs arg INPUTS_ROOT = cmd.commandInputs @@ -415,7 +419,9 @@ def notify(self, args): ) jointConfig.isExpanded = False jointConfig.isVisible = True - jointConfig.tooltip = "Select and define joint occurrences in your assembly." + jointConfig.tooltip = ( + "Select and define joint occurrences in your assembly." + ) joint_inputs = jointConfig.children @@ -565,7 +571,9 @@ def notify(self, args): ) weightTableInput_f.tablePresentationStyle = 2 # set to clear background - weight_name_f = gamepiece_inputs.addStringValueInput("weight_name", "Weight") + weight_name_f = gamepiece_inputs.addStringValueInput( + "weight_name", "Weight" + ) weight_name_f.value = "Unit of Mass" weight_name_f.isReadOnly = True @@ -578,7 +586,9 @@ def notify(self, args): enabled=True, isCheckBox=False, ) - auto_calc_weight_f.resourceFolder = IconPaths.stringIcons["calculate-enabled"] + auto_calc_weight_f.resourceFolder = IconPaths.stringIcons[ + "calculate-enabled" + ] auto_calc_weight_f.isFullWidth = True weight_unit_f = gamepiece_inputs.addDropDownCommandInput( @@ -620,7 +630,9 @@ def notify(self, args): 50, ) - addFieldInput = gamepiece_inputs.addBoolValueInput("field_add", "Add", False) + addFieldInput = gamepiece_inputs.addBoolValueInput( + "field_add", "Add", False + ) removeFieldInput = gamepiece_inputs.addBoolValueInput( "field_delete", "Remove", False @@ -1415,7 +1427,9 @@ def traverseAssembly( if occ in value: return [joint, occ] # occurrence that is jointed - if occ.childOccurrences: # if occurrence has children, traverse sub-tree + if ( + occ.childOccurrences + ): # if occurrence has children, traverse sub-tree self.traverseAssembly(occ.childOccurrences, jointedOcc) return None # no jointed occurrence found except: @@ -1488,7 +1502,9 @@ def wheelParent(self, occ: adsk.fusion.Occurrence): treeParent = parent # each parent that will traverse up in algorithm. while treeParent != None: # loops until reaches top-level component - returned = self.traverseAssembly(treeParent.childOccurrences, jointedOcc) + returned = self.traverseAssembly( + treeParent.childOccurrences, jointedOcc + ) if returned != None: for i in range(parentLevel): @@ -1960,7 +1976,10 @@ def notify(self, args): # gm.ui.activeSelections.clear() addWheelInput.isEnabled = True - if wheelTableInput.selectedRow == -1 or wheelTableInput.selectedRow == 0: + if ( + wheelTableInput.selectedRow == -1 + or wheelTableInput.selectedRow == 0 + ): wheelTableInput.selectedRow = wheelTableInput.rowCount - 1 gm.ui.messageBox("Select a row to delete.") else: @@ -1973,7 +1992,10 @@ def notify(self, args): addJointInput.isEnabled = True addWheelInput.isEnabled = True - if jointTableInput.selectedRow == -1 or jointTableInput.selectedRow == 0: + if ( + jointTableInput.selectedRow == -1 + or jointTableInput.selectedRow == 0 + ): jointTableInput.selectedRow = jointTableInput.rowCount - 1 gm.ui.messageBox("Select a row to delete.") else: @@ -2129,7 +2151,9 @@ def notify(self, args): onSelect.allWheelPreselections.clear() onSelect.wheelJointList.clear() - for group in gm.app.activeDocument.design.rootComponent.customGraphicsGroups: + for ( + group + ) in gm.app.activeDocument.design.rootComponent.customGraphicsGroups: group.deleteMe() # Currently causes Internal Autodesk Error @@ -2198,7 +2222,9 @@ def addJointToTable(joint: adsk.fusion.Joint) -> None: icon.tooltip = "Ball joint" # joint name - name = cmdInputs.addTextBoxCommandInput("name_j", "Occurrence name", "", 1, True) + name = cmdInputs.addTextBoxCommandInput( + "name_j", "Occurrence name", "", 1, True + ) name.tooltip = joint.name name.formattedText = "

{}

".format(joint.name) @@ -2471,9 +2497,9 @@ def removeWheelFromTable(index: int) -> None: except IndexError: pass except: - logging.getLogger("{INTERNAL_ID}.UI.ConfigCommand.removeWheelFromTable()").error( - "Failed:\n{}".format(traceback.format_exc()) - ) + logging.getLogger( + "{INTERNAL_ID}.UI.ConfigCommand.removeWheelFromTable()" + ).error("Failed:\n{}".format(traceback.format_exc())) def removeJointFromTable(joint: adsk.fusion.Joint) -> None: @@ -2509,9 +2535,9 @@ def removeJointFromTable(joint: adsk.fusion.Joint) -> None: else: listItems.item(index).deleteMe() except: - logging.getLogger("{INTERNAL_ID}.UI.ConfigCommand.removeJointFromTable()").error( - "Failed:\n{}".format(traceback.format_exc()) - ) + logging.getLogger( + "{INTERNAL_ID}.UI.ConfigCommand.removeJointFromTable()" + ).error("Failed:\n{}".format(traceback.format_exc())) def removeGamePieceFromTable(index: int) -> None: diff --git a/exporter/SynthesisFusionAddin/src/UI/CustomGraphics.py b/exporter/SynthesisFusionAddin/src/UI/CustomGraphics.py index 9b99310657..d6986f9844 100644 --- a/exporter/SynthesisFusionAddin/src/UI/CustomGraphics.py +++ b/exporter/SynthesisFusionAddin/src/UI/CustomGraphics.py @@ -32,8 +32,10 @@ def createTextGraphics(wheel: adsk.fusion.Occurrence, _wheels) -> None: graphicsText.cullMode = ( adsk.fusion.CustomGraphicsCullModes.CustomGraphicsCullBack ) - graphicsText.color = adsk.fusion.CustomGraphicsShowThroughColorEffect.create( - adsk.core.Color.create(230, 146, 18, 255), 1 + graphicsText.color = ( + adsk.fusion.CustomGraphicsShowThroughColorEffect.create( + adsk.core.Color.create(230, 146, 18, 255), 1 + ) ) # orange/synthesis theme graphicsText.depthPriority = 0 diff --git a/exporter/SynthesisFusionAddin/src/UI/HUI.py b/exporter/SynthesisFusionAddin/src/UI/HUI.py index 13a8b6fc12..d8933e4ed5 100644 --- a/exporter/SynthesisFusionAddin/src/UI/HUI.py +++ b/exporter/SynthesisFusionAddin/src/UI/HUI.py @@ -138,7 +138,9 @@ def __init__( cmdDef = gm.ui.commandDefinitions.itemById(self.uid) if cmdDef: # gm.ui.messageBox("Looks like you have experienced a crash we will do cleanup.") - self.logger.debug("Looks like there was a crash, doing cleanup in button id") + self.logger.debug( + "Looks like there was a crash, doing cleanup in button id" + ) self.scrub() # needs to updated with new OString data @@ -146,7 +148,9 @@ def __init__( self.uid, f"{name}", f"{description}", - OsHelper.getOSPath(".", "src", "Resources", f'{self.name.replace(" ", "")}'), + OsHelper.getOSPath( + ".", "src", "Resources", f'{self.name.replace(" ", "")}' + ), ) """ Button Command Definition stored as a member """ @@ -204,7 +208,9 @@ def deleteMe(self): self.logger.debug(f"Removing Button {self.uid}") cmdDef.deleteMe() - ctrl = gm.ui.allToolbarPanels.itemById(self.location).controls.itemById(self.uid) + ctrl = gm.ui.allToolbarPanels.itemById(self.location).controls.itemById( + self.uid + ) if ctrl: self.logger.debug(f"Removing Button Control {self.location}:{self.uid}") ctrl.deleteMe() diff --git a/exporter/SynthesisFusionAddin/src/UI/IconPaths.py b/exporter/SynthesisFusionAddin/src/UI/IconPaths.py index 9508f5e4ff..b07edab1fb 100644 --- a/exporter/SynthesisFusionAddin/src/UI/IconPaths.py +++ b/exporter/SynthesisFusionAddin/src/UI/IconPaths.py @@ -12,12 +12,14 @@ "omni": resources + os.path.join("WheelIcons", "omni-wheel-preview190x24.png"), "standard": resources + os.path.join("WheelIcons", "standard-wheel-preview190x24.png"), - "mecanum": resources + os.path.join("WheelIcons", "mecanum-wheel-preview190x24.png"), + "mecanum": resources + + os.path.join("WheelIcons", "mecanum-wheel-preview190x24.png"), } jointIcons = { "rigid": resources + os.path.join("JointIcons", "JointRigid", "rigid190x24.png"), - "revolute": resources + os.path.join("JointIcons", "JointRev", "revolute190x24.png"), + "revolute": resources + + os.path.join("JointIcons", "JointRev", "revolute190x24.png"), "slider": resources + os.path.join("JointIcons", "JointSlider", "slider190x24.png"), "cylindrical": resources + os.path.join("JointIcons", "JointCyl", "cylindrical190x24.png"), diff --git a/exporter/SynthesisFusionAddin/src/UI/Toolbar.py b/exporter/SynthesisFusionAddin/src/UI/Toolbar.py index 8044c46e7d..4a260ae18a 100644 --- a/exporter/SynthesisFusionAddin/src/UI/Toolbar.py +++ b/exporter/SynthesisFusionAddin/src/UI/Toolbar.py @@ -57,7 +57,9 @@ def getPanel(self, name: str, visibility: bool = True) -> str or None: self.logger.debug(f"Created Panel {panel_uid} in Toolbar {self.uid}") return panel_uid else: - self.logger.error(f"Failed to Create Panel {panel_uid} in Toolbar {self.uid}") + self.logger.error( + f"Failed to Create Panel {panel_uid} in Toolbar {self.uid}" + ) return None @staticmethod diff --git a/fission/.gitignore b/fission/.gitignore index a547bf36d8..449d7eb050 100644 --- a/fission/.gitignore +++ b/fission/.gitignore @@ -1,3 +1,6 @@ +public/Downloadables + + # Logs logs *.log diff --git a/fission/README.md b/fission/README.md index 2d8d0f3891..7f94a3b56b 100644 --- a/fission/README.md +++ b/fission/README.md @@ -1,59 +1,71 @@ # Fission: Synthesis' web-based robot simulator ## Gettings Started + ### Requirements + 1. NPM (v10.2.4 recommended) 2. NodeJS (v20.10.0 recommended) -3. TypeScript (v4.8.4 recommended) *Unknown if this is actually required* +3. TypeScript (v4.8.4 recommended) _Unknown if this is actually required_ ### Assets + For the asset pack that will be available in production, download the asset pack [here](https://synthesis.autodesk.com/Downloadables/assetpack.zip) and unzip it. Make sure that the Downloadables directory is placed inside of the public directory like so: + ``` /fission/public/Downloadables/ ``` This can be accomplished with the `assetpack` npm script: + ``` npm run assetpack ``` ### Building + To build, install all dependencies: + ```bash npm i ``` + ### NPM Scripts -| Script | Description | -| ------ | ----------- | -| `dev` | Starts the development server used for testing. Supports hotloading (though finicky with WASM module loading). | -| `test` | Runs the unit tests via vitest. | -| `build` | Builds the project into it's packaged form. Uses root base path. | -| `build:prod` | Builds the project into it's packaged form. Uses the `/fission/` base path. | -| `preview` | Runs the built project for preview locally before deploying. | -| `lint` | Runs eslint on the project. | -| `lint:fix` | Attempts to fix issues found with eslint. | -| `prettier` | Runs prettier on the project as a check. | -| `prettier:fix` | Runs prettier on the project to fix any issues with formating. | -| `format` | Runs `prettier:fix` and `lint:fix`. | -| `assetpack` | Downloads the assetpack and unzips/installs it in the correct location. | +| Script | Description | +| -------------- | ----------------------------------------------------------------------------------------------------------------------- | +| `dev` | Starts the development server used for testing. Supports hotloading (though finicky with WASM module loading). | +| `test` | Runs the unit tests via vitest. | +| `build` | Builds the project into it's packaged form. Uses root base path. | +| `build:prod` | Builds the project into it's packaged form. Uses the `/fission/` base path. | +| `preview` | Runs the built project for preview locally before deploying. | +| `lint` | Runs eslint on the project. | +| `lint:fix` | Attempts to fix issues found with eslint. | +| `prettier` | Runs prettier on the project as a check. | +| `prettier:fix` | Runs prettier on the project to fix any issues with formating. **DO NOT USE**, I don't like the current format it uses. | +| `format` | Runs `prettier:fix` and `lint:fix`. **Do not use** for the same reasons as `prettier:fix`. | +| `assetpack` | Downloads the assetpack and unzips/installs it in the correct location. | ### Autodesk Platform Services To test/enable the use of Autodesk Platform Services (APS), please follow instructions for development web server (Closed Source). ## Core Systems + These core systems make up the bulk of the vital technologies to make Synthesis work. The idea is that these systems will serve as a jumping off point for anything requiring real-time simulation. ### World + The world serves as a hub for all of the core systems. It is a static class that handles system update execution order and lifetime. ### Scene Renderer + The Scene Renderer is our interface with rendering within the Canvas. This is primarily done via ThreeJS, however can be extended later on. ### Physics System + This Physics System is our interface with Jolt, ensuring objects are properly handled and provides utility functions that are more custom fit to our purposes. [Jolt Physics Architecture](https://jrouwe.github.io/JoltPhysics/) @@ -63,18 +75,23 @@ This Physics System is our interface with Jolt, ensuring objects are properly ha ### UI System ## Additional Systems + These systems will extend off of the core systems to build out features in Synthesis. ### Simulation System + The Simulation System articulates dynamic elements of the scene via the Physics System. At it's core there are 3 main components: #### Driver + Drivers are mostly write-only. They take in values to know how to articulate the physics objects and contraints. #### Stimulus -Stimulu are mostly read-only. They read values from given physics objects and constraints. + +Stimuli are mostly read-only. They read values from given physics objects and constraints. #### Brain + Brains are the controllers of the mechanisms. They use a combination of Drivers and Stimuli to control a given mechanism. For basic user control of the mechanisms, we'll have a Synthesis Brain. By the end of Summer 2024, I hope to have an additional brain, the WPIBrain for facilitating WPILib code control over the mechanisms inside of Synthesis. diff --git a/fission/bun.lockb b/fission/bun.lockb index e8363f113e..291a31f135 100755 Binary files a/fission/bun.lockb and b/fission/bun.lockb differ diff --git a/fission/src/mirabuf/MirabufSceneObject.ts b/fission/src/mirabuf/MirabufSceneObject.ts index b2783a81a5..9c6c57b9a8 100644 --- a/fission/src/mirabuf/MirabufSceneObject.ts +++ b/fission/src/mirabuf/MirabufSceneObject.ts @@ -96,10 +96,10 @@ class MirabufSceneObject extends SceneObject { colliderMesh.position.setFromMatrixPosition(transform) colliderMesh.rotation.setFromRotationMatrix(transform) - const comTransform = JoltMat44_ThreeMatrix4(body.GetCenterOfMassTransform()); + const comTransform = JoltMat44_ThreeMatrix4(body.GetCenterOfMassTransform()) - comMesh.position.setFromMatrixPosition(comTransform); - comMesh.rotation.setFromRotationMatrix(comTransform); + comMesh.position.setFromMatrixPosition(comTransform) + comMesh.rotation.setFromRotationMatrix(comTransform) } }) } @@ -119,6 +119,10 @@ class MirabufSceneObject extends SceneObject { this._physicsLayerReserve?.Release() } + public GetRootNodeId(): Jolt.BodyID | undefined { + return this._mechanism.nodeToBody.get(this._mechanism.rootBody) + } + private CreateMeshForShape(shape: Jolt.Shape): THREE.Mesh { const scale = new JOLT.Vec3(1, 1, 1) const triangleContext = new JOLT.ShapeGetTriangles( diff --git a/fission/src/systems/World.ts b/fission/src/systems/World.ts index e38fac6813..7470618dc8 100644 --- a/fission/src/systems/World.ts +++ b/fission/src/systems/World.ts @@ -1,9 +1,9 @@ import * as THREE from "three" -import PhysicsSystem from "./physics/PhysicsSystem"; -import SceneRenderer from "./scene/SceneRenderer"; -import SimulationSystem from "./simulation/SimulationSystem"; -import InputSystem from "./input/InputSystem"; +import PhysicsSystem from "./physics/PhysicsSystem" +import SceneRenderer from "./scene/SceneRenderer" +import SimulationSystem from "./simulation/SimulationSystem" +import InputSystem from "./input/InputSystem" class World { private static _isAlive: boolean = false @@ -37,10 +37,10 @@ class World { World._clock = new THREE.Clock() World._isAlive = true - World._sceneRenderer = new SceneRenderer(); - World._physicsSystem = new PhysicsSystem(); - World._simulationSystem = new SimulationSystem(); - World._inputSystem = new InputSystem(); + World._sceneRenderer = new SceneRenderer() + World._physicsSystem = new PhysicsSystem() + World._simulationSystem = new SimulationSystem() + World._inputSystem = new InputSystem() } public static DestroyWorld() { @@ -48,18 +48,18 @@ class World { World._isAlive = false - World._physicsSystem.Destroy(); - World._sceneRenderer.Destroy(); - World._simulationSystem.Destroy(); - World._inputSystem.Destroy(); + World._physicsSystem.Destroy() + World._sceneRenderer.Destroy() + World._simulationSystem.Destroy() + World._inputSystem.Destroy() } public static UpdateWorld() { - const deltaT = World._clock.getDelta(); - World._simulationSystem.Update(deltaT); - World._physicsSystem.Update(deltaT); - World._inputSystem.Update(deltaT); - World._sceneRenderer.Update(deltaT); + const deltaT = World._clock.getDelta() + World._simulationSystem.Update(deltaT) + World._physicsSystem.Update(deltaT) + World._inputSystem.Update(deltaT) + World._sceneRenderer.Update(deltaT) } } diff --git a/fission/src/systems/input/InputSystem.ts b/fission/src/systems/input/InputSystem.ts index cd7f4610e6..21aca82ee7 100644 --- a/fission/src/systems/input/InputSystem.ts +++ b/fission/src/systems/input/InputSystem.ts @@ -1,4 +1,4 @@ -import WorldSystem from "../WorldSystem"; +import WorldSystem from "../WorldSystem" declare global { type ModifierState = { @@ -16,115 +16,119 @@ declare global { } } -export const emptyModifierState: ModifierState = { ctrl: false, alt: false, shift: false, meta: false }; +export const emptyModifierState: ModifierState = { ctrl: false, alt: false, shift: false, meta: false } // When a robot is loaded, default inputs replace any unassigned inputs const defaultInputs: { [key: string]: Input } = { - "intake": { name: "intake", keyCode: "KeyE", isGlobal: true, modifiers: emptyModifierState }, - "shootGamepiece": { name: "shootGamepiece", keyCode: "KeyQ", isGlobal: true, modifiers: emptyModifierState }, - "enableGodMode": { name: "enableGodMode", keyCode: "KeyG", isGlobal: true, modifiers: emptyModifierState }, - - "arcadeForward": { name: "arcadeForward", keyCode: "KeyW", isGlobal: false, modifiers: emptyModifierState }, - "arcadeBackward": { name: "arcadeBackward", keyCode: "KeyS", isGlobal: false, modifiers: emptyModifierState }, - "arcadeLeft": { name: "arcadeLeft", keyCode: "KeyA", isGlobal: false, modifiers: emptyModifierState }, - "arcadeRight": { name: "arcadeRight", keyCode: "KeyD", isGlobal: false, modifiers: emptyModifierState }, + intake: { name: "intake", keyCode: "KeyE", isGlobal: true, modifiers: emptyModifierState }, + shootGamepiece: { name: "shootGamepiece", keyCode: "KeyQ", isGlobal: true, modifiers: emptyModifierState }, + enableGodMode: { name: "enableGodMode", keyCode: "KeyG", isGlobal: true, modifiers: emptyModifierState }, + + arcadeForward: { name: "arcadeForward", keyCode: "KeyW", isGlobal: false, modifiers: emptyModifierState }, + arcadeBackward: { name: "arcadeBackward", keyCode: "KeyS", isGlobal: false, modifiers: emptyModifierState }, + arcadeLeft: { name: "arcadeLeft", keyCode: "KeyA", isGlobal: false, modifiers: emptyModifierState }, + arcadeRight: { name: "arcadeRight", keyCode: "KeyD", isGlobal: false, modifiers: emptyModifierState }, } class InputSystem extends WorldSystem { - public static allInputs: { [key: string]: Input } = { } - private static _currentModifierState: ModifierState; + public static allInputs: { [key: string]: Input } = {} + private static _currentModifierState: ModifierState // Inputs global to all of synthesis like camera controls public static get globalInputs(): { [key: string]: Input } { - return Object.fromEntries( - Object.entries(InputSystem.allInputs) - .filter(([_, input]) => input.isGlobal)); + return Object.fromEntries(Object.entries(InputSystem.allInputs).filter(([_, input]) => input.isGlobal)) } // Robot specific controls like driving public static get robotInputs(): { [key: string]: Input } { - return Object.fromEntries( - Object.entries(InputSystem.allInputs) - .filter(([_, input]) => !input.isGlobal)); + return Object.fromEntries(Object.entries(InputSystem.allInputs).filter(([_, input]) => !input.isGlobal)) } // A list of keys currently being pressed - private static _keysPressed: { [key: string]: boolean } = {}; + private static _keysPressed: { [key: string]: boolean } = {} constructor() { - super(); + super() - this.HandleKeyDown = this.HandleKeyDown.bind(this); - document.addEventListener('keydown', this.HandleKeyDown); + this.HandleKeyDown = this.HandleKeyDown.bind(this) + document.addEventListener("keydown", this.HandleKeyDown) + + this.HandleKeyUp = this.HandleKeyUp.bind(this) + document.addEventListener("keyup", this.HandleKeyUp) - this.HandleKeyUp = this.HandleKeyUp.bind(this); - document.addEventListener('keyup', this.HandleKeyUp); - // TODO: Load saved inputs from mira (robot specific) & global inputs for (const key in defaultInputs) { if (Object.prototype.hasOwnProperty.call(defaultInputs, key)) { - InputSystem.allInputs[key] = defaultInputs[key]; + InputSystem.allInputs[key] = defaultInputs[key] } } } public Update(_: number): void { if (!document.hasFocus()) { - for (const keyCode in InputSystem._keysPressed) - delete InputSystem._keysPressed[keyCode]; - return; + for (const keyCode in InputSystem._keysPressed) delete InputSystem._keysPressed[keyCode] + return } - InputSystem._currentModifierState = { ctrl: InputSystem.isKeyPressed("ControlLeft") || InputSystem.isKeyPressed("ControlRight"), alt: InputSystem.isKeyPressed("AltLeft") || InputSystem.isKeyPressed("AltRight"), shift: InputSystem.isKeyPressed("ShiftLeft") || InputSystem.isKeyPressed("ShiftRight"), meta: InputSystem.isKeyPressed("MetaLeft") || InputSystem.isKeyPressed("MetaRight") } + InputSystem._currentModifierState = { + ctrl: InputSystem.isKeyPressed("ControlLeft") || InputSystem.isKeyPressed("ControlRight"), + alt: InputSystem.isKeyPressed("AltLeft") || InputSystem.isKeyPressed("AltRight"), + shift: InputSystem.isKeyPressed("ShiftLeft") || InputSystem.isKeyPressed("ShiftRight"), + meta: InputSystem.isKeyPressed("MetaLeft") || InputSystem.isKeyPressed("MetaRight"), + } } - public Destroy(): void { - document.removeEventListener('keydown', this.HandleKeyDown); - document.removeEventListener('keyup', this.HandleKeyUp); - } + public Destroy(): void { + document.removeEventListener("keydown", this.HandleKeyDown) + document.removeEventListener("keyup", this.HandleKeyUp) + } // Called when any key is pressed private HandleKeyDown(event: KeyboardEvent) { - InputSystem._keysPressed[event.code] = true; + InputSystem._keysPressed[event.code] = true } // Called when any key is released private HandleKeyUp(event: KeyboardEvent) { - InputSystem._keysPressed[event.code] = false; + InputSystem._keysPressed[event.code] = false } // Returns true if the given key is currently down private static isKeyPressed(key: string): boolean { - return !!InputSystem._keysPressed[key]; + return !!InputSystem._keysPressed[key] } // If an input exists, return true if it is pressed - public static getInput(inputName: string) : boolean { + public static getInput(inputName: string): boolean { // Checks if there is an input assigned to this action if (inputName in this.allInputs) { - const targetInput = this.allInputs[inputName]; + const targetInput = this.allInputs[inputName] // Check for input modifiers - if (!this.CompareModifiers(InputSystem._currentModifierState, targetInput.modifiers)) - return false; + if (!this.CompareModifiers(InputSystem._currentModifierState, targetInput.modifiers)) return false - return this.isKeyPressed(targetInput.keyCode); + return this.isKeyPressed(targetInput.keyCode) } // If the input does not exist, returns false - return false; + return false } // Combines two inputs into a positive/negative axis public static GetAxis(positive: string, negative: string) { - return (this.getInput(positive) ? 1 : 0) - (this.getInput(negative) ? 1 : 0); + return (this.getInput(positive) ? 1 : 0) - (this.getInput(negative) ? 1 : 0) } // Returns true if two modifier states are identical - private static CompareModifiers(state1: ModifierState, state2: ModifierState) : boolean { - return state1.alt == state2.alt && state1.ctrl == state2.ctrl && state1.meta == state2.meta && state1.shift == state2.shift; + private static CompareModifiers(state1: ModifierState, state2: ModifierState): boolean { + return ( + state1.alt == state2.alt && + state1.ctrl == state2.ctrl && + state1.meta == state2.meta && + state1.shift == state2.shift + ) } } -export default InputSystem; \ No newline at end of file +export default InputSystem diff --git a/fission/src/systems/physics/PhysicsSystem.ts b/fission/src/systems/physics/PhysicsSystem.ts index 4c5bf336e3..a4a2cd49bc 100644 --- a/fission/src/systems/physics/PhysicsSystem.ts +++ b/fission/src/systems/physics/PhysicsSystem.ts @@ -1,4 +1,5 @@ import { + JoltVec3_ThreeVector3, MirabufFloatArr_JoltVec3, MirabufVector3_JoltVec3, ThreeMatrix4_JoltMat44, @@ -24,16 +25,19 @@ const RobotLayers: number[] = [ 3, 4, 5, 6, 7, 8, 9, ] +// Layer for ghost object in god mode, interacts with nothing +const LAYER_GHOST = 10 + // Please update this accordingly. -const COUNT_OBJECT_LAYERS = 10 +const COUNT_OBJECT_LAYERS = 11 export const SIMULATION_PERIOD = 1.0 / 120.0 const STANDARD_SUB_STEPS = 3 // Friction constants -const FLOOR_FRICTION = 0.7; -const SUSPENSION_MIN_FACTOR = 0.1; -const SUSPENSION_MAX_FACTOR = 0.3; +const FLOOR_FRICTION = 0.7 +const SUSPENSION_MIN_FACTOR = 0.1 +const SUSPENSION_MAX_FACTOR = 0.3 /** * The PhysicsSystem handles all Jolt Phyiscs interactions within Synthesis. @@ -73,10 +77,9 @@ class PhysicsSystem extends WorldSystem { new THREE.Vector3(0.0, -2.0, 0.0), undefined ) - ground.SetFriction(FLOOR_FRICTION); + ground.SetFriction(FLOOR_FRICTION) this._joltBodyInterface.AddBody(ground.GetID(), JOLT.EActivation_Activate) } - /** * TEMPORARY @@ -603,7 +606,6 @@ class PhysicsSystem extends WorldSystem { // Little testing components body.SetRestitution(0.4) } - // Cleanup JOLT.destroy(compoundShapeSettings) }) @@ -720,6 +722,50 @@ class PhysicsSystem extends WorldSystem { JOLT.destroy(this._joltBodyInterface) JOLT.destroy(this._joltInterface) } + + /** + * Creates a ghost object and a distance constraint that connects it to the given body + * The ghost body is part of the LAYER_GHOST which doesn't interact with any other layer + * The caller is responsible for cleaning up the ghost body and the constraint + * + * @param id The id of the body to be attatched to and moved + * @returns The ghost body and the constraint + */ + + public CreateGodModeBody(id: Jolt.BodyID, anchorPoint: Jolt.Vec3): [Jolt.Body, Jolt.Constraint] { + const body = this.GetBody(id) + const ghostBody = this.CreateBox( + new THREE.Vector3(0.05, 0.05, 0.05), + undefined, + JoltVec3_ThreeVector3(anchorPoint), + undefined + ) + + const ghostBodyId = ghostBody.GetID() + this._joltBodyInterface.SetObjectLayer(ghostBodyId, LAYER_GHOST) + this._joltBodyInterface.AddBody(ghostBodyId, JOLT.EActivation_Activate) + this._bodies.push(ghostBodyId) + + const constraintSettings = new JOLT.PointConstraintSettings() + constraintSettings.set_mPoint1(anchorPoint) + constraintSettings.set_mPoint2(anchorPoint) + const constraint = constraintSettings.Create(ghostBody, body) + this._joltPhysSystem.AddConstraint(constraint) + this._constraints.push(constraint) + + return [ghostBody, constraint] + } + + /** + * Exposes the SetPosition method on the _joltBodyInterface + * Sets the position of the body + * + * @param id The id of the body + * @param position The new position of the body + */ + public SetBodyPosition(id: Jolt.BodyID, position: Jolt.Vec3): void { + this._joltBodyInterface.SetPosition(id, position, JOLT.EActivation_Activate) + } } export class LayerReserve { @@ -738,7 +784,7 @@ export class LayerReserve { this._isReleased = false } - public Release() { + public Release(): void { if (!this._isReleased) { RobotLayers.push(this._layer) this._isReleased = true diff --git a/fission/src/systems/simulation/SimulationSystem.ts b/fission/src/systems/simulation/SimulationSystem.ts index 0bc789040e..331739f8a7 100644 --- a/fission/src/systems/simulation/SimulationSystem.ts +++ b/fission/src/systems/simulation/SimulationSystem.ts @@ -58,9 +58,15 @@ class SimulationLayer { private _drivers: Driver[] private _stimuli: Stimulus[] - public get brain() { return this._brain; } - public get drivers() { return this._drivers; } - public get stimuli() { return this._stimuli; } + public get brain() { + return this._brain + } + public get drivers() { + return this._drivers + } + public get stimuli() { + return this._stimuli + } constructor(mechanism: Mechanism) { this._mechanism = mechanism @@ -101,13 +107,13 @@ class SimulationLayer { public SetBrain(brain: T | undefined) { if (this._brain) this._brain.Disable() - this._brain = brain; - + this._brain = brain + if (this._brain) { - this._brain.Enable(); + this._brain.Enable() } } } -export default SimulationSystem; -export {SimulationLayer}; \ No newline at end of file +export default SimulationSystem +export { SimulationLayer } diff --git a/fission/src/systems/simulation/behavior/ArcadeDriveBehavior.ts b/fission/src/systems/simulation/behavior/ArcadeDriveBehavior.ts index 4d5ed5497c..c9bb881a85 100644 --- a/fission/src/systems/simulation/behavior/ArcadeDriveBehavior.ts +++ b/fission/src/systems/simulation/behavior/ArcadeDriveBehavior.ts @@ -1,35 +1,42 @@ -import WheelDriver from "../driver/WheelDriver"; -import WheelRotationStimulus from "../stimulus/WheelStimulus"; -import Behavior from "./Behavior"; -import InputSystem from "@/systems/input/InputSystem"; +import WheelDriver from "../driver/WheelDriver" +import WheelRotationStimulus from "../stimulus/WheelStimulus" +import Behavior from "./Behavior" +import InputSystem from "@/systems/input/InputSystem" class ArcadeDriveBehavior extends Behavior { - leftWheels: WheelDriver[]; - rightWheels: WheelDriver[]; + leftWheels: WheelDriver[] + rightWheels: WheelDriver[] - private _driveSpeed = 30; - private _turnSpeed = 30; + private _driveSpeed = 30 + private _turnSpeed = 30 - constructor(leftWheels: WheelDriver[], rightWheels: WheelDriver[], leftStimuli: WheelRotationStimulus[], rightStimuli: WheelRotationStimulus[]) { - super(leftWheels.concat(rightWheels), leftStimuli.concat(rightStimuli)); - - this.leftWheels = leftWheels; - this.rightWheels = rightWheels; + constructor( + leftWheels: WheelDriver[], + rightWheels: WheelDriver[], + leftStimuli: WheelRotationStimulus[], + rightStimuli: WheelRotationStimulus[] + ) { + super(leftWheels.concat(rightWheels), leftStimuli.concat(rightStimuli)) + + this.leftWheels = leftWheels + this.rightWheels = rightWheels } // Sets the drivetrains target linear and rotational velocity private DriveSpeeds(linearVelocity: number, rotationVelocity: number) { - const leftSpeed = linearVelocity + rotationVelocity; - const rightSpeed = linearVelocity - rotationVelocity; - - this.leftWheels.forEach((wheel) => wheel.targetWheelSpeed = leftSpeed); - this.rightWheels.forEach((wheel) => wheel.targetWheelSpeed = rightSpeed); + const leftSpeed = linearVelocity + rotationVelocity + const rightSpeed = linearVelocity - rotationVelocity + + this.leftWheels.forEach(wheel => (wheel.targetWheelSpeed = leftSpeed)) + this.rightWheels.forEach(wheel => (wheel.targetWheelSpeed = rightSpeed)) } public Update(_: number): void { - this.DriveSpeeds(InputSystem.GetAxis("arcadeForward", "arcadeBackward")*this._driveSpeed, - InputSystem.GetAxis("arcadeRight", "arcadeLeft")*this._turnSpeed); + this.DriveSpeeds( + InputSystem.GetAxis("arcadeForward", "arcadeBackward") * this._driveSpeed, + InputSystem.GetAxis("arcadeRight", "arcadeLeft") * this._turnSpeed + ) } } -export default ArcadeDriveBehavior; \ No newline at end of file +export default ArcadeDriveBehavior diff --git a/fission/src/systems/simulation/behavior/Behavior.ts b/fission/src/systems/simulation/behavior/Behavior.ts index fcfc54cca3..2dd0fcb190 100644 --- a/fission/src/systems/simulation/behavior/Behavior.ts +++ b/fission/src/systems/simulation/behavior/Behavior.ts @@ -17,7 +17,7 @@ abstract class Behavior { this._stimuli = stimuli } - public abstract Update(deltaT: number): void; + public abstract Update(deltaT: number): void } export default Behavior diff --git a/fission/src/systems/simulation/behavior/GenericArmBehavior.ts b/fission/src/systems/simulation/behavior/GenericArmBehavior.ts index 6e5a0b7c0c..b2d50d8064 100644 --- a/fission/src/systems/simulation/behavior/GenericArmBehavior.ts +++ b/fission/src/systems/simulation/behavior/GenericArmBehavior.ts @@ -1,37 +1,46 @@ -import HingeDriver from "../driver/HingeDriver"; -import HingeStimulus from "../stimulus/HingeStimulus"; -import Behavior from "./Behavior"; -import InputSystem, { emptyModifierState } from "@/systems/input/InputSystem"; +import HingeDriver from "../driver/HingeDriver" +import HingeStimulus from "../stimulus/HingeStimulus" +import Behavior from "./Behavior" +import InputSystem, { emptyModifierState } from "@/systems/input/InputSystem" class GenericArmBehavior extends Behavior { - private _hingeDriver: HingeDriver; + private _hingeDriver: HingeDriver - private _positiveInput: string; - private _negativeInput: string; - - private _rotationalSpeed = 30; + private _positiveInput: string + private _negativeInput: string + + private _rotationalSpeed = 30 constructor(hingeDriver: HingeDriver, hingeStimulus: HingeStimulus, jointIndex: number) { - super([hingeDriver], [hingeStimulus]); - this._hingeDriver = hingeDriver; + super([hingeDriver], [hingeStimulus]) + this._hingeDriver = hingeDriver - this._positiveInput = "joint " + jointIndex + " Positive"; - this._negativeInput = "joint " + jointIndex + " Negative"; + this._positiveInput = "joint " + jointIndex + " Positive" + this._negativeInput = "joint " + jointIndex + " Negative" // TODO: load inputs from mira - InputSystem.allInputs[this._positiveInput] = { name: this._positiveInput, keyCode: "Digit" + jointIndex.toString(), isGlobal: false, modifiers: emptyModifierState }; - InputSystem.allInputs[this._negativeInput] = { name: this._negativeInput, keyCode: "Digit" + jointIndex.toString(), isGlobal: false, - modifiers: { ctrl: false, alt: false, shift: true, meta: false } }; + InputSystem.allInputs[this._positiveInput] = { + name: this._positiveInput, + keyCode: "Digit" + jointIndex.toString(), + isGlobal: false, + modifiers: emptyModifierState, + } + InputSystem.allInputs[this._negativeInput] = { + name: this._negativeInput, + keyCode: "Digit" + jointIndex.toString(), + isGlobal: false, + modifiers: { ctrl: false, alt: false, shift: true, meta: false }, + } } // Sets the arms target rotational velocity rotateArm(rotationalVelocity: number) { - this._hingeDriver.targetVelocity = rotationalVelocity; + this._hingeDriver.targetVelocity = rotationalVelocity } public Update(_: number): void { - this.rotateArm(InputSystem.GetAxis(this._positiveInput, this._negativeInput)*this._rotationalSpeed); + this.rotateArm(InputSystem.GetAxis(this._positiveInput, this._negativeInput) * this._rotationalSpeed) } } -export default GenericArmBehavior; \ No newline at end of file +export default GenericArmBehavior diff --git a/fission/src/systems/simulation/behavior/GenericElevatorBehavior.ts b/fission/src/systems/simulation/behavior/GenericElevatorBehavior.ts index 1190cff16a..0d9ce8de79 100644 --- a/fission/src/systems/simulation/behavior/GenericElevatorBehavior.ts +++ b/fission/src/systems/simulation/behavior/GenericElevatorBehavior.ts @@ -1,37 +1,46 @@ -import SliderDriver from "../driver/SliderDriver"; -import SliderStimulus from "../stimulus/SliderStimulus"; -import Behavior from "./Behavior"; -import InputSystem, { emptyModifierState } from "@/systems/input/InputSystem"; +import SliderDriver from "../driver/SliderDriver" +import SliderStimulus from "../stimulus/SliderStimulus" +import Behavior from "./Behavior" +import InputSystem, { emptyModifierState } from "@/systems/input/InputSystem" class GenericElevatorBehavior extends Behavior { - private _sliderDriver: SliderDriver; + private _sliderDriver: SliderDriver - private _positiveInput: string; - private _negativeInput: string; + private _positiveInput: string + private _negativeInput: string - private _linearSpeed = 1; + private _linearSpeed = 1 constructor(sliderDriver: SliderDriver, sliderStimulus: SliderStimulus, jointIndex: number) { - super([sliderDriver], [sliderStimulus]); - this._sliderDriver = sliderDriver; + super([sliderDriver], [sliderStimulus]) + this._sliderDriver = sliderDriver - this._positiveInput = "joint " + jointIndex + " Positive"; - this._negativeInput = "joint " + jointIndex + " Negative"; + this._positiveInput = "joint " + jointIndex + " Positive" + this._negativeInput = "joint " + jointIndex + " Negative" // TODO: load inputs from mira - InputSystem.allInputs[this._positiveInput] = { name: this._positiveInput, keyCode: "Digit" + jointIndex.toString(), isGlobal: false, modifiers: emptyModifierState }; - InputSystem.allInputs[this._negativeInput] = { name: this._negativeInput, keyCode: "Digit" + jointIndex.toString(), isGlobal: false, - modifiers: { ctrl: false, alt: false, shift: true, meta: false } }; + InputSystem.allInputs[this._positiveInput] = { + name: this._positiveInput, + keyCode: "Digit" + jointIndex.toString(), + isGlobal: false, + modifiers: emptyModifierState, + } + InputSystem.allInputs[this._negativeInput] = { + name: this._negativeInput, + keyCode: "Digit" + jointIndex.toString(), + isGlobal: false, + modifiers: { ctrl: false, alt: false, shift: true, meta: false }, + } } // Changes the elevators target position moveElevator(positionDelta: number) { - this._sliderDriver.targetPosition += positionDelta; + this._sliderDriver.targetPosition += positionDelta } public Update(deltaT: number): void { - this.moveElevator(InputSystem.GetAxis(this._positiveInput, this._negativeInput)*this._linearSpeed*deltaT); + this.moveElevator(InputSystem.GetAxis(this._positiveInput, this._negativeInput) * this._linearSpeed * deltaT) } } -export default GenericElevatorBehavior; \ No newline at end of file +export default GenericElevatorBehavior diff --git a/fission/src/systems/simulation/driver/SliderDriver.ts b/fission/src/systems/simulation/driver/SliderDriver.ts index 5cd0282524..050a41c925 100644 --- a/fission/src/systems/simulation/driver/SliderDriver.ts +++ b/fission/src/systems/simulation/driver/SliderDriver.ts @@ -1,8 +1,8 @@ -import Jolt from "@barclah/jolt-physics"; -import Driver from "./Driver"; -import { SIMULATION_PERIOD } from "@/systems/physics/PhysicsSystem"; -import JOLT from "@/util/loading/JoltSyncLoader"; -import InputSystem from "@/systems/input/InputSystem"; +import Jolt from "@barclah/jolt-physics" +import Driver from "./Driver" +import { SIMULATION_PERIOD } from "@/systems/physics/PhysicsSystem" +import JOLT from "@/util/loading/JoltSyncLoader" +import InputSystem from "@/systems/input/InputSystem" class SliderDriver extends Driver { private _constraint: Jolt.SliderConstraint @@ -47,8 +47,9 @@ class SliderDriver extends Driver { } public Update(_: number): void { - this._targetPosition += ((InputSystem.getInput("sliderUp") ? 1 : 0) - (InputSystem.getInput("sliderDown") ? 1 : 0))*3; - this._constraint.SetTargetPosition(this._targetPosition); + this._targetPosition += + ((InputSystem.getInput("sliderUp") ? 1 : 0) - (InputSystem.getInput("sliderDown") ? 1 : 0)) * 3 + this._constraint.SetTargetPosition(this._targetPosition) } } diff --git a/fission/src/systems/simulation/driver/WheelDriver.ts b/fission/src/systems/simulation/driver/WheelDriver.ts index 627e48b384..4371e7bda3 100644 --- a/fission/src/systems/simulation/driver/WheelDriver.ts +++ b/fission/src/systems/simulation/driver/WheelDriver.ts @@ -2,8 +2,8 @@ import Jolt from "@barclah/jolt-physics" import Driver from "./Driver" import JOLT from "@/util/loading/JoltSyncLoader" -const LATERIAL_FRICTION = 0.6; -const LONGITUDINAL_FRICTION = 0.8; +const LATERIAL_FRICTION = 0.6 +const LONGITUDINAL_FRICTION = 0.8 class WheelDriver extends Driver { private _constraint: Jolt.VehicleConstraint @@ -18,19 +18,21 @@ class WheelDriver extends Driver { this._targetWheelSpeed = radsPerSec } - public get constraint(): Jolt.VehicleConstraint { return this._constraint } + public get constraint(): Jolt.VehicleConstraint { + return this._constraint + } public constructor(constraint: Jolt.VehicleConstraint) { super() - this._constraint = constraint; - this._wheel = JOLT.castObject(this._constraint.GetWheel(0), JOLT.WheelWV); - this._wheel.set_mCombinedLateralFriction(LATERIAL_FRICTION); - this._wheel.set_mCombinedLongitudinalFriction(LONGITUDINAL_FRICTION); + this._constraint = constraint + this._wheel = JOLT.castObject(this._constraint.GetWheel(0), JOLT.WheelWV) + this._wheel.set_mCombinedLateralFriction(LATERIAL_FRICTION) + this._wheel.set_mCombinedLongitudinalFriction(LONGITUDINAL_FRICTION) } - public Update(_: number): void { - this._wheel.SetAngularVelocity(this._targetWheelSpeed); + public Update(_: number): void { + this._wheel.SetAngularVelocity(this._targetWheelSpeed) } } diff --git a/fission/src/systems/simulation/stimulus/WheelStimulus.ts b/fission/src/systems/simulation/stimulus/WheelStimulus.ts index 27426933bd..cfb6206085 100644 --- a/fission/src/systems/simulation/stimulus/WheelStimulus.ts +++ b/fission/src/systems/simulation/stimulus/WheelStimulus.ts @@ -36,7 +36,7 @@ class WheelRotationStimulus extends EncoderStimulus { public Update(deltaT: number): void { if (this._accum) { - this._wheelRotationAccum += this._wheel.GetAngularVelocity() * deltaT; + this._wheelRotationAccum += this._wheel.GetAngularVelocity() * deltaT } } diff --git a/fission/src/systems/simulation/synthesis_brain/SynthesisBrain.ts b/fission/src/systems/simulation/synthesis_brain/SynthesisBrain.ts index 7ce89ec572..cbce7c4b61 100644 --- a/fission/src/systems/simulation/synthesis_brain/SynthesisBrain.ts +++ b/fission/src/systems/simulation/synthesis_brain/SynthesisBrain.ts @@ -1,49 +1,48 @@ -import Mechanism from "@/systems/physics/Mechanism"; -import Brain from "../Brain"; -import Behavior from "../behavior/Behavior"; -import World from "@/systems/World"; -import WheelDriver from "../driver/WheelDriver"; -import WheelRotationStimulus from "../stimulus/WheelStimulus"; -import ArcadeDriveBehavior from "../behavior/ArcadeDriveBehavior"; -import { SimulationLayer } from "../SimulationSystem"; -import Jolt from "@barclah/jolt-physics"; -import JOLT from "@/util/loading/JoltSyncLoader"; -import HingeDriver from "../driver/HingeDriver"; -import HingeStimulus from "../stimulus/HingeStimulus"; -import GenericArmBehavior from "../behavior/GenericArmBehavior"; -import SliderDriver from "../driver/SliderDriver"; -import SliderStimulus from "../stimulus/SliderStimulus"; -import GenericElevatorBehavior from "../behavior/GenericElevatorBehavior"; - +import Mechanism from "@/systems/physics/Mechanism" +import Brain from "../Brain" +import Behavior from "../behavior/Behavior" +import World from "@/systems/World" +import WheelDriver from "../driver/WheelDriver" +import WheelRotationStimulus from "../stimulus/WheelStimulus" +import ArcadeDriveBehavior from "../behavior/ArcadeDriveBehavior" +import { SimulationLayer } from "../SimulationSystem" +import Jolt from "@barclah/jolt-physics" +import JOLT from "@/util/loading/JoltSyncLoader" +import HingeDriver from "../driver/HingeDriver" +import HingeStimulus from "../stimulus/HingeStimulus" +import GenericArmBehavior from "../behavior/GenericArmBehavior" +import SliderDriver from "../driver/SliderDriver" +import SliderStimulus from "../stimulus/SliderStimulus" +import GenericElevatorBehavior from "../behavior/GenericElevatorBehavior" class SynthesisBrain extends Brain { - private _behaviors: Behavior[] = []; - private _simLayer: SimulationLayer; + private _behaviors: Behavior[] = [] + private _simLayer: SimulationLayer - _leftWheelIndices: number[] = []; + _leftWheelIndices: number[] = [] // Tracks how many joins have been made for unique controls - _currentJointIndex = 1; + _currentJointIndex = 1 public constructor(mechanism: Mechanism) { - super(mechanism); + super(mechanism) - this._simLayer = World.SimulationSystem.GetSimulationLayer(mechanism)!; + this._simLayer = World.SimulationSystem.GetSimulationLayer(mechanism)! - if (!this._simLayer) { - console.log("SimulationLayer is undefined"); - return; + if (!this._simLayer) { + console.log("SimulationLayer is undefined") + return } - this.ConfigureArcadeDriveBehavior(); - this.ConfigureArmBehaviors(); - this.ConfigureElevatorBehaviors(); + this.ConfigureArcadeDriveBehavior() + this.ConfigureArmBehaviors() + this.ConfigureElevatorBehaviors() } - public Enable(): void { } + public Enable(): void {} - public Update(deltaT: number): void { - this._behaviors.forEach((b) => b.Update(deltaT)); + public Update(deltaT: number): void { + this._behaviors.forEach(b => b.Update(deltaT)) } public Disable(): void { @@ -52,59 +51,76 @@ class SynthesisBrain extends Brain { // Creates an instance of ArcadeDriveBehavior and automatically configures it public ConfigureArcadeDriveBehavior() { - const wheelDrivers: WheelDriver[] = this._simLayer.drivers.filter((driver) => driver instanceof WheelDriver) as WheelDriver[]; - const wheelStimuli: WheelRotationStimulus[] = this._simLayer.stimuli.filter((stimulus) => stimulus instanceof WheelRotationStimulus) as WheelRotationStimulus[]; + const wheelDrivers: WheelDriver[] = this._simLayer.drivers.filter( + driver => driver instanceof WheelDriver + ) as WheelDriver[] + const wheelStimuli: WheelRotationStimulus[] = this._simLayer.stimuli.filter( + stimulus => stimulus instanceof WheelRotationStimulus + ) as WheelRotationStimulus[] // Two body constraints are part of wheels and are used to determine which way a wheel is facing - const fixedConstraints: Jolt.TwoBodyConstraint[] = this._mechanism.constraints.filter((mechConstraint) => mechConstraint.constraint instanceof JOLT.TwoBodyConstraint).map((mechConstraint) => mechConstraint.constraint as Jolt.TwoBodyConstraint); + const fixedConstraints: Jolt.TwoBodyConstraint[] = this._mechanism.constraints + .filter(mechConstraint => mechConstraint.constraint instanceof JOLT.TwoBodyConstraint) + .map(mechConstraint => mechConstraint.constraint as Jolt.TwoBodyConstraint) - const leftWheels: WheelDriver[] = []; - const leftStimuli: WheelRotationStimulus[] = []; + const leftWheels: WheelDriver[] = [] + const leftStimuli: WheelRotationStimulus[] = [] - const rightWheels: WheelDriver[] = []; - const rightStimuli: WheelRotationStimulus[] = []; + const rightWheels: WheelDriver[] = [] + const rightStimuli: WheelRotationStimulus[] = [] // Determines which wheels and stimuli belong to which side of the robot for (let i = 0; i < wheelDrivers.length; i++) { - const wheelPos = fixedConstraints[i].GetConstraintToBody1Matrix().GetTranslation(); + const wheelPos = fixedConstraints[i].GetConstraintToBody1Matrix().GetTranslation() - const robotCOM = World.PhysicsSystem.GetBody(this._mechanism.constraints[0].childBody).GetCenterOfMassPosition() as Jolt.Vec3; - const rightVector = new JOLT.Vec3(1, 0, 0); + const robotCOM = World.PhysicsSystem.GetBody( + this._mechanism.constraints[0].childBody + ).GetCenterOfMassPosition() as Jolt.Vec3 + const rightVector = new JOLT.Vec3(1, 0, 0) const dotProduct = rightVector.Dot(wheelPos.Sub(robotCOM)) if (dotProduct < 0) { - rightWheels.push(wheelDrivers[i]); - rightStimuli.push(wheelStimuli[i]); - } - else { - leftWheels.push(wheelDrivers[i]); - leftStimuli.push(wheelStimuli[i]); + rightWheels.push(wheelDrivers[i]) + rightStimuli.push(wheelStimuli[i]) + } else { + leftWheels.push(wheelDrivers[i]) + leftStimuli.push(wheelStimuli[i]) } } - this._behaviors.push(new ArcadeDriveBehavior(leftWheels, rightWheels, leftStimuli, rightStimuli)); + this._behaviors.push(new ArcadeDriveBehavior(leftWheels, rightWheels, leftStimuli, rightStimuli)) } // Creates instances of ArmBehavior and automatically configures them public ConfigureArmBehaviors() { - const hingeDrivers: HingeDriver[] = this._simLayer.drivers.filter((driver) => driver instanceof HingeDriver) as HingeDriver[]; - const hingeStimuli: HingeStimulus[] = this._simLayer.stimuli.filter((stimulus) => stimulus instanceof HingeStimulus) as HingeStimulus[]; + const hingeDrivers: HingeDriver[] = this._simLayer.drivers.filter( + driver => driver instanceof HingeDriver + ) as HingeDriver[] + const hingeStimuli: HingeStimulus[] = this._simLayer.stimuli.filter( + stimulus => stimulus instanceof HingeStimulus + ) as HingeStimulus[] for (let i = 0; i < hingeDrivers.length; i++) { - this._behaviors.push(new GenericArmBehavior(hingeDrivers[i], hingeStimuli[i], this._currentJointIndex)); - this._currentJointIndex++; + this._behaviors.push(new GenericArmBehavior(hingeDrivers[i], hingeStimuli[i], this._currentJointIndex)) + this._currentJointIndex++ } } // Creates instances of ElevatorBehavior and automatically configures them public ConfigureElevatorBehaviors() { - const sliderDrivers: SliderDriver[] = this._simLayer.drivers.filter((driver) => driver instanceof SliderDriver) as SliderDriver[]; - const sliderStimuli: SliderStimulus[] = this._simLayer.stimuli.filter((stimulus) => stimulus instanceof SliderStimulus) as SliderStimulus[]; + const sliderDrivers: SliderDriver[] = this._simLayer.drivers.filter( + driver => driver instanceof SliderDriver + ) as SliderDriver[] + const sliderStimuli: SliderStimulus[] = this._simLayer.stimuli.filter( + stimulus => stimulus instanceof SliderStimulus + ) as SliderStimulus[] for (let i = 0; i < sliderDrivers.length; i++) { - this._behaviors.push(new GenericElevatorBehavior(sliderDrivers[i], sliderStimuli[i], this._currentJointIndex)); - this._currentJointIndex++; + this._behaviors.push( + new GenericElevatorBehavior(sliderDrivers[i], sliderStimuli[i], this._currentJointIndex) + ) + this._currentJointIndex++ } } } diff --git a/fission/src/test/PhysicsSystem.test.ts b/fission/src/test/PhysicsSystem.test.ts index 692989e8e9..ebc5233d8d 100644 --- a/fission/src/test/PhysicsSystem.test.ts +++ b/fission/src/test/PhysicsSystem.test.ts @@ -2,6 +2,8 @@ import { test, expect, describe, assert } from "vitest" import PhysicsSystem, { LayerReserve } from "../systems/physics/PhysicsSystem" import { LoadMirabufLocal } from "@/mirabuf/MirabufLoader" import MirabufParser from "@/mirabuf/MirabufParser" +import * as THREE from "three" +import Jolt from "@barclah/jolt-physics" describe("Physics Sansity Checks", () => { test("Convex Hull Shape (Cube)", () => { @@ -48,6 +50,31 @@ describe("Physics Sansity Checks", () => { }) }) +describe("GodMode", () => { + test("Basic", () => { + const system = new PhysicsSystem() + const box = system.CreateBox(new THREE.Vector3(1, 1, 1), 1, new THREE.Vector3(0, 0, 0), undefined) + const [ghostObject, ghostConstraint] = system.CreateGodModeBody(box.GetID(), box.GetPosition() as Jolt.Vec3) + + assert(system.GetBody(ghostObject.GetID()) != undefined) + assert(system.GetBody(box.GetID()) != undefined) + assert(ghostConstraint != undefined) + // Check constraint after running for a few seconds + // TODO: Make sure this is the correct way to do this + // TODO: Figure out how to make it use substeps to check instead + for (let i = 0; i < 30; i++) { + // TODO: Change this once this function actually uses deltaT + system.Update(i) + } + + assert(system.GetBody(ghostObject.GetID()) != undefined) + assert(system.GetBody(box.GetID()) != undefined) + assert(ghostConstraint != undefined) + + //system.Destroy() + }) +}) + describe("Mirabuf Physics Loading", () => { test("Body Loading (Dozer)", () => { const assembly = LoadMirabufLocal("./public/Downloadables/Mira/Robots/Dozer_v9.mira") diff --git a/fission/src/ui/components/MainHUD.tsx b/fission/src/ui/components/MainHUD.tsx index 427381549b..aa0d681cec 100644 --- a/fission/src/ui/components/MainHUD.tsx +++ b/fission/src/ui/components/MainHUD.tsx @@ -14,7 +14,11 @@ import { ToastType, useToastContext } from "@/ui/ToastContext" import { Random } from "@/util/Random" import APS, { APS_USER_INFO_UPDATE_EVENT } from "@/aps/APS" import { UserIcon } from "./UserIcon" +import World from "@/systems/World" +import JOLT from "@/util/loading/JoltSyncLoader" +import MirabufSceneObject from "@/mirabuf/MirabufSceneObject" import { Button } from "@mui/base/Button" +import Jolt from "@barclah/jolt-physics" type ButtonProps = { value: string @@ -116,6 +120,7 @@ const MainHUD: React.FC = () => { icon={} onClick={() => openModal("import-mirabuf")} /> + } onClick={TestGodMode} />
{ ) } +async function TestGodMode() { + const robot: MirabufSceneObject = [...World.SceneRenderer.sceneObjects.entries()] + .filter(x => { + const y = x[1] instanceof MirabufSceneObject + return y + }) + .map(x => x[1])[0] as MirabufSceneObject + const rootNodeId = robot.GetRootNodeId() + if (rootNodeId == undefined) { + console.error("Robot root node not found for god mode") + return + } + const robotPosition = World.PhysicsSystem.GetBody(rootNodeId).GetPosition() + const [ghostBody, _ghostConstraint] = World.PhysicsSystem.CreateGodModeBody(rootNodeId, robotPosition as Jolt.Vec3) + + // Move ghostBody to demonstrate godMode movement + await new Promise(f => setTimeout(f, 1000)) + World.PhysicsSystem.SetBodyPosition( + ghostBody.GetID(), + new JOLT.Vec3(robotPosition.GetX(), robotPosition.GetY() + 2, robotPosition.GetZ()) + ) + await new Promise(f => setTimeout(f, 1000)) + World.PhysicsSystem.SetBodyPosition(ghostBody.GetID(), new JOLT.Vec3(2, 2, 2)) +} + export default MainHUD diff --git a/fission/src/ui/modals/configuring/ChangeInputsModal.tsx b/fission/src/ui/modals/configuring/ChangeInputsModal.tsx index 80673645a3..9b3a1bffaf 100644 --- a/fission/src/ui/modals/configuring/ChangeInputsModal.tsx +++ b/fission/src/ui/modals/configuring/ChangeInputsModal.tsx @@ -16,42 +16,39 @@ const transformKeyName = (control: Input) => { if (control.modifiers.alt) prefix += "Alt + " } - return prefix + keyCodeToCharacter(control.keyCode); + return prefix + keyCodeToCharacter(control.keyCode) } - + // Converts camelCase to Title Case for the inputs modal const toTitleCase = (camelCase: string) => { -const result = camelCase.replace(/([A-Z])/g, " $1"); -const finalResult = result.charAt(0).toUpperCase() + result.slice(1); - return finalResult; + const result = camelCase.replace(/([A-Z])/g, " $1") + const finalResult = result.charAt(0).toUpperCase() + result.slice(1) + return finalResult } const codeToCharacterMap: { [code: string]: string } = { - "Slash": "/", - "Comma": ",", - "Period": ".", - "BracketLeft": "{", - "BracketRight": "}", - "BackQuote": "`", - "Minus": "-", - "Equal": "=", - "Backslash": "\\", //TODO - "Semicolon": ";", - "Quote": "\"" -}; + Slash: "/", + Comma: ",", + Period: ".", + BracketLeft: "{", + BracketRight: "}", + BackQuote: "`", + Minus: "-", + Equal: "=", + Backslash: "\\", //TODO + Semicolon: ";", + Quote: '"', +} // Converts a key code to displayable character (ex: KeyA -> "A") const keyCodeToCharacter = (code: string) => { - if (code.startsWith("Key")) - return code.charAt(3); + if (code.startsWith("Key")) return code.charAt(3) - if (code.startsWith("Digit")) - return code.charAt(5); + if (code.startsWith("Digit")) return code.charAt(5) - if (code in codeToCharacterMap) - return codeToCharacterMap[code]; + if (code in codeToCharacterMap) return codeToCharacterMap[code] - return code; + return code } const ChangeInputsModal: React.FC = ({ modalId }) => {