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