lunchboxjs 0.1.4001 → 0.1.4005
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.
- package/README.md +3 -0
- package/dist/lunchboxjs.js +407 -226
- package/dist/lunchboxjs.min.js +1 -1
- package/dist/lunchboxjs.module.js +405 -227
- package/extras/Gltf.vue +2 -2
- package/extras/OrbitControlsWrapper.vue +9 -10
- package/package.json +24 -3
- package/src/components/LunchboxWrapper/LunchboxWrapper.ts +125 -33
- package/src/components/LunchboxWrapper/resizeCanvas.ts +17 -7
- package/src/components/autoGeneratedComponents.ts +175 -0
- package/src/components/index.ts +5 -188
- package/src/core/createNode.ts +19 -9
- package/src/core/ensure.ts +166 -130
- package/src/core/instantiateThreeObject/index.ts +6 -4
- package/src/core/interaction/index.ts +4 -2
- package/src/core/interaction/setupAutoRaycaster.ts +6 -6
- package/src/core/minidom.ts +85 -27
- package/src/core/update.ts +36 -26
- package/src/index.ts +62 -22
- package/src/nodeOps/createElement.ts +3 -6
- package/src/nodeOps/insert.ts +6 -4
- package/src/nodeOps/remove.ts +19 -5
- package/src/types.ts +10 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { interactables } from '.'
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
ensuredCamera,
|
|
4
|
+
ensuredRaycaster,
|
|
5
|
+
ensureRenderer,
|
|
6
6
|
onBeforeRender,
|
|
7
7
|
} from '..'
|
|
8
8
|
import { globals, Lunch } from '../../'
|
|
@@ -25,7 +25,7 @@ export const setupAutoRaycaster = (node: Lunch.Node<THREE.Raycaster>) => {
|
|
|
25
25
|
// add mouse events once renderer is ready
|
|
26
26
|
let stopWatcher: WatchStopHandle | null = null
|
|
27
27
|
stopWatcher = watch(
|
|
28
|
-
() =>
|
|
28
|
+
() => ensureRenderer.value,
|
|
29
29
|
(renderer) => {
|
|
30
30
|
// make sure renderer exists
|
|
31
31
|
if (!renderer?.instance) return
|
|
@@ -91,8 +91,8 @@ export let currentIntersections: Array<{
|
|
|
91
91
|
|
|
92
92
|
const autoRaycasterBeforeRender = () => {
|
|
93
93
|
// setup
|
|
94
|
-
const raycaster =
|
|
95
|
-
const camera =
|
|
94
|
+
const raycaster = ensuredRaycaster.value?.instance
|
|
95
|
+
const camera = ensuredCamera.value?.instance
|
|
96
96
|
if (!raycaster || !camera) return
|
|
97
97
|
|
|
98
98
|
raycaster.setFromCamera(globals.mousePos.value, camera)
|
package/src/core/minidom.ts
CHANGED
|
@@ -9,7 +9,10 @@ import { Lunch } from '..'
|
|
|
9
9
|
// to make a DOM-like but otherwise agnostic hierarchy structure.
|
|
10
10
|
export namespace MiniDom {
|
|
11
11
|
export class BaseNode {
|
|
12
|
-
constructor(
|
|
12
|
+
constructor(
|
|
13
|
+
options: Partial<BaseNode> = {},
|
|
14
|
+
parent?: MiniDom.BaseNode
|
|
15
|
+
) {
|
|
13
16
|
this.parentNode = options?.parentNode ?? parent ?? null
|
|
14
17
|
this.minidomType = 'MinidomBaseNode'
|
|
15
18
|
this.uuid = options?.uuid ?? createUuid()
|
|
@@ -25,7 +28,9 @@ export namespace MiniDom {
|
|
|
25
28
|
get nextSibling(): MiniDom.BaseNode | null {
|
|
26
29
|
if (!this.parentNode) return null
|
|
27
30
|
|
|
28
|
-
const idx = this.parentNode.children.findIndex(
|
|
31
|
+
const idx = this.parentNode.children.findIndex(
|
|
32
|
+
(n) => n.uuid === this.uuid
|
|
33
|
+
)
|
|
29
34
|
// return next sibling if we're present and not the last child of the parent
|
|
30
35
|
if (idx !== -1 && idx < this.parentNode.children.length - 1) {
|
|
31
36
|
return this.parentNode.children[idx + 1]
|
|
@@ -34,10 +39,15 @@ export namespace MiniDom {
|
|
|
34
39
|
return null
|
|
35
40
|
}
|
|
36
41
|
|
|
37
|
-
insertBefore(
|
|
42
|
+
insertBefore(
|
|
43
|
+
child: MiniDom.BaseNode,
|
|
44
|
+
anchor?: MiniDom.BaseNode | null
|
|
45
|
+
) {
|
|
38
46
|
child.removeAsChildFromAnyParents()
|
|
39
47
|
child.parentNode = this
|
|
40
|
-
const anchorIdx = this.children.findIndex(
|
|
48
|
+
const anchorIdx = this.children.findIndex(
|
|
49
|
+
(n) => n.uuid === anchor?.uuid
|
|
50
|
+
)
|
|
41
51
|
if (anchorIdx !== -1) {
|
|
42
52
|
this.children.splice(anchorIdx, 0, child)
|
|
43
53
|
} else {
|
|
@@ -45,7 +55,7 @@ export namespace MiniDom {
|
|
|
45
55
|
}
|
|
46
56
|
}
|
|
47
57
|
removeChild(child: MiniDom.BaseNode) {
|
|
48
|
-
const idx = this.children.findIndex(n => n?.uuid === child?.uuid)
|
|
58
|
+
const idx = this.children.findIndex((n) => n?.uuid === child?.uuid)
|
|
49
59
|
if (idx !== -1) {
|
|
50
60
|
this.children.splice(idx, 1)
|
|
51
61
|
}
|
|
@@ -59,7 +69,7 @@ export namespace MiniDom {
|
|
|
59
69
|
// remove child from any other parents
|
|
60
70
|
child.removeAsChildFromAnyParents()
|
|
61
71
|
|
|
62
|
-
// add to this node
|
|
72
|
+
// add to this node
|
|
63
73
|
child.parentNode = this
|
|
64
74
|
this.insertBefore(child, null)
|
|
65
75
|
}
|
|
@@ -76,7 +86,7 @@ export namespace MiniDom {
|
|
|
76
86
|
return output
|
|
77
87
|
}
|
|
78
88
|
|
|
79
|
-
/** Drop this node. Removes parent's knowledge of this node
|
|
89
|
+
/** Drop this node. Removes parent's knowledge of this node
|
|
80
90
|
* and resets this node's internal parent. */
|
|
81
91
|
drop() {
|
|
82
92
|
// remove parent
|
|
@@ -89,13 +99,23 @@ export namespace MiniDom {
|
|
|
89
99
|
// TODO: depth-first vs breadth-first
|
|
90
100
|
walk(callback: (item: MiniDom.BaseNode) => boolean) {
|
|
91
101
|
const queue = [this, ...this.children] as MiniDom.BaseNode[]
|
|
102
|
+
const traversed: MiniDom.BaseNode[] = []
|
|
92
103
|
let canContinue = true
|
|
93
104
|
while (queue.length && canContinue) {
|
|
94
105
|
const current = queue.shift()
|
|
95
106
|
if (current) {
|
|
96
|
-
|
|
107
|
+
if (traversed.includes(current)) continue
|
|
108
|
+
|
|
109
|
+
traversed.push(current)
|
|
110
|
+
queue.push(
|
|
111
|
+
...current.children.filter(
|
|
112
|
+
(child) => !traversed.includes(child)
|
|
113
|
+
)
|
|
114
|
+
)
|
|
97
115
|
canContinue = callback(current)
|
|
98
|
-
} else {
|
|
116
|
+
} else {
|
|
117
|
+
canContinue = false
|
|
118
|
+
}
|
|
99
119
|
}
|
|
100
120
|
}
|
|
101
121
|
|
|
@@ -104,12 +124,18 @@ export namespace MiniDom {
|
|
|
104
124
|
minidomType: MiniDom.NodeType
|
|
105
125
|
|
|
106
126
|
removeAsChildFromAnyParents() {
|
|
107
|
-
allNodes.forEach(node => node.removeChild(this))
|
|
127
|
+
allNodes.forEach((node) => node.removeChild(this))
|
|
108
128
|
}
|
|
109
129
|
}
|
|
110
130
|
|
|
111
|
-
export class RendererBaseNode
|
|
112
|
-
|
|
131
|
+
export class RendererBaseNode
|
|
132
|
+
extends MiniDom.BaseNode
|
|
133
|
+
implements Lunch.MetaBase
|
|
134
|
+
{
|
|
135
|
+
constructor(
|
|
136
|
+
options: Partial<Lunch.MetaBase> = {},
|
|
137
|
+
parent?: MiniDom.BaseNode
|
|
138
|
+
) {
|
|
113
139
|
super(options, parent)
|
|
114
140
|
this.minidomType = 'RendererNode'
|
|
115
141
|
|
|
@@ -131,8 +157,8 @@ export namespace MiniDom {
|
|
|
131
157
|
drop() {
|
|
132
158
|
super.drop()
|
|
133
159
|
// handle remove functions
|
|
134
|
-
Object.keys(this.eventListenerRemoveFunctions).forEach(key => {
|
|
135
|
-
this.eventListenerRemoveFunctions[key].forEach(func => func())
|
|
160
|
+
Object.keys(this.eventListenerRemoveFunctions).forEach((key) => {
|
|
161
|
+
this.eventListenerRemoveFunctions[key].forEach((func) => func())
|
|
136
162
|
})
|
|
137
163
|
}
|
|
138
164
|
}
|
|
@@ -141,18 +167,31 @@ export namespace MiniDom {
|
|
|
141
167
|
// SPECIFIC RENDERER NODES BELOW
|
|
142
168
|
// ====================
|
|
143
169
|
|
|
144
|
-
export class RendererRootNode
|
|
145
|
-
|
|
170
|
+
export class RendererRootNode
|
|
171
|
+
extends MiniDom.RendererBaseNode
|
|
172
|
+
implements Lunch.RootMeta
|
|
173
|
+
{
|
|
174
|
+
constructor(
|
|
175
|
+
options: Partial<Lunch.RootMeta> = {},
|
|
176
|
+
parent?: MiniDom.BaseNode
|
|
177
|
+
) {
|
|
146
178
|
super(options, parent)
|
|
147
|
-
this.domElement =
|
|
179
|
+
this.domElement =
|
|
180
|
+
options.domElement ?? document.createElement('div')
|
|
148
181
|
}
|
|
149
182
|
|
|
150
183
|
domElement: HTMLElement
|
|
151
184
|
isLunchboxRootNode = true
|
|
152
185
|
}
|
|
153
186
|
|
|
154
|
-
export class RendererCommentNode
|
|
155
|
-
|
|
187
|
+
export class RendererCommentNode
|
|
188
|
+
extends MiniDom.RendererBaseNode
|
|
189
|
+
implements Lunch.CommentMeta
|
|
190
|
+
{
|
|
191
|
+
constructor(
|
|
192
|
+
options: Partial<Lunch.CommentMeta> = {},
|
|
193
|
+
parent?: MiniDom.BaseNode
|
|
194
|
+
) {
|
|
156
195
|
super(options, parent)
|
|
157
196
|
this.text = options.text ?? ''
|
|
158
197
|
}
|
|
@@ -160,17 +199,30 @@ export namespace MiniDom {
|
|
|
160
199
|
text: string
|
|
161
200
|
}
|
|
162
201
|
|
|
163
|
-
export class RendererDomNode
|
|
164
|
-
|
|
202
|
+
export class RendererDomNode
|
|
203
|
+
extends MiniDom.RendererBaseNode
|
|
204
|
+
implements Lunch.DomMeta
|
|
205
|
+
{
|
|
206
|
+
constructor(
|
|
207
|
+
options: Partial<Lunch.DomMeta> = {},
|
|
208
|
+
parent?: MiniDom.BaseNode
|
|
209
|
+
) {
|
|
165
210
|
super(options, parent)
|
|
166
|
-
this.domElement =
|
|
211
|
+
this.domElement =
|
|
212
|
+
options.domElement ?? document.createElement('div')
|
|
167
213
|
}
|
|
168
214
|
|
|
169
215
|
domElement: HTMLElement
|
|
170
216
|
}
|
|
171
217
|
|
|
172
|
-
export class RendererTextNode
|
|
173
|
-
|
|
218
|
+
export class RendererTextNode
|
|
219
|
+
extends MiniDom.RendererBaseNode
|
|
220
|
+
implements Lunch.TextMeta
|
|
221
|
+
{
|
|
222
|
+
constructor(
|
|
223
|
+
options: Partial<Lunch.TextMeta> = {},
|
|
224
|
+
parent?: MiniDom.BaseNode
|
|
225
|
+
) {
|
|
174
226
|
super(options, parent)
|
|
175
227
|
this.text = options.text ?? ''
|
|
176
228
|
}
|
|
@@ -178,8 +230,14 @@ export namespace MiniDom {
|
|
|
178
230
|
text: string
|
|
179
231
|
}
|
|
180
232
|
|
|
181
|
-
export class RendererStandardNode<T = THREE.Object3D>
|
|
182
|
-
|
|
233
|
+
export class RendererStandardNode<T = THREE.Object3D>
|
|
234
|
+
extends MiniDom.RendererBaseNode
|
|
235
|
+
implements Lunch.StandardMeta<T>
|
|
236
|
+
{
|
|
237
|
+
constructor(
|
|
238
|
+
options: Partial<Lunch.StandardMeta<T>> = {},
|
|
239
|
+
parent?: MiniDom.BaseNode
|
|
240
|
+
) {
|
|
183
241
|
super(options, parent)
|
|
184
242
|
this.attached = options.attached ?? []
|
|
185
243
|
this.attachedArray = options.attachedArray ?? {}
|
|
@@ -199,4 +257,4 @@ export function isMinidomNode(item: any): item is MiniDom.RendererBaseNode {
|
|
|
199
257
|
}
|
|
200
258
|
|
|
201
259
|
export const rootNode = new MiniDom.RendererRootNode()
|
|
202
|
-
rootNode.minidomType = 'RootNode'
|
|
260
|
+
rootNode.minidomType = 'RootNode'
|
package/src/core/update.ts
CHANGED
|
@@ -1,19 +1,24 @@
|
|
|
1
|
-
import { ensureRenderer,
|
|
1
|
+
import { ensureRenderer, ensuredScene, ensuredCamera } from '.'
|
|
2
2
|
import { Lunch } from '..'
|
|
3
|
+
import { toRaw } from 'vue'
|
|
3
4
|
|
|
4
5
|
let frameID: number
|
|
5
6
|
|
|
6
7
|
export const beforeRender = [] as Lunch.UpdateCallback[]
|
|
7
8
|
export const afterRender = [] as Lunch.UpdateCallback[]
|
|
8
9
|
|
|
9
|
-
export const update: Lunch.UpdateCallback = (opts
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
export const update: Lunch.UpdateCallback = (opts) => {
|
|
11
|
+
// request next frame
|
|
12
|
+
frameID = requestAnimationFrame(() =>
|
|
13
|
+
update({
|
|
14
|
+
app: opts.app,
|
|
15
|
+
renderer: ensureRenderer.value?.instance,
|
|
16
|
+
scene: ensuredScene.value.instance,
|
|
17
|
+
camera: ensuredCamera.value?.instance,
|
|
18
|
+
})
|
|
19
|
+
)
|
|
16
20
|
|
|
21
|
+
// prep options
|
|
17
22
|
const { app, renderer, scene, camera } = opts
|
|
18
23
|
|
|
19
24
|
// BEFORE RENDER
|
|
@@ -25,7 +30,11 @@ export const update: Lunch.UpdateCallback = (opts: {
|
|
|
25
30
|
|
|
26
31
|
// RENDER
|
|
27
32
|
if (renderer && scene && camera) {
|
|
28
|
-
|
|
33
|
+
if (app.customRender) {
|
|
34
|
+
app.customRender(opts)
|
|
35
|
+
} else {
|
|
36
|
+
renderer.render(toRaw(scene), toRaw(camera))
|
|
37
|
+
}
|
|
29
38
|
}
|
|
30
39
|
|
|
31
40
|
// AFTER RENDER
|
|
@@ -34,23 +43,6 @@ export const update: Lunch.UpdateCallback = (opts: {
|
|
|
34
43
|
cb(opts)
|
|
35
44
|
}
|
|
36
45
|
})
|
|
37
|
-
|
|
38
|
-
/*
|
|
39
|
-
frameID = requestAnimationFrame(() => update(renderer, scene, camera))
|
|
40
|
-
|
|
41
|
-
// Make sure we have all necessary components
|
|
42
|
-
if (!renderer) {
|
|
43
|
-
renderer = ensureRenderer().instance
|
|
44
|
-
}
|
|
45
|
-
if (!scene) {
|
|
46
|
-
scene = ensureScene().instance
|
|
47
|
-
}
|
|
48
|
-
if (!camera) {
|
|
49
|
-
camera = ensureCamera().instance
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
*/
|
|
54
46
|
}
|
|
55
47
|
|
|
56
48
|
export const onBeforeRender = (cb: Lunch.UpdateCallback, index = Infinity) => {
|
|
@@ -61,6 +53,15 @@ export const onBeforeRender = (cb: Lunch.UpdateCallback, index = Infinity) => {
|
|
|
61
53
|
}
|
|
62
54
|
}
|
|
63
55
|
|
|
56
|
+
export const offBeforeRender = (cb: Lunch.UpdateCallback | number) => {
|
|
57
|
+
if (isFinite(cb as number)) {
|
|
58
|
+
beforeRender.splice(cb as number, 1)
|
|
59
|
+
} else {
|
|
60
|
+
const idx = beforeRender.findIndex((v) => v == cb)
|
|
61
|
+
beforeRender.splice(idx, 1)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
64
65
|
export const onAfterRender = (cb: Lunch.UpdateCallback, index = Infinity) => {
|
|
65
66
|
if (index === Infinity) {
|
|
66
67
|
afterRender.push(cb)
|
|
@@ -69,6 +70,15 @@ export const onAfterRender = (cb: Lunch.UpdateCallback, index = Infinity) => {
|
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
export const offAfterRender = (cb: Lunch.UpdateCallback | number) => {
|
|
74
|
+
if (isFinite(cb as number)) {
|
|
75
|
+
afterRender.splice(cb as number, 1)
|
|
76
|
+
} else {
|
|
77
|
+
const idx = afterRender.findIndex((v) => v == cb)
|
|
78
|
+
afterRender.splice(idx, 1)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
72
82
|
export const cancelUpdate = () => {
|
|
73
83
|
if (frameID) cancelAnimationFrame(frameID)
|
|
74
84
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,21 +1,28 @@
|
|
|
1
|
-
import { computed, createRenderer, Component, ref } from 'vue'
|
|
1
|
+
import { computed, createRenderer, Component, ref, watch } from 'vue'
|
|
2
2
|
import { nodeOps } from './nodeOps'
|
|
3
3
|
import {
|
|
4
|
-
createdCamera,
|
|
5
|
-
createdRenderer,
|
|
6
|
-
|
|
4
|
+
// createdCamera,
|
|
5
|
+
// createdRenderer,
|
|
6
|
+
// autoScene,
|
|
7
|
+
ensuredCamera,
|
|
8
|
+
ensureRenderer,
|
|
9
|
+
ensuredScene,
|
|
7
10
|
ensureRootNode,
|
|
8
11
|
extend,
|
|
9
12
|
inputActive,
|
|
10
13
|
mousePos,
|
|
11
14
|
rootUuid,
|
|
12
|
-
update,
|
|
13
15
|
} from './core'
|
|
14
16
|
import { components } from './components'
|
|
15
17
|
import { Lunch } from './types'
|
|
16
18
|
|
|
17
19
|
export { lunchboxRootNode as lunchboxTree } from './core'
|
|
18
|
-
export {
|
|
20
|
+
export {
|
|
21
|
+
offAfterRender,
|
|
22
|
+
offBeforeRender,
|
|
23
|
+
onBeforeRender,
|
|
24
|
+
onAfterRender,
|
|
25
|
+
} from './core'
|
|
19
26
|
export * from './types'
|
|
20
27
|
|
|
21
28
|
// Utilities
|
|
@@ -26,22 +33,43 @@ export const globals = {
|
|
|
26
33
|
dpr: ref(1),
|
|
27
34
|
inputActive,
|
|
28
35
|
mousePos,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const camera = computed(() => ensuredCamera.value?.instance ?? null)
|
|
39
|
+
export const renderer = computed(() => ensureRenderer.value?.instance ?? null)
|
|
40
|
+
export const scene = computed(() => ensuredScene.value.instance)
|
|
29
41
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
42
|
+
// CUSTOM RENDER SUPPORT
|
|
43
|
+
// ====================
|
|
44
|
+
let app: Lunch.App | null = null
|
|
45
|
+
let queuedCustomRenderFunction:
|
|
46
|
+
| ((opts: Lunch.UpdateCallbackProperties) => void)
|
|
47
|
+
| null = null
|
|
48
|
+
|
|
49
|
+
/** Set a custom render function, overriding the Lunchbox app's default render function.
|
|
50
|
+
* Changing this requires the user to manually render their scene.
|
|
51
|
+
*/
|
|
52
|
+
export const setCustomRender = (
|
|
53
|
+
render: (opts: Lunch.UpdateCallbackProperties) => void
|
|
54
|
+
) => {
|
|
55
|
+
if (app) app.setCustomRender(render)
|
|
56
|
+
else queuedCustomRenderFunction = render
|
|
33
57
|
}
|
|
34
58
|
|
|
35
|
-
|
|
36
|
-
export const
|
|
37
|
-
|
|
59
|
+
/** Clear the active app's custom render function. */
|
|
60
|
+
export const clearCustomRender = () => {
|
|
61
|
+
if (app) app.clearCustomRender()
|
|
62
|
+
else queuedCustomRenderFunction = null
|
|
63
|
+
}
|
|
38
64
|
|
|
65
|
+
// CREATE APP
|
|
66
|
+
// ====================
|
|
39
67
|
export const createApp = (root: Component) => {
|
|
40
|
-
|
|
68
|
+
app = createRenderer(nodeOps).createApp(root) as Lunch.App
|
|
41
69
|
|
|
42
70
|
// register all components
|
|
43
71
|
Object.keys(components).forEach((key) => {
|
|
44
|
-
app
|
|
72
|
+
app!.component(key, (components as any)[key])
|
|
45
73
|
})
|
|
46
74
|
|
|
47
75
|
// update mount function to match Lunchbox.Node
|
|
@@ -58,22 +86,34 @@ export const createApp = (root: Component) => {
|
|
|
58
86
|
type: 'root',
|
|
59
87
|
uuid: rootUuid,
|
|
60
88
|
})
|
|
61
|
-
app
|
|
89
|
+
app!.rootNode = rootNode
|
|
62
90
|
const mounted = mount(rootNode, ...args)
|
|
63
91
|
return mounted
|
|
64
92
|
}
|
|
65
93
|
|
|
66
94
|
// embed .extend function
|
|
67
95
|
app.extend = (targets: Record<string, any>) => {
|
|
68
|
-
extend({ app
|
|
69
|
-
return app
|
|
96
|
+
extend({ app: app!, ...targets })
|
|
97
|
+
return app!
|
|
70
98
|
}
|
|
71
99
|
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
100
|
+
// prep for custom render support
|
|
101
|
+
app.setCustomRender = (
|
|
102
|
+
newRender: (opts: Lunch.UpdateCallbackProperties) => void
|
|
103
|
+
) => {
|
|
104
|
+
app!.customRender = newRender
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// add queued custom render if we have one
|
|
108
|
+
if (queuedCustomRenderFunction) {
|
|
109
|
+
app.setCustomRender(queuedCustomRenderFunction)
|
|
110
|
+
queuedCustomRenderFunction = null
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// add custom render removal
|
|
114
|
+
app.clearCustomRender = () => {
|
|
115
|
+
app!.customRender = null
|
|
116
|
+
}
|
|
77
117
|
|
|
78
118
|
// done
|
|
79
119
|
return app
|
|
@@ -2,10 +2,7 @@ import { createDomNode, createNode } from '../core'
|
|
|
2
2
|
import { isLunchboxDomComponent } from '../utils'
|
|
3
3
|
import { Lunch } from '..'
|
|
4
4
|
|
|
5
|
-
const autoAttach = [
|
|
6
|
-
'geometry',
|
|
7
|
-
'material',
|
|
8
|
-
]
|
|
5
|
+
const autoAttach = ['geometry', 'material']
|
|
9
6
|
|
|
10
7
|
export const createElement = (
|
|
11
8
|
type: string,
|
|
@@ -29,7 +26,7 @@ export const createElement = (
|
|
|
29
26
|
const node = createNode(options)
|
|
30
27
|
|
|
31
28
|
// autoattach
|
|
32
|
-
autoAttach.forEach(key => {
|
|
29
|
+
autoAttach.forEach((key) => {
|
|
33
30
|
if (type.toLowerCase().endsWith(key)) {
|
|
34
31
|
node.props.attach = key
|
|
35
32
|
}
|
|
@@ -37,4 +34,4 @@ export const createElement = (
|
|
|
37
34
|
// TODO: array autoattach
|
|
38
35
|
|
|
39
36
|
return node
|
|
40
|
-
}
|
|
37
|
+
}
|
package/src/nodeOps/insert.ts
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
isLunchboxRootNode,
|
|
4
4
|
isLunchboxStandardNode,
|
|
5
5
|
} from '../utils'
|
|
6
|
-
import { ensureRootNode,
|
|
6
|
+
import { ensureRootNode, ensuredScene } from '../core'
|
|
7
7
|
import { MiniDom } from '../core/minidom'
|
|
8
8
|
import { Lunch } from '..'
|
|
9
9
|
|
|
@@ -54,11 +54,11 @@ export const insert = (
|
|
|
54
54
|
// add to scene if parent is the wrapper node
|
|
55
55
|
if (
|
|
56
56
|
child.metaType === 'standardMeta' &&
|
|
57
|
-
child.type !== '
|
|
57
|
+
child.type !== 'scene' &&
|
|
58
58
|
isLunchboxRootNode(effectiveParent)
|
|
59
59
|
) {
|
|
60
60
|
// ensure scene exists
|
|
61
|
-
const sceneNode =
|
|
61
|
+
const sceneNode = ensuredScene.value
|
|
62
62
|
|
|
63
63
|
if (sceneNode.instance && child) {
|
|
64
64
|
sceneNode.addChild(child)
|
|
@@ -68,7 +68,9 @@ export const insert = (
|
|
|
68
68
|
child.instance.isObject3D &&
|
|
69
69
|
sceneNode.instance
|
|
70
70
|
) {
|
|
71
|
-
sceneNode
|
|
71
|
+
if (sceneNode !== child) {
|
|
72
|
+
sceneNode.instance.add(child.instance)
|
|
73
|
+
}
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
// add to hierarchy otherwise
|
package/src/nodeOps/remove.ts
CHANGED
|
@@ -1,24 +1,38 @@
|
|
|
1
1
|
import { allNodes, MiniDom } from '../core'
|
|
2
|
-
import { isLunchboxStandardNode } from
|
|
2
|
+
import { isLunchboxStandardNode } from '../utils'
|
|
3
|
+
import { overrides } from '../core'
|
|
3
4
|
|
|
4
5
|
export const remove = (node: MiniDom.RendererBaseNode) => {
|
|
5
6
|
if (!node) return
|
|
7
|
+
const overrideKeys = Object.keys(overrides)
|
|
6
8
|
|
|
7
9
|
// prep subtree
|
|
8
10
|
const subtree: MiniDom.BaseNode[] = []
|
|
9
|
-
node.walk(descendant => {
|
|
11
|
+
node.walk((descendant) => {
|
|
10
12
|
subtree.push(descendant)
|
|
11
13
|
return true
|
|
12
14
|
})
|
|
13
15
|
|
|
14
16
|
// clean up subtree
|
|
15
17
|
subtree.forEach((n) => {
|
|
18
|
+
const overrideKey = overrideKeys.find(
|
|
19
|
+
(key) => overrides[key]?.uuid === n.uuid
|
|
20
|
+
)
|
|
21
|
+
// if this node is an override, remove it from the overrides list
|
|
22
|
+
if (overrideKey) {
|
|
23
|
+
overrides[overrideKey] = null
|
|
24
|
+
}
|
|
25
|
+
|
|
16
26
|
if (isLunchboxStandardNode(n)) {
|
|
17
27
|
// try to remove three object
|
|
18
28
|
n.instance?.removeFromParent?.()
|
|
19
29
|
|
|
20
30
|
// try to dispose three object
|
|
21
|
-
const dispose =
|
|
31
|
+
const dispose =
|
|
32
|
+
// calling `dispose` on a scene triggers an error,
|
|
33
|
+
// so let's ignore if this node is a scene
|
|
34
|
+
n.type !== 'scene' &&
|
|
35
|
+
((n.instance as any)?.dispose as (() => void) | null)
|
|
22
36
|
if (dispose) dispose.bind(n.instance)()
|
|
23
37
|
n.instance = null
|
|
24
38
|
}
|
|
@@ -27,9 +41,9 @@ export const remove = (node: MiniDom.RendererBaseNode) => {
|
|
|
27
41
|
n.drop()
|
|
28
42
|
|
|
29
43
|
// remove Lunchbox node from main list
|
|
30
|
-
const idx = allNodes.findIndex(v => v.uuid === n.uuid)
|
|
44
|
+
const idx = allNodes.findIndex((v) => v.uuid === n.uuid)
|
|
31
45
|
if (idx !== -1) {
|
|
32
46
|
allNodes.splice(idx, 1)
|
|
33
47
|
}
|
|
34
48
|
})
|
|
35
|
-
}
|
|
49
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -7,8 +7,13 @@ type RendererStandardNode<T = THREE.Object3D> =
|
|
|
7
7
|
export declare namespace Lunch {
|
|
8
8
|
/** Lunchbox app. */
|
|
9
9
|
type App = VueApp<any> & {
|
|
10
|
+
clearCustomRender: () => void
|
|
11
|
+
customRender: ((opts: UpdateCallbackProperties) => void) | null
|
|
10
12
|
extend: (v: Record<string, any>) => App
|
|
11
13
|
rootNode: RootNode
|
|
14
|
+
setCustomRender: (
|
|
15
|
+
update: (opts: UpdateCallbackProperties) => void
|
|
16
|
+
) => void
|
|
12
17
|
update: UpdateCallback
|
|
13
18
|
}
|
|
14
19
|
|
|
@@ -133,8 +138,13 @@ export declare namespace Lunch {
|
|
|
133
138
|
|
|
134
139
|
interface WrapperProps {
|
|
135
140
|
background?: string
|
|
141
|
+
cameraArgs?: any[]
|
|
142
|
+
cameraLook?: [number, number, number]
|
|
143
|
+
cameraLookAt?: [number, number, number]
|
|
136
144
|
cameraPosition?: [number, number, number]
|
|
137
145
|
dpr?: number
|
|
146
|
+
ortho?: boolean
|
|
147
|
+
orthographic?: boolean
|
|
138
148
|
rendererProperties?: Partial<THREE.WebGLRenderer>
|
|
139
149
|
shadow?: ShadowSugar
|
|
140
150
|
transparent?: boolean
|