Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 41 additions & 16 deletions fission/src/systems/scene/GizmoSceneObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,22 +68,47 @@ class GizmoSceneObject extends SceneObject {

postGizmoCreation?.(this)

if (this._parentObject) {
this._relativeTransformations = new Map<RigidNodeId, THREE.Matrix4>()
const gizmoTransformInv = this._obj.matrix.clone().invert()

/** Due to the limited math functionality exposed to JS for Jolt, we need everything in ThreeJS. */
this._parentObject.mirabufInstance.parser.rigidNodes.forEach(rn => {
const jBodyId = this._parentObject!.mechanism.getBodyByNodeId(rn.id)
if (!jBodyId) return

const worldTransform = convertJoltMat44ToThreeMatrix4(
World.physicsSystem.getBody(jBodyId)!.GetWorldTransform()
)
const relativeTransform = worldTransform.premultiply(gizmoTransformInv)
this._relativeTransformations!.set(rn.id, relativeTransform)
})
}
if (this._parentObject) this.bakeRelativeTransformations()
}

/**
* Records where every body of the parent sits relative to the gizmo, which is what dragging then
* moves them by.
*
* Re-baked rather than updated in place: the offsets only stay valid for as long as the parent moves
Comment thread
PepperLola marked this conversation as resolved.
* rigidly, so anything that repositions bodies on their own (a snap, a reconcile, a joint moving)
* has to be captured fresh.
*/
private bakeRelativeTransformations() {
if (!this._parentObject) return

this._relativeTransformations = new Map<RigidNodeId, THREE.Matrix4>()
const gizmoTransformInv = this._obj.matrix.clone().invert()

this._parentObject.mirabufInstance.parser.rigidNodes.forEach(rn => {
const jBodyId = this._parentObject!.mechanism.getBodyByNodeId(rn.id)
if (!jBodyId) return

const worldTransform = convertJoltMat44ToThreeMatrix4(
World.physicsSystem.getBody(jBodyId)!.GetWorldTransform()
)
const relativeTransform = worldTransform.premultiply(gizmoTransformInv)
this._relativeTransformations!.set(rn.id, relativeTransform)
})
}

/**
* Re-seats the gizmo on its parent after something other than this gizmo moved the parent.
*/
public syncToParent() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might want to mark this as unused so people know not to remove it.

if (!this._parentObject || this.isDragging) return

this._parentObject.postGizmoCreation(this)
// postGizmoCreation seats the gizmo via setTransform, which asks to drive the parent. Nothing
// needs driving here, the parent is already where it wants to be.
this._forceUpdate = false

this.bakeRelativeTransformations()
}

public setup(): void {
Expand Down
41 changes: 41 additions & 0 deletions fission/src/test/scene/GizmoSceneObject.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,47 @@ describe("GizmoSceneObject", () => {
})
})

describe("syncToParent()", () => {
test("should reseat via parent's postGizmoCreation and rebake offsets", () => {
mockParentObject.postGizmoCreation = vi.fn()

gizmoSceneObject.setTransform(new THREE.Matrix4())
const bakeSpy = vi.spyOn(
gizmoSceneObject as unknown as { bakeRelativeTransformations: () => void },
"bakeRelativeTransformations"
)

gizmoSceneObject.syncToParent()

expect(mockParentObject.postGizmoCreation).toHaveBeenCalledWith(gizmoSceneObject)
expect(bakeSpy).toHaveBeenCalledTimes(1)
expect(gizmoSceneObject["_forceUpdate"]).toBe(false)
})

test("should do nothing while dragging", () => {
mockParentObject.postGizmoCreation = vi.fn()
gizmoSceneObject.gizmo.dragging = true

const bakeSpy = vi.spyOn(
gizmoSceneObject as unknown as { bakeRelativeTransformations: () => void },
"bakeRelativeTransformations"
)

gizmoSceneObject.syncToParent()

expect(mockParentObject.postGizmoCreation).not.toHaveBeenCalled()
expect(bakeSpy).not.toHaveBeenCalled()
})

test("should do nothing without parent", () => {
const noParentGizmo = new GizmoSceneObject("translate", 1.0, mockMesh)

expect(() => noParentGizmo.syncToParent()).not.toThrow()

noParentGizmo.dispose()
})
})

describe("updateNodeTransform()", () => {
test("should handle missing parent gracefully", () => {
const noParentGizmo = new GizmoSceneObject("translate", 1.0, mockMesh)
Expand Down
Loading