Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/pretty-garlics-know.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@viamrobotics/motion-tools': minor
---

Use MatrixWorld trait to transform all entities
4 changes: 2 additions & 2 deletions buf.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ deps:
commit: 62f35d8aed1149c291d606d958a7ce32
digest: b5:d66bf04adc77a0870bdc9328aaf887c7188a36fb02b83a480dc45ef9dc031b4d39fc6e9dc6435120ccf4fe5bfd5c6cb6592533c6c316595571f9a31420ab47fe
- name: buf.build/viamrobotics/api
commit: e1e7b37b6b224400984f294513bb276c
digest: b5:e1f825a0a6210ef987b514e1b025fabcadc11ea20d33fe9be119339ab350ac8cb0875834f08e27efe326a0559f02d7740b322fcaf848ca64b326a4ffb642f732
commit: 1a2e081e7d484576b4b1b7a3b0f619c1
digest: b5:e0aedb48a1beb62c9cf0d1b9c5a8ff0f32ddd0598859a0e3b28945bf71ffa582bae48193e133311641565ea22502750fe45a7cc5fa5d20cfe8a17a3e7e199df2
58 changes: 20 additions & 38 deletions src/lib/components/BatchedArrows.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,13 @@
import type { Entity } from 'koota'

import { T } from '@threlte/core'
import { Portal } from '@threlte/extras'
import { Color, Quaternion, Vector3 } from 'three'

import { hierarchy, traits, useWorld } from '$lib/ecs'
import { traits, useWorld } from '$lib/ecs'
import { BatchedArrow } from '$lib/three/BatchedArrow'
import { OrientationVector } from '$lib/three/OrientationVector'

const arrowBatchMap = $state<Record<string, BatchedArrow>>({
world: new BatchedArrow(),
})
const batchEntries = $derived(Object.entries(arrowBatchMap))
const batched = new BatchedArrow()

const world = useWorld()

Expand All @@ -24,24 +20,19 @@
const tempOv = new OrientationVector()

/**
* Decompose the matrix directly into the arrow's direction
* (OV components from the rotation) and origin (translation)
* Decompose the entity's `WorldMatrix` directly into the arrow's world
* origin (translation) and direction (OV components from the rotation).
*/
const decompose = (entity: Entity): boolean => {
const matrix = entity.get(traits.Matrix)
if (!matrix) return false
matrix.decompose(origin, tempQuat, tempScale)
const worldMatrix = entity.get(traits.WorldMatrix)
if (!worldMatrix) return false
worldMatrix.decompose(origin, tempQuat, tempScale)
tempOv.setFromQuaternion(tempQuat)
direction.set(tempOv.x, tempOv.y, tempOv.z)
return true
}

const onAdd = (entity: Entity) => {
const parent = hierarchy.getParentName(entity) ?? 'world'

arrowBatchMap[parent] ??= new BatchedArrow()
const batched = arrowBatchMap[parent]

const colorRGB = entity.get(traits.Color)

if (!decompose(entity)) {
Expand All @@ -58,63 +49,54 @@
entity.add(traits.Instance({ instanceID, meshID: batched.mesh.id }))
}

const onMatrixChange = (entity: Entity) => {
const onWorldMatrixChange = (entity: Entity) => {
if (!entity.has(traits.Arrow)) return

const parent = hierarchy.getParentName(entity) ?? 'world'
const batch = arrowBatchMap[parent]
const instanceID = entity.get(traits.Instance)?.instanceID

if (instanceID && instanceID !== -1 && decompose(entity)) {
batch?.updateArrow(instanceID, direction, origin)
batched.updateArrow(instanceID, direction, origin)
}
}

const onColorChange = (entity: Entity) => {
if (!entity.has(traits.Arrow)) return

const parent = hierarchy.getParentName(entity) ?? 'world'
const batch = arrowBatchMap[parent]
const instanceID = entity.get(traits.Instance)?.instanceID
const colorRGB = entity.get(traits.Color)

if (instanceID && instanceID !== -1 && colorRGB) {
color.set(colorRGB.r, colorRGB.g, colorRGB.b)
batch.mesh.setColorAt(instanceID, color)
batched.mesh.setColorAt(instanceID, color)
}
}

const onInstanceRemove = (entity: Entity) => {
const instance = entity.get(traits.Instance)

for (const [, batch] of batchEntries) {
if (batch.mesh.id === instance?.meshID) {
batch.removeArrow(instance.instanceID)
}
if (instance && instance.meshID === batched.mesh.id) {
batched.removeArrow(instance.instanceID)
}
}

$effect(() => {
const unsubAdd = world.onAdd(traits.Arrow, onAdd)
const unsubRemove = world.onRemove(traits.Instance, onInstanceRemove)
const unsubMatrixChange = world.onChange(traits.Matrix, onMatrixChange)
const unsubMatrixAdd = world.onAdd(traits.WorldMatrix, onWorldMatrixChange)
const unsubMatrixChange = world.onChange(traits.WorldMatrix, onWorldMatrixChange)
const unsubColorChange = world.onChange(traits.Color, onColorChange)

return () => {
unsubAdd()
unsubRemove()
unsubMatrixAdd()
unsubMatrixChange()
unsubColorChange()
}
})
</script>

{#each batchEntries as [parent, batch] (parent)}
<Portal id={parent}>
<T
is={batch.mesh}
dispose={false}
bvh={{ enabled: false }}
/>
</Portal>
{/each}
<T
is={batched.mesh}
dispose={false}
bvh={{ enabled: false }}
/>
64 changes: 35 additions & 29 deletions src/lib/components/Entities/Arrows/Arrows.svelte
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
<script lang="ts">
import type { Entity } from 'koota'

import { T } from '@threlte/core'
import { Portal } from '@threlte/extras'
import { T, useThrelte } from '@threlte/core'

import type { InstancedArrows } from '$lib/three/InstancedArrows/InstancedArrows'

import AxesHelper from '$lib/components/AxesHelper.svelte'
import { useEntityEvents } from '$lib/components/Entities/hooks/useEntityEvents.svelte'
import { traits, useParentName, useTrait } from '$lib/ecs'
import { traits, useTrait } from '$lib/ecs'
import { useFocusedEntity, useSelectedEntity } from '$lib/hooks/useSelection.svelte'
import { meshBoundsRaycast, raycast } from '$lib/three/InstancedArrows/raycast'

Expand All @@ -19,7 +18,8 @@

let { entity, arrows }: Props = $props()

const parent = useParentName(() => entity)
const { invalidate } = useThrelte()
const worldMatrix = useTrait(() => entity, traits.WorldMatrix)
const invisible = useTrait(() => entity, traits.Invisible)
const showAxesHelper = useTrait(() => entity, traits.ShowAxesHelper)

Expand All @@ -35,32 +35,38 @@
}
return meshBoundsRaycast
})

$effect.pre(() => {
arrows.matrixAutoUpdate = false
if (!worldMatrix.current) return
arrows.matrix.copy(worldMatrix.current)
arrows.updateMatrixWorld()
invalidate()
})
</script>

<Portal id={parent.current}>
<T
is={arrows}
name={entity}
{...events}
raycast={raycastFunction}
visible={invisible.current !== true}
>
<T
is={arrows}
name={entity}
{...events}
raycast={raycastFunction}
visible={invisible.current !== true}
>
<T
is={arrows.headMesh}
bvh={{ enabled: false }}
raycast={() => null}
/>
<T
is={arrows.shaftMesh}
bvh={{ enabled: false }}
raycast={() => null}
is={arrows.headMesh}
bvh={{ enabled: false }}
raycast={() => null}
/>
<T
is={arrows.shaftMesh}
bvh={{ enabled: false }}
raycast={() => null}
/>
{#if showAxesHelper.current}
<AxesHelper
name={entity}
width={3}
length={0.1}
/>
{#if showAxesHelper.current}
<AxesHelper
name={entity}
width={3}
length={0.1}
/>
{/if}
</T>
</Portal>
{/if}
</T>
53 changes: 18 additions & 35 deletions src/lib/components/Entities/Frame.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,12 @@ Renders a Viam Frame object
import type { Snippet } from 'svelte'

import { T, useThrelte } from '@threlte/core'
import { Portal, PortalTarget } from '@threlte/extras'
import { Group, type Object3D } from 'three'

import { asColor } from '$lib/buffer'
import { colors, resourceColors } from '$lib/color'
import { traits, useParentName, useTrait } from '$lib/ecs'
import { traits, useTrait } from '$lib/ecs'
import { useResourceByName } from '$lib/hooks/useResourceByName.svelte'
import { composeLocalMatrix } from '$lib/transform'

import { useEntityEvents } from './hooks/useEntityEvents.svelte'
import Mesh from './Mesh.svelte'
Expand All @@ -37,12 +35,9 @@ Renders a Viam Frame object
const resourceByName = useResourceByName()

const name = useTrait(() => entity, traits.Name)
const parent = useParentName(() => entity)
const entityColors = useTrait(() => entity, traits.Colors)
const entityColor = useTrait(() => entity, traits.Color)
const matrix = useTrait(() => entity, traits.Matrix)
const editedMatrix = useTrait(() => entity, traits.EditedMatrix)
const liveMatrix = useTrait(() => entity, traits.LiveMatrix)
const worldMatrix = useTrait(() => entity, traits.WorldMatrix)
const center = useTrait(() => entity, traits.Center)
const invisible = useTrait(() => entity, traits.Invisible)

Expand Down Expand Up @@ -71,15 +66,9 @@ Renders a Viam Frame object
group.matrixAutoUpdate = false

$effect.pre(() => {
if (liveMatrix.current && matrix.current && editedMatrix.current) {
composeLocalMatrix(liveMatrix.current, matrix.current, editedMatrix.current, group.matrix)
} else if (editedMatrix.current) {
group.matrix.copy(editedMatrix.current)
} else if (matrix.current) {
group.matrix.copy(matrix.current)
} else {
return
}
if (!worldMatrix.current) return

group.matrix.copy(worldMatrix.current)

/**
* Keep position/quaternion/scale in sync with matrix so TransformControls
Expand All @@ -94,22 +83,16 @@ Renders a Viam Frame object
})
</script>

<Portal id={parent.current}>
<T
is={group}
visible={invisible.current !== true}
>
<Mesh
{entity}
{color}
{...events}
center={center.current}
/>

{#if name.current}
<PortalTarget id={name.current} />
{/if}

{@render children?.({ ref: group })}
</T>
</Portal>
<T
is={group}
visible={invisible.current !== true}
>
<Mesh
{entity}
{color}
{...events}
center={center.current}
/>

{@render children?.({ ref: group })}
</T>
Loading
Loading