diff --git a/fission/src/Synthesis.tsx b/fission/src/Synthesis.tsx index d303e6c0b3..073246f140 100644 --- a/fission/src/Synthesis.tsx +++ b/fission/src/Synthesis.tsx @@ -49,6 +49,7 @@ import ScoringZonesPanel from "@/panels/configuring/scoring/ScoringZonesPanel" import ZoneConfigPanel from "@/panels/configuring/scoring/ZoneConfigPanel" import ScoreboardPanel from "@/panels/information/ScoreboardPanel" import DriverStationPanel from "@/panels/simulation/DriverStationPanel" +import PokerPanel from "@/panels/PokerPanel.tsx" import ManageAssembliesModal from "@/modals/spawning/ManageAssembliesModal.tsx" import World from "@/systems/World.ts" import { AddRobotsModal, AddFieldsModal, SpawningModal } from "@/modals/spawning/SpawningModals.tsx" @@ -56,7 +57,7 @@ import ImportLocalMirabufModal from "@/modals/mirabuf/ImportLocalMirabufModal.ts import APS from "./aps/APS.ts" import ImportMirabufPanel from "@/ui/panels/mirabuf/ImportMirabufPanel.tsx" import Skybox from "./ui/components/Skybox.tsx" -import PokerPanel from "@/panels/PokerPanel.tsx" +import ConfigureRobotModal from "./ui/modals/configuring/ConfigureRobotModal.tsx" const DEFAULT_MIRA_PATH = "/api/mira/Robots/Team 2471 (2018)_v7.mira" @@ -129,7 +130,7 @@ function Synthesis() { } mainLoop() - World.SceneRenderer.updateSkyboxColors(defaultTheme) + World.SceneRenderer.UpdateSkyboxColors(defaultTheme) // Cleanup return () => { @@ -213,6 +214,7 @@ const initialModals = [ , , , + , ] const initialPanels: ReactElement[] = [ @@ -220,8 +222,18 @@ const initialPanels: ReactElement[] = [ , , , - , - , + , + , , , , diff --git a/fission/src/mirabuf/EjectableSceneObject.ts b/fission/src/mirabuf/EjectableSceneObject.ts new file mode 100644 index 0000000000..53fbdd7a10 --- /dev/null +++ b/fission/src/mirabuf/EjectableSceneObject.ts @@ -0,0 +1,112 @@ +import SceneObject from "@/systems/scene/SceneObject" +import MirabufSceneObject from "./MirabufSceneObject" +import Jolt from "@barclah/jolt-physics" +import World from "@/systems/World" +import { + Array_ThreeMatrix4, + JoltMat44_ThreeMatrix4, + JoltQuat_ThreeQuaternion, + ThreeQuaternion_JoltQuat, + ThreeVector3_JoltVec3, +} from "@/util/TypeConversions" +import * as THREE from "three" + +class EjectableSceneObject extends SceneObject { + private _parentAssembly: MirabufSceneObject + private _gamePieceBodyId?: Jolt.BodyID + + private _parentBodyId?: Jolt.BodyID + private _deltaTransformation?: THREE.Matrix4 + private _ejectVelocity?: number + + public get gamePieceBodyId() { + return this._gamePieceBodyId + } + + public constructor(parentAssembly: MirabufSceneObject, gamePieceBody: Jolt.BodyID) { + super() + + console.debug("Trying to create ejectable...") + + this._parentAssembly = parentAssembly + this._gamePieceBodyId = gamePieceBody + } + + public Setup(): void { + if (this._parentAssembly.ejectorPreferences && this._gamePieceBodyId) { + this._parentBodyId = this._parentAssembly.mechanism.nodeToBody.get( + this._parentAssembly.ejectorPreferences.parentNode ?? this._parentAssembly.rootNodeId + ) + + this._deltaTransformation = Array_ThreeMatrix4(this._parentAssembly.ejectorPreferences.deltaTransformation) + this._ejectVelocity = this._parentAssembly.ejectorPreferences.ejectorVelocity + + World.PhysicsSystem.DisablePhysicsForBody(this._gamePieceBodyId) + + console.debug("Ejectable created successfully!") + } + } + + public Update(): void { + if (this._parentBodyId && this._deltaTransformation && this._gamePieceBodyId) { + if (!World.PhysicsSystem.IsBodyAdded(this._gamePieceBodyId)) { + this._gamePieceBodyId = undefined + return + } + + // I had a think and free wrote this matrix math on a whim. It worked first try and I honestly can't quite remember how it works... -Hunter + const gpBody = World.PhysicsSystem.GetBody(this._gamePieceBodyId) + const posToCOM = JoltMat44_ThreeMatrix4(gpBody.GetCenterOfMassTransform()).premultiply( + JoltMat44_ThreeMatrix4(gpBody.GetWorldTransform()).invert() + ) + + const body = World.PhysicsSystem.GetBody(this._parentBodyId) + const bodyTransform = posToCOM + .invert() + .premultiply( + this._deltaTransformation.clone().premultiply(JoltMat44_ThreeMatrix4(body.GetWorldTransform())) + ) + const position = new THREE.Vector3(0, 0, 0) + const rotation = new THREE.Quaternion(0, 0, 0, 1) + bodyTransform.decompose(position, rotation, new THREE.Vector3(1, 1, 1)) + + World.PhysicsSystem.SetBodyPosition(this._gamePieceBodyId, ThreeVector3_JoltVec3(position), false) + World.PhysicsSystem.SetBodyRotation(this._gamePieceBodyId, ThreeQuaternion_JoltQuat(rotation), false) + } + } + + public Eject() { + if (!this._parentBodyId || !this._ejectVelocity || !this._gamePieceBodyId) { + return + } + + if (!World.PhysicsSystem.IsBodyAdded(this._gamePieceBodyId)) { + this._gamePieceBodyId = undefined + return + } + + const parentBody = World.PhysicsSystem.GetBody(this._parentBodyId) + const gpBody = World.PhysicsSystem.GetBody(this._gamePieceBodyId) + const ejectDir = new THREE.Vector3(0, 0, 1) + .applyQuaternion(JoltQuat_ThreeQuaternion(gpBody.GetRotation())) + .normalize() + + World.PhysicsSystem.EnablePhysicsForBody(this._gamePieceBodyId) + gpBody.SetLinearVelocity( + parentBody.GetLinearVelocity().Add(ThreeVector3_JoltVec3(ejectDir.multiplyScalar(this._ejectVelocity))) + ) + gpBody.SetAngularVelocity(parentBody.GetAngularVelocity()) + + this._parentBodyId = undefined + } + + public Dispose(): void { + console.debug("Destroying ejectable") + + if (this._gamePieceBodyId) { + World.PhysicsSystem.EnablePhysicsForBody(this._gamePieceBodyId) + } + } +} + +export default EjectableSceneObject diff --git a/fission/src/mirabuf/IntakeSensorSceneObject.ts b/fission/src/mirabuf/IntakeSensorSceneObject.ts new file mode 100644 index 0000000000..260523f201 --- /dev/null +++ b/fission/src/mirabuf/IntakeSensorSceneObject.ts @@ -0,0 +1,104 @@ +import SceneObject from "@/systems/scene/SceneObject" +import MirabufSceneObject, { RigidNodeAssociate } from "./MirabufSceneObject" +import Jolt from "@barclah/jolt-physics" +import * as THREE from "three" +import World from "@/systems/World" +import JOLT from "@/util/loading/JoltSyncLoader" +import { + Array_ThreeMatrix4, + JoltMat44_ThreeMatrix4, + ThreeQuaternion_JoltQuat, + ThreeVector3_JoltVec3, +} from "@/util/TypeConversions" + +class IntakeSensorSceneObject extends SceneObject { + private _parentAssembly: MirabufSceneObject + private _parentBodyId?: Jolt.BodyID + private _deltaTransformation?: THREE.Matrix4 + + private _joltBodyId?: Jolt.BodyID + private _mesh?: THREE.Mesh + + public constructor(parentAssembly: MirabufSceneObject) { + super() + + console.debug("Trying to create intake sensor...") + + this._parentAssembly = parentAssembly + } + + public Setup(): void { + if (this._parentAssembly.intakePreferences) { + this._parentBodyId = this._parentAssembly.mechanism.nodeToBody.get( + this._parentAssembly.intakePreferences.parentNode ?? this._parentAssembly.rootNodeId + ) + + this._deltaTransformation = Array_ThreeMatrix4(this._parentAssembly.intakePreferences.deltaTransformation) + + this._joltBodyId = World.PhysicsSystem.CreateSensor( + new JOLT.SphereShapeSettings(this._parentAssembly.intakePreferences.zoneDiameter / 2.0) + ) + if (!this._joltBodyId) { + console.error("Failed to create intake. No Jolt Body") + return + } + + this._mesh = World.SceneRenderer.CreateSphere( + this._parentAssembly.intakePreferences.zoneDiameter / 2.0, + World.SceneRenderer.CreateToonMaterial(0x5eeb67) + ) + World.SceneRenderer.scene.add(this._mesh) + + console.debug("Intake sensor created successfully!") + } + } + + public Update(): void { + if (this._joltBodyId && this._parentBodyId && this._deltaTransformation) { + const parentBody = World.PhysicsSystem.GetBody(this._parentBodyId) + const bodyTransform = this._deltaTransformation + .clone() + .premultiply(JoltMat44_ThreeMatrix4(parentBody.GetWorldTransform())) + const position = new THREE.Vector3(0, 0, 0) + const rotation = new THREE.Quaternion(0, 0, 0, 1) + bodyTransform.decompose(position, rotation, new THREE.Vector3(1, 1, 1)) + + World.PhysicsSystem.SetBodyPosition(this._joltBodyId, ThreeVector3_JoltVec3(position)) + World.PhysicsSystem.SetBodyRotation(this._joltBodyId, ThreeQuaternion_JoltQuat(rotation)) + + if (this._mesh) { + this._mesh.position.setFromMatrixPosition(bodyTransform) + this._mesh.rotation.setFromRotationMatrix(bodyTransform) + } + + if (!World.PhysicsSystem.isPaused) { + // TEMPORARY GAME PIECE DETECTION + const hitRes = World.PhysicsSystem.RayCast(ThreeVector3_JoltVec3(position), new JOLT.Vec3(0, 0, 3)) + if (hitRes) { + const gpAssoc = World.PhysicsSystem.GetBodyAssociation(hitRes.data.mBodyID) + // This works, however the check for game piece is doing two checks. + if (gpAssoc?.isGamePiece) { + console.debug("Found game piece!") + this._parentAssembly.SetEjectable(hitRes.data.mBodyID, false) + } + } + } + } + } + + public Dispose(): void { + console.debug("Destroying intake sensor") + + if (this._joltBodyId) { + World.PhysicsSystem.DestroyBodyIds(this._joltBodyId) + + if (this._mesh) { + this._mesh.geometry.dispose() + ;(this._mesh.material as THREE.Material).dispose() + World.SceneRenderer.scene.remove(this._mesh) + } + } + } +} + +export default IntakeSensorSceneObject diff --git a/fission/src/mirabuf/MirabufParser.ts b/fission/src/mirabuf/MirabufParser.ts index a9a814e85a..8565a00335 100644 --- a/fission/src/mirabuf/MirabufParser.ts +++ b/fission/src/mirabuf/MirabufParser.ts @@ -2,6 +2,8 @@ import * as THREE from "three" import { mirabuf } from "@/proto/mirabuf" import { MirabufTransform_ThreeMatrix4 } from "@/util/TypeConversions" +export type RigidNodeId = string + export enum ParseErrorSeverity { Unimportable = 10, LikelyIssues = 6, @@ -60,8 +62,8 @@ class MirabufParser { public get groundedNode() { return this._groundedNode ? new RigidNodeReadOnly(this._groundedNode) : undefined } - public get rigidNodes(): Array { - return this._rigidNodes.map(x => new RigidNodeReadOnly(x)) + public get rigidNodes(): Map { + return new Map(this._rigidNodes.map(x => [x.id, new RigidNodeReadOnly(x)])) } public get directedGraph() { return this._directedGraph @@ -121,6 +123,7 @@ class MirabufParser { const instNode = this.BinarySearchDesignTree(inst.info!.GUID!) if (instNode) { const gpRn = this.NewRigidNode(GAMEPIECE_SUFFIX) + gpRn.isGamePiece = true this.MovePartToRigidNode(instNode!.value!, gpRn) instNode.children && traverseTree(instNode.children, x => this.MovePartToRigidNode(x.value!, gpRn)) @@ -400,21 +403,23 @@ class MirabufParser { */ class RigidNode { public isRoot: boolean - public id: string + public id: RigidNodeId public parts: Set = new Set() public isDynamic: boolean + public isGamePiece: boolean - public constructor(id: string, isDynamic?: boolean) { + public constructor(id: RigidNodeId, isDynamic?: boolean, isGamePiece?: boolean) { this.id = id this.isDynamic = isDynamic ?? true this.isRoot = false + this.isGamePiece = isGamePiece ?? false } } export class RigidNodeReadOnly { private _original: RigidNode - public get id(): string { + public get id(): RigidNodeId { return this._original.id } @@ -430,6 +435,10 @@ export class RigidNodeReadOnly { return this._original.isRoot } + public get isGamePiece(): boolean { + return this._original.isGamePiece + } + public constructor(original: RigidNode) { this._original = original } diff --git a/fission/src/mirabuf/MirabufSceneObject.ts b/fission/src/mirabuf/MirabufSceneObject.ts index a7d05a7bb8..3366381742 100644 --- a/fission/src/mirabuf/MirabufSceneObject.ts +++ b/fission/src/mirabuf/MirabufSceneObject.ts @@ -1,17 +1,22 @@ import { mirabuf } from "@/proto/mirabuf" import SceneObject from "../systems/scene/SceneObject" import MirabufInstance from "./MirabufInstance" -import MirabufParser, { ParseErrorSeverity } from "./MirabufParser" +import MirabufParser, { ParseErrorSeverity, RigidNodeId, RigidNodeReadOnly } from "./MirabufParser" import World from "@/systems/World" import Jolt from "@barclah/jolt-physics" import { JoltMat44_ThreeMatrix4 } from "@/util/TypeConversions" import * as THREE from "three" import JOLT from "@/util/loading/JoltSyncLoader" -import { LayerReserve } from "@/systems/physics/PhysicsSystem" +import { BodyAssociate, LayerReserve } from "@/systems/physics/PhysicsSystem" import Mechanism from "@/systems/physics/Mechanism" import SynthesisBrain from "@/systems/simulation/synthesis_brain/SynthesisBrain" import InputSystem from "@/systems/input/InputSystem" import TransformGizmos from "@/ui/components/TransformGizmos" +import { EjectorPreferences, IntakePreferences } from "@/systems/preferences/PreferenceTypes" +import PreferencesSystem from "@/systems/preferences/PreferencesSystem" +import { MiraType } from "./MirabufLoader" +import IntakeSensorSceneObject from "./IntakeSensorSceneObject" +import EjectableSceneObject from "./EjectableSceneObject" const DEBUG_BODIES = false @@ -27,11 +32,17 @@ class MirabufSceneObject extends SceneObject { private _brain: SynthesisBrain | undefined private _debugBodies: Map | null - private _physicsLayerReserve: LayerReserve | undefined = undefined + private _physicsLayerReserve: LayerReserve | undefined - private _transformGizmos: TransformGizmos | undefined = undefined + private _transformGizmos: TransformGizmos | undefined private _deleteGizmoOnEscape: boolean = true + private _intakePreferences: IntakePreferences | undefined + private _ejectorPreferences: EjectorPreferences | undefined + + private _intakeSensor?: IntakeSensorSceneObject + private _ejectable?: EjectableSceneObject + get mirabufInstance() { return this._mirabufInstance } @@ -40,6 +51,30 @@ class MirabufSceneObject extends SceneObject { return this._mechanism } + get assemblyName() { + return this._assemblyName + } + + get intakePreferences() { + return this._intakePreferences + } + + get ejectorPreferences() { + return this._ejectorPreferences + } + + public get activeEjectable(): Jolt.BodyID | undefined { + return this._ejectable?.gamePieceBodyId + } + + public get miraType(): MiraType { + return this._mirabufInstance.parser.assembly.dynamic ? MiraType.ROBOT : MiraType.FIELD + } + + public get rootNodeId(): string { + return this._mirabufInstance.parser.rootNode + } + public constructor(mirabufInstance: MirabufInstance, assemblyName: string) { super() @@ -54,6 +89,8 @@ class MirabufSceneObject extends SceneObject { this._debugBodies = null this.EnableTransformControls() // adding transform gizmo to mirabuf object on its creation + + this.getPreferences() } public Setup(): void { @@ -77,14 +114,31 @@ class MirabufSceneObject extends SceneObject { }) } + const rigidNodes = this._mirabufInstance.parser.rigidNodes + this._mechanism.nodeToBody.forEach((bodyId, rigidNodeId) => { + const rigidNode = rigidNodes.get(rigidNodeId) + if (!rigidNode) { + console.warn("Found a RigidNodeId with no related RigidNode. Skipping for now...") + return + } + World.PhysicsSystem.SetBodyAssociation(new RigidNodeAssociate(this, rigidNode, bodyId)) + }) + // Simulation World.SimulationSystem.RegisterMechanism(this._mechanism) const simLayer = World.SimulationSystem.GetSimulationLayer(this._mechanism)! this._brain = new SynthesisBrain(this._mechanism, this._assemblyName) simLayer.SetBrain(this._brain) + + // Intake + this.UpdateIntakeSensor() } public Update(): void { + if (InputSystem.currentModifierState.ctrl && InputSystem.currentModifierState.shift && this._ejectable) { + this.Eject() + } + this._mirabufInstance.parser.rigidNodes.forEach(rn => { if (!this._mirabufInstance.meshes.size) return // if this.dispose() has been ran then return const body = World.PhysicsSystem.GetBody(this._mechanism.GetBodyByNodeId(rn.id)!) @@ -152,6 +206,20 @@ class MirabufSceneObject extends SceneObject { } public Dispose(): void { + if (this._intakeSensor) { + World.SceneRenderer.RemoveSceneObject(this._intakeSensor.id) + this._intakeSensor = undefined + } + + if (this._ejectable) { + World.SceneRenderer.RemoveSceneObject(this._ejectable.id) + this._ejectable = undefined + } + + this._mechanism.nodeToBody.forEach(bodyId => { + World.PhysicsSystem.RemoveBodyAssocation(bodyId) + }) + World.SimulationSystem.UnregisterMechanism(this._mechanism) World.PhysicsSystem.DestroyMechanism(this._mechanism) this._mirabufInstance.Dispose(World.SceneRenderer.scene) @@ -168,8 +236,14 @@ class MirabufSceneObject extends SceneObject { this._brain?.clearControls() } - public GetRootNodeId(): Jolt.BodyID | undefined { - return this._mechanism.nodeToBody.get(this._mechanism.rootBody) + public Eject() { + if (!this._ejectable) { + return + } + + this._ejectable.Eject() + World.SceneRenderer.RemoveSceneObject(this._ejectable.id) + this._ejectable = undefined } private CreateMeshForShape(shape: Jolt.Shape): THREE.Mesh { @@ -205,6 +279,38 @@ class MirabufSceneObject extends SceneObject { return mesh } + public UpdateIntakeSensor() { + if (this._intakeSensor) { + World.SceneRenderer.RemoveSceneObject(this._intakeSensor.id) + this._intakeSensor = undefined + } + + // Do we have an intake, and is it something other than the default. Config will default to root node at least. + if (this._intakePreferences && this._intakePreferences.parentNode) { + this._intakeSensor = new IntakeSensorSceneObject(this) + World.SceneRenderer.RegisterSceneObject(this._intakeSensor) + } + } + + public SetEjectable(bodyId?: Jolt.BodyID, removeExisting: boolean = false): boolean { + if (this._ejectable) { + if (!removeExisting) { + return false + } + + World.SceneRenderer.RemoveSceneObject(this._ejectable.id) + this._ejectable = undefined + } + + if (!this._ejectorPreferences || !this._ejectorPreferences.parentNode || !bodyId) { + return false + } + + this._ejectable = new EjectableSceneObject(this, bodyId) + World.SceneRenderer.RegisterSceneObject(this._ejectable) + return true + } + public EnableTransformControls(): void { this._transformGizmos = new TransformGizmos( new THREE.Mesh( @@ -213,11 +319,16 @@ class MirabufSceneObject extends SceneObject { ) ) this._transformGizmos.AddMeshToScene() - this._transformGizmos.CreateGizmo("translate") + this._transformGizmos.CreateGizmo("translate", 5.0) this.DisablePhysics() } + private getPreferences(): void { + this._intakePreferences = PreferencesSystem.getRobotPreferences(this.assemblyName)?.intake + this._ejectorPreferences = PreferencesSystem.getRobotPreferences(this.assemblyName)?.ejector + } + private EnablePhysics() { this._mirabufInstance.parser.rigidNodes.forEach(rn => { World.PhysicsSystem.EnablePhysicsForBody(this._mechanism.GetBodyByNodeId(rn.id)!) @@ -245,4 +356,26 @@ export async function CreateMirabuf(assembly: mirabuf.Assembly): Promise + public nodeToBody: Map public constraints: Array public stepListeners: Array public layerReserve: LayerReserve | undefined diff --git a/fission/src/systems/physics/PhysicsSystem.ts b/fission/src/systems/physics/PhysicsSystem.ts index dd0e7dc2cd..4736323d12 100644 --- a/fission/src/systems/physics/PhysicsSystem.ts +++ b/fission/src/systems/physics/PhysicsSystem.ts @@ -15,6 +15,8 @@ import MirabufParser, { GAMEPIECE_SUFFIX, GROUNDED_JOINT_ID, RigidNodeReadOnly } import WorldSystem from "../WorldSystem" import Mechanism from "./Mechanism" +export type JoltBodyIndexAndSequence = number + /** * Layers used for determining enabled/disabled collisions. */ @@ -62,6 +64,14 @@ class PhysicsSystem extends WorldSystem { private _bodies: Array private _constraints: Array + private _pauseCounter = 0 + + private _bodyAssociations: Map + + public get isPaused(): boolean { + return this._pauseCounter > 0 + } + /** * Creates a PhysicsSystem object. */ @@ -90,6 +100,58 @@ class PhysicsSystem extends WorldSystem { ) ground.SetFriction(FLOOR_FRICTION) this._joltBodyInterface.AddBody(ground.GetID(), JOLT.EActivation_Activate) + + this._bodyAssociations = new Map() + } + + /** + * Get association to a given Jolt Body. + * + * @param bodyId BodyID to check for association + * @returns Association for given Body + */ + public GetBodyAssociation(bodyId: Jolt.BodyID): BodyAssociate | undefined { + return this._bodyAssociations.get(bodyId.GetIndexAndSequenceNumber()) + } + + /** + * Sets assocation for a body + * + * @param assocation Assocation. See {@link BodyAssociate} + */ + public SetBodyAssociation(assocation: T) { + this._bodyAssociations.set(assocation.associatedBody, assocation) + } + + public RemoveBodyAssocation(bodyId: Jolt.BodyID) { + this._bodyAssociations.delete(bodyId.GetIndexAndSequenceNumber()) + } + + /** + * Holds a pause. + * + * The pause works off of a request counter. + */ + public HoldPause() { + this._pauseCounter++ + } + + /** + * Forces all holds on the pause to be released. + */ + public ForceUnpause() { + this._pauseCounter = 0 + } + + /** + * Releases a pause. + * + * The pause works off of a request counter. + */ + public ReleasePause() { + if (this._pauseCounter > 0) { + this._pauseCounter-- + } } /** @@ -98,6 +160,10 @@ class PhysicsSystem extends WorldSystem { * @param bodyId */ public DisablePhysicsForBody(bodyId: Jolt.BodyID) { + if (!this.IsBodyAdded(bodyId)) { + return + } + this._joltBodyInterface.DeactivateBody(bodyId) this.GetBody(bodyId).SetIsSensor(true) } @@ -108,10 +174,18 @@ class PhysicsSystem extends WorldSystem { * @param bodyId */ public EnablePhysicsForBody(bodyId: Jolt.BodyID) { + if (!this.IsBodyAdded(bodyId)) { + return + } + this._joltBodyInterface.ActivateBody(bodyId) this.GetBody(bodyId).SetIsSensor(false) } + public IsBodyAdded(bodyId: Jolt.BodyID) { + return this._joltBodyInterface.IsAdded(bodyId) + } + /** * TEMPORARY * Create a box. @@ -189,6 +263,13 @@ class PhysicsSystem extends WorldSystem { return body } + public AddBodyToSystem(bodyId: Jolt.BodyID, shouldActivate: boolean) { + this._joltBodyInterface.AddBody( + bodyId, + shouldActivate ? JOLT.EActivation_Activate : JOLT.EActivation_DontActivate + ) + } + /** * Utility function for creating convex hulls. Mostly used for Unit test validation. * @@ -546,7 +627,7 @@ class PhysicsSystem extends WorldSystem { const reservedLayer: number | undefined = layerReserve?.layer - filterNonPhysicsNodes(parser.rigidNodes, parser.assembly).forEach(rn => { + filterNonPhysicsNodes([...parser.rigidNodes.values()], parser.assembly).forEach(rn => { const compoundShapeSettings = new JOLT.StaticCompoundShapeSettings() let shapesAdded = 0 @@ -739,16 +820,19 @@ class PhysicsSystem extends WorldSystem { * @param dir Direction of the ray. Note: Length of dir specifies the maximum length it will check. * @returns Either the hit results of the closest object in the ray's path, or undefined if nothing was hit. */ - public RayCast(from: Jolt.Vec3, dir: Jolt.Vec3): RayCastHit | undefined { + public RayCast(from: Jolt.Vec3, dir: Jolt.Vec3, ...ignoreBodies: Jolt.BodyID[]): RayCastHit | undefined { const ray = new JOLT.RayCast(from, dir) const raySettings = new JOLT.RayCastSettings() + raySettings.mTreatConvexAsSolid = false const collector = new JOLT.CastRayClosestHitCollisionCollector() const bp_filter = new JOLT.BroadPhaseLayerFilter() const object_filter = new JOLT.ObjectLayerFilter() - const body_filter = new JOLT.BodyFilter() // We don't want to filter out any bodies + const body_filter = new JOLT.IgnoreMultipleBodiesFilter() const shape_filter = new JOLT.ShapeFilter() // We don't want to filter out any shapes + ignoreBodies.forEach(x => body_filter.IgnoreBody(x)) + this._joltPhysSystem .GetNarrowPhaseQuery() .CastRay(ray, raySettings, collector, bp_filter, object_filter, body_filter, shape_filter) @@ -815,6 +899,10 @@ class PhysicsSystem extends WorldSystem { } public Update(deltaT: number): void { + if (this._pauseCounter > 0) { + return + } + const diffDeltaT = deltaT - lastDeltaT lastDeltaT = lastDeltaT + Math.min(TIMESTEP_ADJUSTMENT, Math.max(-TIMESTEP_ADJUSTMENT, diffDeltaT)) @@ -874,6 +962,19 @@ class PhysicsSystem extends WorldSystem { return [ghostBody, constraint] } + public CreateSensor(shapeSettings: Jolt.ShapeSettings): Jolt.BodyID | undefined { + const shape = shapeSettings.Create() + if (shape.HasError()) { + console.error(`Failed to create sensor body\n${shape.GetError().c_str}`) + return undefined + } + const body = this.CreateBody(shape.Get(), undefined, undefined, undefined) + this._bodies.push(body.GetID()) + body.SetIsSensor(true) + this._joltBodyInterface.AddBody(body.GetID(), JOLT.EActivation_Activate) + return body.GetID() + } + /** * Exposes the SetPosition method on the _joltBodyInterface * Sets the position of the body @@ -881,12 +982,28 @@ class PhysicsSystem extends WorldSystem { * @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) + public SetBodyPosition(id: Jolt.BodyID, position: Jolt.Vec3, activate: boolean = true): void { + if (!this.IsBodyAdded(id)) { + return + } + + this._joltBodyInterface.SetPosition( + id, + position, + activate ? JOLT.EActivation_Activate : JOLT.EActivation_DontActivate + ) } - public SetBodyRotation(id: Jolt.BodyID, rotation: Jolt.Quat): void { - this._joltBodyInterface.SetRotation(id, rotation, JOLT.EActivation_Activate) + public SetBodyRotation(id: Jolt.BodyID, rotation: Jolt.Quat, activate: boolean = true): void { + if (!this.IsBodyAdded(id)) { + return + } + + this._joltBodyInterface.SetRotation( + id, + rotation, + activate ? JOLT.EActivation_Activate : JOLT.EActivation_DontActivate + ) } } @@ -1001,4 +1118,15 @@ export type RayCastHit = { ray: Jolt.RayCast } +/** + * An interface to create an association between a body and anything. + */ +export class BodyAssociate { + readonly associatedBody: JoltBodyIndexAndSequence + + public constructor(bodyId: Jolt.BodyID) { + this.associatedBody = bodyId.GetIndexAndSequenceNumber() + } +} + export default PhysicsSystem diff --git a/fission/src/systems/preferences/PreferenceTypes.ts b/fission/src/systems/preferences/PreferenceTypes.ts index 03ed82f0db..24223fbaaa 100644 --- a/fission/src/systems/preferences/PreferenceTypes.ts +++ b/fission/src/systems/preferences/PreferenceTypes.ts @@ -26,13 +26,15 @@ export const DefaultGlobalPreferences: { [key: string]: unknown } = { } export type IntakePreferences = { - location: Vector3Tuple - diameter: number + deltaTransformation: number[] + zoneDiameter: number + parentNode: string | undefined } export type EjectorPreferences = { - location: Vector3Tuple + deltaTransformation: number[] ejectorVelocity: number + parentNode: string | undefined } export type RobotPreferences = { @@ -63,8 +65,16 @@ export type FieldPreferences = { export function DefaultRobotPreferences(): RobotPreferences { return { inputsSchemes: [], - intake: { location: [0, 0, 0], diameter: 1 }, - ejector: { location: [0, 0, 0], ejectorVelocity: 1 }, + intake: { + deltaTransformation: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], + zoneDiameter: 0.5, + parentNode: undefined, + }, + ejector: { + deltaTransformation: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], + ejectorVelocity: 1, + parentNode: undefined, + }, } } diff --git a/fission/src/systems/scene/SceneRenderer.ts b/fission/src/systems/scene/SceneRenderer.ts index efc9c925e1..2617401501 100644 --- a/fission/src/systems/scene/SceneRenderer.ts +++ b/fission/src/systems/scene/SceneRenderer.ts @@ -1,7 +1,8 @@ import * as THREE from "three" +import SceneObject from "./SceneObject" +import WorldSystem from "../WorldSystem" + import { TransformControls } from "three/examples/jsm/controls/TransformControls.js" -import SceneObject from "@/systems/scene/SceneObject" -import WorldSystem from "@/systems/WorldSystem" import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js" import { EdgeDetectionMode, EffectComposer, EffectPass, RenderPass, SMAAEffect } from "postprocessing" @@ -218,12 +219,12 @@ class SceneRenderer extends WorldSystem { return screenSpace.unproject(this.mainCamera) } - /* + /** * Updates the skybox colors based on the current theme * @param currentTheme: current theme from ThemeContext.useTheme() */ - public updateSkyboxColors(currentTheme: Theme) { + public UpdateSkyboxColors(currentTheme: Theme) { if (!this._skybox) return if (this._skybox.material instanceof THREE.ShaderMaterial) { this._skybox.material.uniforms.rColor.value = currentTheme["Background"]["color"]["r"] @@ -246,9 +247,7 @@ class SceneRenderer extends WorldSystem { transformControl.attach(obj) // allowing the transform gizmos to rotate with the object - if (mode === "translate") { - transformControl.space = "local" - } + transformControl.space = "local" transformControl.addEventListener( "dragging-changed", diff --git a/fission/src/test/MirabufParser.test.ts b/fission/src/test/MirabufParser.test.ts index e96f35cff3..fbcf93ac64 100644 --- a/fission/src/test/MirabufParser.test.ts +++ b/fission/src/test/MirabufParser.test.ts @@ -11,7 +11,7 @@ describe("Mirabuf Parser Tests", () => { ).then(x => MirabufCachingService.Get(x!.id, MiraType.ROBOT)) const t = new MirabufParser(spikeMira!) - const rn = t.rigidNodes + const rn = [...t.rigidNodes.values()] expect(filterNonPhysicsNodes(rn, spikeMira!).length).toBe(7) }) @@ -23,7 +23,7 @@ describe("Mirabuf Parser Tests", () => { ).then(x => MirabufCachingService.Get(x!.id, MiraType.FIELD)) const t = new MirabufParser(field!) - expect(filterNonPhysicsNodes(t.rigidNodes, field!).length).toBe(34) + expect(filterNonPhysicsNodes([...t.rigidNodes.values()], field!).length).toBe(34) }) test("Generate Rigid Nodes (Team 2471 (2018)_v7.mira)", async () => { @@ -33,7 +33,7 @@ describe("Mirabuf Parser Tests", () => { ).then(x => MirabufCachingService.Get(x!.id, MiraType.ROBOT)) const t = new MirabufParser(mm!) - expect(filterNonPhysicsNodes(t.rigidNodes, mm!).length).toBe(10) + expect(filterNonPhysicsNodes([...t.rigidNodes.values()], mm!).length).toBe(10) }) }) diff --git a/fission/src/test/util/TypeConversions.test.ts b/fission/src/test/util/TypeConversions.test.ts index 7988c2905c..d18eff3fb5 100644 --- a/fission/src/test/util/TypeConversions.test.ts +++ b/fission/src/test/util/TypeConversions.test.ts @@ -1,9 +1,11 @@ import { test, expect, describe } from "vitest" import * as THREE from "three" import { + Array_ThreeMatrix4, JoltMat44_ThreeMatrix4, MirabufTransform_ThreeMatrix4, ThreeEuler_JoltQuat, + ThreeMatrix4_Array, ThreeMatrix4_JoltMat44, ThreeQuaternion_JoltQuat, ThreeVector3_JoltVec3, @@ -147,21 +149,19 @@ describe("Three to Jolt Conversions", async () => { }) }) -// function matToString(mat: THREE.Matrix4) { -// const arr = mat.toArray(); -// return `[\n${arr[0].toFixed(4)}, ${arr[4].toFixed(4)}, ${arr[8].toFixed(4)}, ${arr[12].toFixed(4)},\n` -// + `${arr[1].toFixed(4)}, ${arr[5].toFixed(4)}, ${arr[9].toFixed(4)}, ${arr[13].toFixed(4)},\n` -// + `${arr[2].toFixed(4)}, ${arr[6].toFixed(4)}, ${arr[10].toFixed(4)}, ${arr[14].toFixed(4)},\n` -// + `${arr[3].toFixed(4)}, ${arr[7].toFixed(4)}, ${arr[11].toFixed(4)}, ${arr[15].toFixed(4)},\n]` -// } - -// function miraMatToString(mat: mirabuf.ITransform) { -// const arr = mat.spatialMatrix!; -// return `[\n${arr[0].toFixed(4)}, ${arr[1].toFixed(4)}, ${arr[2].toFixed(4)}, ${arr[3].toFixed(4)},\n` -// + `${arr[4].toFixed(4)}, ${arr[5].toFixed(4)}, ${arr[6].toFixed(4)}, ${arr[7].toFixed(4)},\n` -// + `${arr[8].toFixed(4)}, ${arr[9].toFixed(4)}, ${arr[10].toFixed(4)}, ${arr[11].toFixed(4)},\n` -// + `${arr[12].toFixed(4)}, ${arr[13].toFixed(4)}, ${arr[14].toFixed(4)}, ${arr[15].toFixed(4)},\n]` -// } +describe("Three Storage Conversion", () => { + test("Array -> THREE.Matrix4 -> Array", () => { + const originalArr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + + const threeMat = Array_ThreeMatrix4(originalArr) + const arr = ThreeMatrix4_Array(threeMat) + + expect(arr.length).toBe(originalArr.length) + for (let i = 0; i < arr.length; ++i) { + expect(arr[i]).toBe(originalArr[i]) + } + }) +}) describe("Mirabuf to Three Conversions", () => { test("Mirabuf.Transform [Identity] -> THREE.Matrix4", () => { diff --git a/fission/src/ui/components/MainHUD.tsx b/fission/src/ui/components/MainHUD.tsx index 2139ffdcdd..930b969eb4 100644 --- a/fission/src/ui/components/MainHUD.tsx +++ b/fission/src/ui/components/MainHUD.tsx @@ -20,8 +20,8 @@ import MirabufSceneObject from "@/mirabuf/MirabufSceneObject" import { Button } from "@mui/base/Button" import MirabufCachingService, { MiraType } from "@/mirabuf/MirabufLoader" import Jolt from "@barclah/jolt-physics" -import PreferencesSystem from "@/systems/preferences/PreferencesSystem" import { AiOutlineDoubleRight } from "react-icons/ai" +import PreferencesSystem from "@/systems/preferences/PreferencesSystem" type ButtonProps = { value: string @@ -181,6 +181,7 @@ const MainHUD: React.FC = () => { addToast(type, type, "This is a test toast to test the toast system") }} /> + } onClick={() => openModal("config-robot")} /> {userInfo ? ( void + onSelect?: (value: Jolt.Body) => boolean className?: string } -const SelectButton: React.FC = ({ colorClass, size, placeholder, onSelect, className }) => { - const [value, setValue] = useState() +const SelectButton: React.FC = ({ colorClass, size, value, placeholder, onSelect, className }) => { const [selecting, setSelecting] = useState(false) const timeoutRef = useRef() const onReceiveSelection = useCallback( - (value: string) => { - // TODO remove this when communication works - clearTimeout(timeoutRef.current) - setValue(value) - setSelecting(false) - if (onSelect) onSelect(value) + (value: Jolt.Body) => { + if (onSelect) { + if (onSelect(value)) { + clearTimeout(timeoutRef.current) + setSelecting(false) + } else { + setSelecting(true) + } + } }, - [setValue, setSelecting, onSelect] + [setSelecting, onSelect] ) useEffect(() => { - // simulate receiving a selection from Synthesis - if (selecting) { - timeoutRef.current = setTimeout( - () => { - if (selecting) { - const v = `node_${Math.floor(Random() * 10).toFixed(0)}` - onReceiveSelection(v) - } - }, - Math.floor(Random() * 2_750) + 250 - ) + const onClick = (e: MouseEvent) => { + if (selecting) { + const body = SelectNode(e) + if (body) { + onReceiveSelection(body) + } + } + } + + World.SceneRenderer.renderer.domElement.addEventListener("click", onClick) + + return () => { + World.SceneRenderer.renderer.domElement.removeEventListener("click", onClick) } }, [selecting, onReceiveSelection]) // should send selecting state when clicked and then receive string value to set selecting to false return ( - - + + + ) + })} + + {/* TODO: remove the accept button on this version */} + + ) : ( + <> + {/* Button for user to select the parent node */} + trySetSelectedNode(body.GetID())} + /> + + {/* Slider for user to set velocity of ejector configuration */} + { + setZoneSize(vel as number) + }} + step={0.01} + /> + { + if (transformGizmo) { + const robotTransformation = JoltMat44_ThreeMatrix4( + World.PhysicsSystem.GetBody(selectedRobot.GetRootNodeId()!).GetWorldTransform() + ) + transformGizmo.mesh.position.setFromMatrixPosition(robotTransformation) + transformGizmo.mesh.rotation.setFromRotationMatrix(robotTransformation) + } + setZoneSize((MIN_ZONE_SIZE + MAX_ZONE_SIZE) / 2.0) + setSelectedNode(selectedRobot?.rootNodeId) + }} + /> + + )} ) } diff --git a/fission/src/ui/panels/configuring/ConfigureShotTrajectoryPanel.tsx b/fission/src/ui/panels/configuring/ConfigureShotTrajectoryPanel.tsx index 29f493200b..f0e7d5714b 100644 --- a/fission/src/ui/panels/configuring/ConfigureShotTrajectoryPanel.tsx +++ b/fission/src/ui/panels/configuring/ConfigureShotTrajectoryPanel.tsx @@ -1,34 +1,257 @@ -import { useState } from "react" +import * as THREE from "three" +import { useCallback, useEffect, useMemo, useState } from "react" import { FaGear } from "react-icons/fa6" import Panel, { PanelPropsImpl } from "@/components/Panel" import SelectButton from "@/components/SelectButton" -import Slider from "@/components/Slider" +import TransformGizmos from "@/ui/components/TransformGizmos" +import Slider from "@/ui/components/Slider" +import Jolt from "@barclah/jolt-physics" +import Label from "@/ui/components/Label" +import PreferencesSystem from "@/systems/preferences/PreferencesSystem" +import Button from "@/ui/components/Button" +import MirabufSceneObject, { RigidNodeAssociate } from "@/mirabuf/MirabufSceneObject" +import World from "@/systems/World" +import { MiraType } from "@/mirabuf/MirabufLoader" +import { + Array_ThreeMatrix4, + JoltMat44_ThreeMatrix4, + ReactRgbaColor_ThreeColor, + ThreeMatrix4_Array, +} from "@/util/TypeConversions" +import { useTheme } from "@/ui/ThemeContext" +import LabeledButton, { LabelPlacement } from "@/ui/components/LabeledButton" +import { RigidNodeId } from "@/mirabuf/MirabufParser" + +// slider constants +const MIN_VELOCITY = 0.0 +const MAX_VELOCITY = 20.0 + +/** + * Saves ejector configuration to selected robot. + * + * Math Explanation: + * Let W be the world transformation matrix of the gizmo. + * Let R be the world transformation matrix of the selected robot node. + * Let L be the local transformation matrix of the gizmo, relative to the selected robot node. + * + * We are given W and R, and want to save L with the robot. This way when we create + * the ejection point afterwards, it will be relative to the selected robot node. + * + * W = L R + * L = W R^(-1) + * + * ThreeJS sets the standard multiplication operation for matrices to be premultiply. I really + * don't like this terminology as it's thrown me off multiple times, but I suppose it does go + * against most other multiplication operations. + * + * @param ejectorVelocity Velocity to eject gamepiece at. + * @param gizmo Reference to the transform gizmo object. + * @param selectedRobot Selected robot to save data to. + * @param selectedNode Selected node that configuration is relative to. + */ +function save( + ejectorVelocity: number, + gizmo: TransformGizmos, + selectedRobot: MirabufSceneObject, + selectedNode?: RigidNodeId +) { + if (!selectedRobot?.ejectorPreferences || !gizmo) { + return + } + + selectedNode ??= selectedRobot.rootNodeId + + const nodeBodyId = selectedRobot.mechanism.nodeToBody.get(selectedNode) + if (!nodeBodyId) { + return + } + + const gizmoTransformation = gizmo.mesh.matrixWorld + const robotTransformation = JoltMat44_ThreeMatrix4(World.PhysicsSystem.GetBody(nodeBodyId).GetWorldTransform()) + const deltaTransformation = gizmoTransformation.premultiply(robotTransformation.invert()) + + selectedRobot.ejectorPreferences.deltaTransformation = ThreeMatrix4_Array(deltaTransformation) + selectedRobot.ejectorPreferences.parentNode = selectedNode + selectedRobot.ejectorPreferences.ejectorVelocity = ejectorVelocity + + PreferencesSystem.savePreferences() +} const ConfigureShotTrajectoryPanel: React.FC = ({ panelId, openLocation, sidePadding }) => { - const defaultShootSpeed = 5 - const [, setNode] = useState("Click to select") - const [, setShootSpeed] = useState(defaultShootSpeed) + const { currentTheme, themes } = useTheme() + const theme = useMemo(() => { + return themes[currentTheme] + }, [currentTheme, themes]) + + const [selectedRobot, setSelectedRobot] = useState(undefined) + const [selectedNode, setSelectedNode] = useState(undefined) + const [ejectorVelocity, setEjectorVelocity] = useState((MIN_VELOCITY + MAX_VELOCITY) / 2.0) + const [transformGizmo, setTransformGizmo] = useState(undefined) + const robots = useMemo(() => { + const assemblies = [...World.SceneRenderer.sceneObjects.values()].filter(x => { + if (x instanceof MirabufSceneObject) { + return x.miraType === MiraType.ROBOT + } + return false + }) as MirabufSceneObject[] + return assemblies + }, []) + + // Not sure I like this, but made it a state and effect rather than a memo to add the cleanup to the end + useEffect(() => { + if (!selectedRobot?.ejectorPreferences) { + setTransformGizmo(undefined) + return + } + + const gizmo = new TransformGizmos( + new THREE.Mesh( + new THREE.ConeGeometry(0.1, 0.4, 4).rotateX(Math.PI / 2.0).translate(0, 0, 0.2), + World.SceneRenderer.CreateToonMaterial(ReactRgbaColor_ThreeColor(theme.HighlightSelect.color)) + ) + ) + + ;(gizmo.mesh.material as THREE.Material).depthTest = false + gizmo.AddMeshToScene() + gizmo.CreateGizmo("translate", 1.5) + gizmo.CreateGizmo("rotate", 2.0) + + const deltaTransformation = Array_ThreeMatrix4(selectedRobot.ejectorPreferences.deltaTransformation) + + let nodeBodyId = selectedRobot.mechanism.nodeToBody.get( + selectedRobot.ejectorPreferences.parentNode ?? selectedRobot.rootNodeId + ) + if (!nodeBodyId) { + // In the event that something about the id generation for the rigid nodes changes and parent node id is no longer in use + nodeBodyId = selectedRobot.mechanism.nodeToBody.get(selectedRobot.rootNodeId)! + } + + /** W = L x R. See save() for math details */ + const robotTransformation = JoltMat44_ThreeMatrix4(World.PhysicsSystem.GetBody(nodeBodyId).GetWorldTransform()) + const gizmoTransformation = deltaTransformation.premultiply(robotTransformation) + + gizmo.mesh.position.setFromMatrixPosition(gizmoTransformation) + gizmo.mesh.rotation.setFromRotationMatrix(gizmoTransformation) + + setTransformGizmo(gizmo) + + return () => { + gizmo.RemoveGizmos() + setTransformGizmo(undefined) + } + }, [selectedRobot, theme]) + + useEffect(() => { + if (selectedRobot?.ejectorPreferences) { + setEjectorVelocity(selectedRobot.ejectorPreferences.ejectorVelocity) + setSelectedNode(selectedRobot.ejectorPreferences.parentNode) + } else { + setSelectedNode(undefined) + } + }, [selectedRobot]) + + useEffect(() => { + World.PhysicsSystem.HoldPause() + + return () => { + World.PhysicsSystem.ReleasePause() + } + }, []) + + const trySetSelectedNode = useCallback( + (body: Jolt.BodyID) => { + if (!selectedRobot) { + return false + } + + const assoc = World.PhysicsSystem.GetBodyAssociation(body) as RigidNodeAssociate + if (!assoc || !assoc.sceneObject || assoc.sceneObject != selectedRobot) { + return false + } + + setSelectedNode(assoc.rigidNodeId) + return true + }, + [selectedRobot] + ) return ( } panelId={panelId} openLocation={openLocation} sidePadding={sidePadding} onAccept={() => { - // send node and speed config + if (transformGizmo && selectedRobot) { + save(ejectorVelocity, transformGizmo, selectedRobot, selectedNode) + const currentGp = selectedRobot.activeEjectable + selectedRobot.SetEjectable(undefined, true) + selectedRobot.SetEjectable(currentGp) + } }} + onCancel={() => {}} + acceptEnabled={selectedRobot?.ejectorPreferences != undefined} > - - + {selectedRobot?.ejectorPreferences == undefined ? ( + <> + + {/** Scroll view for selecting a robot to configure */} +
+ {robots.map(mirabufSceneObject => { + return ( + + ) + })} +
+ {/* TODO: remove the accept button on this version */} + + ) : ( + <> + {/* Button for user to select the parent node */} + trySetSelectedNode(body.GetID())} + /> + + {/* Slider for user to set velocity of ejector configuration */} + { + setEjectorVelocity(vel as number) + }} + step={0.01} + /> + { + if (transformGizmo) { + const robotTransformation = JoltMat44_ThreeMatrix4( + World.PhysicsSystem.GetBody(selectedRobot.GetRootNodeId()!).GetWorldTransform() + ) + transformGizmo.mesh.position.setFromMatrixPosition(robotTransformation) + transformGizmo.mesh.rotation.setFromRotationMatrix(robotTransformation) + } + setEjectorVelocity((MIN_VELOCITY + MAX_VELOCITY) / 2.0) + setSelectedNode(selectedRobot?.rootNodeId) + }} + /> + + )}
) } diff --git a/fission/src/ui/panels/configuring/scoring/ZoneConfigPanel.tsx b/fission/src/ui/panels/configuring/scoring/ZoneConfigPanel.tsx index 4fe84b26cf..cd58189d8d 100644 --- a/fission/src/ui/panels/configuring/scoring/ZoneConfigPanel.tsx +++ b/fission/src/ui/panels/configuring/scoring/ZoneConfigPanel.tsx @@ -2,7 +2,6 @@ import { useState } from "react" import Input from "@/components/Input" import Panel, { PanelPropsImpl } from "@/components/Panel" import Button from "@/components/Button" -import SelectButton from "@/components/SelectButton" import Checkbox from "@/components/Checkbox" import Slider from "@/components/Slider" import NumberInput from "@/components/NumberInput" @@ -37,7 +36,7 @@ const ZoneConfigPanel: React.FC = ({ panelId, openLocation, side onClick={() => setAlliance(alliance == "blue" ? "red" : "blue")} colorOverrideClass={`bg-match-${alliance}-alliance`} /> - setParent(p)} /> + {/* setParent(p)} /> */} setPoints(v || 1)} /> diff --git a/fission/src/util/TypeConversions.ts b/fission/src/util/TypeConversions.ts index 2cf99de0cf..d66ea08a6b 100644 --- a/fission/src/util/TypeConversions.ts +++ b/fission/src/util/TypeConversions.ts @@ -2,6 +2,7 @@ import * as THREE from "three" import JOLT from "./loading/JoltSyncLoader" import Jolt from "@barclah/jolt-physics" import { mirabuf } from "../proto/mirabuf" +import { RgbaColor } from "react-colorful" export function _JoltQuat(a: THREE.Euler | THREE.Quaternion | undefined) { if (a instanceof THREE.Euler) { @@ -13,6 +14,21 @@ export function _JoltQuat(a: THREE.Euler | THREE.Quaternion | undefined) { } } +export function Array_ThreeMatrix4(arr: number[]) { + // DO NOT ask me why retrieving and setting the same EXACT data is done is two DIFFERENT majors + // prettier-ignore + return new THREE.Matrix4( + arr[0], arr[4], arr[8], arr[12], + arr[1], arr[5], arr[9], arr[13], + arr[2], arr[6], arr[10], arr[14], + arr[3], arr[7], arr[11], arr[15] + ) +} + +export function ThreeMatrix4_Array(mat: THREE.Matrix4) { + return mat.elements +} + export function ThreeEuler_JoltQuat(euler: THREE.Euler) { const quat = new THREE.Quaternion() quat.setFromEuler(euler) @@ -91,3 +107,7 @@ export function MirabufFloatArr_JoltVec3Arr(v: number[]): Jolt.Vec3[] { } return arr } + +export function ReactRgbaColor_ThreeColor(color: RgbaColor) { + return new THREE.Color(Math.floor(color.r / 255), Math.floor(color.g / 255), Math.floor(color.b / 255)) +}