@kite3d/engine 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/authoring.d.ts +54 -0
  2. package/dist/authoring.js +165 -0
  3. package/dist/authoring.js.map +1 -0
  4. package/dist/authoringValidation.d.ts +110 -0
  5. package/dist/authoringValidation.js +488 -0
  6. package/dist/authoringValidation.js.map +1 -0
  7. package/dist/defaults.d.ts +2 -0
  8. package/dist/fileTypes.d.ts +5 -0
  9. package/dist/fileTypes.js +51 -0
  10. package/dist/fileTypes.js.map +1 -0
  11. package/dist/importMap.d.ts +15 -0
  12. package/dist/importMap.js +62 -0
  13. package/dist/importMap.js.map +1 -0
  14. package/dist/index.d.ts +15 -0
  15. package/dist/index.js +2756 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/migrations.js +5 -0
  18. package/dist/migrations.js.map +1 -0
  19. package/dist/paths.d.ts +9 -0
  20. package/dist/paths.js +13 -0
  21. package/dist/paths.js.map +1 -0
  22. package/dist/plugins/GeneratorComponent.d.ts +40 -0
  23. package/dist/plugins/HtmlUiComponent.d.ts +52 -0
  24. package/dist/plugins/HtmlUiComponent.example.d.ts +0 -0
  25. package/dist/plugins/cannon/Cannon3DBodyComponent.d.ts +27 -0
  26. package/dist/plugins/cannon/Cannon3DShapeComponent.d.ts +57 -0
  27. package/dist/plugins/cannon/CannonPhysicsPlugin.d.ts +63 -0
  28. package/dist/plugins/cannon/CannonRagdollComponent.d.ts +148 -0
  29. package/dist/plugins/cannon/helper.d.ts +24 -0
  30. package/dist/plugins/cannon/threeToCannon.d.ts +63 -0
  31. package/dist/plugins/cannon/utils.d.ts +9 -0
  32. package/dist/projectFormat.js +108 -0
  33. package/dist/projectFormat.js.map +1 -0
  34. package/dist/runtime/createGame.d.ts +29 -0
  35. package/dist/runtime/index.d.ts +18 -0
  36. package/dist/runtime/migrations.d.ts +5 -0
  37. package/dist/runtime/nestedAssets.d.ts +26 -0
  38. package/dist/runtime/projectFormat.d.ts +83 -0
  39. package/dist/runtime/version.d.ts +1 -0
  40. package/dist/runtime.js +72835 -0
  41. package/dist/runtime.js.map +1 -0
  42. package/dist/sceneSerialization.d.ts +17 -0
  43. package/dist/sceneSerialization.js +174 -0
  44. package/dist/sceneSerialization.js.map +1 -0
  45. package/dist/scripts.d.ts +17 -0
  46. package/dist/version.js +7 -0
  47. package/dist/version.js.map +1 -0
  48. package/package.json +86 -0
  49. package/src/authoring.ts +293 -0
  50. package/src/authoringValidation.ts +815 -0
  51. package/src/defaults.ts +277 -0
  52. package/src/fileTypes.ts +53 -0
  53. package/src/importMap.ts +105 -0
  54. package/src/index.ts +15 -0
  55. package/src/paths.ts +9 -0
  56. package/src/plugins/GeneratorComponent.ts +275 -0
  57. package/src/plugins/HtmlUiComponent.example.ts +202 -0
  58. package/src/plugins/HtmlUiComponent.md +219 -0
  59. package/src/plugins/HtmlUiComponent.ts +320 -0
  60. package/src/plugins/cannon/Cannon3DBodyComponent.ts +227 -0
  61. package/src/plugins/cannon/Cannon3DShapeComponent.ts +258 -0
  62. package/src/plugins/cannon/CannonPhysicsPlugin.ts +409 -0
  63. package/src/plugins/cannon/CannonRagdollComponent.ts +1170 -0
  64. package/src/plugins/cannon/helper.ts +255 -0
  65. package/src/plugins/cannon/threeToCannon.ts +441 -0
  66. package/src/plugins/cannon/utils.ts +185 -0
  67. package/src/runtime/createGame.ts +337 -0
  68. package/src/runtime/index.ts +19 -0
  69. package/src/runtime/migrations.ts +6 -0
  70. package/src/runtime/nestedAssets.ts +226 -0
  71. package/src/runtime/projectFormat.ts +233 -0
  72. package/src/runtime/version.ts +3 -0
  73. package/src/sceneSerialization.ts +289 -0
  74. package/src/scripts.ts +52 -0
@@ -0,0 +1,320 @@
1
+ import {
2
+ ComponentDefn,
3
+ ComponentJSON,
4
+ IObject3D,
5
+ IObject3DEventMap,
6
+ ISceneEventMap,
7
+ literalStrings,
8
+ Object3DComponent,
9
+ Vector3,
10
+ ViewerEventMap
11
+ } from "threepipe"
12
+
13
+ export const uiPositionMode = ['world', 'screen', 'viewport'] as const
14
+ export type UiPositionMode = typeof uiPositionMode[number]
15
+
16
+ export class HtmlUiComponent extends Object3DComponent {
17
+ static ComponentType = 'HtmlUiComponent'
18
+ static StateProperties: ComponentDefn['StateProperties'] = [
19
+ 'enabled',
20
+ {
21
+ key: 'htmlData',
22
+ type: 'string',
23
+ uiConfig: {
24
+ multiline: true,
25
+ rows: 5,
26
+ autoResize: true,
27
+ }
28
+ },
29
+ 'width',
30
+ 'height',
31
+ 'scrollable',
32
+ 'interactable',
33
+ {
34
+ key: 'positionMode',
35
+ type: literalStrings(uiPositionMode),
36
+ },
37
+ 'offsetX',
38
+ 'offsetY',
39
+ 'zIndex',
40
+ 'visible',
41
+ 'appendToBody'
42
+ ]
43
+
44
+ declare element: HTMLDivElement
45
+
46
+ enabled = true
47
+
48
+ // HTML content
49
+ htmlData = '<div>Hello World</div>'
50
+
51
+ // Size properties
52
+ width = -1 // in pixels
53
+ height = -1 // in pixels
54
+
55
+ // Behavior properties
56
+ scrollable = false
57
+ interactable = true
58
+ visible = true
59
+ appendToBody = false // false = append to viewer.container (default), true = append to document.body
60
+
61
+ // Position properties
62
+ positionMode: UiPositionMode = 'world' // world follows 3D object, screen is fixed, viewport is relative to viewport
63
+ offsetX = 0 // pixel offset from calculated position
64
+ offsetY = 0 // pixel offset from calculated position
65
+ zIndex = 1000 // CSS z-index
66
+
67
+ private _worldPosition = new Vector3()
68
+ private _animationFrameId?: number
69
+
70
+ constructor() {
71
+ super()
72
+
73
+ this.onStateChange('htmlData', (v) => {
74
+ if (!this.element) return
75
+ this.element.innerHTML = v
76
+ // this.object.setDirty?.({source: 'HtmlUiComponent'})
77
+ })
78
+
79
+ this.onStateChange('width', (v) => {
80
+ if (!this.element) return
81
+ this.element.style.width = v < 0 ? 'auto' : `${v}px`
82
+ this.object.setDirty?.({source: 'HtmlUiComponent'})
83
+ })
84
+
85
+ this.onStateChange('height', (v) => {
86
+ if (!this.element) return
87
+ this.element.style.height = v < 0 ? 'auto' : `${v}px`
88
+ this.object.setDirty?.({source: 'HtmlUiComponent'})
89
+ })
90
+
91
+ this.onStateChange('scrollable', (v) => {
92
+ if (!this.element) return
93
+ this.element.style.overflow = v ? 'auto' : 'hidden'
94
+ this.object.setDirty?.({source: 'HtmlUiComponent'})
95
+ })
96
+
97
+ this.onStateChange('interactable', (v) => {
98
+ if (!this.element) return
99
+ this.element.style.pointerEvents = v ? 'auto' : 'none'
100
+ this.object.setDirty?.({source: 'HtmlUiComponent'})
101
+ })
102
+
103
+ this.onStateChange('visible', (v) => {
104
+ if (!this.element) return
105
+ this.element.style.display = v ? 'block' : 'none'
106
+ this.object.setDirty?.({source: 'HtmlUiComponent'})
107
+ })
108
+
109
+ this.onStateChange('positionMode', () => {
110
+ if (!this.element) return
111
+ this._updateElementPosition()
112
+ this.object.setDirty?.({source: 'HtmlUiComponent'})
113
+ })
114
+
115
+ this.onStateChange('offsetX', () => {
116
+ if (!this.element) return
117
+ this._updateElementPosition()
118
+ this.object.setDirty?.({source: 'HtmlUiComponent'})
119
+ })
120
+
121
+ this.onStateChange('offsetY', () => {
122
+ if (!this.element) return
123
+ this._updateElementPosition()
124
+ this.object.setDirty?.({source: 'HtmlUiComponent'})
125
+ })
126
+
127
+ this.onStateChange('zIndex', (v) => {
128
+ if (!this.element) return
129
+ this.element.style.zIndex = `${v}`
130
+ this.object.setDirty?.({source: 'HtmlUiComponent'})
131
+ })
132
+
133
+ this.onStateChange('appendToBody', (v) => {
134
+ if (!this.element) return
135
+ // Remove from current parent
136
+ if (this.element.parentNode) {
137
+ this.element.parentNode.removeChild(this.element)
138
+ }
139
+ // Append to new container
140
+ const container = v ? document.body : (this.ctx?.viewer?.container || document.body)
141
+ container.appendChild(this.element)
142
+ this.object.setDirty?.({source: 'HtmlUiComponent'})
143
+ })
144
+ }
145
+
146
+ private _objectUpdate = (e: IObject3DEventMap['objectUpdate'] | ISceneEventMap['mainCameraUpdate']) => {
147
+ if (e.source === 'HtmlUiComponent') return
148
+ const changeKey = e?.change ?? e?.key
149
+ const update = !changeKey || changeKey === 'position' || changeKey === 'transform' || changeKey === 'quaternion' || changeKey === 'visible' || changeKey === 'controls'
150
+ if (update && this.positionMode === 'world') {
151
+ this._updateElementPosition()
152
+ }
153
+ }
154
+
155
+ init(object: IObject3D, state: ComponentJSON['state']) {
156
+ super.init(object, state)
157
+
158
+ // Create the HTML element
159
+ this.element = document.createElement('div')
160
+ this.element.className = 'threepipe-html-ui'
161
+
162
+ // Apply initial styles
163
+ this.element.style.position = 'absolute'
164
+ this.element.style.width = this.width < 0 ? 'auto' : `${this.width}px`
165
+ this.element.style.height = this.height < 0 ? 'auto' : `${this.height}px`
166
+ this.element.style.overflow = this.scrollable ? 'auto' : 'hidden'
167
+ this.element.style.pointerEvents = this.interactable ? 'auto' : 'none'
168
+ this.element.style.zIndex = `${this.zIndex}`
169
+ this.element.style.display = this.visible ? 'block' : 'none'
170
+ this.element.style.boxSizing = 'border-box'
171
+
172
+ // Set HTML content
173
+ this.element.innerHTML = this.htmlData
174
+
175
+ // Add to document or viewer container
176
+ const container = this.appendToBody ? document.body : (this.ctx?.viewer?.container || document.body)
177
+ container.appendChild(this.element)
178
+
179
+ // Listen for object updates
180
+ object.addEventListener('objectUpdate', this._objectUpdate)
181
+ this.ctx.viewer.scene.addEventListener('mainCameraUpdate', this._objectUpdate)
182
+
183
+ // Start position tracking for world-positioned elements
184
+ this._updateElementPosition()
185
+ }
186
+
187
+ destroy(): Record<string, any> {
188
+ // Stop position tracking
189
+ this._stopPositionTracking()
190
+
191
+ // Remove event listener
192
+ this.object.removeEventListener('objectUpdate', this._objectUpdate)
193
+ this.ctx.viewer.scene.removeEventListener('mainCameraUpdate', this._objectUpdate)
194
+
195
+ // Remove element from DOM
196
+ if (this.element && this.element.parentNode) {
197
+ this.element.parentNode.removeChild(this.element)
198
+ }
199
+ this.element = null as any
200
+
201
+ return super.destroy()
202
+ }
203
+
204
+ private _stopPositionTracking() {
205
+ if (this._animationFrameId !== undefined) {
206
+ cancelAnimationFrame(this._animationFrameId)
207
+ this._animationFrameId = undefined
208
+ }
209
+ }
210
+
211
+ preFrame({resized}: ViewerEventMap["preFrame"]): boolean | void {
212
+ if(!this.element || !this.enabled) return
213
+ // world - on camera change, canvas size change, object transform change
214
+ // screen - no change
215
+ // viewport - on canvas size change
216
+ if(this.positionMode === 'screen') return
217
+ if(!resized && this.positionMode === 'viewport') return
218
+ if(!resized && this.positionMode === 'world') return // todo: tracked separately
219
+ this._updateElementPosition()
220
+ }
221
+
222
+ private _updateElementPosition() {
223
+ if (!this.element) return
224
+
225
+ switch (this.positionMode) {
226
+ case 'world':
227
+ this._updateWorldPosition()
228
+ break
229
+ case 'screen':
230
+ this._updateScreenPosition()
231
+ break
232
+ case 'viewport':
233
+ this._updateViewportPosition()
234
+ break
235
+ }
236
+ }
237
+
238
+ private _updateWorldPosition() {
239
+ // Get the world position of the object
240
+ this.object.getWorldPosition(this._worldPosition)
241
+
242
+ // Project to screen space
243
+ const camera = this.ctx?.viewer?.scene?.mainCamera
244
+ if (!camera) return
245
+
246
+ const canvas = this.ctx?.viewer?.canvas
247
+ if (!canvas) return
248
+
249
+ // Clone position and project it
250
+ const projected = this._worldPosition.clone()
251
+ projected.project(camera)
252
+
253
+ // Convert to screen coordinates
254
+ const x = (projected.x * 0.5 + 0.5) * canvas.clientWidth
255
+ const y = (projected.y * -0.5 + 0.5) * canvas.clientHeight
256
+
257
+ // Apply position with offset
258
+ this.element.style.left = `${x + this.offsetX}px`
259
+ this.element.style.top = `${y + this.offsetY}px`
260
+ this.element.style.transform = 'translate(-50%, -50%)' // Center on point
261
+ }
262
+
263
+ private _updateScreenPosition() {
264
+ // Fixed screen position
265
+ this.element.style.left = `${this.offsetX}px`
266
+ this.element.style.top = `${this.offsetY}px`
267
+ this.element.style.transform = 'none'
268
+ }
269
+
270
+ private _updateViewportPosition() {
271
+ // Viewport-relative position (percentage-based)
272
+ const canvas = this.ctx?.viewer?.canvas
273
+ if (!canvas) return
274
+
275
+ const x = (this.offsetX / 100) * canvas.clientWidth
276
+ const y = (this.offsetY / 100) * canvas.clientHeight
277
+
278
+ this.element.style.left = `${x}px`
279
+ this.element.style.top = `${y}px`
280
+ this.element.style.transform = 'none'
281
+ }
282
+
283
+ /**
284
+ * Update the HTML content programmatically
285
+ */
286
+ setHtml(html: string) {
287
+ this.htmlData = html
288
+ }
289
+
290
+ /**
291
+ * Set the position
292
+ */
293
+ setPosition(x: number, y: number) {
294
+ this.offsetX = x
295
+ this.offsetY = y
296
+ }
297
+
298
+ /**
299
+ * Set the size
300
+ */
301
+ setSize(width: number, height: number) {
302
+ this.width = width
303
+ this.height = height
304
+ }
305
+
306
+ /**
307
+ * Show the element
308
+ */
309
+ show() {
310
+ this.visible = true
311
+ }
312
+
313
+ /**
314
+ * Hide the element
315
+ */
316
+ hide() {
317
+ this.visible = false
318
+ }
319
+ }
320
+
@@ -0,0 +1,227 @@
1
+ import { Body, Vec3, Quaternion as CQuaternion } from "cannon-es"
2
+ import {
3
+ ComponentDefn,
4
+ ComponentJSON, EntityComponentPlugin,
5
+ IObject3D,
6
+ IObject3DEventMap,
7
+ literalStrings,
8
+ Object3DComponent, Quaternion, Vector3
9
+ } from "threepipe"
10
+ import {Cannon3DShapeComponent} from "./Cannon3DShapeComponent.ts";
11
+ import {CannonMaterial2, CannonPhysicsPlugin} from "./CannonPhysicsPlugin.ts";
12
+
13
+ export const physicsBodyType = ['static', 'dynamic', 'kinematic'] as const
14
+ export type PhysicsBodyType = typeof physicsBodyType[number]
15
+
16
+ export class Cannon3DBodyComponent extends Object3DComponent {
17
+ static ComponentType = 'Cannon3DBodyComponent'
18
+ static StateProperties: ComponentDefn['StateProperties'] = ['mass', {
19
+ key: 'type',
20
+ type: literalStrings(physicsBodyType),
21
+ }, 'isTrigger', 'material']
22
+
23
+ declare body: Body
24
+
25
+ mass = 1
26
+ type: PhysicsBodyType = 'dynamic'
27
+ isTrigger = false
28
+ material: CannonMaterial2 = new CannonMaterial2()
29
+
30
+ private _typeToCannon(type: PhysicsBodyType) {
31
+ if (type === 'static') return Body.STATIC
32
+ else if (type === 'dynamic') return Body.DYNAMIC
33
+ else if (type === 'kinematic') return Body.KINEMATIC
34
+ return Body.STATIC
35
+ }
36
+
37
+ updateMassProperties(){
38
+ if (!this.body) return
39
+ try {
40
+ this.body.updateMassProperties()
41
+ }catch (e) {
42
+ console.error('[Cannon3DBodyComponent] updateMassProperties error', e)
43
+ }
44
+ }
45
+ updateBoundingRadius(){
46
+ if (!this.body) return
47
+ try {
48
+ this.body.updateBoundingRadius()
49
+ }catch (e) {
50
+ console.error('[Cannon3DBodyComponent] updateBoundingRadius error', e)
51
+ }
52
+ }
53
+
54
+
55
+ constructor() {
56
+ super()
57
+ this.onStateChange('mass', (v)=>{
58
+ if (!this.body) return
59
+ this.body.mass = v
60
+ this.updateMassProperties()
61
+ this.object.setDirty?.({source: 'Cannon3DBodyComponent'})
62
+ })
63
+ this.onStateChange('type', (v)=>{
64
+ if (!this.body) return
65
+ this.body.type = this._typeToCannon(v)
66
+ this.updateMassProperties()
67
+ this.object.setDirty?.({source: 'Cannon3DBodyComponent'})
68
+ })
69
+ this.onStateChange('isTrigger', (v)=>{
70
+ if (!this.body) return
71
+ this.body.isTrigger = v
72
+ this.object.setDirty?.({source: 'Cannon3DBodyComponent'})
73
+ })
74
+ this.onStateChange('material', (v)=>{
75
+ if (!this.body) return
76
+ this.body.material = v
77
+ this.uiConfig?.uiRefresh?.()
78
+ this.object.setDirty?.({source: 'Cannon3DBodyComponent'})
79
+ })
80
+ }
81
+
82
+ private _objectUpdate = (e: IObject3DEventMap['objectUpdate'])=>{
83
+ if (e.source === 'Cannon3DBodyComponent' || e.source === 'CannonPhysicsPlugin') return
84
+ const changeKey = e?.change ?? e?.key
85
+ const update = !changeKey || changeKey === 'position' || changeKey === 'transform' || changeKey === 'quaternion'
86
+ if (update) this.resetTransform(changeKey)
87
+ }
88
+
89
+ init(object: IObject3D, state: ComponentJSON['state']) {
90
+ super.init(object, state)
91
+ const cannon = this.ctx.plugin(CannonPhysicsPlugin)
92
+ this.body = new Body()
93
+ this.body.material = this.material
94
+ this.body.mass = this.mass
95
+ this.body.type = this._typeToCannon(this.type)
96
+ this.body.isTrigger = this.isTrigger
97
+ this.updateMassProperties()
98
+ this.resetTransform()
99
+ object.addEventListener('objectUpdate', this._objectUpdate)
100
+ cannon.addBody(this)
101
+
102
+ object.traverseModels && object.traverseModels(m=>{
103
+ EntityComponentPlugin.GetComponents(m, Cannon3DShapeComponent).forEach(sh=>{
104
+ this.addShape(sh, false)
105
+ })
106
+ }, {widgets: false, visible: false})
107
+ if (this.shapeRefs.length) {
108
+ this.refreshShapes()
109
+ }
110
+
111
+ }
112
+
113
+ destroy(): Record<string, any> {
114
+ this.shapeRefs.forEach(shape=>{
115
+ this.removeShape(shape, false)
116
+ })
117
+ this.refreshShapes(false)
118
+
119
+ this.object.removeEventListener('objectUpdate', this._objectUpdate)
120
+ const cannon = this.ctx.plugin(CannonPhysicsPlugin)
121
+ cannon.removeBody(this)
122
+ this.body = null as any
123
+ return super.destroy()
124
+ }
125
+
126
+ // update({time}: IAnimationLoopEvent) {
127
+ //
128
+ // }
129
+
130
+ resetTransform(changeKey?: string) {
131
+ // const changeKey = e?.change ?? e?.key
132
+ if (!changeKey || changeKey === 'position' || changeKey === 'transform')
133
+ this.body.position.set(...this.object.getWorldPosition(new Vector3()).toArray())
134
+ if (!changeKey || changeKey === 'quaternion' || changeKey === 'transform')
135
+ this.body.quaternion.set(...(this.object.getWorldQuaternion(new Quaternion()).toArray() as [number, number, number, number]))
136
+ this.body.angularVelocity.set(0, 0, 0)
137
+ this.body.velocity.set(0, 0, 0)
138
+ }
139
+
140
+ // static IsCompatible(_object: IObject3D) { return true }
141
+
142
+ shapeRefs: Cannon3DShapeComponent[] = []
143
+ addShape(shape: Cannon3DShapeComponent, refresh = true) {
144
+ if (!this.shapeRefs.includes(shape)) {
145
+ if (shape.bodyRef && shape.bodyRef !== this) {
146
+ // ignore
147
+ return
148
+ }
149
+ this.shapeRefs.push(shape)
150
+ shape.bodyRef = this
151
+ if (refresh) this.refreshShapes()
152
+ }
153
+ }
154
+ removeShape(shape: Cannon3DShapeComponent, refresh = true) {
155
+ const index = this.shapeRefs.indexOf(shape)
156
+ if (index !== -1) {
157
+ if (shape.bodyRef === this) shape.bodyRef = undefined
158
+ this.shapeRefs.splice(index, 1)
159
+ if (refresh) this.refreshShapes()
160
+ }
161
+ }
162
+ refreshShapes(update = true) {
163
+ let changed = false
164
+ for (const s of this.body.shapes) {
165
+ if (!this.shapeRefs.find(sh=>sh.result.shape === s)) {
166
+ const index = this.body.shapes.indexOf(s)
167
+ if (index !== -1) {
168
+ this.body.shapes.splice(index, 1)
169
+ this.body.shapeOffsets.splice(index, 1)
170
+ this.body.shapeOrientations.splice(index, 1)
171
+ s.body = null
172
+ changed = true
173
+ }
174
+ }
175
+ }
176
+ const bodyObj = this.object
177
+ bodyObj.updateMatrixWorld(true)
178
+ const bodyWorldPos = bodyObj.getWorldPosition(new Vector3())
179
+ const bodyWorldQuat = bodyObj.getWorldQuaternion(new Quaternion())
180
+ for (const s of this.shapeRefs) {
181
+ // s.bodyRef = this
182
+ if (!this.body.shapes.includes(s.result.shape)) {
183
+ const shape = s.result.shape
184
+ this.body.shapes.push(shape)
185
+ const offset = new Vec3()
186
+ const quat = new CQuaternion()
187
+ if(s.result.offset){
188
+ offset.x += s.result.offset.x
189
+ offset.y += s.result.offset.y
190
+ offset.z += s.result.offset.z
191
+ }
192
+ if(s.result.orientation){
193
+ quat.x = s.result.orientation.x
194
+ quat.y = s.result.orientation.y
195
+ quat.z = s.result.orientation.z
196
+ quat.w = s.result.orientation.w
197
+ }
198
+ const shapeObj = s.object
199
+ if(bodyObj !== shapeObj){
200
+ shapeObj.updateMatrixWorld(true)
201
+ const worldPos = shapeObj.getWorldPosition(new Vector3())
202
+ offset.x += worldPos.x - bodyWorldPos.x
203
+ offset.y += worldPos.y - bodyWorldPos.y
204
+ offset.z += worldPos.z - bodyWorldPos.z
205
+ const worldQuat = shapeObj.getWorldQuaternion(new Quaternion())
206
+ const relQuat = worldQuat.multiply(bodyWorldQuat.invert())
207
+ // todo we need to multiply here
208
+ quat.x = relQuat.x
209
+ quat.y = relQuat.y
210
+ quat.z = relQuat.z
211
+ quat.w = relQuat.w
212
+ }
213
+ this.body.shapeOffsets.push(offset)
214
+ this.body.shapeOrientations.push(quat)
215
+ shape.body = this.body
216
+ changed = true
217
+ }
218
+ }
219
+ if (update && changed) {
220
+ this.updateMassProperties()
221
+ this.updateBoundingRadius()
222
+
223
+ this.body.aabbNeedsUpdate = true
224
+ this.object.setDirty?.({source: 'Cannon3DBodyComponent'})
225
+ }
226
+ }
227
+ }