lunchboxjs 0.1.4014 → 0.1.4017
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.js +91 -15
- package/dist/lunchboxjs.min.js +1 -1
- package/dist/lunchboxjs.module.js +92 -17
- package/package.json +4 -3
- package/src/components/LunchboxWrapper/LunchboxWrapper.ts +45 -6
- package/src/components/LunchboxWrapper/prepCanvas.ts +20 -3
- package/src/components/catalogue.ts +1 -1
- package/src/core/instantiateThreeObject/processProps.ts +23 -6
- package/src/core/interaction/input.ts +1 -1
- package/src/core/interaction/setupAutoRaycaster.ts +12 -2
- package/src/core/update.ts +29 -3
- package/src/types.ts +7 -0
package/dist/lunchboxjs.js
CHANGED
|
@@ -64,16 +64,30 @@
|
|
|
64
64
|
}
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
-
const
|
|
67
|
+
const getInnerDimensions = (node) => {
|
|
68
|
+
const computedStyle = getComputedStyle(node);
|
|
69
|
+
const width = node.clientWidth - parseFloat(computedStyle.paddingLeft) - parseFloat(computedStyle.paddingRight);
|
|
70
|
+
const height = node.clientHeight - parseFloat(computedStyle.paddingTop) - parseFloat(computedStyle.paddingBottom);
|
|
71
|
+
return { width, height };
|
|
72
|
+
};
|
|
73
|
+
const prepCanvas = (container, canvasElement, onBeforeUnmount, sizePolicy) => {
|
|
68
74
|
const containerElement = container.value?.domElement;
|
|
69
75
|
if (!containerElement)
|
|
70
76
|
throw new Error('missing container');
|
|
71
77
|
// save...
|
|
72
78
|
// ...and size element
|
|
73
|
-
|
|
79
|
+
const resizeCanvasByPolicy = () => {
|
|
80
|
+
if (sizePolicy === "container") {
|
|
81
|
+
const dims = getInnerDimensions(containerElement);
|
|
82
|
+
resizeCanvas(dims.width, dims.height);
|
|
83
|
+
}
|
|
84
|
+
else
|
|
85
|
+
resizeCanvas();
|
|
86
|
+
};
|
|
87
|
+
resizeCanvasByPolicy();
|
|
74
88
|
// attach listeners
|
|
75
89
|
const observer = new ResizeObserver(([canvas]) => {
|
|
76
|
-
|
|
90
|
+
resizeCanvasByPolicy();
|
|
77
91
|
});
|
|
78
92
|
// window.addEventListener('resize', resizeCanvas)
|
|
79
93
|
if (containerElement) {
|
|
@@ -87,6 +101,8 @@
|
|
|
87
101
|
});
|
|
88
102
|
};
|
|
89
103
|
|
|
104
|
+
// TODO:
|
|
105
|
+
// Continue r3f prop - what else (besides camera fov) makes r3f look good?
|
|
90
106
|
/** fixed & fill styling for container */
|
|
91
107
|
const fillStyle = (position) => {
|
|
92
108
|
return {
|
|
@@ -97,6 +113,7 @@
|
|
|
97
113
|
left: 0,
|
|
98
114
|
width: '100%',
|
|
99
115
|
height: '100%',
|
|
116
|
+
display: 'block',
|
|
100
117
|
};
|
|
101
118
|
};
|
|
102
119
|
const LunchboxWrapper = {
|
|
@@ -111,11 +128,14 @@
|
|
|
111
128
|
dpr: Number,
|
|
112
129
|
ortho: Boolean,
|
|
113
130
|
orthographic: Boolean,
|
|
131
|
+
r3f: Boolean,
|
|
114
132
|
rendererArguments: Object,
|
|
115
133
|
rendererProperties: Object,
|
|
134
|
+
sizePolicy: String,
|
|
116
135
|
shadow: [Boolean, Object],
|
|
117
136
|
transparent: Boolean,
|
|
118
137
|
zoom: Number,
|
|
138
|
+
updateSource: Object,
|
|
119
139
|
},
|
|
120
140
|
setup(props, context) {
|
|
121
141
|
const canvas = vue.ref();
|
|
@@ -125,6 +145,10 @@
|
|
|
125
145
|
let renderer;
|
|
126
146
|
let camera;
|
|
127
147
|
let scene;
|
|
148
|
+
// https://threejs.org/docs/index.html#manual/en/introduction/Color-management
|
|
149
|
+
if (props.r3f && THREE__namespace?.ColorManagement) {
|
|
150
|
+
THREE__namespace.ColorManagement.legacyMode = false;
|
|
151
|
+
}
|
|
128
152
|
// MOUNT
|
|
129
153
|
// ====================
|
|
130
154
|
vue.onMounted(() => {
|
|
@@ -145,6 +169,9 @@
|
|
|
145
169
|
alpha: props.transparent,
|
|
146
170
|
antialias: true,
|
|
147
171
|
canvas: canvas.value.domElement,
|
|
172
|
+
powerPreference: !!props.r3f
|
|
173
|
+
? 'high-performance'
|
|
174
|
+
: 'default',
|
|
148
175
|
...(props.rendererArguments ?? {}),
|
|
149
176
|
};
|
|
150
177
|
// create new renderer
|
|
@@ -158,6 +185,15 @@
|
|
|
158
185
|
// we've initialized the renderer, so anything depending on it can execute now
|
|
159
186
|
rendererReady.value = true;
|
|
160
187
|
const rendererAsWebGlRenderer = ensureRenderer;
|
|
188
|
+
// apply r3f settings if desired
|
|
189
|
+
if (props.r3f) {
|
|
190
|
+
if (rendererAsWebGlRenderer.value.instance) {
|
|
191
|
+
rendererAsWebGlRenderer.value.instance.outputEncoding =
|
|
192
|
+
THREE__namespace.sRGBEncoding;
|
|
193
|
+
rendererAsWebGlRenderer.value.instance.toneMapping =
|
|
194
|
+
THREE__namespace.ACESFilmicToneMapping;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
161
197
|
// update render sugar
|
|
162
198
|
const sugar = {
|
|
163
199
|
shadow: props.shadow,
|
|
@@ -205,7 +241,12 @@
|
|
|
205
241
|
else {
|
|
206
242
|
ensuredCamera.value = createNode({
|
|
207
243
|
props: {
|
|
208
|
-
args: props.cameraArgs ?? [
|
|
244
|
+
args: props.cameraArgs ?? [
|
|
245
|
+
props.r3f ? 75 : 45,
|
|
246
|
+
0.5625,
|
|
247
|
+
1,
|
|
248
|
+
1000,
|
|
249
|
+
],
|
|
209
250
|
},
|
|
210
251
|
type: 'PerspectiveCamera',
|
|
211
252
|
uuid: fallbackCameraUuid,
|
|
@@ -238,7 +279,7 @@
|
|
|
238
279
|
scene = ensuredScene.value;
|
|
239
280
|
// set background color
|
|
240
281
|
if (scene && scene.instance && props.background) {
|
|
241
|
-
scene.instance.background = new
|
|
282
|
+
scene.instance.background = new THREE__namespace.Color(props.background);
|
|
242
283
|
}
|
|
243
284
|
// MISC PROPERTIES
|
|
244
285
|
// ====================
|
|
@@ -249,7 +290,7 @@
|
|
|
249
290
|
renderer.instance.setPixelRatio(dpr.value);
|
|
250
291
|
globals.dpr.value = dpr.value;
|
|
251
292
|
// prep canvas (sizing, observe, unmount, etc)
|
|
252
|
-
prepCanvas(container, renderer.instance.domElement, vue.onBeforeUnmount);
|
|
293
|
+
prepCanvas(container, renderer.instance.domElement, vue.onBeforeUnmount, props.sizePolicy);
|
|
253
294
|
}
|
|
254
295
|
else {
|
|
255
296
|
throw new Error('missing renderer');
|
|
@@ -274,24 +315,28 @@
|
|
|
274
315
|
camera: camera.instance,
|
|
275
316
|
renderer: renderer.instance,
|
|
276
317
|
scene: scene.instance,
|
|
318
|
+
updateSource: props.updateSource,
|
|
277
319
|
});
|
|
278
320
|
});
|
|
279
321
|
// UNMOUNT
|
|
280
322
|
// ====================
|
|
281
323
|
vue.onBeforeUnmount(() => {
|
|
282
324
|
cancelUpdate();
|
|
325
|
+
cancelUpdateSource();
|
|
283
326
|
});
|
|
284
327
|
// RENDER FUNCTION
|
|
285
328
|
// ====================
|
|
329
|
+
const containerFillStyle = props.sizePolicy === 'container' ? 'static' : 'absolute';
|
|
330
|
+
const canvasFillStyle = props.sizePolicy === 'container' ? 'static' : 'fixed';
|
|
286
331
|
return () => [
|
|
287
332
|
context.slots.default?.() ?? null,
|
|
288
333
|
vue.h('div', {
|
|
289
|
-
style: fillStyle(
|
|
334
|
+
style: fillStyle(containerFillStyle),
|
|
290
335
|
ref: container,
|
|
291
336
|
}, [
|
|
292
337
|
useFallbackRenderer.value
|
|
293
338
|
? vue.h('canvas', {
|
|
294
|
-
style: fillStyle(
|
|
339
|
+
style: fillStyle(canvasFillStyle),
|
|
295
340
|
class: 'lunchbox-canvas',
|
|
296
341
|
ref: canvas,
|
|
297
342
|
})
|
|
@@ -625,6 +670,7 @@
|
|
|
625
670
|
let mouseUpListener;
|
|
626
671
|
const mousePos = vue.ref({ x: Infinity, y: Infinity });
|
|
627
672
|
let autoRaycasterEventsInitialized = false;
|
|
673
|
+
let frameID$1;
|
|
628
674
|
const setupAutoRaycaster = (node) => {
|
|
629
675
|
const instance = node.instance;
|
|
630
676
|
if (!instance)
|
|
@@ -657,8 +703,14 @@
|
|
|
657
703
|
renderer.instance.domElement.addEventListener('mousedown', mouseDownListener);
|
|
658
704
|
renderer.instance.domElement.addEventListener('mouseup', mouseUpListener);
|
|
659
705
|
// TODO: add touch events
|
|
660
|
-
//
|
|
661
|
-
|
|
706
|
+
// process mouse events asynchronously, whenever the mouse state changes
|
|
707
|
+
vue.watch(() => [inputActive.value, mousePos.value.x, mousePos.value.y], () => {
|
|
708
|
+
if (frameID$1)
|
|
709
|
+
cancelAnimationFrame(frameID$1);
|
|
710
|
+
frameID$1 = requestAnimationFrame(() => {
|
|
711
|
+
autoRaycasterBeforeRender();
|
|
712
|
+
});
|
|
713
|
+
});
|
|
662
714
|
// mark complete
|
|
663
715
|
autoRaycasterEventsInitialized = true;
|
|
664
716
|
// cancel setup watcher
|
|
@@ -987,7 +1039,7 @@
|
|
|
987
1039
|
};
|
|
988
1040
|
|
|
989
1041
|
/** Process props into either themselves or the $attached value */
|
|
990
|
-
function processProp({ node, prop }) {
|
|
1042
|
+
function processProp({ node, prop, }) {
|
|
991
1043
|
// return $attachedArray value if needed
|
|
992
1044
|
if (typeof prop === 'string' && prop.startsWith('$attachedArray')) {
|
|
993
1045
|
return node.attachedArray[prop.replace('$attachedArray.', '')];
|
|
@@ -999,10 +1051,12 @@
|
|
|
999
1051
|
// otherwise, return plain value
|
|
1000
1052
|
return prop;
|
|
1001
1053
|
}
|
|
1002
|
-
function processPropAsArray({ node, prop }) {
|
|
1054
|
+
function processPropAsArray({ node, prop, }) {
|
|
1003
1055
|
const isAttachedArray = typeof prop === 'string' && prop.startsWith('$attachedArray');
|
|
1004
1056
|
const output = processProp({ node, prop });
|
|
1005
|
-
return Array.isArray(output) && isAttachedArray
|
|
1057
|
+
return Array.isArray(output) && isAttachedArray
|
|
1058
|
+
? output
|
|
1059
|
+
: [output];
|
|
1006
1060
|
}
|
|
1007
1061
|
|
|
1008
1062
|
function instantiateThreeObject(node) {
|
|
@@ -1301,16 +1355,34 @@
|
|
|
1301
1355
|
};
|
|
1302
1356
|
|
|
1303
1357
|
let frameID;
|
|
1358
|
+
let watchStopHandle;
|
|
1304
1359
|
const beforeRender = [];
|
|
1305
1360
|
const afterRender = [];
|
|
1306
|
-
const
|
|
1307
|
-
|
|
1361
|
+
const requestUpdate = (opts) => {
|
|
1362
|
+
cancelUpdate();
|
|
1308
1363
|
frameID = requestAnimationFrame(() => update({
|
|
1309
1364
|
app: opts.app,
|
|
1310
1365
|
renderer: ensureRenderer.value?.instance,
|
|
1311
1366
|
scene: ensuredScene.value.instance,
|
|
1312
1367
|
camera: ensuredCamera.value?.instance,
|
|
1368
|
+
updateSource: opts.updateSource,
|
|
1313
1369
|
}));
|
|
1370
|
+
};
|
|
1371
|
+
const update = (opts) => {
|
|
1372
|
+
if (opts.updateSource) {
|
|
1373
|
+
if (!watchStopHandle) {
|
|
1374
|
+
// request next frame only when state changes
|
|
1375
|
+
watchStopHandle = vue.watch(opts.updateSource, () => {
|
|
1376
|
+
requestUpdate(opts);
|
|
1377
|
+
}, {
|
|
1378
|
+
deep: true,
|
|
1379
|
+
});
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
else {
|
|
1383
|
+
// request next frame on a continuous loop
|
|
1384
|
+
requestUpdate(opts);
|
|
1385
|
+
}
|
|
1314
1386
|
// prep options
|
|
1315
1387
|
const { app, renderer, scene, camera } = opts;
|
|
1316
1388
|
// BEFORE RENDER
|
|
@@ -1373,6 +1445,10 @@
|
|
|
1373
1445
|
if (frameID)
|
|
1374
1446
|
cancelAnimationFrame(frameID);
|
|
1375
1447
|
};
|
|
1448
|
+
const cancelUpdateSource = () => {
|
|
1449
|
+
if (watchStopHandle)
|
|
1450
|
+
watchStopHandle();
|
|
1451
|
+
};
|
|
1376
1452
|
|
|
1377
1453
|
/** Update a single prop on a given node. */
|
|
1378
1454
|
function updateObjectProp({ node, key, value, }) {
|
package/dist/lunchboxjs.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("vue"),require("three"),require("lodash")):"function"==typeof define&&define.amd?define(["exports","vue","three","lodash"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).LunchboxRenderer={},e.vue,e.three,e.lodash)}(this,(function(e,t,n,r){"use strict";function o(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(n){if("default"!==n){var r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r.get?r:{enumerable:!0,get:function(){return e[n]}})}})),t.default=e,Object.freeze(t)}var a=o(n);const s=[],i=(e,n)=>{const r=W.value?.instance,o=I.value.instance,a=D.value;if(!r?.domElement||!o||!a)return;const s=(e=e??window.innerWidth)/(n=n??window.innerHeight);if("perspectivecamera"===a.type?.toLowerCase()){const e=a.instance;e.aspect=s,e.updateProjectionMatrix()}else if("orthographiccamera"===a.type?.toLowerCase()){const t=a.instance,r=n/e;t.top=10*r,t.bottom=10*-r,t.right=10,t.left=-10,t.updateProjectionMatrix()}else console.log("TODO: non-ortho or perspective camera");r.setSize(e,n),o&&a.instance&&r.render(t.toRaw(o),t.toRaw(a.instance))},c=e=>({position:e,top:0,right:0,bottom:0,left:0,width:"100%",height:"100%"}),d={name:"Lunchbox",props:{background:String,cameraArgs:Array,cameraLook:Array,cameraLookAt:Array,cameraPosition:Array,dpr:Number,ortho:Boolean,orthographic:Boolean,rendererArguments:Object,rendererProperties:Object,shadow:[Boolean,Object],transparent:Boolean,zoom:Number},setup(e,o){const a=t.ref(),s=t.ref(!0),d=t.ref(e.dpr??-1),u=t.ref();let l,p,m;return t.onMounted((()=>{if(!a.value)throw new Error("missing canvas");if(l=B(["WebGLRenderer"]),l)s.value=!1,U.value=!0;else{const t={alpha:e.transparent,antialias:!0,canvas:a.value.domElement,...e.rendererArguments??{}};W.value=K({type:"WebGLRenderer",uuid:F,props:{args:[t]}}),U.value=!0;const n=W,o={shadow:e.shadow};n.value.instance&&o?.shadow&&(n.value.instance.shadowMap.enabled=!0,"object"==typeof o.shadow&&(n.value.instance.shadowMap.type=o.shadow.type)),e.rendererProperties&&Object.keys(e.rendererProperties).forEach((t=>{r.set(n.value,t,e.rendererProperties[t])})),l=n.value}if(p=B(["PerspectiveCamera","OrthographicCamera"]),p?S.value=!0:(e.ortho||e.orthographic?D.value=K({props:{args:e.cameraArgs??[]},type:"OrthographicCamera",uuid:j}):D.value=K({props:{args:e.cameraArgs??[45,.5625,1,1e3]},type:"PerspectiveCamera",uuid:j}),S.value=!0,p=D.value),!p.instance)throw new Error("Error creating camera.");if(p&&e.cameraPosition&&p.instance.position.set(...e.cameraPosition),p&&(e.cameraLookAt||e.cameraLook)){const t=e.cameraLookAt||e.cameraLook;p.instance.lookAt(...t)}if(p&&void 0!==e.zoom&&(p.instance.zoom=e.zoom),m=I.value,m&&m.instance&&e.background&&(m.instance.background=new n.Color(e.background)),-1===d.value&&(d.value=window.devicePixelRatio),!l?.instance)throw new Error("missing renderer");l.instance.setPixelRatio(d.value),he.dpr.value=d.value,((e,t,n)=>{const r=e.value?.domElement;if(!r)throw new Error("missing container");i();const o=new ResizeObserver((([e])=>{i()}));r&&o.observe(r),n((()=>{t&&o.unobserve(t)}))})(u,l.instance.domElement,t.onBeforeUnmount);const o=t.getCurrentInstance().appContext.app;for(let e of te)e({app:o,camera:p.instance,renderer:l.instance,scene:m.instance});ae({app:o,camera:p.instance,renderer:l.instance,scene:m.instance})})),t.onBeforeUnmount((()=>{ie()})),()=>[o.slots.default?.()??null,t.h("div",{style:c("absolute"),ref:u},[s.value?t.h("canvas",{style:c("fixed"),class:"lunchbox-canvas",ref:a}):null])]}},u={},l=["canvas","div","LunchboxWrapper"],p={...["mesh","instancedMesh","scene","sprite","object3D","instancedBufferGeometry","bufferGeometry","boxBufferGeometry","circleBufferGeometry","coneBufferGeometry","cylinderBufferGeometry","dodecahedronBufferGeometry","extrudeBufferGeometry","icosahedronBufferGeometry","latheBufferGeometry","octahedronBufferGeometry","parametricBufferGeometry","planeBufferGeometry","polyhedronBufferGeometry","ringBufferGeometry","shapeBufferGeometry","sphereBufferGeometry","tetrahedronBufferGeometry","textBufferGeometry","torusBufferGeometry","torusKnotBufferGeometry","tubeBufferGeometry","wireframeGeometry","parametricGeometry","tetrahedronGeometry","octahedronGeometry","icosahedronGeometry","dodecahedronGeometry","polyhedronGeometry","tubeGeometry","torusKnotGeometry","torusGeometry","sphereGeometry","ringGeometry","planeGeometry","latheGeometry","shapeGeometry","extrudeGeometry","edgesGeometry","coneGeometry","cylinderGeometry","circleGeometry","boxGeometry","material","shadowMaterial","spriteMaterial","rawShaderMaterial","shaderMaterial","pointsMaterial","meshPhysicalMaterial","meshStandardMaterial","meshPhongMaterial","meshToonMaterial","meshNormalMaterial","meshLambertMaterial","meshDepthMaterial","meshDistanceMaterial","meshBasicMaterial","meshMatcapMaterial","lineDashedMaterial","lineBasicMaterial","light","spotLightShadow","spotLight","pointLight","rectAreaLight","hemisphereLight","directionalLightShadow","directionalLight","ambientLight","lightShadow","ambientLightProbe","hemisphereLightProbe","lightProbe","texture","videoTexture","dataTexture","dataTexture3D","compressedTexture","cubeTexture","canvasTexture","depthTexture","textureLoader","group","catmullRomCurve3","points","cameraHelper","camera","perspectiveCamera","orthographicCamera","cubeCamera","arrayCamera","webGLRenderer"].map((e=>t.defineComponent({inheritAttrs:!1,name:e,setup:(n,r)=>()=>t.h(e,r.attrs,r.slots?.default?.()||[])}))).reduce(((e,t)=>(e[t.name]=t,e)),{}),Lunchbox:d};const m=e=>e?.$el&&e?.$el?.hasOwnProperty?.("instance"),h=e=>{if("domMeta"===e?.metaType)return!0;const t="string"==typeof e?e:e?.type;return l.includes(t??"")},f=e=>"standardMeta"===e?.metaType,y=e=>e.isLunchboxRootNode,v=[],g=t.ref(!1);function b({node:e,key:n,value:r}){var o;if(e.eventListeners[n]||(e.eventListeners[n]=[]),e.eventListenerRemoveFunctions[n]||(e.eventListenerRemoveFunctions[n]=[]),e.eventListeners[n].push(r),x.includes(n)&&(z.value,e.instance&&!v.includes(e)&&(o=e,v.push(o),e.eventListenerRemoveFunctions[n].push((()=>(e=>{const t=v.indexOf(e);-1!==t&&v.splice(t,1)})(e))))),"onClick"===n||"onPointerDown"===n||"onPointerUp"===n){const r=t.watch((()=>g.value),(t=>{const r=L.map((e=>e.element)).findIndex((t=>t.instance&&t.instance.uuid===e.instance?.uuid));-1!==r&&((!t||"onClick"!==n&&"onPointerDown"!==n)&&(t||"onPointerUp"!==n)||e.eventListeners[n].forEach((e=>{e({intersection:L[r].intersection})})))}));e.eventListenerRemoveFunctions[n].push(r)}return e}const x=["onClick","onPointerUp","onPointerDown","onPointerOver","onPointerOut","onPointerEnter","onPointerLeave","onPointerMove"];let R,w,A;const C=t.ref({x:1/0,y:1/0});let E=!1;let L=[];const P=()=>{const e=z.value?.instance,t=D.value?.instance;if(!e||!t)return;e.setFromCamera(he.mousePos.value,t);const n=e.intersectObjects(v.map((e=>e.instance)));let r=[],o=[],a=[];r=L.map((e=>e.intersection)),n?.forEach((e=>{if(-1===L.findIndex((t=>t.intersection.object===e.object))){const t=v.find((t=>t.instance?.uuid===e.object.uuid));t&&o.push({element:t,intersection:e})}else{const t=v.find((t=>t.instance?.uuid===e.object.uuid));t&&a.push({element:t,intersection:e})}const t=r.findIndex((t=>t.object.uuid===e.object.uuid));-1!==t&&r.splice(t,1)}));const s=r.map((e=>({element:v.find((t=>t.instance?.uuid===e.object.uuid)),intersection:e})));o.forEach((({element:e,intersection:t})=>{M({element:e,eventKeys:["onPointerEnter"],intersection:t})})),a.forEach((({element:e,intersection:t})=>{M({element:e,eventKeys:["onPointerOver","onPointerMove"],intersection:t})})),s.forEach((({element:e,intersection:t})=>{M({element:e,eventKeys:["onPointerLeave","onPointerOut"],intersection:t})})),L=[].concat(o,a)},M=({element:e,eventKeys:t,intersection:n})=>{e&&t.forEach((t=>{e.eventListeners[t]&&e.eventListeners[t].forEach((e=>{e({intersection:n})}))}))};function N(t={}){return e.lunchboxTree||(e.lunchboxTree=new J.RendererRootNode(t)),e.lunchboxTree}function B(e){Array.isArray(e)||(e=[e]);for(let t of e)if(T[t])return T[t];for(let n of e){const e=G[n]||s.find((e=>e.type?.toLowerCase()===n.toLowerCase()));if(!(t=e,"RendererNode"!==t?.minidomType||!1!==e.props["is-default"]&&!1!=!e.props.isDefault))return null;if(e){const t=e;return G[n]=t,t}}var t;return null}e.lunchboxTree=void 0;const G=t.reactive({}),T=t.reactive({});function O(e,n,r={},o=null){Array.isArray(e)||(e=[e]);for(let t of e)G[t]||(G[t]=null),T[t]||(T[t]=null);return t.computed({get(){const t=B(e);if(t)return t;const a=N(),s=K({type:e[0],uuid:n,props:r});return a.addChild(s),G[e[0]]=s,o&&o(s),s},set(e){const t=e.type??"",n=t[0].toUpperCase()+t.slice(1);T[n]=e}})}const j="FALLBACK_CAMERA",k=O(["PerspectiveCamera","OrthographicCamera"],j,{args:[45,.5625,1,1e3]}),S=t.ref(!1),D=t.computed({get:()=>S.value?k.value:null,set(e){const t=e.type??"",n=t[0].toUpperCase()+t.slice(1);T[n]=e}}),F="FALLBACK_RENDERER",$=O(["WebGLRenderer"],F,{}),U=t.ref(!1),W=t.computed({get:()=>U.value?$.value:null,set(e){const t=e.type??"",n=t[0].toUpperCase()+t.slice(1);T[n]=e}}),I=O("Scene","FALLBACK_SCENE"),z=O("Raycaster","AUTO_RAYCASTER",{},(e=>(e=>{if(!e.instance)return;let n=null;n=t.watch((()=>W.value),(e=>{e?.instance&&(E||(R=t=>{const n=(e.instance.domElement.width??1)/he.dpr.value,r=(e.instance.domElement.height??1)/he.dpr.value;C.value.x=t.offsetX/n*2-1,C.value.y=-t.offsetY/r*2+1},w=()=>g.value=!0,A=()=>g.value=!1,e.instance.domElement.addEventListener("mousemove",R),e.instance.domElement.addEventListener("mousedown",w),e.instance.domElement.addEventListener("mouseup",A),se(P),E=!0),n&&n())}),{immediate:!0})})(e)));function K(e={},t={}){const n={attached:e.attached??[],attachedArray:e.attachedArray??{},instance:e.instance??null},r=new J.RendererStandardNode({...e,...n,metaType:"standardMeta"});return!r.type||y(r)||r.instance||(r.instance=function(e){if(!e.type)return null;const t=e.type[0].toUpperCase()+e.type.slice(1),n=u[e.type]||a[t];if(!n)throw`${t} is not part of the THREE namespace! Did you forget to extend? import {extend} from 'lunchbox'; extend({app, YourComponent, ...})`;const r=(e.props.args??[]).map((t=>function({node:e,prop:t}){const n="string"==typeof t&&t.startsWith("$attachedArray"),r=function({node:e,prop:t}){if("string"==typeof t&&t.startsWith("$attachedArray"))return e.attachedArray[t.replace("$attachedArray.","")];if("string"==typeof t&&t.startsWith("$attached"))return e.attached[t.replace("$attached.","")];return t}({node:e,prop:t});return Array.isArray(r)&&n?r:[r]}({node:e,prop:t})));let o=[];r.forEach((e=>{o=o.concat(e)}));return new n(...o)}({...r,props:{...r.props,...t}})),"scene"===r.type?.toLowerCase()?I.value=r:r.type?.toLowerCase().endsWith("camera")&&(D.value=r),r}const _=e=>t.defineComponent({inheritAttrs:!1,name:e,render(){return t.h(e,this.$attrs,this.$slots?.default?.()||[])}});var V,q=new Uint8Array(16);function H(){if(!V&&!(V="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return V(q)}var Y=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;function X(e){return"string"==typeof e&&Y.test(e)}for(var J,Q=[],Z=0;Z<256;++Z)Q.push((Z+256).toString(16).substr(1));function ee(e,t,n){var r=(e=e||{}).random||(e.rng||H)();if(r[6]=15&r[6]|64,r[8]=63&r[8]|128,t){n=n||0;for(var o=0;o<16;++o)t[n+o]=r[o];return t}return function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=(Q[e[t+0]]+Q[e[t+1]]+Q[e[t+2]]+Q[e[t+3]]+"-"+Q[e[t+4]]+Q[e[t+5]]+"-"+Q[e[t+6]]+Q[e[t+7]]+"-"+Q[e[t+8]]+Q[e[t+9]]+"-"+Q[e[t+10]]+Q[e[t+11]]+Q[e[t+12]]+Q[e[t+13]]+Q[e[t+14]]+Q[e[t+15]]).toLowerCase();if(!X(n))throw TypeError("Stringified UUID is invalid");return n}(r)}!function(e){e.BaseNode=class{constructor(e={},t){this.parentNode=e?.parentNode??t??null,this.minidomType="MinidomBaseNode",this.uuid=e?.uuid??ee(),s.push(this)}uuid;parentNode;get nextSibling(){if(!this.parentNode)return null;const e=this.parentNode.children.findIndex((e=>e.uuid===this.uuid));return-1!==e&&e<this.parentNode.children.length-1?this.parentNode.children[e+1]:null}insertBefore(e,t){e.removeAsChildFromAnyParents(),e.parentNode=this;const n=this.children.findIndex((e=>e.uuid===t?.uuid));-1!==n?this.children.splice(n,0,e):this.children.push(e)}removeChild(e){const t=this.children.findIndex((t=>t?.uuid===e?.uuid));-1!==t&&this.children.splice(t,1)}children=[];addChild(e){return e&&(e.removeAsChildFromAnyParents(),e.parentNode=this,this.insertBefore(e,null)),this}getPath(){const e=[];let t=this;for(;t;)e.unshift(t),t=t.parentNode;return e}drop(){this.parentNode=null,this.removeAsChildFromAnyParents()}walk(e){const t=[this,...this.children],n=[];let r=!0;for(;t.length&&r;){const o=t.shift();if(o){if(n.includes(o))continue;n.push(o),t.push(...o.children.filter((e=>!n.includes(e)))),r=e(o)}else r=!1}}minidomType;removeAsChildFromAnyParents(){s.forEach((e=>e.removeChild(this)))}};class t extends e.BaseNode{constructor(e={},t){super(e,t),this.minidomType="RendererNode",this.eventListeners={},this.eventListenerRemoveFunctions={},this.name=e.name??"",this.metaType=e.metaType??"standardMeta",this.props=e.props??[],this.type=e.type??""}eventListeners;eventListenerRemoveFunctions;name;metaType;props;type;drop(){super.drop(),Object.keys(this.eventListenerRemoveFunctions).forEach((e=>{this.eventListenerRemoveFunctions[e].forEach((e=>e()))}))}}e.RendererBaseNode=t;class n extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.domElement=e.domElement??document.createElement("div")}domElement;isLunchboxRootNode=!0}e.RendererRootNode=n;class r extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.text=e.text??""}text}e.RendererCommentNode=r;class o extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.domElement=e.domElement??document.createElement("div")}domElement}e.RendererDomNode=o;class a extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.text=e.text??""}text}e.RendererTextNode=a;class i extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.attached=e.attached??[],this.attachedArray=e.attachedArray??{},this.instance=e.instance??null}attached;attachedArray;instance}e.RendererStandardNode=i}(J||(J={}));(new J.RendererRootNode).minidomType="RootNode";const te=[];let ne;const re=[],oe=[],ae=e=>{ne=requestAnimationFrame((()=>ae({app:e.app,renderer:W.value?.instance,scene:I.value.instance,camera:D.value?.instance})));const{app:n,renderer:r,scene:o,camera:a}=e;re.forEach((t=>{t&&t(e)})),r&&o&&a&&(n.customRender?n.customRender(e):r.render(t.toRaw(o),t.toRaw(a))),oe.forEach((t=>{t&&t(e)}))},se=(e,t=1/0)=>{t===1/0?re.push(e):re.splice(t,0,e)},ie=()=>{ne&&cancelAnimationFrame(ne)};const ce={x:"position.x",y:"position.y",z:"position.z"},de=["","parameters"],ue=["args","attach","attachArray","is.default","isDefault","key","onAdded","ref","src"],le=["geometry","material"];function pe(e,t,n,r){const o=r??e.instance,a=t.instance;e.props.attach===n&&(t.attached={[n]:o,...t.attached||{}},a[n]=r??e.instance),e.props.attachArray===n&&(t.attachedArray[e.props.attachArray]||(t.attachedArray[e.props.attachArray]=[]),t.attachedArray[e.props.attachArray].push(o),a[n]=[a[n]])}const me={createElement:(e,t,n,r)=>{const o={type:e};r&&(o.props=r);if(h(e)){const e=function(e={}){const t={domElement:document.createElement(e.type??"")};return new J.RendererDomNode({...t,...e,metaType:"domMeta"})}(o);return e}const a=K(o);return le.forEach((t=>{e.toLowerCase().endsWith(t)&&(a.props.attach=t)})),a},createText:e=>function(e={}){const t={text:e.text??""};return new J.RendererTextNode({...e,...t,metaType:"textMeta"})}({text:e}),createComment:e=>function(e={}){const t={text:e.text??""};return new J.RendererCommentNode({...t,...e,metaType:"commentMeta"})}({text:e}),insert:(e,t,n)=>{let r=t??N();if(r.insertBefore(e,n),"commentMeta"!==e.metaType&&"textMeta"!==e.metaType&&(h(e)&&(h(t)||y(t))&&t.domElement.appendChild(e.domElement),f(e))){let n=r.metaType;if("textMeta"===n||"commentMeta"===n){const e=r.getPath();for(let t=e.length-1;t>=0;t--)if("textMeta"!==e[t].metaType&&"commentMeta"!==e[t].metaType){r=e[t];break}}if("standardMeta"===e.metaType&&"scene"!==e.type&&y(r)){const t=I.value;t.instance&&e&&t.addChild(e),e.instance&&e.instance.isObject3D&&t.instance&&t!==e&&t.instance.add(e.instance)}else f(e)&&e.instance?.isObject3D&&f(r)&&r.instance?.isObject3D&&r.instance?.add?.(e.instance);if(e?.props?.attach&&f(t)&&t?.instance){e.type?.toLowerCase().endsWith("loader")&&e.props.src&&(e.props.attach||e.props.attachArray)?function(e,t){const n=e.instance;if(t.attached=t.attached||{},t.attachedArray=t.attachedArray||{},!e.props.attach)return;if("textureloader"===e.type?.toLowerCase()){const r=n.load(e.props.src);pe(e,t,e.props.attach,r)}else n.load(e.props.src,(n=>{pe(e,t,e.props.attach,n)}),null,(e=>{throw new Error(e)}))}(e,t):pe(e,t,e.props.attach)}e.props?.onAdded&&e.props.onAdded({instance:e.instance})}},nextSibling(e){const t=e.nextSibling;return t||null},parentNode(e){const t=e.parentNode;return t||null},patchProp(e,t,n,o){h(e)?"style"===t?Object.keys(o).forEach((t=>{e.domElement.style[t]=o[t]})):e.domElement.setAttribute(t,o):y(e)||t.startsWith("$")||function({node:e,key:t,value:n}){if((e=>["onClick","onContextMenu","onDoubleClick","onPointerUp","onPointerDown","onPointerOver","onPointerOut","onPointerEnter","onPointerLeave","onPointerMove","onWheel"].includes(e))(t))return b({node:e,key:t,value:n});const o=t.replace(/-/g,"."),a=ce[o]||o;if(ue.includes(t)||ue.includes(a))return e;if(!f(e))return e;if("string"==typeof n&&n.startsWith("$attached")){const t=n.replace("$attached.","");n=r.get(e.attached,t,null)}const s=e.instance;if(!s)return e;let i;for(let e=0;e<de.length&&!i;e++){const t=[de[e],a].filter(Boolean).join(".");i=i=r.get(s,t)}if(i&&r.isNumber(n)&&i.setScalar)i.setScalar(n);else if(i&&i.set){const e=Array.isArray(n)?n:[n];s[a].set(...e)}else"function"==typeof i?i.bind(e.instance)(...n):void 0!==r.get(s,a,void 0)?r.set(s,a,""===n||n):console.log(`No property ${a} found on ${s}`);const c=s?.texture?.type||s?.type;if("string"==typeof c){const e=c.toLowerCase();switch(!0){case e.includes("material"):s.needsUpdate=!0;break;case e.includes("camera")&&s.updateProjectionMatrix:s.updateProjectionMatrix()}}}({node:e,key:t,value:o})},remove:e=>{if(!e)return;const t=Object.keys(T),n=[];e.walk((e=>(n.push(e),!0))),n.forEach((e=>{const n=t.find((t=>T[t]?.uuid===e.uuid));if(n&&(T[n]=null),f(e)){e.instance?.removeFromParent?.();const t="scene"!==e.type&&e.instance?.dispose;t&&t.bind(e.instance)(),e.instance=null}e.drop();const r=s.findIndex((t=>t.uuid===e.uuid));-1!==r&&s.splice(r,1)}))},setElementText(){},setText(){}},he={dpr:t.ref(1),inputActive:g,mousePos:C},fe=t.computed((()=>D.value?.instance??null));const ye=t.computed((()=>W.value?.instance??null));const ve=t.computed((()=>I.value.instance));let ge=null,be=null;e.camera=fe,e.clearCustomRender=()=>{ge?ge.clearCustomRender():be=null},e.createApp=e=>{ge=t.createRenderer(me).createApp(e),Object.keys(p).forEach((e=>{ge?.component(e,p[e])}));const{mount:n}=ge;return ge.mount=(e,...t)=>{const r=N({domElement:"string"==typeof e?document.querySelector(e):e,isLunchboxRootNode:!0,name:"root",metaType:"rootMeta",type:"root",uuid:"LUNCHBOX_ROOT"});ge.rootNode=r;return n(r,...t)},ge.extend=e=>((({app:e,...t})=>{Object.keys(t).forEach((n=>{e.component(n,_(n)),u[n]=t[n]}))})({app:ge,...e}),ge),ge.setCustomRender=e=>{ge.customRender=e},be&&(ge.setCustomRender(be),be=null),ge.clearCustomRender=()=>{ge.customRender=null},ge},e.find=function(e){return e=t.isRef(e)?e.value:e,f(e)?e?.instance:m(e)?e?.$el?.instance:t.isVNode(e)?e.el?.instance:null},e.globals=he,e.offAfterRender=e=>{if(isFinite(e))oe.splice(e,1);else{const t=oe.findIndex((t=>t==e));oe.splice(t,1)}},e.offBeforeRender=e=>{if(isFinite(e))re.splice(e,1);else{const t=re.findIndex((t=>t==e));re.splice(t,1)}},e.onAfterRender=(e,t=1/0)=>{t===1/0?oe.push(e):oe.splice(t,0,e)},e.onBeforeRender=se,e.onStart=(e,t=1/0)=>{t===1/0?te.push(e):te.splice(t,0,e)},e.renderer=ye,e.scene=ve,e.setCustomRender=e=>{ge?ge.setCustomRender(e):be=e},e.useCamera=function(e){return t.watch(fe,(t=>{t&&e(t)}),{immediate:!0})},e.useRenderer=function(e){return t.watch(ye,(t=>{t&&e(t)}),{immediate:!0})},e.useScene=function(e){return t.watch(ve,(t=>{t&&e(t)}),{immediate:!0})},Object.defineProperty(e,"__esModule",{value:!0})}));
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("vue"),require("three"),require("lodash")):"function"==typeof define&&define.amd?define(["exports","vue","three","lodash"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).LunchboxRenderer={},e.vue,e.three,e.lodash)}(this,(function(e,t,n,r){"use strict";function o(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(n){if("default"!==n){var r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r.get?r:{enumerable:!0,get:function(){return e[n]}})}})),t.default=e,Object.freeze(t)}var a=o(n);const s=[],i=(e,n)=>{const r=I.value?.instance,o=K.value.instance,a=$.value;if(!r?.domElement||!o||!a)return;const s=(e=e??window.innerWidth)/(n=n??window.innerHeight);if("perspectivecamera"===a.type?.toLowerCase()){const e=a.instance;e.aspect=s,e.updateProjectionMatrix()}else if("orthographiccamera"===a.type?.toLowerCase()){const t=a.instance,r=n/e;t.top=10*r,t.bottom=10*-r,t.right=10,t.left=-10,t.updateProjectionMatrix()}else console.log("TODO: non-ortho or perspective camera");r.setSize(e,n),o&&a.instance&&r.render(t.toRaw(o),t.toRaw(a.instance))},c=(e,t,n,r)=>{const o=e.value?.domElement;if(!o)throw new Error("missing container");const a=()=>{if("container"===r){const e=(e=>{const t=getComputedStyle(e);return{width:e.clientWidth-parseFloat(t.paddingLeft)-parseFloat(t.paddingRight),height:e.clientHeight-parseFloat(t.paddingTop)-parseFloat(t.paddingBottom)}})(o);i(e.width,e.height)}else i()};a();const s=new ResizeObserver((([e])=>{a()}));o&&s.observe(o),n((()=>{t&&s.unobserve(t)}))},d=e=>({position:e,top:0,right:0,bottom:0,left:0,width:"100%",height:"100%",display:"block"}),u={name:"Lunchbox",props:{background:String,cameraArgs:Array,cameraLook:Array,cameraLookAt:Array,cameraPosition:Array,dpr:Number,ortho:Boolean,orthographic:Boolean,r3f:Boolean,rendererArguments:Object,rendererProperties:Object,sizePolicy:String,shadow:[Boolean,Object],transparent:Boolean,zoom:Number,updateSource:Object},setup(e,n){const o=t.ref(),s=t.ref(!0),i=t.ref(e.dpr??-1),u=t.ref();let l,p,m;e.r3f&&a?.ColorManagement&&(a.ColorManagement.legacyMode=!1),t.onMounted((()=>{if(!o.value)throw new Error("missing canvas");if(l=T(["WebGLRenderer"]),l)s.value=!1,z.value=!0;else{const t={alpha:e.transparent,antialias:!0,canvas:o.value.domElement,powerPreference:e.r3f?"high-performance":"default",...e.rendererArguments??{}};I.value=q({type:"WebGLRenderer",uuid:U,props:{args:[t]}}),z.value=!0;const n=I;e.r3f&&n.value.instance&&(n.value.instance.outputEncoding=a.sRGBEncoding,n.value.instance.toneMapping=a.ACESFilmicToneMapping);const s={shadow:e.shadow};n.value.instance&&s?.shadow&&(n.value.instance.shadowMap.enabled=!0,"object"==typeof s.shadow&&(n.value.instance.shadowMap.type=s.shadow.type)),e.rendererProperties&&Object.keys(e.rendererProperties).forEach((t=>{r.set(n.value,t,e.rendererProperties[t])})),l=n.value}if(p=T(["PerspectiveCamera","OrthographicCamera"]),p?D.value=!0:(e.ortho||e.orthographic?$.value=q({props:{args:e.cameraArgs??[]},type:"OrthographicCamera",uuid:S}):$.value=q({props:{args:e.cameraArgs??[e.r3f?75:45,.5625,1,1e3]},type:"PerspectiveCamera",uuid:S}),D.value=!0,p=$.value),!p.instance)throw new Error("Error creating camera.");if(p&&e.cameraPosition&&p.instance.position.set(...e.cameraPosition),p&&(e.cameraLookAt||e.cameraLook)){const t=e.cameraLookAt||e.cameraLook;p.instance.lookAt(...t)}if(p&&void 0!==e.zoom&&(p.instance.zoom=e.zoom),m=K.value,m&&m.instance&&e.background&&(m.instance.background=new a.Color(e.background)),-1===i.value&&(i.value=window.devicePixelRatio),!l?.instance)throw new Error("missing renderer");l.instance.setPixelRatio(i.value),ge.dpr.value=i.value,c(u,l.instance.domElement,t.onBeforeUnmount,e.sizePolicy);const n=t.getCurrentInstance().appContext.app;for(let e of re)e({app:n,camera:p.instance,renderer:l.instance,scene:m.instance});de({app:n,camera:p.instance,renderer:l.instance,scene:m.instance,updateSource:e.updateSource})})),t.onBeforeUnmount((()=>{ue(),le()}));const h="container"===e.sizePolicy?"static":"absolute",f="container"===e.sizePolicy?"static":"fixed";return()=>[n.slots.default?.()??null,t.h("div",{style:d(h),ref:u},[s.value?t.h("canvas",{style:d(f),class:"lunchbox-canvas",ref:o}):null])]}},l={},p=["canvas","div","LunchboxWrapper"],m={...["mesh","instancedMesh","scene","sprite","object3D","instancedBufferGeometry","bufferGeometry","boxBufferGeometry","circleBufferGeometry","coneBufferGeometry","cylinderBufferGeometry","dodecahedronBufferGeometry","extrudeBufferGeometry","icosahedronBufferGeometry","latheBufferGeometry","octahedronBufferGeometry","parametricBufferGeometry","planeBufferGeometry","polyhedronBufferGeometry","ringBufferGeometry","shapeBufferGeometry","sphereBufferGeometry","tetrahedronBufferGeometry","textBufferGeometry","torusBufferGeometry","torusKnotBufferGeometry","tubeBufferGeometry","wireframeGeometry","parametricGeometry","tetrahedronGeometry","octahedronGeometry","icosahedronGeometry","dodecahedronGeometry","polyhedronGeometry","tubeGeometry","torusKnotGeometry","torusGeometry","sphereGeometry","ringGeometry","planeGeometry","latheGeometry","shapeGeometry","extrudeGeometry","edgesGeometry","coneGeometry","cylinderGeometry","circleGeometry","boxGeometry","material","shadowMaterial","spriteMaterial","rawShaderMaterial","shaderMaterial","pointsMaterial","meshPhysicalMaterial","meshStandardMaterial","meshPhongMaterial","meshToonMaterial","meshNormalMaterial","meshLambertMaterial","meshDepthMaterial","meshDistanceMaterial","meshBasicMaterial","meshMatcapMaterial","lineDashedMaterial","lineBasicMaterial","light","spotLightShadow","spotLight","pointLight","rectAreaLight","hemisphereLight","directionalLightShadow","directionalLight","ambientLight","lightShadow","ambientLightProbe","hemisphereLightProbe","lightProbe","texture","videoTexture","dataTexture","dataTexture3D","compressedTexture","cubeTexture","canvasTexture","depthTexture","textureLoader","group","catmullRomCurve3","points","cameraHelper","camera","perspectiveCamera","orthographicCamera","cubeCamera","arrayCamera","webGLRenderer"].map((e=>t.defineComponent({inheritAttrs:!1,name:e,setup:(n,r)=>()=>t.h(e,r.attrs,r.slots?.default?.()||[])}))).reduce(((e,t)=>(e[t.name]=t,e)),{}),Lunchbox:u};const h=e=>e?.$el&&e?.$el?.hasOwnProperty?.("instance"),f=e=>{if("domMeta"===e?.metaType)return!0;const t="string"==typeof e?e:e?.type;return p.includes(t??"")},y=e=>"standardMeta"===e?.metaType,v=e=>e.isLunchboxRootNode,g=[],b=t.ref(!1);function x({node:e,key:n,value:r}){var o;if(e.eventListeners[n]||(e.eventListeners[n]=[]),e.eventListenerRemoveFunctions[n]||(e.eventListenerRemoveFunctions[n]=[]),e.eventListeners[n].push(r),w.includes(n)&&(_.value,e.instance&&!g.includes(e)&&(o=e,g.push(o),e.eventListenerRemoveFunctions[n].push((()=>(e=>{const t=g.indexOf(e);-1!==t&&g.splice(t,1)})(e))))),"onClick"===n||"onPointerDown"===n||"onPointerUp"===n){const r=t.watch((()=>b.value),(t=>{const r=M.map((e=>e.element)).findIndex((t=>t.instance&&t.instance.uuid===e.instance?.uuid));-1!==r&&((!t||"onClick"!==n&&"onPointerDown"!==n)&&(t||"onPointerUp"!==n)||e.eventListeners[n].forEach((e=>{e({intersection:M[r].intersection})})))}));e.eventListenerRemoveFunctions[n].push(r)}return e}const w=["onClick","onPointerUp","onPointerDown","onPointerOver","onPointerOut","onPointerEnter","onPointerLeave","onPointerMove"];let R,A,C;const E=t.ref({x:1/0,y:1/0});let L,P=!1;let M=[];const B=()=>{const e=_.value?.instance,t=$.value?.instance;if(!e||!t)return;e.setFromCamera(ge.mousePos.value,t);const n=e.intersectObjects(g.map((e=>e.instance)));let r=[],o=[],a=[];r=M.map((e=>e.intersection)),n?.forEach((e=>{if(-1===M.findIndex((t=>t.intersection.object===e.object))){const t=g.find((t=>t.instance?.uuid===e.object.uuid));t&&o.push({element:t,intersection:e})}else{const t=g.find((t=>t.instance?.uuid===e.object.uuid));t&&a.push({element:t,intersection:e})}const t=r.findIndex((t=>t.object.uuid===e.object.uuid));-1!==t&&r.splice(t,1)}));const s=r.map((e=>({element:g.find((t=>t.instance?.uuid===e.object.uuid)),intersection:e})));o.forEach((({element:e,intersection:t})=>{N({element:e,eventKeys:["onPointerEnter"],intersection:t})})),a.forEach((({element:e,intersection:t})=>{N({element:e,eventKeys:["onPointerOver","onPointerMove"],intersection:t})})),s.forEach((({element:e,intersection:t})=>{N({element:e,eventKeys:["onPointerLeave","onPointerOut"],intersection:t})})),M=[].concat(o,a)},N=({element:e,eventKeys:t,intersection:n})=>{e&&t.forEach((t=>{e.eventListeners[t]&&e.eventListeners[t].forEach((e=>{e({intersection:n})}))}))};function G(t={}){return e.lunchboxTree||(e.lunchboxTree=new Z.RendererRootNode(t)),e.lunchboxTree}function T(e){Array.isArray(e)||(e=[e]);for(let t of e)if(j[t])return j[t];for(let n of e){const e=O[n]||s.find((e=>e.type?.toLowerCase()===n.toLowerCase()));if(!(t=e,"RendererNode"!==t?.minidomType||!1!==e.props["is-default"]&&!1!=!e.props.isDefault))return null;if(e){const t=e;return O[n]=t,t}}var t;return null}e.lunchboxTree=void 0;const O=t.reactive({}),j=t.reactive({});function k(e,n,r={},o=null){Array.isArray(e)||(e=[e]);for(let t of e)O[t]||(O[t]=null),j[t]||(j[t]=null);return t.computed({get(){const t=T(e);if(t)return t;const a=G(),s=q({type:e[0],uuid:n,props:r});return a.addChild(s),O[e[0]]=s,o&&o(s),s},set(e){const t=e.type??"",n=t[0].toUpperCase()+t.slice(1);j[n]=e}})}const S="FALLBACK_CAMERA",F=k(["PerspectiveCamera","OrthographicCamera"],S,{args:[45,.5625,1,1e3]}),D=t.ref(!1),$=t.computed({get:()=>D.value?F.value:null,set(e){const t=e.type??"",n=t[0].toUpperCase()+t.slice(1);j[n]=e}}),U="FALLBACK_RENDERER",W=k(["WebGLRenderer"],U,{}),z=t.ref(!1),I=t.computed({get:()=>z.value?W.value:null,set(e){const t=e.type??"",n=t[0].toUpperCase()+t.slice(1);j[n]=e}}),K=k("Scene","FALLBACK_SCENE"),_=k("Raycaster","AUTO_RAYCASTER",{},(e=>(e=>{if(!e.instance)return;let n=null;n=t.watch((()=>I.value),(e=>{e?.instance&&(P||(R=t=>{const n=(e.instance.domElement.width??1)/ge.dpr.value,r=(e.instance.domElement.height??1)/ge.dpr.value;E.value.x=t.offsetX/n*2-1,E.value.y=-t.offsetY/r*2+1},A=()=>b.value=!0,C=()=>b.value=!1,e.instance.domElement.addEventListener("mousemove",R),e.instance.domElement.addEventListener("mousedown",A),e.instance.domElement.addEventListener("mouseup",C),t.watch((()=>[b.value,E.value.x,E.value.y]),(()=>{L&&cancelAnimationFrame(L),L=requestAnimationFrame((()=>{B()}))})),P=!0),n&&n())}),{immediate:!0})})(e)));function q(e={},t={}){const n={attached:e.attached??[],attachedArray:e.attachedArray??{},instance:e.instance??null},r=new Z.RendererStandardNode({...e,...n,metaType:"standardMeta"});return!r.type||v(r)||r.instance||(r.instance=function(e){if(!e.type)return null;const t=e.type[0].toUpperCase()+e.type.slice(1),n=l[e.type]||a[t];if(!n)throw`${t} is not part of the THREE namespace! Did you forget to extend? import {extend} from 'lunchbox'; extend({app, YourComponent, ...})`;const r=(e.props.args??[]).map((t=>function({node:e,prop:t}){const n="string"==typeof t&&t.startsWith("$attachedArray"),r=function({node:e,prop:t}){if("string"==typeof t&&t.startsWith("$attachedArray"))return e.attachedArray[t.replace("$attachedArray.","")];if("string"==typeof t&&t.startsWith("$attached"))return e.attached[t.replace("$attached.","")];return t}({node:e,prop:t});return Array.isArray(r)&&n?r:[r]}({node:e,prop:t})));let o=[];r.forEach((e=>{o=o.concat(e)}));return new n(...o)}({...r,props:{...r.props,...t}})),"scene"===r.type?.toLowerCase()?K.value=r:r.type?.toLowerCase().endsWith("camera")&&($.value=r),r}const V=e=>t.defineComponent({inheritAttrs:!1,name:e,render(){return t.h(e,this.$attrs,this.$slots?.default?.()||[])}});var H,Y=new Uint8Array(16);function X(){if(!H&&!(H="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return H(Y)}var J=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;function Q(e){return"string"==typeof e&&J.test(e)}for(var Z,ee=[],te=0;te<256;++te)ee.push((te+256).toString(16).substr(1));function ne(e,t,n){var r=(e=e||{}).random||(e.rng||X)();if(r[6]=15&r[6]|64,r[8]=63&r[8]|128,t){n=n||0;for(var o=0;o<16;++o)t[n+o]=r[o];return t}return function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=(ee[e[t+0]]+ee[e[t+1]]+ee[e[t+2]]+ee[e[t+3]]+"-"+ee[e[t+4]]+ee[e[t+5]]+"-"+ee[e[t+6]]+ee[e[t+7]]+"-"+ee[e[t+8]]+ee[e[t+9]]+"-"+ee[e[t+10]]+ee[e[t+11]]+ee[e[t+12]]+ee[e[t+13]]+ee[e[t+14]]+ee[e[t+15]]).toLowerCase();if(!Q(n))throw TypeError("Stringified UUID is invalid");return n}(r)}!function(e){e.BaseNode=class{constructor(e={},t){this.parentNode=e?.parentNode??t??null,this.minidomType="MinidomBaseNode",this.uuid=e?.uuid??ne(),s.push(this)}uuid;parentNode;get nextSibling(){if(!this.parentNode)return null;const e=this.parentNode.children.findIndex((e=>e.uuid===this.uuid));return-1!==e&&e<this.parentNode.children.length-1?this.parentNode.children[e+1]:null}insertBefore(e,t){e.removeAsChildFromAnyParents(),e.parentNode=this;const n=this.children.findIndex((e=>e.uuid===t?.uuid));-1!==n?this.children.splice(n,0,e):this.children.push(e)}removeChild(e){const t=this.children.findIndex((t=>t?.uuid===e?.uuid));-1!==t&&this.children.splice(t,1)}children=[];addChild(e){return e&&(e.removeAsChildFromAnyParents(),e.parentNode=this,this.insertBefore(e,null)),this}getPath(){const e=[];let t=this;for(;t;)e.unshift(t),t=t.parentNode;return e}drop(){this.parentNode=null,this.removeAsChildFromAnyParents()}walk(e){const t=[this,...this.children],n=[];let r=!0;for(;t.length&&r;){const o=t.shift();if(o){if(n.includes(o))continue;n.push(o),t.push(...o.children.filter((e=>!n.includes(e)))),r=e(o)}else r=!1}}minidomType;removeAsChildFromAnyParents(){s.forEach((e=>e.removeChild(this)))}};class t extends e.BaseNode{constructor(e={},t){super(e,t),this.minidomType="RendererNode",this.eventListeners={},this.eventListenerRemoveFunctions={},this.name=e.name??"",this.metaType=e.metaType??"standardMeta",this.props=e.props??[],this.type=e.type??""}eventListeners;eventListenerRemoveFunctions;name;metaType;props;type;drop(){super.drop(),Object.keys(this.eventListenerRemoveFunctions).forEach((e=>{this.eventListenerRemoveFunctions[e].forEach((e=>e()))}))}}e.RendererBaseNode=t;class n extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.domElement=e.domElement??document.createElement("div")}domElement;isLunchboxRootNode=!0}e.RendererRootNode=n;class r extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.text=e.text??""}text}e.RendererCommentNode=r;class o extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.domElement=e.domElement??document.createElement("div")}domElement}e.RendererDomNode=o;class a extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.text=e.text??""}text}e.RendererTextNode=a;class i extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.attached=e.attached??[],this.attachedArray=e.attachedArray??{},this.instance=e.instance??null}attached;attachedArray;instance}e.RendererStandardNode=i}(Z||(Z={}));(new Z.RendererRootNode).minidomType="RootNode";const re=[];let oe,ae;const se=[],ie=[],ce=e=>{ue(),oe=requestAnimationFrame((()=>de({app:e.app,renderer:I.value?.instance,scene:K.value.instance,camera:$.value?.instance,updateSource:e.updateSource})))},de=e=>{e.updateSource?ae||(ae=t.watch(e.updateSource,(()=>{ce(e)}),{deep:!0})):ce(e);const{app:n,renderer:r,scene:o,camera:a}=e;se.forEach((t=>{t&&t(e)})),r&&o&&a&&(n.customRender?n.customRender(e):r.render(t.toRaw(o),t.toRaw(a))),ie.forEach((t=>{t&&t(e)}))},ue=()=>{oe&&cancelAnimationFrame(oe)},le=()=>{ae&&ae()};const pe={x:"position.x",y:"position.y",z:"position.z"},me=["","parameters"],he=["args","attach","attachArray","is.default","isDefault","key","onAdded","ref","src"],fe=["geometry","material"];function ye(e,t,n,r){const o=r??e.instance,a=t.instance;e.props.attach===n&&(t.attached={[n]:o,...t.attached||{}},a[n]=r??e.instance),e.props.attachArray===n&&(t.attachedArray[e.props.attachArray]||(t.attachedArray[e.props.attachArray]=[]),t.attachedArray[e.props.attachArray].push(o),a[n]=[a[n]])}const ve={createElement:(e,t,n,r)=>{const o={type:e};r&&(o.props=r);if(f(e)){const e=function(e={}){const t={domElement:document.createElement(e.type??"")};return new Z.RendererDomNode({...t,...e,metaType:"domMeta"})}(o);return e}const a=q(o);return fe.forEach((t=>{e.toLowerCase().endsWith(t)&&(a.props.attach=t)})),a},createText:e=>function(e={}){const t={text:e.text??""};return new Z.RendererTextNode({...e,...t,metaType:"textMeta"})}({text:e}),createComment:e=>function(e={}){const t={text:e.text??""};return new Z.RendererCommentNode({...t,...e,metaType:"commentMeta"})}({text:e}),insert:(e,t,n)=>{let r=t??G();if(r.insertBefore(e,n),"commentMeta"!==e.metaType&&"textMeta"!==e.metaType&&(f(e)&&(f(t)||v(t))&&t.domElement.appendChild(e.domElement),y(e))){let n=r.metaType;if("textMeta"===n||"commentMeta"===n){const e=r.getPath();for(let t=e.length-1;t>=0;t--)if("textMeta"!==e[t].metaType&&"commentMeta"!==e[t].metaType){r=e[t];break}}if("standardMeta"===e.metaType&&"scene"!==e.type&&v(r)){const t=K.value;t.instance&&e&&t.addChild(e),e.instance&&e.instance.isObject3D&&t.instance&&t!==e&&t.instance.add(e.instance)}else y(e)&&e.instance?.isObject3D&&y(r)&&r.instance?.isObject3D&&r.instance?.add?.(e.instance);if(e?.props?.attach&&y(t)&&t?.instance){e.type?.toLowerCase().endsWith("loader")&&e.props.src&&(e.props.attach||e.props.attachArray)?function(e,t){const n=e.instance;if(t.attached=t.attached||{},t.attachedArray=t.attachedArray||{},!e.props.attach)return;if("textureloader"===e.type?.toLowerCase()){const r=n.load(e.props.src);ye(e,t,e.props.attach,r)}else n.load(e.props.src,(n=>{ye(e,t,e.props.attach,n)}),null,(e=>{throw new Error(e)}))}(e,t):ye(e,t,e.props.attach)}e.props?.onAdded&&e.props.onAdded({instance:e.instance})}},nextSibling(e){const t=e.nextSibling;return t||null},parentNode(e){const t=e.parentNode;return t||null},patchProp(e,t,n,o){f(e)?"style"===t?Object.keys(o).forEach((t=>{e.domElement.style[t]=o[t]})):e.domElement.setAttribute(t,o):v(e)||t.startsWith("$")||function({node:e,key:t,value:n}){if((e=>["onClick","onContextMenu","onDoubleClick","onPointerUp","onPointerDown","onPointerOver","onPointerOut","onPointerEnter","onPointerLeave","onPointerMove","onWheel"].includes(e))(t))return x({node:e,key:t,value:n});const o=t.replace(/-/g,"."),a=pe[o]||o;if(he.includes(t)||he.includes(a))return e;if(!y(e))return e;if("string"==typeof n&&n.startsWith("$attached")){const t=n.replace("$attached.","");n=r.get(e.attached,t,null)}const s=e.instance;if(!s)return e;let i;for(let e=0;e<me.length&&!i;e++){const t=[me[e],a].filter(Boolean).join(".");i=i=r.get(s,t)}if(i&&r.isNumber(n)&&i.setScalar)i.setScalar(n);else if(i&&i.set){const e=Array.isArray(n)?n:[n];s[a].set(...e)}else"function"==typeof i?i.bind(e.instance)(...n):void 0!==r.get(s,a,void 0)?r.set(s,a,""===n||n):console.log(`No property ${a} found on ${s}`);const c=s?.texture?.type||s?.type;if("string"==typeof c){const e=c.toLowerCase();switch(!0){case e.includes("material"):s.needsUpdate=!0;break;case e.includes("camera")&&s.updateProjectionMatrix:s.updateProjectionMatrix()}}}({node:e,key:t,value:o})},remove:e=>{if(!e)return;const t=Object.keys(j),n=[];e.walk((e=>(n.push(e),!0))),n.forEach((e=>{const n=t.find((t=>j[t]?.uuid===e.uuid));if(n&&(j[n]=null),y(e)){e.instance?.removeFromParent?.();const t="scene"!==e.type&&e.instance?.dispose;t&&t.bind(e.instance)(),e.instance=null}e.drop();const r=s.findIndex((t=>t.uuid===e.uuid));-1!==r&&s.splice(r,1)}))},setElementText(){},setText(){}},ge={dpr:t.ref(1),inputActive:b,mousePos:E},be=t.computed((()=>$.value?.instance??null));const xe=t.computed((()=>I.value?.instance??null));const we=t.computed((()=>K.value.instance));let Re=null,Ae=null;e.camera=be,e.clearCustomRender=()=>{Re?Re.clearCustomRender():Ae=null},e.createApp=e=>{Re=t.createRenderer(ve).createApp(e),Object.keys(m).forEach((e=>{Re?.component(e,m[e])}));const{mount:n}=Re;return Re.mount=(e,...t)=>{const r=G({domElement:"string"==typeof e?document.querySelector(e):e,isLunchboxRootNode:!0,name:"root",metaType:"rootMeta",type:"root",uuid:"LUNCHBOX_ROOT"});Re.rootNode=r;return n(r,...t)},Re.extend=e=>((({app:e,...t})=>{Object.keys(t).forEach((n=>{e.component(n,V(n)),l[n]=t[n]}))})({app:Re,...e}),Re),Re.setCustomRender=e=>{Re.customRender=e},Ae&&(Re.setCustomRender(Ae),Ae=null),Re.clearCustomRender=()=>{Re.customRender=null},Re},e.find=function(e){return e=t.isRef(e)?e.value:e,y(e)?e?.instance:h(e)?e?.$el?.instance:t.isVNode(e)?e.el?.instance:null},e.globals=ge,e.offAfterRender=e=>{if(isFinite(e))ie.splice(e,1);else{const t=ie.findIndex((t=>t==e));ie.splice(t,1)}},e.offBeforeRender=e=>{if(isFinite(e))se.splice(e,1);else{const t=se.findIndex((t=>t==e));se.splice(t,1)}},e.onAfterRender=(e,t=1/0)=>{t===1/0?ie.push(e):ie.splice(t,0,e)},e.onBeforeRender=(e,t=1/0)=>{t===1/0?se.push(e):se.splice(t,0,e)},e.onStart=(e,t=1/0)=>{t===1/0?re.push(e):re.splice(t,0,e)},e.renderer=xe,e.scene=we,e.setCustomRender=e=>{Re?Re.setCustomRender(e):Ae=e},e.useCamera=function(e){return t.watch(be,(t=>{t&&e(t)}),{immediate:!0})},e.useRenderer=function(e){return t.watch(xe,(t=>{t&&e(t)}),{immediate:!0})},e.useScene=function(e){return t.watch(we,(t=>{t&&e(t)}),{immediate:!0})},Object.defineProperty(e,"__esModule",{value:!0})}));
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { toRaw, ref, onMounted,
|
|
1
|
+
import { toRaw, ref, onMounted, onBeforeUnmount, getCurrentInstance, h, defineComponent, isRef, isVNode, watch, reactive, computed, createRenderer } from 'vue';
|
|
2
2
|
import * as THREE from 'three';
|
|
3
|
-
import { Color } from 'three';
|
|
4
3
|
import { set, get, isNumber } from 'lodash';
|
|
5
4
|
|
|
6
5
|
// this needs to be in a separate file to ensure it's created immediately
|
|
@@ -43,16 +42,30 @@ const resizeCanvas = (width, height) => {
|
|
|
43
42
|
}
|
|
44
43
|
};
|
|
45
44
|
|
|
46
|
-
const
|
|
45
|
+
const getInnerDimensions = (node) => {
|
|
46
|
+
const computedStyle = getComputedStyle(node);
|
|
47
|
+
const width = node.clientWidth - parseFloat(computedStyle.paddingLeft) - parseFloat(computedStyle.paddingRight);
|
|
48
|
+
const height = node.clientHeight - parseFloat(computedStyle.paddingTop) - parseFloat(computedStyle.paddingBottom);
|
|
49
|
+
return { width, height };
|
|
50
|
+
};
|
|
51
|
+
const prepCanvas = (container, canvasElement, onBeforeUnmount, sizePolicy) => {
|
|
47
52
|
const containerElement = container.value?.domElement;
|
|
48
53
|
if (!containerElement)
|
|
49
54
|
throw new Error('missing container');
|
|
50
55
|
// save...
|
|
51
56
|
// ...and size element
|
|
52
|
-
|
|
57
|
+
const resizeCanvasByPolicy = () => {
|
|
58
|
+
if (sizePolicy === "container") {
|
|
59
|
+
const dims = getInnerDimensions(containerElement);
|
|
60
|
+
resizeCanvas(dims.width, dims.height);
|
|
61
|
+
}
|
|
62
|
+
else
|
|
63
|
+
resizeCanvas();
|
|
64
|
+
};
|
|
65
|
+
resizeCanvasByPolicy();
|
|
53
66
|
// attach listeners
|
|
54
67
|
const observer = new ResizeObserver(([canvas]) => {
|
|
55
|
-
|
|
68
|
+
resizeCanvasByPolicy();
|
|
56
69
|
});
|
|
57
70
|
// window.addEventListener('resize', resizeCanvas)
|
|
58
71
|
if (containerElement) {
|
|
@@ -66,6 +79,8 @@ const prepCanvas = (container, canvasElement, onBeforeUnmount) => {
|
|
|
66
79
|
});
|
|
67
80
|
};
|
|
68
81
|
|
|
82
|
+
// TODO:
|
|
83
|
+
// Continue r3f prop - what else (besides camera fov) makes r3f look good?
|
|
69
84
|
/** fixed & fill styling for container */
|
|
70
85
|
const fillStyle = (position) => {
|
|
71
86
|
return {
|
|
@@ -76,6 +91,7 @@ const fillStyle = (position) => {
|
|
|
76
91
|
left: 0,
|
|
77
92
|
width: '100%',
|
|
78
93
|
height: '100%',
|
|
94
|
+
display: 'block',
|
|
79
95
|
};
|
|
80
96
|
};
|
|
81
97
|
const LunchboxWrapper = {
|
|
@@ -90,11 +106,14 @@ const LunchboxWrapper = {
|
|
|
90
106
|
dpr: Number,
|
|
91
107
|
ortho: Boolean,
|
|
92
108
|
orthographic: Boolean,
|
|
109
|
+
r3f: Boolean,
|
|
93
110
|
rendererArguments: Object,
|
|
94
111
|
rendererProperties: Object,
|
|
112
|
+
sizePolicy: String,
|
|
95
113
|
shadow: [Boolean, Object],
|
|
96
114
|
transparent: Boolean,
|
|
97
115
|
zoom: Number,
|
|
116
|
+
updateSource: Object,
|
|
98
117
|
},
|
|
99
118
|
setup(props, context) {
|
|
100
119
|
const canvas = ref();
|
|
@@ -104,6 +123,10 @@ const LunchboxWrapper = {
|
|
|
104
123
|
let renderer;
|
|
105
124
|
let camera;
|
|
106
125
|
let scene;
|
|
126
|
+
// https://threejs.org/docs/index.html#manual/en/introduction/Color-management
|
|
127
|
+
if (props.r3f && THREE?.ColorManagement) {
|
|
128
|
+
THREE.ColorManagement.legacyMode = false;
|
|
129
|
+
}
|
|
107
130
|
// MOUNT
|
|
108
131
|
// ====================
|
|
109
132
|
onMounted(() => {
|
|
@@ -124,6 +147,9 @@ const LunchboxWrapper = {
|
|
|
124
147
|
alpha: props.transparent,
|
|
125
148
|
antialias: true,
|
|
126
149
|
canvas: canvas.value.domElement,
|
|
150
|
+
powerPreference: !!props.r3f
|
|
151
|
+
? 'high-performance'
|
|
152
|
+
: 'default',
|
|
127
153
|
...(props.rendererArguments ?? {}),
|
|
128
154
|
};
|
|
129
155
|
// create new renderer
|
|
@@ -137,6 +163,15 @@ const LunchboxWrapper = {
|
|
|
137
163
|
// we've initialized the renderer, so anything depending on it can execute now
|
|
138
164
|
rendererReady.value = true;
|
|
139
165
|
const rendererAsWebGlRenderer = ensureRenderer;
|
|
166
|
+
// apply r3f settings if desired
|
|
167
|
+
if (props.r3f) {
|
|
168
|
+
if (rendererAsWebGlRenderer.value.instance) {
|
|
169
|
+
rendererAsWebGlRenderer.value.instance.outputEncoding =
|
|
170
|
+
THREE.sRGBEncoding;
|
|
171
|
+
rendererAsWebGlRenderer.value.instance.toneMapping =
|
|
172
|
+
THREE.ACESFilmicToneMapping;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
140
175
|
// update render sugar
|
|
141
176
|
const sugar = {
|
|
142
177
|
shadow: props.shadow,
|
|
@@ -184,7 +219,12 @@ const LunchboxWrapper = {
|
|
|
184
219
|
else {
|
|
185
220
|
ensuredCamera.value = createNode({
|
|
186
221
|
props: {
|
|
187
|
-
args: props.cameraArgs ?? [
|
|
222
|
+
args: props.cameraArgs ?? [
|
|
223
|
+
props.r3f ? 75 : 45,
|
|
224
|
+
0.5625,
|
|
225
|
+
1,
|
|
226
|
+
1000,
|
|
227
|
+
],
|
|
188
228
|
},
|
|
189
229
|
type: 'PerspectiveCamera',
|
|
190
230
|
uuid: fallbackCameraUuid,
|
|
@@ -217,7 +257,7 @@ const LunchboxWrapper = {
|
|
|
217
257
|
scene = ensuredScene.value;
|
|
218
258
|
// set background color
|
|
219
259
|
if (scene && scene.instance && props.background) {
|
|
220
|
-
scene.instance.background = new Color(props.background);
|
|
260
|
+
scene.instance.background = new THREE.Color(props.background);
|
|
221
261
|
}
|
|
222
262
|
// MISC PROPERTIES
|
|
223
263
|
// ====================
|
|
@@ -228,7 +268,7 @@ const LunchboxWrapper = {
|
|
|
228
268
|
renderer.instance.setPixelRatio(dpr.value);
|
|
229
269
|
globals.dpr.value = dpr.value;
|
|
230
270
|
// prep canvas (sizing, observe, unmount, etc)
|
|
231
|
-
prepCanvas(container, renderer.instance.domElement, onBeforeUnmount);
|
|
271
|
+
prepCanvas(container, renderer.instance.domElement, onBeforeUnmount, props.sizePolicy);
|
|
232
272
|
}
|
|
233
273
|
else {
|
|
234
274
|
throw new Error('missing renderer');
|
|
@@ -253,24 +293,28 @@ const LunchboxWrapper = {
|
|
|
253
293
|
camera: camera.instance,
|
|
254
294
|
renderer: renderer.instance,
|
|
255
295
|
scene: scene.instance,
|
|
296
|
+
updateSource: props.updateSource,
|
|
256
297
|
});
|
|
257
298
|
});
|
|
258
299
|
// UNMOUNT
|
|
259
300
|
// ====================
|
|
260
301
|
onBeforeUnmount(() => {
|
|
261
302
|
cancelUpdate();
|
|
303
|
+
cancelUpdateSource();
|
|
262
304
|
});
|
|
263
305
|
// RENDER FUNCTION
|
|
264
306
|
// ====================
|
|
307
|
+
const containerFillStyle = props.sizePolicy === 'container' ? 'static' : 'absolute';
|
|
308
|
+
const canvasFillStyle = props.sizePolicy === 'container' ? 'static' : 'fixed';
|
|
265
309
|
return () => [
|
|
266
310
|
context.slots.default?.() ?? null,
|
|
267
311
|
h('div', {
|
|
268
|
-
style: fillStyle(
|
|
312
|
+
style: fillStyle(containerFillStyle),
|
|
269
313
|
ref: container,
|
|
270
314
|
}, [
|
|
271
315
|
useFallbackRenderer.value
|
|
272
316
|
? h('canvas', {
|
|
273
|
-
style: fillStyle(
|
|
317
|
+
style: fillStyle(canvasFillStyle),
|
|
274
318
|
class: 'lunchbox-canvas',
|
|
275
319
|
ref: canvas,
|
|
276
320
|
})
|
|
@@ -604,6 +648,7 @@ let mouseDownListener;
|
|
|
604
648
|
let mouseUpListener;
|
|
605
649
|
const mousePos = ref({ x: Infinity, y: Infinity });
|
|
606
650
|
let autoRaycasterEventsInitialized = false;
|
|
651
|
+
let frameID$1;
|
|
607
652
|
const setupAutoRaycaster = (node) => {
|
|
608
653
|
const instance = node.instance;
|
|
609
654
|
if (!instance)
|
|
@@ -636,8 +681,14 @@ const setupAutoRaycaster = (node) => {
|
|
|
636
681
|
renderer.instance.domElement.addEventListener('mousedown', mouseDownListener);
|
|
637
682
|
renderer.instance.domElement.addEventListener('mouseup', mouseUpListener);
|
|
638
683
|
// TODO: add touch events
|
|
639
|
-
//
|
|
640
|
-
|
|
684
|
+
// process mouse events asynchronously, whenever the mouse state changes
|
|
685
|
+
watch(() => [inputActive.value, mousePos.value.x, mousePos.value.y], () => {
|
|
686
|
+
if (frameID$1)
|
|
687
|
+
cancelAnimationFrame(frameID$1);
|
|
688
|
+
frameID$1 = requestAnimationFrame(() => {
|
|
689
|
+
autoRaycasterBeforeRender();
|
|
690
|
+
});
|
|
691
|
+
});
|
|
641
692
|
// mark complete
|
|
642
693
|
autoRaycasterEventsInitialized = true;
|
|
643
694
|
// cancel setup watcher
|
|
@@ -966,7 +1017,7 @@ const extend = ({ app, ...targets }) => {
|
|
|
966
1017
|
};
|
|
967
1018
|
|
|
968
1019
|
/** Process props into either themselves or the $attached value */
|
|
969
|
-
function processProp({ node, prop }) {
|
|
1020
|
+
function processProp({ node, prop, }) {
|
|
970
1021
|
// return $attachedArray value if needed
|
|
971
1022
|
if (typeof prop === 'string' && prop.startsWith('$attachedArray')) {
|
|
972
1023
|
return node.attachedArray[prop.replace('$attachedArray.', '')];
|
|
@@ -978,10 +1029,12 @@ function processProp({ node, prop }) {
|
|
|
978
1029
|
// otherwise, return plain value
|
|
979
1030
|
return prop;
|
|
980
1031
|
}
|
|
981
|
-
function processPropAsArray({ node, prop }) {
|
|
1032
|
+
function processPropAsArray({ node, prop, }) {
|
|
982
1033
|
const isAttachedArray = typeof prop === 'string' && prop.startsWith('$attachedArray');
|
|
983
1034
|
const output = processProp({ node, prop });
|
|
984
|
-
return Array.isArray(output) && isAttachedArray
|
|
1035
|
+
return Array.isArray(output) && isAttachedArray
|
|
1036
|
+
? output
|
|
1037
|
+
: [output];
|
|
985
1038
|
}
|
|
986
1039
|
|
|
987
1040
|
function instantiateThreeObject(node) {
|
|
@@ -1280,16 +1333,34 @@ const onStart = (cb, index = Infinity) => {
|
|
|
1280
1333
|
};
|
|
1281
1334
|
|
|
1282
1335
|
let frameID;
|
|
1336
|
+
let watchStopHandle;
|
|
1283
1337
|
const beforeRender = [];
|
|
1284
1338
|
const afterRender = [];
|
|
1285
|
-
const
|
|
1286
|
-
|
|
1339
|
+
const requestUpdate = (opts) => {
|
|
1340
|
+
cancelUpdate();
|
|
1287
1341
|
frameID = requestAnimationFrame(() => update({
|
|
1288
1342
|
app: opts.app,
|
|
1289
1343
|
renderer: ensureRenderer.value?.instance,
|
|
1290
1344
|
scene: ensuredScene.value.instance,
|
|
1291
1345
|
camera: ensuredCamera.value?.instance,
|
|
1346
|
+
updateSource: opts.updateSource,
|
|
1292
1347
|
}));
|
|
1348
|
+
};
|
|
1349
|
+
const update = (opts) => {
|
|
1350
|
+
if (opts.updateSource) {
|
|
1351
|
+
if (!watchStopHandle) {
|
|
1352
|
+
// request next frame only when state changes
|
|
1353
|
+
watchStopHandle = watch(opts.updateSource, () => {
|
|
1354
|
+
requestUpdate(opts);
|
|
1355
|
+
}, {
|
|
1356
|
+
deep: true,
|
|
1357
|
+
});
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
else {
|
|
1361
|
+
// request next frame on a continuous loop
|
|
1362
|
+
requestUpdate(opts);
|
|
1363
|
+
}
|
|
1293
1364
|
// prep options
|
|
1294
1365
|
const { app, renderer, scene, camera } = opts;
|
|
1295
1366
|
// BEFORE RENDER
|
|
@@ -1352,6 +1423,10 @@ const cancelUpdate = () => {
|
|
|
1352
1423
|
if (frameID)
|
|
1353
1424
|
cancelAnimationFrame(frameID);
|
|
1354
1425
|
};
|
|
1426
|
+
const cancelUpdateSource = () => {
|
|
1427
|
+
if (watchStopHandle)
|
|
1428
|
+
watchStopHandle();
|
|
1429
|
+
};
|
|
1355
1430
|
|
|
1356
1431
|
/** Update a single prop on a given node. */
|
|
1357
1432
|
function updateObjectProp({ node, key, value, }) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lunchboxjs",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4017",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "vite -c utils/vite.config.ts",
|
|
6
6
|
"build": "vue-tsc --noEmit && vite build -c utils/vite.config.ts",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"@rollup/plugin-node-resolve": "13.0.5",
|
|
23
23
|
"@rollup/plugin-typescript": "8.3.0",
|
|
24
24
|
"@types/lodash": "4.14.175",
|
|
25
|
-
"@types/three": "0.
|
|
25
|
+
"@types/three": "0.141.0",
|
|
26
26
|
"@types/uuid": "8.3.1",
|
|
27
27
|
"@vitejs/plugin-vue": "^1.9.3",
|
|
28
28
|
"chroma-js": "2.1.2",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"prompts": "2.4.2",
|
|
34
34
|
"rollup-plugin-delete": "2.0.0",
|
|
35
35
|
"rollup-plugin-terser": "7.0.2",
|
|
36
|
+
"three": "0.141.0",
|
|
36
37
|
"typescript": "^4.4.3",
|
|
37
38
|
"vite": "^2.6.4",
|
|
38
39
|
"vite-plugin-glsl": "0.0.5",
|
|
@@ -40,7 +41,7 @@
|
|
|
40
41
|
"vue-tsc": "^0.3.0"
|
|
41
42
|
},
|
|
42
43
|
"peerDependencies": {
|
|
43
|
-
"three": "0.
|
|
44
|
+
"three": "0.141.0"
|
|
44
45
|
},
|
|
45
46
|
"files": [
|
|
46
47
|
"dist",
|
|
@@ -5,11 +5,13 @@ import {
|
|
|
5
5
|
onBeforeUnmount,
|
|
6
6
|
onMounted,
|
|
7
7
|
ref,
|
|
8
|
+
WatchSource,
|
|
8
9
|
WritableComputedRef,
|
|
9
10
|
} from 'vue'
|
|
10
11
|
import {
|
|
11
12
|
cameraReady,
|
|
12
13
|
cancelUpdate,
|
|
14
|
+
cancelUpdateSource,
|
|
13
15
|
createNode,
|
|
14
16
|
ensuredCamera,
|
|
15
17
|
ensureRenderer,
|
|
@@ -24,9 +26,13 @@ import {
|
|
|
24
26
|
} from '../../core'
|
|
25
27
|
import { set } from 'lodash'
|
|
26
28
|
import { globals, Lunch } from '../..'
|
|
27
|
-
import { Color, Vector2 } from 'three'
|
|
29
|
+
// import { Color, Vector2, sRGBEncoding, ACESFilmicToneMapping } from 'three'
|
|
30
|
+
import * as THREE from 'three'
|
|
28
31
|
import { prepCanvas } from './prepCanvas'
|
|
29
32
|
|
|
33
|
+
// TODO:
|
|
34
|
+
// Continue r3f prop - what else (besides camera fov) makes r3f look good?
|
|
35
|
+
|
|
30
36
|
/** fixed & fill styling for container */
|
|
31
37
|
const fillStyle = (position: string) => {
|
|
32
38
|
return {
|
|
@@ -37,6 +43,7 @@ const fillStyle = (position: string) => {
|
|
|
37
43
|
left: 0,
|
|
38
44
|
width: '100%',
|
|
39
45
|
height: '100%',
|
|
46
|
+
display: 'block',
|
|
40
47
|
}
|
|
41
48
|
}
|
|
42
49
|
|
|
@@ -52,11 +59,14 @@ export const LunchboxWrapper: ComponentOptions = {
|
|
|
52
59
|
dpr: Number,
|
|
53
60
|
ortho: Boolean,
|
|
54
61
|
orthographic: Boolean,
|
|
62
|
+
r3f: Boolean,
|
|
55
63
|
rendererArguments: Object,
|
|
56
64
|
rendererProperties: Object,
|
|
65
|
+
sizePolicy: String,
|
|
57
66
|
shadow: [Boolean, Object],
|
|
58
67
|
transparent: Boolean,
|
|
59
68
|
zoom: Number,
|
|
69
|
+
updateSource: Object,
|
|
60
70
|
},
|
|
61
71
|
setup(props: Lunch.WrapperProps, context) {
|
|
62
72
|
const canvas = ref<MiniDom.RendererDomNode>()
|
|
@@ -67,6 +77,11 @@ export const LunchboxWrapper: ComponentOptions = {
|
|
|
67
77
|
let camera: Lunch.Node<THREE.Camera> | null
|
|
68
78
|
let scene: MiniDom.RendererStandardNode<THREE.Scene>
|
|
69
79
|
|
|
80
|
+
// https://threejs.org/docs/index.html#manual/en/introduction/Color-management
|
|
81
|
+
if (props.r3f && (THREE as any)?.ColorManagement) {
|
|
82
|
+
;(THREE as any).ColorManagement.legacyMode = false
|
|
83
|
+
}
|
|
84
|
+
|
|
70
85
|
// MOUNT
|
|
71
86
|
// ====================
|
|
72
87
|
onMounted(() => {
|
|
@@ -88,6 +103,9 @@ export const LunchboxWrapper: ComponentOptions = {
|
|
|
88
103
|
alpha: props.transparent,
|
|
89
104
|
antialias: true,
|
|
90
105
|
canvas: canvas.value.domElement,
|
|
106
|
+
powerPreference: !!props.r3f
|
|
107
|
+
? 'high-performance'
|
|
108
|
+
: 'default',
|
|
91
109
|
...(props.rendererArguments ?? {}),
|
|
92
110
|
}
|
|
93
111
|
|
|
@@ -108,6 +126,16 @@ export const LunchboxWrapper: ComponentOptions = {
|
|
|
108
126
|
Lunch.Node<THREE.WebGLRenderer>
|
|
109
127
|
>
|
|
110
128
|
|
|
129
|
+
// apply r3f settings if desired
|
|
130
|
+
if (props.r3f) {
|
|
131
|
+
if (rendererAsWebGlRenderer.value.instance) {
|
|
132
|
+
rendererAsWebGlRenderer.value.instance.outputEncoding =
|
|
133
|
+
THREE.sRGBEncoding
|
|
134
|
+
rendererAsWebGlRenderer.value.instance.toneMapping =
|
|
135
|
+
THREE.ACESFilmicToneMapping
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
111
139
|
// update render sugar
|
|
112
140
|
const sugar = {
|
|
113
141
|
shadow: props.shadow,
|
|
@@ -160,7 +188,12 @@ export const LunchboxWrapper: ComponentOptions = {
|
|
|
160
188
|
} else {
|
|
161
189
|
ensuredCamera.value = createNode<THREE.PerspectiveCamera>({
|
|
162
190
|
props: {
|
|
163
|
-
args: props.cameraArgs ?? [
|
|
191
|
+
args: props.cameraArgs ?? [
|
|
192
|
+
props.r3f ? 75 : 45,
|
|
193
|
+
0.5625,
|
|
194
|
+
1,
|
|
195
|
+
1000,
|
|
196
|
+
],
|
|
164
197
|
},
|
|
165
198
|
type: 'PerspectiveCamera',
|
|
166
199
|
uuid: fallbackCameraUuid,
|
|
@@ -195,7 +228,7 @@ export const LunchboxWrapper: ComponentOptions = {
|
|
|
195
228
|
scene = ensuredScene.value
|
|
196
229
|
// set background color
|
|
197
230
|
if (scene && scene.instance && props.background) {
|
|
198
|
-
scene.instance.background = new Color(props.background)
|
|
231
|
+
scene.instance.background = new THREE.Color(props.background)
|
|
199
232
|
}
|
|
200
233
|
|
|
201
234
|
// MISC PROPERTIES
|
|
@@ -211,7 +244,8 @@ export const LunchboxWrapper: ComponentOptions = {
|
|
|
211
244
|
prepCanvas(
|
|
212
245
|
container,
|
|
213
246
|
renderer.instance.domElement,
|
|
214
|
-
onBeforeUnmount
|
|
247
|
+
onBeforeUnmount,
|
|
248
|
+
props.sizePolicy,
|
|
215
249
|
)
|
|
216
250
|
} else {
|
|
217
251
|
throw new Error('missing renderer')
|
|
@@ -239,6 +273,7 @@ export const LunchboxWrapper: ComponentOptions = {
|
|
|
239
273
|
camera: camera.instance,
|
|
240
274
|
renderer: renderer.instance,
|
|
241
275
|
scene: scene.instance,
|
|
276
|
+
updateSource: props.updateSource,
|
|
242
277
|
})
|
|
243
278
|
})
|
|
244
279
|
|
|
@@ -246,22 +281,26 @@ export const LunchboxWrapper: ComponentOptions = {
|
|
|
246
281
|
// ====================
|
|
247
282
|
onBeforeUnmount(() => {
|
|
248
283
|
cancelUpdate()
|
|
284
|
+
cancelUpdateSource()
|
|
249
285
|
})
|
|
250
286
|
|
|
251
287
|
// RENDER FUNCTION
|
|
252
288
|
// ====================
|
|
289
|
+
const containerFillStyle = props.sizePolicy === 'container' ? 'static' : 'absolute'
|
|
290
|
+
const canvasFillStyle = props.sizePolicy === 'container' ? 'static' : 'fixed'
|
|
291
|
+
|
|
253
292
|
return () => [
|
|
254
293
|
context.slots.default?.() ?? null,
|
|
255
294
|
h(
|
|
256
295
|
'div',
|
|
257
296
|
{
|
|
258
|
-
style: fillStyle(
|
|
297
|
+
style: fillStyle(containerFillStyle),
|
|
259
298
|
ref: container,
|
|
260
299
|
},
|
|
261
300
|
[
|
|
262
301
|
useFallbackRenderer.value
|
|
263
302
|
? h('canvas', {
|
|
264
|
-
style: fillStyle(
|
|
303
|
+
style: fillStyle(canvasFillStyle),
|
|
265
304
|
class: 'lunchbox-canvas',
|
|
266
305
|
ref: canvas,
|
|
267
306
|
})
|
|
@@ -1,22 +1,39 @@
|
|
|
1
1
|
import { MiniDom } from '../../core'
|
|
2
|
+
import { Lunch } from '../../types'
|
|
2
3
|
import { Ref } from 'vue'
|
|
3
4
|
import { resizeCanvas } from './resizeCanvas'
|
|
4
5
|
|
|
6
|
+
const getInnerDimensions = (node: Element) => {
|
|
7
|
+
const computedStyle = getComputedStyle(node)
|
|
8
|
+
const width = node.clientWidth - parseFloat(computedStyle.paddingLeft) - parseFloat(computedStyle.paddingRight)
|
|
9
|
+
const height = node.clientHeight - parseFloat(computedStyle.paddingTop) - parseFloat(computedStyle.paddingBottom)
|
|
10
|
+
return { width, height }
|
|
11
|
+
}
|
|
12
|
+
|
|
5
13
|
export const prepCanvas = (
|
|
6
14
|
container: Ref<MiniDom.RendererDomNode | undefined>,
|
|
7
15
|
canvasElement: HTMLCanvasElement,
|
|
8
16
|
onBeforeUnmount: Function,
|
|
17
|
+
sizePolicy?: Lunch.SizePolicy
|
|
9
18
|
) => {
|
|
10
19
|
const containerElement = container.value?.domElement
|
|
11
20
|
if (!containerElement) throw new Error('missing container')
|
|
12
21
|
|
|
13
22
|
// save...
|
|
14
23
|
// ...and size element
|
|
15
|
-
|
|
24
|
+
const resizeCanvasByPolicy = () => {
|
|
25
|
+
if (sizePolicy === "container") {
|
|
26
|
+
const dims = getInnerDimensions(containerElement);
|
|
27
|
+
resizeCanvas(dims.width, dims.height);
|
|
28
|
+
}
|
|
29
|
+
else
|
|
30
|
+
resizeCanvas()
|
|
31
|
+
};
|
|
32
|
+
resizeCanvasByPolicy();
|
|
16
33
|
|
|
17
34
|
// attach listeners
|
|
18
35
|
const observer = new ResizeObserver(([canvas]) => {
|
|
19
|
-
|
|
36
|
+
resizeCanvasByPolicy();
|
|
20
37
|
})
|
|
21
38
|
// window.addEventListener('resize', resizeCanvas)
|
|
22
39
|
if (containerElement) {
|
|
@@ -29,4 +46,4 @@ export const prepCanvas = (
|
|
|
29
46
|
observer.unobserve(canvasElement)
|
|
30
47
|
}
|
|
31
48
|
})
|
|
32
|
-
}
|
|
49
|
+
}
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { Lunch } from '../..'
|
|
2
2
|
|
|
3
3
|
/** Process props into either themselves or the $attached value */
|
|
4
|
-
export function processProp<T, U = THREE.Object3D>({
|
|
4
|
+
export function processProp<T, U = THREE.Object3D>({
|
|
5
|
+
node,
|
|
6
|
+
prop,
|
|
7
|
+
}: {
|
|
8
|
+
node: Lunch.StandardMeta<U>
|
|
9
|
+
prop: any
|
|
10
|
+
}) {
|
|
5
11
|
// return $attachedArray value if needed
|
|
6
12
|
if (typeof prop === 'string' && prop.startsWith('$attachedArray')) {
|
|
7
|
-
return node.attachedArray[
|
|
13
|
+
return node.attachedArray[
|
|
14
|
+
prop.replace('$attachedArray.', '')
|
|
15
|
+
] as any as T
|
|
8
16
|
}
|
|
9
17
|
|
|
10
18
|
// return $attached value if needed
|
|
@@ -16,8 +24,17 @@ export function processProp<T, U = THREE.Object3D>({ node, prop }: { node: Lunch
|
|
|
16
24
|
return prop as T
|
|
17
25
|
}
|
|
18
26
|
|
|
19
|
-
export function processPropAsArray<T, U = THREE.Object3D>({
|
|
20
|
-
|
|
27
|
+
export function processPropAsArray<T, U = THREE.Object3D>({
|
|
28
|
+
node,
|
|
29
|
+
prop,
|
|
30
|
+
}: {
|
|
31
|
+
node: Lunch.StandardMeta<U>
|
|
32
|
+
prop: any
|
|
33
|
+
}) {
|
|
34
|
+
const isAttachedArray =
|
|
35
|
+
typeof prop === 'string' && prop.startsWith('$attachedArray')
|
|
21
36
|
const output = processProp<T, U>({ node, prop })
|
|
22
|
-
return Array.isArray(output) && isAttachedArray
|
|
23
|
-
|
|
37
|
+
return Array.isArray(output) && isAttachedArray
|
|
38
|
+
? (output as Array<T>)
|
|
39
|
+
: [output]
|
|
40
|
+
}
|
|
@@ -17,6 +17,8 @@ let mouseUpListener: (event: MouseEvent) => void
|
|
|
17
17
|
export const mousePos = ref({ x: Infinity, y: Infinity })
|
|
18
18
|
let autoRaycasterEventsInitialized = false
|
|
19
19
|
|
|
20
|
+
let frameID: number
|
|
21
|
+
|
|
20
22
|
export const setupAutoRaycaster = (node: Lunch.Node<THREE.Raycaster>) => {
|
|
21
23
|
const instance = node.instance
|
|
22
24
|
|
|
@@ -67,8 +69,16 @@ export const setupAutoRaycaster = (node: Lunch.Node<THREE.Raycaster>) => {
|
|
|
67
69
|
|
|
68
70
|
// TODO: add touch events
|
|
69
71
|
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
+
// process mouse events asynchronously, whenever the mouse state changes
|
|
73
|
+
watch(
|
|
74
|
+
() => [inputActive.value, mousePos.value.x, mousePos.value.y],
|
|
75
|
+
() => {
|
|
76
|
+
if (frameID) cancelAnimationFrame(frameID)
|
|
77
|
+
frameID = requestAnimationFrame(() => {
|
|
78
|
+
autoRaycasterBeforeRender()
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
)
|
|
72
82
|
|
|
73
83
|
// mark complete
|
|
74
84
|
autoRaycasterEventsInitialized = true
|
package/src/core/update.ts
CHANGED
|
@@ -1,22 +1,44 @@
|
|
|
1
1
|
import { ensureRenderer, ensuredScene, ensuredCamera } from '.'
|
|
2
2
|
import { Lunch } from '..'
|
|
3
|
-
import { toRaw } from 'vue'
|
|
3
|
+
import { toRaw, watch, WatchStopHandle } from 'vue'
|
|
4
4
|
|
|
5
5
|
let frameID: number
|
|
6
|
+
let watchStopHandle: WatchStopHandle
|
|
6
7
|
|
|
7
8
|
export const beforeRender = [] as Lunch.UpdateCallback[]
|
|
8
9
|
export const afterRender = [] as Lunch.UpdateCallback[]
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
const requestUpdate = (opts: Lunch.UpdateCallbackProperties) => {
|
|
12
|
+
cancelUpdate()
|
|
12
13
|
frameID = requestAnimationFrame(() =>
|
|
13
14
|
update({
|
|
14
15
|
app: opts.app,
|
|
15
16
|
renderer: ensureRenderer.value?.instance,
|
|
16
17
|
scene: ensuredScene.value.instance,
|
|
17
18
|
camera: ensuredCamera.value?.instance,
|
|
19
|
+
updateSource: opts.updateSource,
|
|
18
20
|
})
|
|
19
21
|
)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const update: Lunch.UpdateCallback = (opts) => {
|
|
25
|
+
if (opts.updateSource) {
|
|
26
|
+
if (!watchStopHandle) {
|
|
27
|
+
// request next frame only when state changes
|
|
28
|
+
watchStopHandle = watch(
|
|
29
|
+
opts.updateSource,
|
|
30
|
+
() => {
|
|
31
|
+
requestUpdate(opts)
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
deep: true,
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
// request next frame on a continuous loop
|
|
40
|
+
requestUpdate(opts)
|
|
41
|
+
}
|
|
20
42
|
|
|
21
43
|
// prep options
|
|
22
44
|
const { app, renderer, scene, camera } = opts
|
|
@@ -82,3 +104,7 @@ export const offAfterRender = (cb: Lunch.UpdateCallback | number) => {
|
|
|
82
104
|
export const cancelUpdate = () => {
|
|
83
105
|
if (frameID) cancelAnimationFrame(frameID)
|
|
84
106
|
}
|
|
107
|
+
|
|
108
|
+
export const cancelUpdateSource = () => {
|
|
109
|
+
if (watchStopHandle) watchStopHandle()
|
|
110
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
type RootNode = import('./core/minidom').MiniDom.RendererRootNode
|
|
2
2
|
type VNodeProps = import('vue').VNodeProps
|
|
3
3
|
type VueApp<T> = import('vue').App<T>
|
|
4
|
+
type WatchSource = import('vue').WatchSource
|
|
4
5
|
type RendererStandardNode<T = THREE.Object3D> =
|
|
5
6
|
import('./core').MiniDom.RendererStandardNode<T>
|
|
6
7
|
|
|
@@ -127,6 +128,7 @@ export declare namespace Lunch {
|
|
|
127
128
|
scene?: THREE.Scene | null
|
|
128
129
|
renderer?: THREE.Renderer | null
|
|
129
130
|
camera?: THREE.Camera | null
|
|
131
|
+
updateSource?: WatchSource | null
|
|
130
132
|
|
|
131
133
|
// sceneNode: Node<THREE.Scene> | null
|
|
132
134
|
// rendererNode: Node<THREE.Renderer> | null
|
|
@@ -136,6 +138,8 @@ export declare namespace Lunch {
|
|
|
136
138
|
/** Universally unique identifier. */
|
|
137
139
|
type Uuid = string
|
|
138
140
|
|
|
141
|
+
type SizePolicy = 'full' | 'container'
|
|
142
|
+
|
|
139
143
|
interface WrapperProps {
|
|
140
144
|
background?: string
|
|
141
145
|
cameraArgs?: any[]
|
|
@@ -145,11 +149,14 @@ export declare namespace Lunch {
|
|
|
145
149
|
dpr?: number
|
|
146
150
|
ortho?: boolean
|
|
147
151
|
orthographic?: boolean
|
|
152
|
+
r3f?: boolean
|
|
148
153
|
// TODO: Why doesn't ConstructorParameters<THREE.WebGLRenderer> work here?
|
|
149
154
|
rendererArguments?: object
|
|
150
155
|
rendererProperties?: Partial<THREE.WebGLRenderer>
|
|
156
|
+
sizePolicy?: SizePolicy
|
|
151
157
|
shadow?: ShadowSugar
|
|
152
158
|
transparent?: boolean
|
|
153
159
|
zoom?: number
|
|
160
|
+
updateSource?: WatchSource | null
|
|
154
161
|
}
|
|
155
162
|
}
|