lunchboxjs 0.1.4004 → 0.1.4010

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -0,0 +1,3 @@
1
+ Custom Vue 3 renderer for ThreeJS.
2
+
3
+ [Docs](//docs.lunchboxjs.com)
@@ -105,10 +105,13 @@
105
105
  // These should match the Lunchbox.WrapperProps interface
106
106
  background: String,
107
107
  cameraArgs: Array,
108
+ cameraLook: Array,
109
+ cameraLookAt: Array,
108
110
  cameraPosition: Array,
109
111
  dpr: Number,
110
112
  ortho: Boolean,
111
113
  orthographic: Boolean,
114
+ rendererArguments: Object,
112
115
  rendererProperties: Object,
113
116
  shadow: [Boolean, Object],
114
117
  transparent: Boolean,
@@ -138,12 +141,11 @@
138
141
  if (!renderer) {
139
142
  // build renderer args
140
143
  const rendererArgs = {
144
+ alpha: props.transparent,
141
145
  antialias: true,
142
146
  canvas: canvas.value.domElement,
147
+ ...(props.rendererArguments ?? {}),
143
148
  };
144
- if (props.transparent) {
145
- rendererArgs.alpha = true;
146
- }
147
149
  // create new renderer
148
150
  ensureRenderer.value = createNode({
149
151
  type: 'WebGLRenderer',
@@ -181,7 +183,6 @@
181
183
  // the user has initialized the renderer, so anything depending
182
184
  // on the renderer can execute
183
185
  rendererReady.value = true;
184
- return;
185
186
  }
186
187
  // CAMERA
187
188
  // ====================
@@ -194,7 +195,6 @@
194
195
  if (!camera) {
195
196
  // create ortho camera
196
197
  if (props.ortho || props.orthographic) {
197
- // const size: Vector2 = new Vector2()
198
198
  ensuredCamera.value = createNode({
199
199
  props: { args: props.cameraArgs ?? [] },
200
200
  type: 'OrthographicCamera',
@@ -216,9 +216,17 @@
216
216
  else {
217
217
  cameraReady.value = true;
218
218
  }
219
+ if (!camera.instance) {
220
+ throw new Error('Error creating camera.');
221
+ }
219
222
  // move camera if needed
220
223
  if (camera && props.cameraPosition) {
221
- camera.instance?.position.set(...props.cameraPosition);
224
+ camera.instance.position.set(...props.cameraPosition);
225
+ }
226
+ // angle camera if needed
227
+ if (camera && (props.cameraLookAt || props.cameraLook)) {
228
+ const source = (props.cameraLookAt || props.cameraLook);
229
+ camera.instance.lookAt(...source);
222
230
  }
223
231
  // SCENE
224
232
  // ====================
@@ -241,11 +249,23 @@
241
249
  else {
242
250
  throw new Error('missing renderer');
243
251
  }
252
+ // CALLBACK PREP
253
+ // ====================
254
+ const app = vue.getCurrentInstance().appContext.app;
255
+ // START
256
+ // ====================
257
+ for (let startCallback of startCallbacks) {
258
+ startCallback({
259
+ app,
260
+ camera: camera.instance,
261
+ renderer: renderer.instance,
262
+ scene: scene.instance,
263
+ });
264
+ }
244
265
  // KICK UPDATE
245
266
  // ====================
246
- // console.log(scene)
247
267
  update({
248
- app: vue.getCurrentInstance().appContext.app,
268
+ app,
249
269
  camera: camera.instance,
250
270
  renderer: renderer.instance,
251
271
  scene: scene.instance,
@@ -276,21 +296,6 @@
276
296
  },
277
297
  };
278
298
 
279
- const catalogue = {};
280
-
281
- const lunchboxDomComponentNames = [
282
- 'canvas',
283
- 'div',
284
- 'LunchboxWrapper',
285
- ];
286
- // component creation utility
287
- const createComponent$1 = (tag) => vue.defineComponent({
288
- inheritAttrs: false,
289
- name: tag,
290
- setup(props, context) {
291
- return () => vue.h(tag, context.attrs, context.slots?.default?.() || []);
292
- },
293
- });
294
299
  // list of all components to register out of the box
295
300
  const autoGeneratedComponents = [
296
301
  // ThreeJS basics
@@ -403,21 +408,11 @@
403
408
  'arrayCamera',
404
409
  // renderers
405
410
  'webGLRenderer',
406
- ].map(createComponent$1).reduce((acc, curr) => {
407
- acc[curr.name] = curr;
408
- return acc;
409
- });
410
- const components = {
411
- ...autoGeneratedComponents,
412
- 'Lunchbox': LunchboxWrapper,
413
- // Gltf,
414
- };
415
- // console.log(components, Gltf)
416
- /*
417
- // List copied from r3f
418
- // https://github.com/pmndrs/react-three-fiber/blob/master/packages/fiber/src/three-types.ts
411
+ /*
412
+ // List copied from r3f:
413
+ // https://github.com/pmndrs/react-three-fiber/blob/master/packages/fiber/src/three-types.ts
419
414
 
420
- // NOT IMPLEMENTED:
415
+ // NOT IMPLEMENTED (can be added via Extend - docs.lunchboxjs.com/components/extend/):
421
416
  audioListener: AudioListenerProps
422
417
  positionalAudio: PositionalAudioProps
423
418
 
@@ -465,6 +460,27 @@
465
460
  fogExp2: FogExp2Props
466
461
  shape: ShapeProps
467
462
  */
463
+ ];
464
+
465
+ const catalogue = {};
466
+
467
+ const lunchboxDomComponentNames = ['canvas', 'div', 'LunchboxWrapper'];
468
+ // component creation utility
469
+ const createComponent$1 = (tag) => vue.defineComponent({
470
+ inheritAttrs: false,
471
+ name: tag,
472
+ setup(props, context) {
473
+ return () => vue.h(tag, context.attrs, context.slots?.default?.() || []);
474
+ },
475
+ });
476
+ autoGeneratedComponents.map(createComponent$1).reduce((acc, curr) => {
477
+ acc[curr.name] = curr;
478
+ return acc;
479
+ });
480
+ const components = {
481
+ ...autoGeneratedComponents,
482
+ Lunchbox: LunchboxWrapper,
483
+ };
468
484
 
469
485
  function find(target) {
470
486
  target = vue.isRef(target) ? target.value : target;
@@ -1258,6 +1274,16 @@
1258
1274
  const rootNode = new MiniDom.RendererRootNode();
1259
1275
  rootNode.minidomType = 'RootNode';
1260
1276
 
1277
+ const startCallbacks = [];
1278
+ const onStart = (cb, index = Infinity) => {
1279
+ if (index === Infinity) {
1280
+ startCallbacks.push(cb);
1281
+ }
1282
+ else {
1283
+ startCallbacks.splice(index, 0, cb);
1284
+ }
1285
+ };
1286
+
1261
1287
  let frameID;
1262
1288
  const beforeRender = [];
1263
1289
  const afterRender = [];
@@ -1301,6 +1327,15 @@
1301
1327
  beforeRender.splice(index, 0, cb);
1302
1328
  }
1303
1329
  };
1330
+ const offBeforeRender = (cb) => {
1331
+ if (isFinite(cb)) {
1332
+ beforeRender.splice(cb, 1);
1333
+ }
1334
+ else {
1335
+ const idx = beforeRender.findIndex((v) => v == cb);
1336
+ beforeRender.splice(idx, 1);
1337
+ }
1338
+ };
1304
1339
  const onAfterRender = (cb, index = Infinity) => {
1305
1340
  if (index === Infinity) {
1306
1341
  afterRender.push(cb);
@@ -1309,6 +1344,15 @@
1309
1344
  afterRender.splice(index, 0, cb);
1310
1345
  }
1311
1346
  };
1347
+ const offAfterRender = (cb) => {
1348
+ if (isFinite(cb)) {
1349
+ afterRender.splice(cb, 1);
1350
+ }
1351
+ else {
1352
+ const idx = afterRender.findIndex((v) => v == cb);
1353
+ afterRender.splice(idx, 1);
1354
+ }
1355
+ };
1312
1356
  const cancelUpdate = () => {
1313
1357
  if (frameID)
1314
1358
  cancelAnimationFrame(frameID);
@@ -1662,9 +1706,51 @@
1662
1706
  inputActive,
1663
1707
  mousePos,
1664
1708
  };
1709
+ /** The current camera. Often easier to use `useCamera` instead of this. */
1665
1710
  const camera = vue.computed(() => ensuredCamera.value?.instance ?? null);
1711
+ /** Run a function using the current camera when it's present. */
1712
+ function useCamera(callback, once = true) {
1713
+ let destroy;
1714
+ destroy = vue.watch(camera, (newVal) => {
1715
+ if (!newVal)
1716
+ return;
1717
+ // TODO: better fix than `any`?
1718
+ callback(newVal);
1719
+ if (once) {
1720
+ destroy?.();
1721
+ }
1722
+ }, { immediate: true });
1723
+ }
1724
+ /** The current renderer. Often easier to use `useRenderer` instead of this. */
1666
1725
  const renderer = vue.computed(() => ensureRenderer.value?.instance ?? null);
1726
+ /** Run a function using the current renderer when it's present. */
1727
+ function useRenderer(callback, once = true) {
1728
+ let destroy;
1729
+ destroy = vue.watch(renderer, (newVal) => {
1730
+ if (!newVal)
1731
+ return;
1732
+ // TODO: better fix than `any`?
1733
+ callback(newVal);
1734
+ if (once) {
1735
+ destroy?.();
1736
+ }
1737
+ }, { immediate: true });
1738
+ }
1739
+ /** The current scene. Often easier to use `useScene` instead of this. */
1667
1740
  const scene = vue.computed(() => ensuredScene.value.instance);
1741
+ /** Run a function using the current scene when it's present. */
1742
+ function useScene(callback, once = true) {
1743
+ let destroy;
1744
+ destroy = vue.watch(scene, (newVal) => {
1745
+ if (!newVal)
1746
+ return;
1747
+ // TODO: better fix than `any`?
1748
+ callback(newVal);
1749
+ if (once) {
1750
+ destroy?.();
1751
+ }
1752
+ }, { immediate: true });
1753
+ }
1668
1754
  // CUSTOM RENDER SUPPORT
1669
1755
  // ====================
1670
1756
  let app = null;
@@ -1736,11 +1822,17 @@
1736
1822
  exports.createApp = createApp;
1737
1823
  exports.find = find;
1738
1824
  exports.globals = globals;
1825
+ exports.offAfterRender = offAfterRender;
1826
+ exports.offBeforeRender = offBeforeRender;
1739
1827
  exports.onAfterRender = onAfterRender;
1740
1828
  exports.onBeforeRender = onBeforeRender;
1829
+ exports.onStart = onStart;
1741
1830
  exports.renderer = renderer;
1742
1831
  exports.scene = scene;
1743
1832
  exports.setCustomRender = setCustomRender;
1833
+ exports.useCamera = useCamera;
1834
+ exports.useRenderer = useRenderer;
1835
+ exports.useScene = useScene;
1744
1836
 
1745
1837
  Object.defineProperty(exports, '__esModule', { value: true });
1746
1838
 
@@ -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=I.value?.instance,o=K.value.instance,a=F.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,cameraPosition:Array,dpr:Number,ortho:Boolean,orthographic:Boolean,rendererProperties:Object,shadow:[Boolean,Object],transparent:Boolean},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=N(["WebGLRenderer"]),l)return s.value=!1,void(W.value=!0);{const t={antialias:!0,canvas:a.value.domElement};e.transparent&&(t.alpha=!0),I.value=v({type:"WebGLRenderer",uuid:$,props:{args:[t]}}),W.value=!0;const n=I,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=N(["PerspectiveCamera","OrthographicCamera"]),p?D.value=!0:(e.ortho||e.orthographic?F.value=v({props:{args:e.cameraArgs??[]},type:"OrthographicCamera",uuid:k}):F.value=v({props:{args:e.cameraArgs??[45,.5625,1,1e3]},type:"PerspectiveCamera",uuid:k}),D.value=!0,p=F.value),p&&e.cameraPosition&&p.instance?.position.set(...e.cameraPosition),m=K.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),me.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),oe({app:t.getCurrentInstance().appContext.app,camera:p.instance,renderer:l.instance,scene:m.instance})})),t.onBeforeUnmount((()=>{se()})),()=>[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;function v(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&&(K.value=r),r}const 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),R.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=P.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:P[r].intersection})})))}));e.eventListenerRemoveFunctions[n].push(r)}return e}const R=["onClick","onPointerUp","onPointerDown","onPointerOver","onPointerOut","onPointerEnter","onPointerLeave","onPointerMove"];let w,C,E;const A=t.ref({x:1/0,y:1/0});let L=!1;let P=[];const M=()=>{const e=_.value?.instance,t=F.value?.instance;if(!e||!t)return;e.setFromCamera(me.mousePos.value,t);const n=e.intersectObjects(g.map((e=>e.instance)));let r=[],o=[],a=[];r=P.map((e=>e.intersection)),n?.forEach((e=>{const t=P.findIndex((t=>t.intersection.object===e.object));if(-1===t){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 n=r.findIndex((t=>t.object.uuid===e.object.uuid));-1!==n&&r.splice(n,1)}));const s=r.map((e=>({element:g.find((t=>t.instance?.uuid===e.object.uuid)),intersection:e})));o.forEach((({element:e,intersection:t})=>{B({element:e,eventKeys:["onPointerEnter"],intersection:t})})),a.forEach((({element:e,intersection:t})=>{B({element:e,eventKeys:["onPointerOver","onPointerMove"],intersection:t})})),s.forEach((({element:e,intersection:t})=>{B({element:e,eventKeys:["onPointerLeave","onPointerOut"],intersection:t})})),P=[].concat(o,a)},B=({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 J.RendererRootNode(t)),e.lunchboxTree}function N(e){Array.isArray(e)||(e=[e]);for(let t of e)if(O[t])return O[t];for(let t of e){const e=T[t]||s.find((e=>e.type?.toLowerCase()===t.toLowerCase()));if(e){const n=e;return T[t]=n,n}}return null}e.lunchboxTree=void 0;const T=t.reactive({}),O=t.reactive({});function j(e,n,r={},o=null){Array.isArray(e)||(e=[e]);for(let t of e)T[t]||(T[t]=null),O[t]||(O[t]=null);return t.computed({get(){const t=N(e);if(t)return t;const a=G(),s=v({type:e[0],uuid:n,props:r});return a.addChild(s),T[e[0]]=s,o&&o(s),s},set(e){const t=e.type??"",n=t[0].toUpperCase()+t.slice(1);O[n]=e}})}const k="FALLBACK_CAMERA",S=j(["PerspectiveCamera","OrthographicCamera"],k,{args:[45,.5625,1,1e3]}),D=t.ref(!1),F=t.computed({get:()=>D.value?S.value:null,set(e){const t=e.type??"",n=t[0].toUpperCase()+t.slice(1);O[n]=e}}),$="FALLBACK_RENDERER",U=j(["WebGLRenderer"],$,{}),W=t.ref(!1),I=t.computed({get:()=>W.value?U.value:null,set(e){const t=e.type??"",n=t[0].toUpperCase()+t.slice(1);O[n]=e}}),K=j("Scene","FALLBACK_SCENE"),_=j("Raycaster","AUTO_RAYCASTER",{},(e=>(e=>{if(!e.instance)return;let n=null;n=t.watch((()=>I.value),(e=>{e?.instance&&(L||(w=t=>{const n=(e.instance.domElement.width??1)/me.dpr.value,r=(e.instance.domElement.height??1)/me.dpr.value;A.value.x=t.offsetX/n*2-1,A.value.y=-t.offsetY/r*2+1},C=()=>b.value=!0,E=()=>b.value=!1,e.instance.domElement.addEventListener("mousemove",w),e.instance.domElement.addEventListener("mousedown",C),e.instance.domElement.addEventListener("mouseup",E),ae(M),L=!0),n&&n())}),{immediate:!0})})(e))),V=e=>t.defineComponent({inheritAttrs:!1,name:e,render(){return t.h(e,this.$attrs,this.$slots?.default?.()||[])}});var q,z=new Uint8Array(16);function H(){if(!q&&!(q="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 q(z)}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={}));let te;(new J.RendererRootNode).minidomType="RootNode";const ne=[],re=[],oe=e=>{te=requestAnimationFrame((()=>oe({app:e.app,renderer:I.value?.instance,scene:K.value.instance,camera:F.value?.instance})));const{app:n,renderer:r,scene:o,camera:a}=e;ne.forEach((t=>{t&&t(e)})),r&&o&&a&&(n.customRender?n.customRender(e):r.render(t.toRaw(o),t.toRaw(a))),re.forEach((t=>{t&&t(e)}))},ae=(e,t=1/0)=>{t===1/0?ne.push(e):ne.splice(t,0,e)},se=()=>{te&&cancelAnimationFrame(te)};const ie={x:"position.x",y:"position.y",z:"position.z"},ce=["","parameters"],de=["args","attach","attachArray","key","onAdded","ref","src"],ue=["geometry","material"];function le(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 pe={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=v(o);return ue.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??G();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=K.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);le(e,t,e.props.attach,r)}else n.load(e.props.src,(n=>{le(e,t,e.props.attach,n)}),null,(e=>{throw new Error(e)}))}(e,t):le(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 x({node:e,key:t,value:n});if(de.includes(t))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 o=e.instance;if(!o)return e;const a=t.replace(/-/g,".");let s,i=ie[a]||a;for(let e=0;e<ce.length&&!s;e++){const t=[ce[e],i].filter(Boolean).join(".");s=s=r.get(o,t)}if(s&&r.isNumber(n)&&s.setScalar)s.setScalar(n);else if(s&&s.set){const e=Array.isArray(n)?n:[n];o[i].set(...e)}else"function"==typeof s?s.bind(e.instance)(...n):void 0!==r.get(o,i,void 0)?r.set(o,i,""===n||n):console.log(`No property ${i} found on ${o}`);const c=o?.texture?.type||o?.type;if("string"==typeof c){const e=c.toLowerCase();switch(!0){case e.includes("material"):o.needsUpdate=!0;break;case e.includes("camera")&&o.updateProjectionMatrix:o.updateProjectionMatrix()}}}({node:e,key:t,value:o})},remove:e=>{if(!e)return;const t=Object.keys(O),n=[];e.walk((e=>(n.push(e),!0))),n.forEach((e=>{const n=t.find((t=>O[t]?.uuid===e.uuid));if(n&&(O[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(){}},me={dpr:t.ref(1),inputActive:b,mousePos:A},he=t.computed((()=>F.value?.instance??null)),fe=t.computed((()=>I.value?.instance??null)),ye=t.computed((()=>K.value.instance));let ve=null,ge=null;e.camera=he,e.clearCustomRender=()=>{ve?ve.clearCustomRender():ge=null},e.createApp=e=>{ve=t.createRenderer(pe).createApp(e),Object.keys(p).forEach((e=>{ve.component(e,p[e])}));const{mount:n}=ve;return ve.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"});ve.rootNode=r;return n(r,...t)},ve.extend=e=>((({app:e,...t})=>{Object.keys(t).forEach((n=>{e.component(n,V(n)),u[n]=t[n]}))})({app:ve,...e}),ve),ve.setCustomRender=e=>{ve.customRender=e},ge&&(ve.setCustomRender(ge),ge=null),ve.clearCustomRender=()=>{ve.customRender=null},ve},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=me,e.onAfterRender=(e,t=1/0)=>{t===1/0?re.push(e):re.splice(t,0,e)},e.onBeforeRender=ae,e.renderer=fe,e.scene=ye,e.setCustomRender=e=>{ve?ve.setCustomRender(e):ge=e},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=K.value?.instance,o=_.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=>({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},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=T(["WebGLRenderer"]),l)s.value=!1,I.value=!0;else{const t={alpha:e.transparent,antialias:!0,canvas:a.value.domElement,...e.rendererArguments??{}};K.value=g({type:"WebGLRenderer",uuid:U,props:{args:[t]}}),I.value=!0;const n=K,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=T(["PerspectiveCamera","OrthographicCamera"]),p?D.value=!0:(e.ortho||e.orthographic?$.value=g({props:{args:e.cameraArgs??[]},type:"OrthographicCamera",uuid:S}):$.value=g({props:{args:e.cameraArgs??[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(m=_.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),fe.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 ne)e({app:o,camera:p.instance,renderer:l.instance,scene:m.instance});se({app:o,camera:p.instance,renderer:l.instance,scene:m.instance})})),t.onBeforeUnmount((()=>{ce()})),()=>[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=["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"],l={},p=["canvas","div","LunchboxWrapper"];u.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)));const m={...u,Lunchbox:d};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;function g(e={},t={}){const n={attached:e.attached??[],attachedArray:e.attachedArray??{},instance:e.instance??null},r=new Q.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&&(_.value=r),r}const b=[],x=t.ref(!1);function R({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)&&(V.value,e.instance&&!b.includes(e)&&(o=e,b.push(o),e.eventListenerRemoveFunctions[n].push((()=>(e=>{const t=b.indexOf(e);-1!==t&&b.splice(t,1)})(e))))),"onClick"===n||"onPointerDown"===n||"onPointerUp"===n){const r=t.watch((()=>x.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 A,C,E;const L=t.ref({x:1/0,y:1/0});let P=!1;let M=[];const B=()=>{const e=V.value?.instance,t=$.value?.instance;if(!e||!t)return;e.setFromCamera(fe.mousePos.value,t);const n=e.intersectObjects(b.map((e=>e.instance)));let r=[],o=[],a=[];r=M.map((e=>e.intersection)),n?.forEach((e=>{const t=M.findIndex((t=>t.intersection.object===e.object));if(-1===t){const t=b.find((t=>t.instance?.uuid===e.object.uuid));t&&o.push({element:t,intersection:e})}else{const t=b.find((t=>t.instance?.uuid===e.object.uuid));t&&a.push({element:t,intersection:e})}const n=r.findIndex((t=>t.object.uuid===e.object.uuid));-1!==n&&r.splice(n,1)}));const s=r.map((e=>({element:b.find((t=>t.instance?.uuid===e.object.uuid)),intersection:e})));o.forEach((({element:e,intersection:t})=>{G({element:e,eventKeys:["onPointerEnter"],intersection:t})})),a.forEach((({element:e,intersection:t})=>{G({element:e,eventKeys:["onPointerOver","onPointerMove"],intersection:t})})),s.forEach((({element:e,intersection:t})=>{G({element:e,eventKeys:["onPointerLeave","onPointerOut"],intersection:t})})),M=[].concat(o,a)},G=({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 Q.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 t of e){const e=O[t]||s.find((e=>e.type?.toLowerCase()===t.toLowerCase()));if(e){const n=e;return O[t]=n,n}}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=N(),s=g({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,{}),I=t.ref(!1),K=t.computed({get:()=>I.value?W.value:null,set(e){const t=e.type??"",n=t[0].toUpperCase()+t.slice(1);j[n]=e}}),_=k("Scene","FALLBACK_SCENE"),V=k("Raycaster","AUTO_RAYCASTER",{},(e=>(e=>{if(!e.instance)return;let n=null;n=t.watch((()=>K.value),(e=>{e?.instance&&(P||(A=t=>{const n=(e.instance.domElement.width??1)/fe.dpr.value,r=(e.instance.domElement.height??1)/fe.dpr.value;L.value.x=t.offsetX/n*2-1,L.value.y=-t.offsetY/r*2+1},C=()=>x.value=!0,E=()=>x.value=!1,e.instance.domElement.addEventListener("mousemove",A),e.instance.domElement.addEventListener("mousedown",C),e.instance.domElement.addEventListener("mouseup",E),ie(B),P=!0),n&&n())}),{immediate:!0})})(e))),q=e=>t.defineComponent({inheritAttrs:!1,name:e,render(){return t.h(e,this.$attrs,this.$slots?.default?.()||[])}});var z,H=new Uint8Array(16);function Y(){if(!z&&!(z="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 z(H)}var X=/^(?:[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 J(e){return"string"==typeof e&&X.test(e)}for(var Q,Z=[],ee=0;ee<256;++ee)Z.push((ee+256).toString(16).substr(1));function te(e,t,n){var r=(e=e||{}).random||(e.rng||Y)();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=(Z[e[t+0]]+Z[e[t+1]]+Z[e[t+2]]+Z[e[t+3]]+"-"+Z[e[t+4]]+Z[e[t+5]]+"-"+Z[e[t+6]]+Z[e[t+7]]+"-"+Z[e[t+8]]+Z[e[t+9]]+"-"+Z[e[t+10]]+Z[e[t+11]]+Z[e[t+12]]+Z[e[t+13]]+Z[e[t+14]]+Z[e[t+15]]).toLowerCase();if(!J(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??te(),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}(Q||(Q={}));(new Q.RendererRootNode).minidomType="RootNode";const ne=[];let re;const oe=[],ae=[],se=e=>{re=requestAnimationFrame((()=>se({app:e.app,renderer:K.value?.instance,scene:_.value.instance,camera:$.value?.instance})));const{app:n,renderer:r,scene:o,camera:a}=e;oe.forEach((t=>{t&&t(e)})),r&&o&&a&&(n.customRender?n.customRender(e):r.render(t.toRaw(o),t.toRaw(a))),ae.forEach((t=>{t&&t(e)}))},ie=(e,t=1/0)=>{t===1/0?oe.push(e):oe.splice(t,0,e)},ce=()=>{re&&cancelAnimationFrame(re)};const de={x:"position.x",y:"position.y",z:"position.z"},ue=["","parameters"],le=["args","attach","attachArray","key","onAdded","ref","src"],pe=["geometry","material"];function me(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 he={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 Q.RendererDomNode({...t,...e,metaType:"domMeta"})}(o);return e}const a=g(o);return pe.forEach((t=>{e.toLowerCase().endsWith(t)&&(a.props.attach=t)})),a},createText:e=>function(e={}){const t={text:e.text??""};return new Q.RendererTextNode({...e,...t,metaType:"textMeta"})}({text:e}),createComment:e=>function(e={}){const t={text:e.text??""};return new Q.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&&(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=_.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);me(e,t,e.props.attach,r)}else n.load(e.props.src,(n=>{me(e,t,e.props.attach,n)}),null,(e=>{throw new Error(e)}))}(e,t):me(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 R({node:e,key:t,value:n});if(le.includes(t))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 o=e.instance;if(!o)return e;const a=t.replace(/-/g,".");let s,i=de[a]||a;for(let e=0;e<ue.length&&!s;e++){const t=[ue[e],i].filter(Boolean).join(".");s=s=r.get(o,t)}if(s&&r.isNumber(n)&&s.setScalar)s.setScalar(n);else if(s&&s.set){const e=Array.isArray(n)?n:[n];o[i].set(...e)}else"function"==typeof s?s.bind(e.instance)(...n):void 0!==r.get(o,i,void 0)?r.set(o,i,""===n||n):console.log(`No property ${i} found on ${o}`);const c=o?.texture?.type||o?.type;if("string"==typeof c){const e=c.toLowerCase();switch(!0){case e.includes("material"):o.needsUpdate=!0;break;case e.includes("camera")&&o.updateProjectionMatrix:o.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(){}},fe={dpr:t.ref(1),inputActive:x,mousePos:L},ye=t.computed((()=>$.value?.instance??null));const ve=t.computed((()=>K.value?.instance??null));const ge=t.computed((()=>_.value.instance));let be=null,xe=null;e.camera=ye,e.clearCustomRender=()=>{be?be.clearCustomRender():xe=null},e.createApp=e=>{be=t.createRenderer(he).createApp(e),Object.keys(m).forEach((e=>{be.component(e,m[e])}));const{mount:n}=be;return be.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"});be.rootNode=r;return n(r,...t)},be.extend=e=>((({app:e,...t})=>{Object.keys(t).forEach((n=>{e.component(n,q(n)),l[n]=t[n]}))})({app:be,...e}),be),be.setCustomRender=e=>{be.customRender=e},xe&&(be.setCustomRender(xe),xe=null),be.clearCustomRender=()=>{be.customRender=null},be},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=fe,e.offAfterRender=e=>{if(isFinite(e))ae.splice(e,1);else{const t=ae.findIndex((t=>t==e));ae.splice(t,1)}},e.offBeforeRender=e=>{if(isFinite(e))oe.splice(e,1);else{const t=oe.findIndex((t=>t==e));oe.splice(t,1)}},e.onAfterRender=(e,t=1/0)=>{t===1/0?ae.push(e):ae.splice(t,0,e)},e.onBeforeRender=ie,e.onStart=(e,t=1/0)=>{t===1/0?ne.push(e):ne.splice(t,0,e)},e.renderer=ve,e.scene=ge,e.setCustomRender=e=>{be?be.setCustomRender(e):xe=e},e.useCamera=function(e,n=!0){let r;r=t.watch(ye,(t=>{t&&(e(t),n&&r?.())}),{immediate:!0})},e.useRenderer=function(e,n=!0){let r;r=t.watch(ve,(t=>{t&&(e(t),n&&r?.())}),{immediate:!0})},e.useScene=function(e,n=!0){let r;r=t.watch(ge,(t=>{t&&(e(t),n&&r?.())}),{immediate:!0})},Object.defineProperty(e,"__esModule",{value:!0})}));
@@ -84,10 +84,13 @@ const LunchboxWrapper = {
84
84
  // These should match the Lunchbox.WrapperProps interface
85
85
  background: String,
86
86
  cameraArgs: Array,
87
+ cameraLook: Array,
88
+ cameraLookAt: Array,
87
89
  cameraPosition: Array,
88
90
  dpr: Number,
89
91
  ortho: Boolean,
90
92
  orthographic: Boolean,
93
+ rendererArguments: Object,
91
94
  rendererProperties: Object,
92
95
  shadow: [Boolean, Object],
93
96
  transparent: Boolean,
@@ -117,12 +120,11 @@ const LunchboxWrapper = {
117
120
  if (!renderer) {
118
121
  // build renderer args
119
122
  const rendererArgs = {
123
+ alpha: props.transparent,
120
124
  antialias: true,
121
125
  canvas: canvas.value.domElement,
126
+ ...(props.rendererArguments ?? {}),
122
127
  };
123
- if (props.transparent) {
124
- rendererArgs.alpha = true;
125
- }
126
128
  // create new renderer
127
129
  ensureRenderer.value = createNode({
128
130
  type: 'WebGLRenderer',
@@ -160,7 +162,6 @@ const LunchboxWrapper = {
160
162
  // the user has initialized the renderer, so anything depending
161
163
  // on the renderer can execute
162
164
  rendererReady.value = true;
163
- return;
164
165
  }
165
166
  // CAMERA
166
167
  // ====================
@@ -173,7 +174,6 @@ const LunchboxWrapper = {
173
174
  if (!camera) {
174
175
  // create ortho camera
175
176
  if (props.ortho || props.orthographic) {
176
- // const size: Vector2 = new Vector2()
177
177
  ensuredCamera.value = createNode({
178
178
  props: { args: props.cameraArgs ?? [] },
179
179
  type: 'OrthographicCamera',
@@ -195,9 +195,17 @@ const LunchboxWrapper = {
195
195
  else {
196
196
  cameraReady.value = true;
197
197
  }
198
+ if (!camera.instance) {
199
+ throw new Error('Error creating camera.');
200
+ }
198
201
  // move camera if needed
199
202
  if (camera && props.cameraPosition) {
200
- camera.instance?.position.set(...props.cameraPosition);
203
+ camera.instance.position.set(...props.cameraPosition);
204
+ }
205
+ // angle camera if needed
206
+ if (camera && (props.cameraLookAt || props.cameraLook)) {
207
+ const source = (props.cameraLookAt || props.cameraLook);
208
+ camera.instance.lookAt(...source);
201
209
  }
202
210
  // SCENE
203
211
  // ====================
@@ -220,11 +228,23 @@ const LunchboxWrapper = {
220
228
  else {
221
229
  throw new Error('missing renderer');
222
230
  }
231
+ // CALLBACK PREP
232
+ // ====================
233
+ const app = getCurrentInstance().appContext.app;
234
+ // START
235
+ // ====================
236
+ for (let startCallback of startCallbacks) {
237
+ startCallback({
238
+ app,
239
+ camera: camera.instance,
240
+ renderer: renderer.instance,
241
+ scene: scene.instance,
242
+ });
243
+ }
223
244
  // KICK UPDATE
224
245
  // ====================
225
- // console.log(scene)
226
246
  update({
227
- app: getCurrentInstance().appContext.app,
247
+ app,
228
248
  camera: camera.instance,
229
249
  renderer: renderer.instance,
230
250
  scene: scene.instance,
@@ -255,21 +275,6 @@ const LunchboxWrapper = {
255
275
  },
256
276
  };
257
277
 
258
- const catalogue = {};
259
-
260
- const lunchboxDomComponentNames = [
261
- 'canvas',
262
- 'div',
263
- 'LunchboxWrapper',
264
- ];
265
- // component creation utility
266
- const createComponent$1 = (tag) => defineComponent({
267
- inheritAttrs: false,
268
- name: tag,
269
- setup(props, context) {
270
- return () => h(tag, context.attrs, context.slots?.default?.() || []);
271
- },
272
- });
273
278
  // list of all components to register out of the box
274
279
  const autoGeneratedComponents = [
275
280
  // ThreeJS basics
@@ -382,21 +387,11 @@ const autoGeneratedComponents = [
382
387
  'arrayCamera',
383
388
  // renderers
384
389
  'webGLRenderer',
385
- ].map(createComponent$1).reduce((acc, curr) => {
386
- acc[curr.name] = curr;
387
- return acc;
388
- });
389
- const components = {
390
- ...autoGeneratedComponents,
391
- 'Lunchbox': LunchboxWrapper,
392
- // Gltf,
393
- };
394
- // console.log(components, Gltf)
395
- /*
396
- // List copied from r3f
397
- // https://github.com/pmndrs/react-three-fiber/blob/master/packages/fiber/src/three-types.ts
390
+ /*
391
+ // List copied from r3f:
392
+ // https://github.com/pmndrs/react-three-fiber/blob/master/packages/fiber/src/three-types.ts
398
393
 
399
- // NOT IMPLEMENTED:
394
+ // NOT IMPLEMENTED (can be added via Extend - docs.lunchboxjs.com/components/extend/):
400
395
  audioListener: AudioListenerProps
401
396
  positionalAudio: PositionalAudioProps
402
397
 
@@ -444,6 +439,27 @@ const components = {
444
439
  fogExp2: FogExp2Props
445
440
  shape: ShapeProps
446
441
  */
442
+ ];
443
+
444
+ const catalogue = {};
445
+
446
+ const lunchboxDomComponentNames = ['canvas', 'div', 'LunchboxWrapper'];
447
+ // component creation utility
448
+ const createComponent$1 = (tag) => defineComponent({
449
+ inheritAttrs: false,
450
+ name: tag,
451
+ setup(props, context) {
452
+ return () => h(tag, context.attrs, context.slots?.default?.() || []);
453
+ },
454
+ });
455
+ autoGeneratedComponents.map(createComponent$1).reduce((acc, curr) => {
456
+ acc[curr.name] = curr;
457
+ return acc;
458
+ });
459
+ const components = {
460
+ ...autoGeneratedComponents,
461
+ Lunchbox: LunchboxWrapper,
462
+ };
447
463
 
448
464
  function find(target) {
449
465
  target = isRef(target) ? target.value : target;
@@ -1237,6 +1253,16 @@ var MiniDom;
1237
1253
  const rootNode = new MiniDom.RendererRootNode();
1238
1254
  rootNode.minidomType = 'RootNode';
1239
1255
 
1256
+ const startCallbacks = [];
1257
+ const onStart = (cb, index = Infinity) => {
1258
+ if (index === Infinity) {
1259
+ startCallbacks.push(cb);
1260
+ }
1261
+ else {
1262
+ startCallbacks.splice(index, 0, cb);
1263
+ }
1264
+ };
1265
+
1240
1266
  let frameID;
1241
1267
  const beforeRender = [];
1242
1268
  const afterRender = [];
@@ -1280,6 +1306,15 @@ const onBeforeRender = (cb, index = Infinity) => {
1280
1306
  beforeRender.splice(index, 0, cb);
1281
1307
  }
1282
1308
  };
1309
+ const offBeforeRender = (cb) => {
1310
+ if (isFinite(cb)) {
1311
+ beforeRender.splice(cb, 1);
1312
+ }
1313
+ else {
1314
+ const idx = beforeRender.findIndex((v) => v == cb);
1315
+ beforeRender.splice(idx, 1);
1316
+ }
1317
+ };
1283
1318
  const onAfterRender = (cb, index = Infinity) => {
1284
1319
  if (index === Infinity) {
1285
1320
  afterRender.push(cb);
@@ -1288,6 +1323,15 @@ const onAfterRender = (cb, index = Infinity) => {
1288
1323
  afterRender.splice(index, 0, cb);
1289
1324
  }
1290
1325
  };
1326
+ const offAfterRender = (cb) => {
1327
+ if (isFinite(cb)) {
1328
+ afterRender.splice(cb, 1);
1329
+ }
1330
+ else {
1331
+ const idx = afterRender.findIndex((v) => v == cb);
1332
+ afterRender.splice(idx, 1);
1333
+ }
1334
+ };
1291
1335
  const cancelUpdate = () => {
1292
1336
  if (frameID)
1293
1337
  cancelAnimationFrame(frameID);
@@ -1641,9 +1685,51 @@ const globals = {
1641
1685
  inputActive,
1642
1686
  mousePos,
1643
1687
  };
1688
+ /** The current camera. Often easier to use `useCamera` instead of this. */
1644
1689
  const camera = computed(() => ensuredCamera.value?.instance ?? null);
1690
+ /** Run a function using the current camera when it's present. */
1691
+ function useCamera(callback, once = true) {
1692
+ let destroy;
1693
+ destroy = watch(camera, (newVal) => {
1694
+ if (!newVal)
1695
+ return;
1696
+ // TODO: better fix than `any`?
1697
+ callback(newVal);
1698
+ if (once) {
1699
+ destroy?.();
1700
+ }
1701
+ }, { immediate: true });
1702
+ }
1703
+ /** The current renderer. Often easier to use `useRenderer` instead of this. */
1645
1704
  const renderer = computed(() => ensureRenderer.value?.instance ?? null);
1705
+ /** Run a function using the current renderer when it's present. */
1706
+ function useRenderer(callback, once = true) {
1707
+ let destroy;
1708
+ destroy = watch(renderer, (newVal) => {
1709
+ if (!newVal)
1710
+ return;
1711
+ // TODO: better fix than `any`?
1712
+ callback(newVal);
1713
+ if (once) {
1714
+ destroy?.();
1715
+ }
1716
+ }, { immediate: true });
1717
+ }
1718
+ /** The current scene. Often easier to use `useScene` instead of this. */
1646
1719
  const scene = computed(() => ensuredScene.value.instance);
1720
+ /** Run a function using the current scene when it's present. */
1721
+ function useScene(callback, once = true) {
1722
+ let destroy;
1723
+ destroy = watch(scene, (newVal) => {
1724
+ if (!newVal)
1725
+ return;
1726
+ // TODO: better fix than `any`?
1727
+ callback(newVal);
1728
+ if (once) {
1729
+ destroy?.();
1730
+ }
1731
+ }, { immediate: true });
1732
+ }
1647
1733
  // CUSTOM RENDER SUPPORT
1648
1734
  // ====================
1649
1735
  let app = null;
@@ -1710,4 +1796,4 @@ const createApp = (root) => {
1710
1796
  return app;
1711
1797
  };
1712
1798
 
1713
- export { camera, clearCustomRender, createApp, find, globals, lunchboxRootNode as lunchboxTree, onAfterRender, onBeforeRender, renderer, scene, setCustomRender };
1799
+ export { camera, clearCustomRender, createApp, find, globals, lunchboxRootNode as lunchboxTree, offAfterRender, offBeforeRender, onAfterRender, onBeforeRender, onStart, renderer, scene, setCustomRender, useCamera, useRenderer, useScene };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lunchboxjs",
3
- "version": "0.1.4004",
3
+ "version": "0.1.4010",
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",
@@ -25,6 +25,8 @@
25
25
  "@types/three": "0.133.0",
26
26
  "@types/uuid": "8.3.1",
27
27
  "@vitejs/plugin-vue": "^1.9.3",
28
+ "chroma-js": "2.1.2",
29
+ "nice-color-palettes": "3.0.0",
28
30
  "rollup-plugin-delete": "2.0.0",
29
31
  "rollup-plugin-terser": "7.0.2",
30
32
  "typescript": "^4.4.3",
@@ -18,6 +18,7 @@ import {
18
18
  fallbackRendererUuid,
19
19
  MiniDom,
20
20
  rendererReady,
21
+ startCallbacks,
21
22
  tryGetNodeWithInstanceType,
22
23
  update,
23
24
  } from '../../core'
@@ -45,10 +46,13 @@ export const LunchboxWrapper: ComponentOptions = {
45
46
  // These should match the Lunchbox.WrapperProps interface
46
47
  background: String,
47
48
  cameraArgs: Array,
49
+ cameraLook: Array,
50
+ cameraLookAt: Array,
48
51
  cameraPosition: Array,
49
52
  dpr: Number,
50
53
  ortho: Boolean,
51
54
  orthographic: Boolean,
55
+ rendererArguments: Object,
52
56
  rendererProperties: Object,
53
57
  shadow: [Boolean, Object],
54
58
  transparent: Boolean,
@@ -80,11 +84,10 @@ export const LunchboxWrapper: ComponentOptions = {
80
84
  if (!renderer) {
81
85
  // build renderer args
82
86
  const rendererArgs: THREE.WebGLRendererParameters = {
87
+ alpha: props.transparent,
83
88
  antialias: true,
84
89
  canvas: canvas.value.domElement,
85
- }
86
- if (props.transparent) {
87
- rendererArgs.alpha = true
90
+ ...(props.rendererArguments ?? {}),
88
91
  }
89
92
 
90
93
  // create new renderer
@@ -135,7 +138,6 @@ export const LunchboxWrapper: ComponentOptions = {
135
138
  // the user has initialized the renderer, so anything depending
136
139
  // on the renderer can execute
137
140
  rendererReady.value = true
138
- return
139
141
  }
140
142
 
141
143
  // CAMERA
@@ -149,8 +151,6 @@ export const LunchboxWrapper: ComponentOptions = {
149
151
  if (!camera) {
150
152
  // create ortho camera
151
153
  if (props.ortho || props.orthographic) {
152
- // const size: Vector2 = new Vector2()
153
-
154
154
  ensuredCamera.value = createNode<THREE.OrthographicCamera>({
155
155
  props: { args: props.cameraArgs ?? [] },
156
156
  type: 'OrthographicCamera',
@@ -172,9 +172,17 @@ export const LunchboxWrapper: ComponentOptions = {
172
172
  } else {
173
173
  cameraReady.value = true
174
174
  }
175
+ if (!camera.instance) {
176
+ throw new Error('Error creating camera.')
177
+ }
175
178
  // move camera if needed
176
179
  if (camera && props.cameraPosition) {
177
- camera.instance?.position.set(...props.cameraPosition)
180
+ camera.instance.position.set(...props.cameraPosition)
181
+ }
182
+ // angle camera if needed
183
+ if (camera && (props.cameraLookAt || props.cameraLook)) {
184
+ const source = (props.cameraLookAt || props.cameraLook)!
185
+ camera.instance.lookAt(...source)
178
186
  }
179
187
 
180
188
  // SCENE
@@ -204,11 +212,25 @@ export const LunchboxWrapper: ComponentOptions = {
204
212
  throw new Error('missing renderer')
205
213
  }
206
214
 
215
+ // CALLBACK PREP
216
+ // ====================
217
+ const app = getCurrentInstance()!.appContext.app as Lunch.App
218
+
219
+ // START
220
+ // ====================
221
+ for (let startCallback of startCallbacks) {
222
+ startCallback({
223
+ app,
224
+ camera: camera.instance,
225
+ renderer: renderer.instance,
226
+ scene: scene.instance,
227
+ })
228
+ }
229
+
207
230
  // KICK UPDATE
208
231
  // ====================
209
- // console.log(scene)
210
232
  update({
211
- app: getCurrentInstance()!.appContext.app as Lunch.App,
233
+ app,
212
234
  camera: camera.instance,
213
235
  renderer: renderer.instance,
214
236
  scene: scene.instance,
@@ -0,0 +1,175 @@
1
+ // list of all components to register out of the box
2
+ export const autoGeneratedComponents = [
3
+ // ThreeJS basics
4
+ 'mesh',
5
+ 'instancedMesh',
6
+ 'scene',
7
+ 'sprite',
8
+ 'object3D',
9
+
10
+ // geometry
11
+ 'instancedBufferGeometry',
12
+ 'bufferGeometry',
13
+ 'boxBufferGeometry',
14
+ 'circleBufferGeometry',
15
+ 'coneBufferGeometry',
16
+ 'cylinderBufferGeometry',
17
+ 'dodecahedronBufferGeometry',
18
+ 'extrudeBufferGeometry',
19
+ 'icosahedronBufferGeometry',
20
+ 'latheBufferGeometry',
21
+ 'octahedronBufferGeometry',
22
+ 'parametricBufferGeometry',
23
+ 'planeBufferGeometry',
24
+ 'polyhedronBufferGeometry',
25
+ 'ringBufferGeometry',
26
+ 'shapeBufferGeometry',
27
+ 'sphereBufferGeometry',
28
+ 'tetrahedronBufferGeometry',
29
+ 'textBufferGeometry',
30
+ 'torusBufferGeometry',
31
+ 'torusKnotBufferGeometry',
32
+ 'tubeBufferGeometry',
33
+ 'wireframeGeometry',
34
+ 'parametricGeometry',
35
+ 'tetrahedronGeometry',
36
+ 'octahedronGeometry',
37
+ 'icosahedronGeometry',
38
+ 'dodecahedronGeometry',
39
+ 'polyhedronGeometry',
40
+ 'tubeGeometry',
41
+ 'torusKnotGeometry',
42
+ 'torusGeometry',
43
+ // textgeometry has been moved to /examples/jsm/geometries/TextGeometry
44
+ // 'textGeometry',
45
+ 'sphereGeometry',
46
+ 'ringGeometry',
47
+ 'planeGeometry',
48
+ 'latheGeometry',
49
+ 'shapeGeometry',
50
+ 'extrudeGeometry',
51
+ 'edgesGeometry',
52
+ 'coneGeometry',
53
+ 'cylinderGeometry',
54
+ 'circleGeometry',
55
+ 'boxGeometry',
56
+
57
+ // materials
58
+ 'material',
59
+ 'shadowMaterial',
60
+ 'spriteMaterial',
61
+ 'rawShaderMaterial',
62
+ 'shaderMaterial',
63
+ 'pointsMaterial',
64
+ 'meshPhysicalMaterial',
65
+ 'meshStandardMaterial',
66
+ 'meshPhongMaterial',
67
+ 'meshToonMaterial',
68
+ 'meshNormalMaterial',
69
+ 'meshLambertMaterial',
70
+ 'meshDepthMaterial',
71
+ 'meshDistanceMaterial',
72
+ 'meshBasicMaterial',
73
+ 'meshMatcapMaterial',
74
+ 'lineDashedMaterial',
75
+ 'lineBasicMaterial',
76
+
77
+ // lights
78
+ 'light',
79
+ 'spotLightShadow',
80
+ 'spotLight',
81
+ 'pointLight',
82
+ 'rectAreaLight',
83
+ 'hemisphereLight',
84
+ 'directionalLightShadow',
85
+ 'directionalLight',
86
+ 'ambientLight',
87
+ 'lightShadow',
88
+ 'ambientLightProbe',
89
+ 'hemisphereLightProbe',
90
+ 'lightProbe',
91
+
92
+ // textures
93
+ 'texture',
94
+ 'videoTexture',
95
+ 'dataTexture',
96
+ 'dataTexture3D',
97
+ 'compressedTexture',
98
+ 'cubeTexture',
99
+ 'canvasTexture',
100
+ 'depthTexture',
101
+
102
+ // Texture loaders
103
+ 'textureLoader',
104
+
105
+ // misc
106
+ 'group',
107
+ 'catmullRomCurve3',
108
+ 'points',
109
+
110
+ // helpers
111
+ 'cameraHelper',
112
+
113
+ // cameras
114
+ 'camera',
115
+ 'perspectiveCamera',
116
+ 'orthographicCamera',
117
+ 'cubeCamera',
118
+ 'arrayCamera',
119
+
120
+ // renderers
121
+ 'webGLRenderer',
122
+
123
+ /*
124
+ // List copied from r3f:
125
+ // https://github.com/pmndrs/react-three-fiber/blob/master/packages/fiber/src/three-types.ts
126
+
127
+ // NOT IMPLEMENTED (can be added via Extend - docs.lunchboxjs.com/components/extend/):
128
+ audioListener: AudioListenerProps
129
+ positionalAudio: PositionalAudioProps
130
+
131
+ lOD: LODProps
132
+ skinnedMesh: SkinnedMeshProps
133
+ skeleton: SkeletonProps
134
+ bone: BoneProps
135
+ lineSegments: LineSegmentsProps
136
+ lineLoop: LineLoopProps
137
+ // see `audio`
138
+ // line: LineProps
139
+ immediateRenderObject: ImmediateRenderObjectProps
140
+
141
+ // primitive
142
+ primitive: PrimitiveProps
143
+
144
+ // helpers
145
+ spotLightHelper: SpotLightHelperProps
146
+ skeletonHelper: SkeletonHelperProps
147
+ pointLightHelper: PointLightHelperProps
148
+ hemisphereLightHelper: HemisphereLightHelperProps
149
+ gridHelper: GridHelperProps
150
+ polarGridHelper: PolarGridHelperProps
151
+ directionalLightHelper: DirectionalLightHelperProps
152
+ boxHelper: BoxHelperProps
153
+ box3Helper: Box3HelperProps
154
+ planeHelper: PlaneHelperProps
155
+ arrowHelper: ArrowHelperProps
156
+ axesHelper: AxesHelperProps
157
+
158
+
159
+ // misc
160
+ raycaster: RaycasterProps
161
+ vector2: Vector2Props
162
+ vector3: Vector3Props
163
+ vector4: Vector4Props
164
+ euler: EulerProps
165
+ matrix3: Matrix3Props
166
+ matrix4: Matrix4Props
167
+ quaternion: QuaternionProps
168
+ bufferAttribute: BufferAttributeProps
169
+ instancedBufferAttribute: InstancedBufferAttributeProps
170
+ color: ColorProps
171
+ fog: FogProps
172
+ fogExp2: FogExp2Props
173
+ shape: ShapeProps
174
+ */
175
+ ]
@@ -1,16 +1,11 @@
1
1
  import { h, defineComponent } from 'vue'
2
- // import Gltf from './Gltf'
3
- // import { Lunchbox } from '../types'
4
2
  import { LunchboxWrapper } from './LunchboxWrapper/LunchboxWrapper'
3
+ import { autoGeneratedComponents } from './autoGeneratedComponents'
5
4
 
6
5
  import { catalogue } from './catalogue'
7
6
  export { catalogue }
8
7
 
9
- export const lunchboxDomComponentNames = [
10
- 'canvas',
11
- 'div',
12
- 'LunchboxWrapper',
13
- ]
8
+ export const lunchboxDomComponentNames = ['canvas', 'div', 'LunchboxWrapper']
14
9
 
15
10
  // component creation utility
16
11
  const createComponent = (tag: string) =>
@@ -22,190 +17,12 @@ const createComponent = (tag: string) =>
22
17
  },
23
18
  })
24
19
 
25
- // list of all components to register out of the box
26
- const autoGeneratedComponents = [
27
-
28
- // ThreeJS basics
29
- 'mesh',
30
- 'instancedMesh',
31
- 'scene',
32
- 'sprite',
33
- 'object3D',
34
-
35
- // geometry
36
- 'instancedBufferGeometry',
37
- 'bufferGeometry',
38
- 'boxBufferGeometry',
39
- 'circleBufferGeometry',
40
- 'coneBufferGeometry',
41
- 'cylinderBufferGeometry',
42
- 'dodecahedronBufferGeometry',
43
- 'extrudeBufferGeometry',
44
- 'icosahedronBufferGeometry',
45
- 'latheBufferGeometry',
46
- 'octahedronBufferGeometry',
47
- 'parametricBufferGeometry',
48
- 'planeBufferGeometry',
49
- 'polyhedronBufferGeometry',
50
- 'ringBufferGeometry',
51
- 'shapeBufferGeometry',
52
- 'sphereBufferGeometry',
53
- 'tetrahedronBufferGeometry',
54
- 'textBufferGeometry',
55
- 'torusBufferGeometry',
56
- 'torusKnotBufferGeometry',
57
- 'tubeBufferGeometry',
58
- 'wireframeGeometry',
59
- 'parametricGeometry',
60
- 'tetrahedronGeometry',
61
- 'octahedronGeometry',
62
- 'icosahedronGeometry',
63
- 'dodecahedronGeometry',
64
- 'polyhedronGeometry',
65
- 'tubeGeometry',
66
- 'torusKnotGeometry',
67
- 'torusGeometry',
68
- // textgeometry has been moved to /examples/jsm/geometries/TextGeometry
69
- // 'textGeometry',
70
- 'sphereGeometry',
71
- 'ringGeometry',
72
- 'planeGeometry',
73
- 'latheGeometry',
74
- 'shapeGeometry',
75
- 'extrudeGeometry',
76
- 'edgesGeometry',
77
- 'coneGeometry',
78
- 'cylinderGeometry',
79
- 'circleGeometry',
80
- 'boxGeometry',
81
-
82
- // materials
83
- 'material',
84
- 'shadowMaterial',
85
- 'spriteMaterial',
86
- 'rawShaderMaterial',
87
- 'shaderMaterial',
88
- 'pointsMaterial',
89
- 'meshPhysicalMaterial',
90
- 'meshStandardMaterial',
91
- 'meshPhongMaterial',
92
- 'meshToonMaterial',
93
- 'meshNormalMaterial',
94
- 'meshLambertMaterial',
95
- 'meshDepthMaterial',
96
- 'meshDistanceMaterial',
97
- 'meshBasicMaterial',
98
- 'meshMatcapMaterial',
99
- 'lineDashedMaterial',
100
- 'lineBasicMaterial',
101
-
102
- // lights
103
- 'light',
104
- 'spotLightShadow',
105
- 'spotLight',
106
- 'pointLight',
107
- 'rectAreaLight',
108
- 'hemisphereLight',
109
- 'directionalLightShadow',
110
- 'directionalLight',
111
- 'ambientLight',
112
- 'lightShadow',
113
- 'ambientLightProbe',
114
- 'hemisphereLightProbe',
115
- 'lightProbe',
116
-
117
- // textures
118
- 'texture',
119
- 'videoTexture',
120
- 'dataTexture',
121
- 'dataTexture3D',
122
- 'compressedTexture',
123
- 'cubeTexture',
124
- 'canvasTexture',
125
- 'depthTexture',
126
-
127
- // Texture loaders
128
- 'textureLoader',
129
-
130
- // misc
131
- 'group',
132
- 'catmullRomCurve3',
133
- 'points',
134
-
135
- // helpers
136
- 'cameraHelper',
137
-
138
- // cameras
139
- 'camera',
140
- 'perspectiveCamera',
141
- 'orthographicCamera',
142
- 'cubeCamera',
143
- 'arrayCamera',
144
-
145
- // renderers
146
- 'webGLRenderer',
147
- ].map(createComponent).reduce((acc, curr) => {
148
- ; (acc as any)[curr.name] = curr
20
+ autoGeneratedComponents.map(createComponent).reduce((acc, curr) => {
21
+ ;(acc as any)[curr.name] = curr
149
22
  return acc
150
23
  })
151
24
 
152
25
  export const components = {
153
26
  ...autoGeneratedComponents,
154
- 'Lunchbox': LunchboxWrapper,
155
- // Gltf,
27
+ Lunchbox: LunchboxWrapper,
156
28
  }
157
-
158
- // console.log(components, Gltf)
159
-
160
- /*
161
- // List copied from r3f
162
- // https://github.com/pmndrs/react-three-fiber/blob/master/packages/fiber/src/three-types.ts
163
-
164
- // NOT IMPLEMENTED:
165
- audioListener: AudioListenerProps
166
- positionalAudio: PositionalAudioProps
167
-
168
- lOD: LODProps
169
- skinnedMesh: SkinnedMeshProps
170
- skeleton: SkeletonProps
171
- bone: BoneProps
172
- lineSegments: LineSegmentsProps
173
- lineLoop: LineLoopProps
174
- // see `audio`
175
- // line: LineProps
176
- immediateRenderObject: ImmediateRenderObjectProps
177
-
178
- // primitive
179
- primitive: PrimitiveProps
180
-
181
- // helpers
182
- spotLightHelper: SpotLightHelperProps
183
- skeletonHelper: SkeletonHelperProps
184
- pointLightHelper: PointLightHelperProps
185
- hemisphereLightHelper: HemisphereLightHelperProps
186
- gridHelper: GridHelperProps
187
- polarGridHelper: PolarGridHelperProps
188
- directionalLightHelper: DirectionalLightHelperProps
189
- boxHelper: BoxHelperProps
190
- box3Helper: Box3HelperProps
191
- planeHelper: PlaneHelperProps
192
- arrowHelper: ArrowHelperProps
193
- axesHelper: AxesHelperProps
194
-
195
-
196
- // misc
197
- raycaster: RaycasterProps
198
- vector2: Vector2Props
199
- vector3: Vector3Props
200
- vector4: Vector4Props
201
- euler: EulerProps
202
- matrix3: Matrix3Props
203
- matrix4: Matrix4Props
204
- quaternion: QuaternionProps
205
- bufferAttribute: BufferAttributeProps
206
- instancedBufferAttribute: InstancedBufferAttributeProps
207
- color: ColorProps
208
- fog: FogProps
209
- fogExp2: FogExp2Props
210
- shape: ShapeProps
211
- */
package/src/core/index.ts CHANGED
@@ -5,5 +5,6 @@ export * from './interaction'
5
5
  export * from './extend'
6
6
  export * from './instantiateThreeObject'
7
7
  export * from './minidom'
8
+ export * from './start'
8
9
  export * from './update'
9
- export * from './updateObjectProp'
10
+ export * from './updateObjectProp'
@@ -0,0 +1,11 @@
1
+ import { Lunch } from '..'
2
+
3
+ export const startCallbacks = [] as Lunch.UpdateCallback[]
4
+
5
+ export const onStart = (cb: Lunch.UpdateCallback, index = Infinity) => {
6
+ if (index === Infinity) {
7
+ startCallbacks.push(cb)
8
+ } else {
9
+ startCallbacks.splice(index, 0, cb)
10
+ }
11
+ }
@@ -53,6 +53,15 @@ export const onBeforeRender = (cb: Lunch.UpdateCallback, index = Infinity) => {
53
53
  }
54
54
  }
55
55
 
56
+ export const offBeforeRender = (cb: Lunch.UpdateCallback | number) => {
57
+ if (isFinite(cb as number)) {
58
+ beforeRender.splice(cb as number, 1)
59
+ } else {
60
+ const idx = beforeRender.findIndex((v) => v == cb)
61
+ beforeRender.splice(idx, 1)
62
+ }
63
+ }
64
+
56
65
  export const onAfterRender = (cb: Lunch.UpdateCallback, index = Infinity) => {
57
66
  if (index === Infinity) {
58
67
  afterRender.push(cb)
@@ -61,6 +70,15 @@ export const onAfterRender = (cb: Lunch.UpdateCallback, index = Infinity) => {
61
70
  }
62
71
  }
63
72
 
73
+ export const offAfterRender = (cb: Lunch.UpdateCallback | number) => {
74
+ if (isFinite(cb as number)) {
75
+ afterRender.splice(cb as number, 1)
76
+ } else {
77
+ const idx = afterRender.findIndex((v) => v == cb)
78
+ afterRender.splice(idx, 1)
79
+ }
80
+ }
81
+
64
82
  export const cancelUpdate = () => {
65
83
  if (frameID) cancelAnimationFrame(frameID)
66
84
  }
package/src/index.ts CHANGED
@@ -1,4 +1,11 @@
1
- import { computed, createRenderer, Component, ref, watch } from 'vue'
1
+ import {
2
+ computed,
3
+ createRenderer,
4
+ Component,
5
+ ref,
6
+ watch,
7
+ WatchStopHandle,
8
+ } from 'vue'
2
9
  import { nodeOps } from './nodeOps'
3
10
  import {
4
11
  // createdCamera,
@@ -17,7 +24,13 @@ import { components } from './components'
17
24
  import { Lunch } from './types'
18
25
 
19
26
  export { lunchboxRootNode as lunchboxTree } from './core'
20
- export { onBeforeRender, onAfterRender } from './core'
27
+ export {
28
+ offAfterRender,
29
+ offBeforeRender,
30
+ onAfterRender,
31
+ onBeforeRender,
32
+ onStart,
33
+ } from './core'
21
34
  export * from './types'
22
35
 
23
36
  // Utilities
@@ -30,9 +43,74 @@ export const globals = {
30
43
  mousePos,
31
44
  }
32
45
 
46
+ /** The current camera. Often easier to use `useCamera` instead of this. */
33
47
  export const camera = computed(() => ensuredCamera.value?.instance ?? null)
48
+ /** Run a function using the current camera when it's present. */
49
+ export function useCamera<T extends THREE.Camera = THREE.PerspectiveCamera>(
50
+ callback: (cam: T) => void,
51
+ once = true
52
+ ) {
53
+ let destroy: WatchStopHandle
54
+ destroy = watch(
55
+ camera,
56
+ (newVal) => {
57
+ if (!newVal) return
58
+
59
+ // TODO: better fix than `any`?
60
+ callback(newVal as any)
61
+ if (once) {
62
+ destroy?.()
63
+ }
64
+ },
65
+ { immediate: true }
66
+ )
67
+ }
68
+
69
+ /** The current renderer. Often easier to use `useRenderer` instead of this. */
34
70
  export const renderer = computed(() => ensureRenderer.value?.instance ?? null)
71
+ /** Run a function using the current renderer when it's present. */
72
+ export function useRenderer<T extends THREE.Renderer = THREE.WebGLRenderer>(
73
+ callback: (rend: T) => void,
74
+ once = true
75
+ ) {
76
+ let destroy: WatchStopHandle
77
+ destroy = watch(
78
+ renderer,
79
+ (newVal) => {
80
+ if (!newVal) return
81
+
82
+ // TODO: better fix than `any`?
83
+ callback(newVal as any)
84
+ if (once) {
85
+ destroy?.()
86
+ }
87
+ },
88
+ { immediate: true }
89
+ )
90
+ }
91
+
92
+ /** The current scene. Often easier to use `useScene` instead of this. */
35
93
  export const scene = computed(() => ensuredScene.value.instance)
94
+ /** Run a function using the current scene when it's present. */
95
+ export function useScene(
96
+ callback: (newScene: THREE.Scene) => void,
97
+ once = true
98
+ ) {
99
+ let destroy: WatchStopHandle
100
+ destroy = watch(
101
+ scene,
102
+ (newVal) => {
103
+ if (!newVal) return
104
+
105
+ // TODO: better fix than `any`?
106
+ callback(newVal as any)
107
+ if (once) {
108
+ destroy?.()
109
+ }
110
+ },
111
+ { immediate: true }
112
+ )
113
+ }
36
114
 
37
115
  // CUSTOM RENDER SUPPORT
38
116
  // ====================
package/src/types.ts CHANGED
@@ -139,10 +139,14 @@ export declare namespace Lunch {
139
139
  interface WrapperProps {
140
140
  background?: string
141
141
  cameraArgs?: any[]
142
+ cameraLook?: [number, number, number]
143
+ cameraLookAt?: [number, number, number]
142
144
  cameraPosition?: [number, number, number]
143
145
  dpr?: number
144
146
  ortho?: boolean
145
147
  orthographic?: boolean
148
+ // TODO: Why doesn't ConstructorParameters<THREE.WebGLRenderer> work here?
149
+ rendererArguments?: object
146
150
  rendererProperties?: Partial<THREE.WebGLRenderer>
147
151
  shadow?: ShadowSugar
148
152
  transparent?: boolean