lunchboxjs 0.2.1020 → 2.0.0-beta.1
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/dist/lunchboxjs.cjs +46 -0
- package/dist/lunchboxjs.d.ts +1 -0
- package/dist/lunchboxjs.js +1632 -1962
- package/dist/lunchboxjs.umd.cjs +46 -0
- package/package.json +36 -81
- package/LICENSE.md +0 -7
- package/README.md +0 -17
- package/dist/lunchboxjs.es.d.ts +0 -1
- package/dist/lunchboxjs.min.js +0 -1
- package/dist/lunchboxjs.module.js +0 -1924
- package/dist/lunchboxjs.umd.d.ts +0 -1
- package/src/components/LunchboxEventHandlers.tsx +0 -237
- package/src/components/LunchboxWrapper/LunchboxScene.tsx +0 -8
- package/src/components/LunchboxWrapper/LunchboxWrapper.tsx +0 -341
- package/src/components/LunchboxWrapper/prepCanvas.ts +0 -55
- package/src/components/LunchboxWrapper/resizeCanvas.ts +0 -41
- package/src/components/autoGeneratedComponents.ts +0 -175
- package/src/components/index.ts +0 -31
- package/src/core/createNode.ts +0 -71
- package/src/core/extend.ts +0 -25
- package/src/core/index.ts +0 -7
- package/src/core/instantiateThreeObject/index.ts +0 -37
- package/src/core/instantiateThreeObject/processProps.ts +0 -40
- package/src/core/interaction.ts +0 -55
- package/src/core/minidom.ts +0 -256
- package/src/core/update.ts +0 -149
- package/src/core/updateObjectProp.ts +0 -153
- package/src/index.ts +0 -400
- package/src/keys.ts +0 -31
- package/src/nodeOps/createElement.ts +0 -34
- package/src/nodeOps/index.ts +0 -83
- package/src/nodeOps/insert.ts +0 -165
- package/src/nodeOps/remove.ts +0 -32
- package/src/plugins/bridge/BridgeComponent.tsx +0 -60
- package/src/plugins/bridge/bridge.ts +0 -9
- package/src/types.ts +0 -186
- package/src/utils/find.ts +0 -24
- package/src/utils/get.ts +0 -18
- package/src/utils/index.ts +0 -60
- package/src/utils/isNumber.ts +0 -87
- package/src/utils/set.ts +0 -14
package/dist/lunchboxjs.umd.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "../src/index"
|
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
import { defineComponent, onBeforeUnmount, ref, watch } from 'vue'
|
|
2
|
-
import {
|
|
3
|
-
useCamera,
|
|
4
|
-
Lunch,
|
|
5
|
-
useGlobals,
|
|
6
|
-
useLunchboxInteractables,
|
|
7
|
-
onRendererReady,
|
|
8
|
-
} from '..'
|
|
9
|
-
import * as THREE from 'three'
|
|
10
|
-
import { offBeforeRender, onBeforeRender } from '../core'
|
|
11
|
-
|
|
12
|
-
export const LunchboxEventHandlers = defineComponent({
|
|
13
|
-
name: 'LunchboxEventHandlers',
|
|
14
|
-
setup() {
|
|
15
|
-
const interactables = useLunchboxInteractables()
|
|
16
|
-
const globals = useGlobals()
|
|
17
|
-
const mousePos = ref({ x: Infinity, y: Infinity })
|
|
18
|
-
const inputActive = ref(false)
|
|
19
|
-
|
|
20
|
-
let currentIntersections: Array<{
|
|
21
|
-
element: Lunch.Node
|
|
22
|
-
intersection: THREE.Intersection<THREE.Object3D>
|
|
23
|
-
}> = []
|
|
24
|
-
|
|
25
|
-
const raycaster = new THREE.Raycaster(
|
|
26
|
-
new THREE.Vector3(),
|
|
27
|
-
new THREE.Vector3(0, 0, -1)
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
const fireEventsFromIntersections = ({
|
|
31
|
-
element,
|
|
32
|
-
eventKeys,
|
|
33
|
-
intersection,
|
|
34
|
-
}: {
|
|
35
|
-
element: Lunch.Node
|
|
36
|
-
eventKeys: Array<Lunch.EventKey>
|
|
37
|
-
intersection: THREE.Intersection<THREE.Object3D>
|
|
38
|
-
}) => {
|
|
39
|
-
if (!element) return
|
|
40
|
-
eventKeys.forEach((eventKey) => {
|
|
41
|
-
if (element.eventListeners[eventKey]) {
|
|
42
|
-
element.eventListeners[eventKey].forEach((cb) => {
|
|
43
|
-
cb({ intersection })
|
|
44
|
-
})
|
|
45
|
-
}
|
|
46
|
-
})
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// add mouse listener to renderer DOM element when the element is ready
|
|
50
|
-
onRendererReady((v) => {
|
|
51
|
-
if (!v?.domElement) return
|
|
52
|
-
|
|
53
|
-
// we have a DOM element, so let's add mouse listeners
|
|
54
|
-
const { domElement } = v
|
|
55
|
-
|
|
56
|
-
const mouseMoveListener = (evt: PointerEvent) => {
|
|
57
|
-
const screenWidth = (domElement.width ?? 1) / globals.dpr
|
|
58
|
-
const screenHeight = (domElement.height ?? 1) / globals.dpr
|
|
59
|
-
mousePos.value.x = (evt.offsetX / screenWidth) * 2 - 1
|
|
60
|
-
mousePos.value.y = -(evt.offsetY / screenHeight) * 2 + 1
|
|
61
|
-
}
|
|
62
|
-
const mouseDownListener = () => (inputActive.value = true)
|
|
63
|
-
const mouseUpListener = () => (inputActive.value = false)
|
|
64
|
-
|
|
65
|
-
// add mouse events
|
|
66
|
-
domElement.addEventListener('pointermove', mouseMoveListener)
|
|
67
|
-
domElement.addEventListener('pointerdown', mouseDownListener)
|
|
68
|
-
domElement.addEventListener('pointerup', mouseUpListener)
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
const camera = useCamera()
|
|
72
|
-
const update = () => {
|
|
73
|
-
const c = camera.value
|
|
74
|
-
if (!c) return
|
|
75
|
-
|
|
76
|
-
// console.log(camera.value)
|
|
77
|
-
|
|
78
|
-
raycaster.setFromCamera(mousePos.value, c)
|
|
79
|
-
const intersections = raycaster.intersectObjects(
|
|
80
|
-
interactables?.value.map(
|
|
81
|
-
(v) => v.instance as any as THREE.Object3D
|
|
82
|
-
) ?? []
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
let enterValues: Array<THREE.Intersection<THREE.Object3D>> = [],
|
|
86
|
-
sameValues: Array<THREE.Intersection<THREE.Object3D>> = [],
|
|
87
|
-
leaveValues: Array<THREE.Intersection<THREE.Object3D>> = [],
|
|
88
|
-
entering: Array<{
|
|
89
|
-
element: Lunch.Node
|
|
90
|
-
intersection: THREE.Intersection<THREE.Object3D>
|
|
91
|
-
}> = [],
|
|
92
|
-
staying: Array<{
|
|
93
|
-
element: Lunch.Node
|
|
94
|
-
intersection: THREE.Intersection<THREE.Object3D>
|
|
95
|
-
}> = []
|
|
96
|
-
|
|
97
|
-
// intersection arrays
|
|
98
|
-
leaveValues = currentIntersections.map((v) => v.intersection)
|
|
99
|
-
|
|
100
|
-
// element arrays
|
|
101
|
-
intersections?.forEach((intersection) => {
|
|
102
|
-
const currentIdx = currentIntersections.findIndex(
|
|
103
|
-
(v) => v.intersection.object === intersection.object
|
|
104
|
-
)
|
|
105
|
-
if (currentIdx === -1) {
|
|
106
|
-
// new intersection
|
|
107
|
-
enterValues.push(intersection)
|
|
108
|
-
|
|
109
|
-
const found = interactables?.value.find(
|
|
110
|
-
(v) => v.instance?.uuid === intersection.object.uuid
|
|
111
|
-
)
|
|
112
|
-
if (found) {
|
|
113
|
-
entering.push({ element: found, intersection })
|
|
114
|
-
}
|
|
115
|
-
} else {
|
|
116
|
-
// existing intersection
|
|
117
|
-
sameValues.push(intersection)
|
|
118
|
-
|
|
119
|
-
const found = interactables?.value.find(
|
|
120
|
-
(v) => v.instance?.uuid === intersection.object.uuid
|
|
121
|
-
)
|
|
122
|
-
if (found) {
|
|
123
|
-
staying.push({ element: found, intersection })
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
// this is a current intersection, so it won't be in our `leave` array
|
|
127
|
-
const leaveIdx = leaveValues.findIndex(
|
|
128
|
-
(v) => v.object.uuid === intersection.object.uuid
|
|
129
|
-
)
|
|
130
|
-
if (leaveIdx !== -1) {
|
|
131
|
-
leaveValues.splice(leaveIdx, 1)
|
|
132
|
-
}
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
const leaving: Array<{
|
|
136
|
-
element: Lunch.Node
|
|
137
|
-
intersection: THREE.Intersection<THREE.Object3D>
|
|
138
|
-
}> = leaveValues.map((intersection) => {
|
|
139
|
-
return {
|
|
140
|
-
element: interactables?.value.find(
|
|
141
|
-
(interactable) =>
|
|
142
|
-
interactable.instance?.uuid ===
|
|
143
|
-
intersection.object.uuid
|
|
144
|
-
) as any as Lunch.Node,
|
|
145
|
-
intersection,
|
|
146
|
-
}
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
// new interactions
|
|
150
|
-
entering.forEach(({ element, intersection }) => {
|
|
151
|
-
fireEventsFromIntersections({
|
|
152
|
-
element,
|
|
153
|
-
eventKeys: ['onPointerEnter'],
|
|
154
|
-
intersection,
|
|
155
|
-
})
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
// unchanged interactions
|
|
159
|
-
staying.forEach(({ element, intersection }) => {
|
|
160
|
-
const eventKeys: Array<Lunch.EventKey> = [
|
|
161
|
-
'onPointerOver',
|
|
162
|
-
'onPointerMove',
|
|
163
|
-
]
|
|
164
|
-
fireEventsFromIntersections({
|
|
165
|
-
element,
|
|
166
|
-
eventKeys,
|
|
167
|
-
intersection,
|
|
168
|
-
})
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
// exited interactions
|
|
172
|
-
leaving.forEach(({ element, intersection }) => {
|
|
173
|
-
const eventKeys: Array<Lunch.EventKey> = [
|
|
174
|
-
'onPointerLeave',
|
|
175
|
-
'onPointerOut',
|
|
176
|
-
]
|
|
177
|
-
fireEventsFromIntersections({
|
|
178
|
-
element,
|
|
179
|
-
eventKeys,
|
|
180
|
-
intersection,
|
|
181
|
-
})
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
currentIntersections = ([] as any).concat(entering, staying)
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// update function
|
|
188
|
-
onBeforeRender(update)
|
|
189
|
-
|
|
190
|
-
const teardown = () => offBeforeRender(update)
|
|
191
|
-
onBeforeUnmount(teardown)
|
|
192
|
-
|
|
193
|
-
const clickEventKeys: Lunch.EventKey[] = [
|
|
194
|
-
'onClick',
|
|
195
|
-
'onPointerDown',
|
|
196
|
-
'onPointerUp',
|
|
197
|
-
]
|
|
198
|
-
watch(inputActive, (isDown) => {
|
|
199
|
-
// run raycaster on click (necessary when `update` is not automatically called,
|
|
200
|
-
// for example in `updateSource` functions)
|
|
201
|
-
update()
|
|
202
|
-
|
|
203
|
-
// meshes with multiple intersections receive multiple callbacks by default -
|
|
204
|
-
// let's make it so they only receive one callback of each type per frame.
|
|
205
|
-
// (ie usually when you click on a mesh, you expect only one click event to fire, even
|
|
206
|
-
// if there are technically multiple intersections with that mesh)
|
|
207
|
-
const uuidsInteractedWithThisFrame: string[] = []
|
|
208
|
-
currentIntersections.forEach((v) => {
|
|
209
|
-
clickEventKeys.forEach((key) => {
|
|
210
|
-
const id = v.element.uuid + key
|
|
211
|
-
if (
|
|
212
|
-
isDown &&
|
|
213
|
-
(key === 'onClick' || key === 'onPointerDown')
|
|
214
|
-
) {
|
|
215
|
-
if (!uuidsInteractedWithThisFrame.includes(id)) {
|
|
216
|
-
v.element.eventListeners[key]?.forEach((cb) =>
|
|
217
|
-
cb({ intersection: v.intersection })
|
|
218
|
-
)
|
|
219
|
-
uuidsInteractedWithThisFrame.push(id)
|
|
220
|
-
}
|
|
221
|
-
} else if (!isDown && key === 'onPointerUp') {
|
|
222
|
-
if (!uuidsInteractedWithThisFrame.includes(id)) {
|
|
223
|
-
v.element.eventListeners[key]?.forEach((cb) =>
|
|
224
|
-
cb({ intersection: v.intersection })
|
|
225
|
-
)
|
|
226
|
-
uuidsInteractedWithThisFrame.push(id)
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
})
|
|
230
|
-
})
|
|
231
|
-
})
|
|
232
|
-
|
|
233
|
-
// return arbitrary object to ensure instantiation
|
|
234
|
-
// TODO: why can't we return a <raycaster/> here?
|
|
235
|
-
return () => <object3D />
|
|
236
|
-
},
|
|
237
|
-
})
|
|
@@ -1,341 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
computed,
|
|
3
|
-
defineComponent,
|
|
4
|
-
onBeforeUnmount,
|
|
5
|
-
onMounted,
|
|
6
|
-
PropType,
|
|
7
|
-
reactive,
|
|
8
|
-
ref,
|
|
9
|
-
watch,
|
|
10
|
-
WatchSource,
|
|
11
|
-
} from 'vue'
|
|
12
|
-
import { cancelUpdate, cancelUpdateSource, MiniDom, update } from '../../core'
|
|
13
|
-
import { Lunch, useApp, useGlobals, useLunchboxInteractables } from '../..'
|
|
14
|
-
import * as THREE from 'three'
|
|
15
|
-
import { prepCanvas } from './prepCanvas'
|
|
16
|
-
import { useUpdateGlobals, useStartCallbacks } from '../..'
|
|
17
|
-
import { LunchboxScene } from './LunchboxScene'
|
|
18
|
-
import { LunchboxEventHandlers } from '../LunchboxEventHandlers'
|
|
19
|
-
import * as Keys from '../../keys'
|
|
20
|
-
import { waitFor } from '../../utils'
|
|
21
|
-
|
|
22
|
-
/** fixed & fill styling for container */
|
|
23
|
-
const fillStyle = (position: string) => {
|
|
24
|
-
return {
|
|
25
|
-
position,
|
|
26
|
-
top: 0,
|
|
27
|
-
right: 0,
|
|
28
|
-
bottom: 0,
|
|
29
|
-
left: 0,
|
|
30
|
-
width: '100%',
|
|
31
|
-
height: '100%',
|
|
32
|
-
display: 'block',
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export const LunchboxWrapper = defineComponent({
|
|
37
|
-
name: 'Lunchbox',
|
|
38
|
-
props: {
|
|
39
|
-
// These should match the Lunchbox.WrapperProps interface
|
|
40
|
-
background: String,
|
|
41
|
-
cameraArgs: Array,
|
|
42
|
-
cameraLook: Array as unknown as PropType<Lunch.Vector3AsArray>,
|
|
43
|
-
cameraLookAt: Array as unknown as PropType<Lunch.Vector3AsArray>,
|
|
44
|
-
cameraPosition: Array as unknown as PropType<Lunch.Vector3AsArray>,
|
|
45
|
-
dpr: Number,
|
|
46
|
-
ortho: Boolean,
|
|
47
|
-
orthographic: Boolean,
|
|
48
|
-
r3f: Boolean,
|
|
49
|
-
rendererArguments: Object,
|
|
50
|
-
rendererProperties: Object,
|
|
51
|
-
sizePolicy: String as PropType<Lunch.SizePolicy>,
|
|
52
|
-
shadow: [Boolean, Object],
|
|
53
|
-
transparent: Boolean,
|
|
54
|
-
zoom: Number,
|
|
55
|
-
updateSource: Object as PropType<WatchSource>,
|
|
56
|
-
},
|
|
57
|
-
setup(props, context) {
|
|
58
|
-
const canvas = ref<MiniDom.RendererDomNode>()
|
|
59
|
-
let dpr = props.dpr ?? -1
|
|
60
|
-
const container = ref<MiniDom.RendererDomNode>()
|
|
61
|
-
const renderer = ref<Lunch.LunchboxComponent<THREE.Renderer>>()
|
|
62
|
-
const camera = ref<Lunch.LunchboxComponent<THREE.Camera>>()
|
|
63
|
-
const scene = ref<Lunch.LunchboxComponent<THREE.Scene>>()
|
|
64
|
-
const globals = useGlobals()
|
|
65
|
-
const updateGlobals = useUpdateGlobals()
|
|
66
|
-
const app = useApp()
|
|
67
|
-
const consolidatedCameraProperties: Record<string, any> = reactive({})
|
|
68
|
-
const startCallbacks = useStartCallbacks()
|
|
69
|
-
|
|
70
|
-
// https://threejs.org/docs/index.html#manual/en/introduction/Color-management
|
|
71
|
-
if (props.r3f && (THREE as any)?.ColorManagement) {
|
|
72
|
-
;(THREE as any).ColorManagement.legacyMode = false
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const interactables = useLunchboxInteractables()
|
|
76
|
-
|
|
77
|
-
// MOUNT
|
|
78
|
-
// ====================
|
|
79
|
-
onMounted(async () => {
|
|
80
|
-
// canvas needs to exist (or user needs to handle it on their own)
|
|
81
|
-
if (!canvas.value && !context.slots?.renderer?.()?.length)
|
|
82
|
-
throw new Error('missing canvas')
|
|
83
|
-
|
|
84
|
-
// no camera provided, so let's create one
|
|
85
|
-
if (!context.slots?.camera?.()?.length) {
|
|
86
|
-
if (props.cameraPosition) {
|
|
87
|
-
consolidatedCameraProperties.position = props.cameraPosition
|
|
88
|
-
}
|
|
89
|
-
if (props.cameraLook || props.cameraLookAt) {
|
|
90
|
-
consolidatedCameraProperties.lookAt =
|
|
91
|
-
props.cameraLook || props.cameraLookAt
|
|
92
|
-
}
|
|
93
|
-
if (props.zoom !== undefined) {
|
|
94
|
-
consolidatedCameraProperties.zoom = props.zoom
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// SCENE
|
|
99
|
-
// ====================
|
|
100
|
-
// set background color
|
|
101
|
-
if (scene.value?.$el?.instance && props.background) {
|
|
102
|
-
scene.value.$el.instance.background = new THREE.Color(
|
|
103
|
-
props.background
|
|
104
|
-
)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// MISC PROPERTIES
|
|
108
|
-
// ====================
|
|
109
|
-
if (dpr === -1) {
|
|
110
|
-
dpr = window.devicePixelRatio
|
|
111
|
-
}
|
|
112
|
-
updateGlobals?.({ dpr })
|
|
113
|
-
|
|
114
|
-
while (
|
|
115
|
-
!renderer.value?.$el?.instance &&
|
|
116
|
-
// TODO: remove `as any`
|
|
117
|
-
!(renderer.value as any)?.component?.ctx.$el?.instance
|
|
118
|
-
) {
|
|
119
|
-
await new Promise((r) => requestAnimationFrame(r))
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
while (
|
|
123
|
-
!scene.value?.$el?.instance &&
|
|
124
|
-
// TODO: remove `as any`
|
|
125
|
-
!(scene.value as any)?.component?.ctx.$el?.instance
|
|
126
|
-
) {
|
|
127
|
-
await new Promise((r) => requestAnimationFrame(r))
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const normalizedRenderer = (renderer.value?.$el?.instance ??
|
|
131
|
-
(renderer.value as any)?.component?.ctx.$el
|
|
132
|
-
?.instance) as THREE.WebGLRenderer
|
|
133
|
-
|
|
134
|
-
normalizedRenderer.setPixelRatio(globals.dpr)
|
|
135
|
-
|
|
136
|
-
const normalizedScene = (scene.value?.$el?.instance ??
|
|
137
|
-
(scene.value as any)?.component?.ctx.$el
|
|
138
|
-
?.instance) as THREE.Scene
|
|
139
|
-
|
|
140
|
-
const normalizedCamera = (camera.value?.$el?.instance ??
|
|
141
|
-
(camera.value as any)?.component?.ctx.$el
|
|
142
|
-
?.instance) as THREE.Camera
|
|
143
|
-
|
|
144
|
-
// TODO: update DPR on monitor switch
|
|
145
|
-
// prep canvas (sizing, observe, unmount, etc)
|
|
146
|
-
// (only run if no custom renderer)
|
|
147
|
-
if (!context.slots?.renderer?.()?.length) {
|
|
148
|
-
// TODO: use dispose
|
|
149
|
-
const { dispose } = prepCanvas(
|
|
150
|
-
container,
|
|
151
|
-
normalizedCamera,
|
|
152
|
-
normalizedRenderer,
|
|
153
|
-
normalizedScene,
|
|
154
|
-
props.sizePolicy
|
|
155
|
-
)
|
|
156
|
-
|
|
157
|
-
if (props.r3f) {
|
|
158
|
-
normalizedRenderer.outputEncoding = THREE.sRGBEncoding
|
|
159
|
-
normalizedRenderer.toneMapping = THREE.ACESFilmicToneMapping
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// update render sugar
|
|
163
|
-
const sugar = {
|
|
164
|
-
shadow: props.shadow,
|
|
165
|
-
}
|
|
166
|
-
if (sugar?.shadow) {
|
|
167
|
-
normalizedRenderer.shadowMap.enabled = true
|
|
168
|
-
if (typeof sugar.shadow === 'object') {
|
|
169
|
-
normalizedRenderer.shadowMap.type = sugar.shadow.type
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// START
|
|
175
|
-
// ====================
|
|
176
|
-
if (!app) {
|
|
177
|
-
throw new Error('error creating app')
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// save renderer, scene, camera
|
|
181
|
-
app.config.globalProperties.lunchbox.camera = normalizedCamera
|
|
182
|
-
app.config.globalProperties.lunchbox.renderer = normalizedRenderer
|
|
183
|
-
app.config.globalProperties.lunchbox.scene = normalizedScene
|
|
184
|
-
|
|
185
|
-
for (let startCallback of startCallbacks ?? []) {
|
|
186
|
-
startCallback({
|
|
187
|
-
app,
|
|
188
|
-
camera: normalizedCamera,
|
|
189
|
-
renderer: normalizedRenderer,
|
|
190
|
-
scene: normalizedScene,
|
|
191
|
-
})
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// KICK UPDATE
|
|
195
|
-
// ====================
|
|
196
|
-
update({
|
|
197
|
-
app,
|
|
198
|
-
camera: normalizedCamera,
|
|
199
|
-
renderer: normalizedRenderer,
|
|
200
|
-
scene: normalizedScene,
|
|
201
|
-
updateSource: props.updateSource,
|
|
202
|
-
})
|
|
203
|
-
})
|
|
204
|
-
|
|
205
|
-
// UNMOUNT
|
|
206
|
-
// ====================
|
|
207
|
-
onBeforeUnmount(() => {
|
|
208
|
-
cancelUpdate()
|
|
209
|
-
cancelUpdateSource()
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
// RENDER FUNCTION
|
|
213
|
-
// ====================
|
|
214
|
-
const containerFillStyle =
|
|
215
|
-
props.sizePolicy === 'container' ? 'static' : 'absolute'
|
|
216
|
-
const canvasFillStyle =
|
|
217
|
-
props.sizePolicy === 'container' ? 'static' : 'fixed'
|
|
218
|
-
|
|
219
|
-
// REACTIVE CUSTOM CAMERAS
|
|
220
|
-
// ====================
|
|
221
|
-
// find first camera with `type.name` property
|
|
222
|
-
// (which indicates a Lunch.Node)
|
|
223
|
-
const activeCamera = computed(() => {
|
|
224
|
-
const output = context.slots
|
|
225
|
-
?.camera?.()
|
|
226
|
-
.find((c) => (c.type as any)?.name)
|
|
227
|
-
if (output) {
|
|
228
|
-
return output
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return output
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
// TODO: make custom cameras reactive
|
|
235
|
-
watch(
|
|
236
|
-
activeCamera,
|
|
237
|
-
async (newVal, oldVal) => {
|
|
238
|
-
// console.log('got camera', newVal)
|
|
239
|
-
if (newVal && newVal?.props?.key !== oldVal?.props?.key) {
|
|
240
|
-
// TODO: remove cast
|
|
241
|
-
camera.value = newVal as any
|
|
242
|
-
|
|
243
|
-
// TODO: why isn't this updating app camera?
|
|
244
|
-
// const el = await waitFor(() => newVal.el)
|
|
245
|
-
// console.log(el)
|
|
246
|
-
// camera.value = el
|
|
247
|
-
// console.log(newVal.uuid)
|
|
248
|
-
// updateGlobals?.({ camera: el })
|
|
249
|
-
}
|
|
250
|
-
},
|
|
251
|
-
{ immediate: true }
|
|
252
|
-
)
|
|
253
|
-
|
|
254
|
-
// RENDER FUNCTION
|
|
255
|
-
// ====================
|
|
256
|
-
return () => (
|
|
257
|
-
<>
|
|
258
|
-
{/* use renderer slot if provided... */}
|
|
259
|
-
{context.slots?.renderer?.()?.length ? (
|
|
260
|
-
// TODO: remove `as any` cast
|
|
261
|
-
(renderer.value = context.slots?.renderer?.()[0] as any)
|
|
262
|
-
) : (
|
|
263
|
-
// ...otherwise, add canvas...
|
|
264
|
-
<>
|
|
265
|
-
<div
|
|
266
|
-
class="lunchbox-container"
|
|
267
|
-
style={fillStyle(containerFillStyle) as any}
|
|
268
|
-
ref={container}
|
|
269
|
-
data-lunchbox="true"
|
|
270
|
-
>
|
|
271
|
-
<canvas
|
|
272
|
-
ref={canvas}
|
|
273
|
-
class="lunchbox-canvas"
|
|
274
|
-
style={fillStyle(canvasFillStyle) as any}
|
|
275
|
-
data-lunchbox="true"
|
|
276
|
-
></canvas>
|
|
277
|
-
</div>
|
|
278
|
-
{/* ...and create default renderer once canvas is present */}
|
|
279
|
-
{canvas.value?.domElement && (
|
|
280
|
-
<webGLRenderer
|
|
281
|
-
{...(props.rendererProperties ?? {})}
|
|
282
|
-
ref={renderer}
|
|
283
|
-
args={[
|
|
284
|
-
{
|
|
285
|
-
alpha: props.transparent,
|
|
286
|
-
antialias: true,
|
|
287
|
-
canvas: canvas.value?.domElement,
|
|
288
|
-
powerPreference: !!props.r3f
|
|
289
|
-
? 'high-performance'
|
|
290
|
-
: 'default',
|
|
291
|
-
...(props.rendererArguments ?? {}),
|
|
292
|
-
},
|
|
293
|
-
]}
|
|
294
|
-
/>
|
|
295
|
-
)}
|
|
296
|
-
</>
|
|
297
|
-
)}
|
|
298
|
-
|
|
299
|
-
{/* use scene slot if provided... */}
|
|
300
|
-
{context.slots?.scene?.()?.length ? (
|
|
301
|
-
// TODO: remove `as any` cast
|
|
302
|
-
(scene.value = context.slots?.scene?.()[0] as any)
|
|
303
|
-
) : (
|
|
304
|
-
// ...otherwise, add default scene
|
|
305
|
-
// TODO: why does this need to be a separate component? <scene> throws an error
|
|
306
|
-
<LunchboxScene ref={scene}>
|
|
307
|
-
{context.slots?.default?.()}
|
|
308
|
-
</LunchboxScene>
|
|
309
|
-
)}
|
|
310
|
-
|
|
311
|
-
{/* use camera slot if provided... */}
|
|
312
|
-
{context.slots?.camera?.()?.length ? (
|
|
313
|
-
// TODO: remove `any` cast
|
|
314
|
-
camera.value
|
|
315
|
-
) : props.ortho || props.orthographic ? (
|
|
316
|
-
<orthographicCamera
|
|
317
|
-
ref={camera}
|
|
318
|
-
args={props.cameraArgs ?? []}
|
|
319
|
-
{...consolidatedCameraProperties}
|
|
320
|
-
/>
|
|
321
|
-
) : (
|
|
322
|
-
<perspectiveCamera
|
|
323
|
-
ref={camera}
|
|
324
|
-
args={
|
|
325
|
-
props.cameraArgs ?? [
|
|
326
|
-
props.r3f ? 75 : 45,
|
|
327
|
-
0.5625,
|
|
328
|
-
1,
|
|
329
|
-
1000,
|
|
330
|
-
]
|
|
331
|
-
}
|
|
332
|
-
{...consolidatedCameraProperties}
|
|
333
|
-
/>
|
|
334
|
-
)}
|
|
335
|
-
|
|
336
|
-
{/* Lunchbox interaction handlers */}
|
|
337
|
-
{interactables?.value.length && <LunchboxEventHandlers />}
|
|
338
|
-
</>
|
|
339
|
-
)
|
|
340
|
-
},
|
|
341
|
-
})
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { MiniDom } from '../../core'
|
|
2
|
-
import { Lunch } from '../../types'
|
|
3
|
-
import { Ref } from 'vue'
|
|
4
|
-
import { resizeCanvas } from './resizeCanvas'
|
|
5
|
-
|
|
6
|
-
const getInnerDimensions = (node: Element) => {
|
|
7
|
-
const computedStyle = getComputedStyle(node)
|
|
8
|
-
const width =
|
|
9
|
-
node.clientWidth -
|
|
10
|
-
parseFloat(computedStyle.paddingLeft) -
|
|
11
|
-
parseFloat(computedStyle.paddingRight)
|
|
12
|
-
const height =
|
|
13
|
-
node.clientHeight -
|
|
14
|
-
parseFloat(computedStyle.paddingTop) -
|
|
15
|
-
parseFloat(computedStyle.paddingBottom)
|
|
16
|
-
return { width, height }
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export const prepCanvas = (
|
|
20
|
-
container: Ref<MiniDom.RendererDomNode | undefined>,
|
|
21
|
-
camera: THREE.Camera,
|
|
22
|
-
renderer: THREE.Renderer,
|
|
23
|
-
scene: THREE.Scene,
|
|
24
|
-
sizePolicy?: Lunch.SizePolicy
|
|
25
|
-
) => {
|
|
26
|
-
const containerElement = container.value?.domElement
|
|
27
|
-
if (!containerElement) throw new Error('missing container')
|
|
28
|
-
|
|
29
|
-
// save and size element
|
|
30
|
-
const resizeCanvasByPolicy = () => {
|
|
31
|
-
if (sizePolicy === 'container') {
|
|
32
|
-
const dims = getInnerDimensions(containerElement)
|
|
33
|
-
resizeCanvas(camera, renderer, scene, dims.width, dims.height)
|
|
34
|
-
} else resizeCanvas(camera, renderer, scene)
|
|
35
|
-
}
|
|
36
|
-
resizeCanvasByPolicy()
|
|
37
|
-
|
|
38
|
-
// attach listeners
|
|
39
|
-
let observer = new ResizeObserver(() => {
|
|
40
|
-
resizeCanvasByPolicy()
|
|
41
|
-
})
|
|
42
|
-
// window.addEventListener('resize', resizeCanvas)
|
|
43
|
-
if (containerElement) {
|
|
44
|
-
observer.observe(containerElement)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// cleanup
|
|
48
|
-
return {
|
|
49
|
-
dispose() {
|
|
50
|
-
if (containerElement) {
|
|
51
|
-
observer.unobserve(containerElement)
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
|
-
}
|
|
55
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { toRaw } from 'vue'
|
|
2
|
-
|
|
3
|
-
export const resizeCanvas = (
|
|
4
|
-
camera: THREE.Camera,
|
|
5
|
-
renderer: THREE.Renderer,
|
|
6
|
-
scene: THREE.Scene,
|
|
7
|
-
width?: number,
|
|
8
|
-
height?: number
|
|
9
|
-
) => {
|
|
10
|
-
// ignore if no element
|
|
11
|
-
if (!renderer?.domElement || !scene || !camera) return
|
|
12
|
-
|
|
13
|
-
width = width ?? window.innerWidth
|
|
14
|
-
height = height ?? window.innerHeight
|
|
15
|
-
|
|
16
|
-
// update camera
|
|
17
|
-
const aspect = width / height
|
|
18
|
-
if (camera.type?.toLowerCase() === 'perspectivecamera') {
|
|
19
|
-
const perspectiveCamera = camera as THREE.PerspectiveCamera
|
|
20
|
-
perspectiveCamera.aspect = aspect
|
|
21
|
-
perspectiveCamera.updateProjectionMatrix()
|
|
22
|
-
} else if (camera.type?.toLowerCase() === 'orthographiccamera') {
|
|
23
|
-
// TODO: ortho camera update
|
|
24
|
-
const orthoCamera = camera as THREE.OrthographicCamera
|
|
25
|
-
const heightInTermsOfWidth = height / width
|
|
26
|
-
orthoCamera.top = heightInTermsOfWidth * 10
|
|
27
|
-
orthoCamera.bottom = -heightInTermsOfWidth * 10
|
|
28
|
-
orthoCamera.right = 10
|
|
29
|
-
orthoCamera.left = -10
|
|
30
|
-
orthoCamera.updateProjectionMatrix()
|
|
31
|
-
} else {
|
|
32
|
-
// TODO: non-ortho or perspective camera
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// update canvas
|
|
36
|
-
renderer.setSize(width, height)
|
|
37
|
-
// render immediately so there's no flicker
|
|
38
|
-
if (scene && camera) {
|
|
39
|
-
renderer.render(toRaw(scene), toRaw(camera))
|
|
40
|
-
}
|
|
41
|
-
}
|