lunchboxjs 0.1.4005 → 0.1.4011

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.
@@ -111,6 +111,7 @@
111
111
  dpr: Number,
112
112
  ortho: Boolean,
113
113
  orthographic: Boolean,
114
+ rendererArguments: Object,
114
115
  rendererProperties: Object,
115
116
  shadow: [Boolean, Object],
116
117
  transparent: Boolean,
@@ -140,12 +141,11 @@
140
141
  if (!renderer) {
141
142
  // build renderer args
142
143
  const rendererArgs = {
144
+ alpha: props.transparent,
143
145
  antialias: true,
144
146
  canvas: canvas.value.domElement,
147
+ ...(props.rendererArguments ?? {}),
145
148
  };
146
- if (props.transparent) {
147
- rendererArgs.alpha = true;
148
- }
149
149
  // create new renderer
150
150
  ensureRenderer.value = createNode({
151
151
  type: 'WebGLRenderer',
@@ -183,7 +183,6 @@
183
183
  // the user has initialized the renderer, so anything depending
184
184
  // on the renderer can execute
185
185
  rendererReady.value = true;
186
- return;
187
186
  }
188
187
  // CAMERA
189
188
  // ====================
@@ -217,14 +216,17 @@
217
216
  else {
218
217
  cameraReady.value = true;
219
218
  }
219
+ if (!camera.instance) {
220
+ throw new Error('Error creating camera.');
221
+ }
220
222
  // move camera if needed
221
223
  if (camera && props.cameraPosition) {
222
- camera.instance?.position.set(...props.cameraPosition);
224
+ camera.instance.position.set(...props.cameraPosition);
223
225
  }
224
226
  // angle camera if needed
225
227
  if (camera && (props.cameraLookAt || props.cameraLook)) {
226
228
  const source = (props.cameraLookAt || props.cameraLook);
227
- camera.instance?.lookAt(...source);
229
+ camera.instance.lookAt(...source);
228
230
  }
229
231
  // SCENE
230
232
  // ====================
@@ -247,11 +249,23 @@
247
249
  else {
248
250
  throw new Error('missing renderer');
249
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
+ }
250
265
  // KICK UPDATE
251
266
  // ====================
252
- // console.log(scene)
253
267
  update({
254
- app: vue.getCurrentInstance().appContext.app,
268
+ app,
255
269
  camera: camera.instance,
256
270
  renderer: renderer.instance,
257
271
  scene: scene.instance,
@@ -456,15 +470,20 @@
456
470
  inheritAttrs: false,
457
471
  name: tag,
458
472
  setup(props, context) {
459
- return () => vue.h(tag, context.attrs, context.slots?.default?.() || []);
473
+ return () => {
474
+ return vue.h(tag, context.attrs, context.slots?.default?.() || []);
475
+ };
460
476
  },
461
477
  });
462
- autoGeneratedComponents.map(createComponent$1).reduce((acc, curr) => {
478
+ // turn components into registered map
479
+ const processed = autoGeneratedComponents
480
+ .map(createComponent$1)
481
+ .reduce((acc, curr) => {
463
482
  acc[curr.name] = curr;
464
483
  return acc;
465
- });
484
+ }, {});
466
485
  const components = {
467
- ...autoGeneratedComponents,
486
+ ...processed,
468
487
  Lunchbox: LunchboxWrapper,
469
488
  };
470
489
 
@@ -819,6 +838,11 @@
819
838
  const found = autoCreated[singleType] ||
820
839
  allNodes.find((node) => node.type?.toLowerCase() ===
821
840
  singleType.toLowerCase());
841
+ // cancel if found example is marked !isDefault
842
+ if (isMinidomNode(found) &&
843
+ (!found.props['is-default'] || !found.props['isDefault'])) {
844
+ return null;
845
+ }
822
846
  // if we have one, save and return
823
847
  if (found) {
824
848
  const createdAsNode = found;
@@ -950,10 +974,10 @@
950
974
  name: tag,
951
975
  render() {
952
976
  return vue.h(tag, this.$attrs, this.$slots?.default?.() || []);
953
- }
977
+ },
954
978
  });
955
979
  const extend = ({ app, ...targets }) => {
956
- Object.keys(targets).forEach(key => {
980
+ Object.keys(targets).forEach((key) => {
957
981
  app.component(key, createComponent(key));
958
982
  catalogue[key] = targets[key];
959
983
  });
@@ -1257,9 +1281,22 @@
1257
1281
  }
1258
1282
  MiniDom.RendererStandardNode = RendererStandardNode;
1259
1283
  })(MiniDom || (MiniDom = {}));
1284
+ function isMinidomNode(item) {
1285
+ return item?.minidomType === 'RendererNode';
1286
+ }
1260
1287
  const rootNode = new MiniDom.RendererRootNode();
1261
1288
  rootNode.minidomType = 'RootNode';
1262
1289
 
1290
+ const startCallbacks = [];
1291
+ const onStart = (cb, index = Infinity) => {
1292
+ if (index === Infinity) {
1293
+ startCallbacks.push(cb);
1294
+ }
1295
+ else {
1296
+ startCallbacks.splice(index, 0, cb);
1297
+ }
1298
+ };
1299
+
1263
1300
  let frameID;
1264
1301
  const beforeRender = [];
1265
1302
  const afterRender = [];
@@ -1341,8 +1378,13 @@
1341
1378
  if (isEventKey(key)) {
1342
1379
  return addEventListener({ node, key, value });
1343
1380
  }
1381
+ // update THREE property
1382
+ // get final key
1383
+ const camelKey = key.replace(/-/g, '.');
1384
+ const finalKey = propertyShortcuts[camelKey] || camelKey;
1344
1385
  // handle and return early if prop is specific to Vue/Lunchbox
1345
- if (internalLunchboxVueKeys.includes(key))
1386
+ if (internalLunchboxVueKeys.includes(key) ||
1387
+ internalLunchboxVueKeys.includes(finalKey))
1346
1388
  return node;
1347
1389
  // everything else should be Three-specific, so let's cancel if this isn't a standard node
1348
1390
  if (!isLunchboxStandardNode(node))
@@ -1357,12 +1399,8 @@
1357
1399
  // cancel if no target
1358
1400
  if (!target)
1359
1401
  return node;
1360
- // update THREE property
1361
- // get final key
1362
- const camelKey = key.replace(/-/g, '.');
1363
- let finalKey = propertyShortcuts[camelKey] || camelKey;
1364
- let liveProperty;
1365
1402
  // burrow down until we get property to change
1403
+ let liveProperty;
1366
1404
  for (let i = 0; i < nestedPropertiesToCheck.length && !liveProperty; i++) {
1367
1405
  const nestedProperty = nestedPropertiesToCheck[i];
1368
1406
  const fullPath = [nestedProperty, finalKey].filter(Boolean).join('.');
@@ -1427,6 +1465,8 @@
1427
1465
  'args',
1428
1466
  'attach',
1429
1467
  'attachArray',
1468
+ 'is.default',
1469
+ 'isDefault',
1430
1470
  'key',
1431
1471
  'onAdded',
1432
1472
  // 'onReady',
@@ -1682,9 +1722,51 @@
1682
1722
  inputActive,
1683
1723
  mousePos,
1684
1724
  };
1725
+ /** The current camera. Often easier to use `useCamera` instead of this. */
1685
1726
  const camera = vue.computed(() => ensuredCamera.value?.instance ?? null);
1727
+ /** Run a function using the current camera when it's present. */
1728
+ function useCamera(callback, once = true) {
1729
+ let destroy;
1730
+ destroy = vue.watch(camera, (newVal) => {
1731
+ if (!newVal)
1732
+ return;
1733
+ // TODO: better fix than `any`?
1734
+ callback(newVal);
1735
+ if (once) {
1736
+ destroy?.();
1737
+ }
1738
+ }, { immediate: true });
1739
+ }
1740
+ /** The current renderer. Often easier to use `useRenderer` instead of this. */
1686
1741
  const renderer = vue.computed(() => ensureRenderer.value?.instance ?? null);
1742
+ /** Run a function using the current renderer when it's present. */
1743
+ function useRenderer(callback, once = true) {
1744
+ let destroy;
1745
+ destroy = vue.watch(renderer, (newVal) => {
1746
+ if (!newVal)
1747
+ return;
1748
+ // TODO: better fix than `any`?
1749
+ callback(newVal);
1750
+ if (once) {
1751
+ destroy?.();
1752
+ }
1753
+ }, { immediate: true });
1754
+ }
1755
+ /** The current scene. Often easier to use `useScene` instead of this. */
1687
1756
  const scene = vue.computed(() => ensuredScene.value.instance);
1757
+ /** Run a function using the current scene when it's present. */
1758
+ function useScene(callback, once = true) {
1759
+ let destroy;
1760
+ destroy = vue.watch(scene, (newVal) => {
1761
+ if (!newVal)
1762
+ return;
1763
+ // TODO: better fix than `any`?
1764
+ callback(newVal);
1765
+ if (once) {
1766
+ destroy?.();
1767
+ }
1768
+ }, { immediate: true });
1769
+ }
1688
1770
  // CUSTOM RENDER SUPPORT
1689
1771
  // ====================
1690
1772
  let app = null;
@@ -1760,9 +1842,13 @@
1760
1842
  exports.offBeforeRender = offBeforeRender;
1761
1843
  exports.onAfterRender = onAfterRender;
1762
1844
  exports.onBeforeRender = onBeforeRender;
1845
+ exports.onStart = onStart;
1763
1846
  exports.renderer = renderer;
1764
1847
  exports.scene = scene;
1765
1848
  exports.setCustomRender = setCustomRender;
1849
+ exports.useCamera = useCamera;
1850
+ exports.useRenderer = useRenderer;
1851
+ exports.useScene = useScene;
1766
1852
 
1767
1853
  Object.defineProperty(exports, '__esModule', { value: true });
1768
1854
 
@@ -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=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,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)return s.value=!1,void(I.value=!0);{const t={antialias:!0,canvas:a.value.domElement};e.transparent&&(t.alpha=!0),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:F}):$.value=g({props:{args:e.cameraArgs??[45,.5625,1,1e3]},type:"PerspectiveCamera",uuid:F}),D.value=!0,p=$.value),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),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),ae({app:t.getCurrentInstance().appContext.app,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=["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),A.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 A=["onClick","onPointerUp","onPointerDown","onPointerOver","onPointerOut","onPointerEnter","onPointerLeave","onPointerMove"];let w,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(he.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 F="FALLBACK_CAMERA",S=k(["PerspectiveCamera","OrthographicCamera"],F,{args:[45,.5625,1,1e3]}),D=t.ref(!1),$=t.computed({get:()=>D.value?S.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||(w=t=>{const n=(e.instance.domElement.width??1)/he.dpr.value,r=(e.instance.domElement.height??1)/he.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",w),e.instance.domElement.addEventListener("mousedown",C),e.instance.domElement.addEventListener("mouseup",E),se(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={}));let ne;(new Q.RendererRootNode).minidomType="RootNode";const re=[],oe=[],ae=e=>{ne=requestAnimationFrame((()=>ae({app:e.app,renderer:K.value?.instance,scene:_.value.instance,camera:$.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","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(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 le.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);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){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(ue.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=ce[a]||a;for(let e=0;e<de.length&&!s;e++){const t=[de[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(){}},he={dpr:t.ref(1),inputActive:x,mousePos:L},fe=t.computed((()=>$.value?.instance??null)),ye=t.computed((()=>K.value?.instance??null)),ve=t.computed((()=>_.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(m).forEach((e=>{ge.component(e,m[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,q(n)),l[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,y(e)?e?.instance:h(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.renderer=ye,e.scene=ve,e.setCustomRender=e=>{ge?ge.setCustomRender(e):be=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=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,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=G(["WebGLRenderer"]),l)s.value=!1,W.value=!0;else{const t={alpha:e.transparent,antialias:!0,canvas:a.value.domElement,...e.rendererArguments??{}};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=G(["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.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=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),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;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,A,C;const E=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(he.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 N(t={}){return e.lunchboxTree||(e.lunchboxTree=new J.RendererRootNode(t)),e.lunchboxTree}function G(e){Array.isArray(e)||(e=[e]);for(let t of e)if(O[t])return O[t];for(let n of e){const e=T[n]||s.find((e=>e.type?.toLowerCase()===n.toLowerCase()));if(!(t=e,"RendererNode"!==t?.minidomType||e.props["is-default"]&&e.props.isDefault))return null;if(e){const t=e;return T[n]=t,t}}var t;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=G(e);if(t)return t;const a=N(),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)/he.dpr.value,r=(e.instance.domElement.height??1)/he.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",w),e.instance.domElement.addEventListener("mousedown",A),e.instance.domElement.addEventListener("mouseup",C),se(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={}));(new J.RendererRootNode).minidomType="RootNode";const te=[];let ne;const re=[],oe=[],ae=e=>{ne=requestAnimationFrame((()=>ae({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;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=v(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=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);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 x({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(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(){}},he={dpr:t.ref(1),inputActive:b,mousePos:E},fe=t.computed((()=>F.value?.instance??null));const ye=t.computed((()=>I.value?.instance??null));const ve=t.computed((()=>K.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,V(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,n=!0){let r;r=t.watch(fe,(t=>{t&&(e(t),n&&r?.())}),{immediate:!0})},e.useRenderer=function(e,n=!0){let r;r=t.watch(ye,(t=>{t&&(e(t),n&&r?.())}),{immediate:!0})},e.useScene=function(e,n=!0){let r;r=t.watch(ve,(t=>{t&&(e(t),n&&r?.())}),{immediate:!0})},Object.defineProperty(e,"__esModule",{value:!0})}));
@@ -90,6 +90,7 @@ const LunchboxWrapper = {
90
90
  dpr: Number,
91
91
  ortho: Boolean,
92
92
  orthographic: Boolean,
93
+ rendererArguments: Object,
93
94
  rendererProperties: Object,
94
95
  shadow: [Boolean, Object],
95
96
  transparent: Boolean,
@@ -119,12 +120,11 @@ const LunchboxWrapper = {
119
120
  if (!renderer) {
120
121
  // build renderer args
121
122
  const rendererArgs = {
123
+ alpha: props.transparent,
122
124
  antialias: true,
123
125
  canvas: canvas.value.domElement,
126
+ ...(props.rendererArguments ?? {}),
124
127
  };
125
- if (props.transparent) {
126
- rendererArgs.alpha = true;
127
- }
128
128
  // create new renderer
129
129
  ensureRenderer.value = createNode({
130
130
  type: 'WebGLRenderer',
@@ -162,7 +162,6 @@ const LunchboxWrapper = {
162
162
  // the user has initialized the renderer, so anything depending
163
163
  // on the renderer can execute
164
164
  rendererReady.value = true;
165
- return;
166
165
  }
167
166
  // CAMERA
168
167
  // ====================
@@ -196,14 +195,17 @@ const LunchboxWrapper = {
196
195
  else {
197
196
  cameraReady.value = true;
198
197
  }
198
+ if (!camera.instance) {
199
+ throw new Error('Error creating camera.');
200
+ }
199
201
  // move camera if needed
200
202
  if (camera && props.cameraPosition) {
201
- camera.instance?.position.set(...props.cameraPosition);
203
+ camera.instance.position.set(...props.cameraPosition);
202
204
  }
203
205
  // angle camera if needed
204
206
  if (camera && (props.cameraLookAt || props.cameraLook)) {
205
207
  const source = (props.cameraLookAt || props.cameraLook);
206
- camera.instance?.lookAt(...source);
208
+ camera.instance.lookAt(...source);
207
209
  }
208
210
  // SCENE
209
211
  // ====================
@@ -226,11 +228,23 @@ const LunchboxWrapper = {
226
228
  else {
227
229
  throw new Error('missing renderer');
228
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
+ }
229
244
  // KICK UPDATE
230
245
  // ====================
231
- // console.log(scene)
232
246
  update({
233
- app: getCurrentInstance().appContext.app,
247
+ app,
234
248
  camera: camera.instance,
235
249
  renderer: renderer.instance,
236
250
  scene: scene.instance,
@@ -435,15 +449,20 @@ const createComponent$1 = (tag) => defineComponent({
435
449
  inheritAttrs: false,
436
450
  name: tag,
437
451
  setup(props, context) {
438
- return () => h(tag, context.attrs, context.slots?.default?.() || []);
452
+ return () => {
453
+ return h(tag, context.attrs, context.slots?.default?.() || []);
454
+ };
439
455
  },
440
456
  });
441
- autoGeneratedComponents.map(createComponent$1).reduce((acc, curr) => {
457
+ // turn components into registered map
458
+ const processed = autoGeneratedComponents
459
+ .map(createComponent$1)
460
+ .reduce((acc, curr) => {
442
461
  acc[curr.name] = curr;
443
462
  return acc;
444
- });
463
+ }, {});
445
464
  const components = {
446
- ...autoGeneratedComponents,
465
+ ...processed,
447
466
  Lunchbox: LunchboxWrapper,
448
467
  };
449
468
 
@@ -798,6 +817,11 @@ function tryGetNodeWithInstanceType(pascalCaseTypes) {
798
817
  const found = autoCreated[singleType] ||
799
818
  allNodes.find((node) => node.type?.toLowerCase() ===
800
819
  singleType.toLowerCase());
820
+ // cancel if found example is marked !isDefault
821
+ if (isMinidomNode(found) &&
822
+ (!found.props['is-default'] || !found.props['isDefault'])) {
823
+ return null;
824
+ }
801
825
  // if we have one, save and return
802
826
  if (found) {
803
827
  const createdAsNode = found;
@@ -929,10 +953,10 @@ const createComponent = (tag) => defineComponent({
929
953
  name: tag,
930
954
  render() {
931
955
  return h(tag, this.$attrs, this.$slots?.default?.() || []);
932
- }
956
+ },
933
957
  });
934
958
  const extend = ({ app, ...targets }) => {
935
- Object.keys(targets).forEach(key => {
959
+ Object.keys(targets).forEach((key) => {
936
960
  app.component(key, createComponent(key));
937
961
  catalogue[key] = targets[key];
938
962
  });
@@ -1236,9 +1260,22 @@ var MiniDom;
1236
1260
  }
1237
1261
  MiniDom.RendererStandardNode = RendererStandardNode;
1238
1262
  })(MiniDom || (MiniDom = {}));
1263
+ function isMinidomNode(item) {
1264
+ return item?.minidomType === 'RendererNode';
1265
+ }
1239
1266
  const rootNode = new MiniDom.RendererRootNode();
1240
1267
  rootNode.minidomType = 'RootNode';
1241
1268
 
1269
+ const startCallbacks = [];
1270
+ const onStart = (cb, index = Infinity) => {
1271
+ if (index === Infinity) {
1272
+ startCallbacks.push(cb);
1273
+ }
1274
+ else {
1275
+ startCallbacks.splice(index, 0, cb);
1276
+ }
1277
+ };
1278
+
1242
1279
  let frameID;
1243
1280
  const beforeRender = [];
1244
1281
  const afterRender = [];
@@ -1320,8 +1357,13 @@ function updateObjectProp({ node, key, value, }) {
1320
1357
  if (isEventKey(key)) {
1321
1358
  return addEventListener({ node, key, value });
1322
1359
  }
1360
+ // update THREE property
1361
+ // get final key
1362
+ const camelKey = key.replace(/-/g, '.');
1363
+ const finalKey = propertyShortcuts[camelKey] || camelKey;
1323
1364
  // handle and return early if prop is specific to Vue/Lunchbox
1324
- if (internalLunchboxVueKeys.includes(key))
1365
+ if (internalLunchboxVueKeys.includes(key) ||
1366
+ internalLunchboxVueKeys.includes(finalKey))
1325
1367
  return node;
1326
1368
  // everything else should be Three-specific, so let's cancel if this isn't a standard node
1327
1369
  if (!isLunchboxStandardNode(node))
@@ -1336,12 +1378,8 @@ function updateObjectProp({ node, key, value, }) {
1336
1378
  // cancel if no target
1337
1379
  if (!target)
1338
1380
  return node;
1339
- // update THREE property
1340
- // get final key
1341
- const camelKey = key.replace(/-/g, '.');
1342
- let finalKey = propertyShortcuts[camelKey] || camelKey;
1343
- let liveProperty;
1344
1381
  // burrow down until we get property to change
1382
+ let liveProperty;
1345
1383
  for (let i = 0; i < nestedPropertiesToCheck.length && !liveProperty; i++) {
1346
1384
  const nestedProperty = nestedPropertiesToCheck[i];
1347
1385
  const fullPath = [nestedProperty, finalKey].filter(Boolean).join('.');
@@ -1406,6 +1444,8 @@ const internalLunchboxVueKeys = [
1406
1444
  'args',
1407
1445
  'attach',
1408
1446
  'attachArray',
1447
+ 'is.default',
1448
+ 'isDefault',
1409
1449
  'key',
1410
1450
  'onAdded',
1411
1451
  // 'onReady',
@@ -1661,9 +1701,51 @@ const globals = {
1661
1701
  inputActive,
1662
1702
  mousePos,
1663
1703
  };
1704
+ /** The current camera. Often easier to use `useCamera` instead of this. */
1664
1705
  const camera = computed(() => ensuredCamera.value?.instance ?? null);
1706
+ /** Run a function using the current camera when it's present. */
1707
+ function useCamera(callback, once = true) {
1708
+ let destroy;
1709
+ destroy = watch(camera, (newVal) => {
1710
+ if (!newVal)
1711
+ return;
1712
+ // TODO: better fix than `any`?
1713
+ callback(newVal);
1714
+ if (once) {
1715
+ destroy?.();
1716
+ }
1717
+ }, { immediate: true });
1718
+ }
1719
+ /** The current renderer. Often easier to use `useRenderer` instead of this. */
1665
1720
  const renderer = computed(() => ensureRenderer.value?.instance ?? null);
1721
+ /** Run a function using the current renderer when it's present. */
1722
+ function useRenderer(callback, once = true) {
1723
+ let destroy;
1724
+ destroy = watch(renderer, (newVal) => {
1725
+ if (!newVal)
1726
+ return;
1727
+ // TODO: better fix than `any`?
1728
+ callback(newVal);
1729
+ if (once) {
1730
+ destroy?.();
1731
+ }
1732
+ }, { immediate: true });
1733
+ }
1734
+ /** The current scene. Often easier to use `useScene` instead of this. */
1666
1735
  const scene = computed(() => ensuredScene.value.instance);
1736
+ /** Run a function using the current scene when it's present. */
1737
+ function useScene(callback, once = true) {
1738
+ let destroy;
1739
+ destroy = watch(scene, (newVal) => {
1740
+ if (!newVal)
1741
+ return;
1742
+ // TODO: better fix than `any`?
1743
+ callback(newVal);
1744
+ if (once) {
1745
+ destroy?.();
1746
+ }
1747
+ }, { immediate: true });
1748
+ }
1667
1749
  // CUSTOM RENDER SUPPORT
1668
1750
  // ====================
1669
1751
  let app = null;
@@ -1730,4 +1812,4 @@ const createApp = (root) => {
1730
1812
  return app;
1731
1813
  };
1732
1814
 
1733
- export { camera, clearCustomRender, createApp, find, globals, lunchboxRootNode as lunchboxTree, offAfterRender, offBeforeRender, onAfterRender, onBeforeRender, renderer, scene, setCustomRender };
1815
+ 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.4005",
3
+ "version": "0.1.4011",
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",
@@ -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'
@@ -51,6 +52,7 @@ export const LunchboxWrapper: ComponentOptions = {
51
52
  dpr: Number,
52
53
  ortho: Boolean,
53
54
  orthographic: Boolean,
55
+ rendererArguments: Object,
54
56
  rendererProperties: Object,
55
57
  shadow: [Boolean, Object],
56
58
  transparent: Boolean,
@@ -82,11 +84,10 @@ export const LunchboxWrapper: ComponentOptions = {
82
84
  if (!renderer) {
83
85
  // build renderer args
84
86
  const rendererArgs: THREE.WebGLRendererParameters = {
87
+ alpha: props.transparent,
85
88
  antialias: true,
86
89
  canvas: canvas.value.domElement,
87
- }
88
- if (props.transparent) {
89
- rendererArgs.alpha = true
90
+ ...(props.rendererArguments ?? {}),
90
91
  }
91
92
 
92
93
  // create new renderer
@@ -137,7 +138,6 @@ export const LunchboxWrapper: ComponentOptions = {
137
138
  // the user has initialized the renderer, so anything depending
138
139
  // on the renderer can execute
139
140
  rendererReady.value = true
140
- return
141
141
  }
142
142
 
143
143
  // CAMERA
@@ -172,14 +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)
178
181
  }
179
182
  // angle camera if needed
180
183
  if (camera && (props.cameraLookAt || props.cameraLook)) {
181
184
  const source = (props.cameraLookAt || props.cameraLook)!
182
- camera.instance?.lookAt(...source)
185
+ camera.instance.lookAt(...source)
183
186
  }
184
187
 
185
188
  // SCENE
@@ -209,11 +212,25 @@ export const LunchboxWrapper: ComponentOptions = {
209
212
  throw new Error('missing renderer')
210
213
  }
211
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
+
212
230
  // KICK UPDATE
213
231
  // ====================
214
- // console.log(scene)
215
232
  update({
216
- app: getCurrentInstance()!.appContext.app as Lunch.App,
233
+ app,
217
234
  camera: camera.instance,
218
235
  renderer: renderer.instance,
219
236
  scene: scene.instance,
@@ -13,16 +13,21 @@ const createComponent = (tag: string) =>
13
13
  inheritAttrs: false,
14
14
  name: tag,
15
15
  setup(props, context) {
16
- return () => h(tag, context.attrs, context.slots?.default?.() || [])
16
+ return () => {
17
+ return h(tag, context.attrs, context.slots?.default?.() || [])
18
+ }
17
19
  },
18
20
  })
19
21
 
20
- autoGeneratedComponents.map(createComponent).reduce((acc, curr) => {
21
- ;(acc as any)[curr.name] = curr
22
- return acc
23
- })
22
+ // turn components into registered map
23
+ const processed = autoGeneratedComponents
24
+ .map(createComponent)
25
+ .reduce((acc, curr) => {
26
+ acc[curr.name] = curr
27
+ return acc
28
+ }, {} as Record<string, ReturnType<typeof defineComponent>>)
24
29
 
25
30
  export const components = {
26
- ...autoGeneratedComponents,
31
+ ...processed,
27
32
  Lunchbox: LunchboxWrapper,
28
33
  }
@@ -1,4 +1,4 @@
1
- import { allNodes, createNode, MiniDom } from '.'
1
+ import { allNodes, createNode, isMinidomNode, MiniDom } from '.'
2
2
  import { setupAutoRaycaster } from './interaction/setupAutoRaycaster'
3
3
  import { computed, reactive, ref, WritableComputedRef } from 'vue'
4
4
  import { Lunch } from '..'
@@ -38,6 +38,14 @@ export function tryGetNodeWithInstanceType<T extends THREE.Object3D>(
38
38
  singleType.toLowerCase()
39
39
  )
40
40
 
41
+ // cancel if found example is marked !isDefault
42
+ if (
43
+ isMinidomNode(found) &&
44
+ (!found.props['is-default'] || !found.props['isDefault'])
45
+ ) {
46
+ return null
47
+ }
48
+
41
49
  // if we have one, save and return
42
50
  if (found) {
43
51
  const createdAsNode = found as MiniDom.RendererStandardNode<T>
@@ -8,12 +8,18 @@ const createComponent = (tag: string) =>
8
8
  name: tag,
9
9
  render() {
10
10
  return h(tag, this.$attrs, this.$slots?.default?.() || [])
11
- }
11
+ },
12
12
  })
13
13
 
14
- export const extend = ({ app, ...targets }: { app: Lunch.App, [key: string]: any }) => {
15
- Object.keys(targets).forEach(key => {
14
+ export const extend = ({
15
+ app,
16
+ ...targets
17
+ }: {
18
+ app: Lunch.App
19
+ [key: string]: any
20
+ }) => {
21
+ Object.keys(targets).forEach((key) => {
16
22
  app.component(key, createComponent(key))
17
23
  catalogue[key] = targets[key]
18
24
  })
19
- }
25
+ }
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
+ }
@@ -31,8 +31,17 @@ export function updateObjectProp({
31
31
  return addEventListener({ node, key, value })
32
32
  }
33
33
 
34
+ // update THREE property
35
+ // get final key
36
+ const camelKey = key.replace(/-/g, '.')
37
+ const finalKey = propertyShortcuts[camelKey] || camelKey
38
+
34
39
  // handle and return early if prop is specific to Vue/Lunchbox
35
- if (internalLunchboxVueKeys.includes(key)) return node
40
+ if (
41
+ internalLunchboxVueKeys.includes(key) ||
42
+ internalLunchboxVueKeys.includes(finalKey)
43
+ )
44
+ return node
36
45
 
37
46
  // everything else should be Three-specific, so let's cancel if this isn't a standard node
38
47
  if (!isLunchboxStandardNode(node)) return node
@@ -49,12 +58,8 @@ export function updateObjectProp({
49
58
  // cancel if no target
50
59
  if (!target) return node
51
60
 
52
- // update THREE property
53
- // get final key
54
- const camelKey = key.replace(/-/g, '.')
55
- let finalKey = propertyShortcuts[camelKey] || camelKey
56
- let liveProperty
57
61
  // burrow down until we get property to change
62
+ let liveProperty
58
63
  for (let i = 0; i < nestedPropertiesToCheck.length && !liveProperty; i++) {
59
64
  const nestedProperty = nestedPropertiesToCheck[i]
60
65
  const fullPath = [nestedProperty, finalKey].filter(Boolean).join('.')
@@ -128,6 +133,8 @@ const internalLunchboxVueKeys = [
128
133
  'args',
129
134
  'attach',
130
135
  'attachArray',
136
+ 'is.default',
137
+ 'isDefault',
131
138
  'key',
132
139
  'onAdded',
133
140
  // 'onReady',
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,
@@ -20,8 +27,9 @@ export { lunchboxRootNode as lunchboxTree } from './core'
20
27
  export {
21
28
  offAfterRender,
22
29
  offBeforeRender,
23
- onBeforeRender,
24
30
  onAfterRender,
31
+ onBeforeRender,
32
+ onStart,
25
33
  } from './core'
26
34
  export * from './types'
27
35
 
@@ -35,9 +43,74 @@ export const globals = {
35
43
  mousePos,
36
44
  }
37
45
 
46
+ /** The current camera. Often easier to use `useCamera` instead of this. */
38
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. */
39
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. */
40
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
+ }
41
114
 
42
115
  // CUSTOM RENDER SUPPORT
43
116
  // ====================
package/src/types.ts CHANGED
@@ -145,6 +145,8 @@ export declare namespace Lunch {
145
145
  dpr?: number
146
146
  ortho?: boolean
147
147
  orthographic?: boolean
148
+ // TODO: Why doesn't ConstructorParameters<THREE.WebGLRenderer> work here?
149
+ rendererArguments?: object
148
150
  rendererProperties?: Partial<THREE.WebGLRenderer>
149
151
  shadow?: ShadowSugar
150
152
  transparent?: boolean
@@ -23,7 +23,9 @@ export const isEventKey = (target: any): target is Lunch.EventKey => {
23
23
  ].includes(target)
24
24
  }
25
25
 
26
- export const isLunchboxComponent = (node: any): node is Lunch.LunchboxComponent => {
26
+ export const isLunchboxComponent = (
27
+ node: any
28
+ ): node is Lunch.LunchboxComponent => {
27
29
  return node?.$el && node?.$el?.hasOwnProperty?.('instance')
28
30
  }
29
31
 
@@ -35,10 +37,12 @@ export const isLunchboxDomComponent = (node: any): node is Lunch.DomMeta => {
35
37
  return lunchboxDomComponentNames.includes(typeToCheck ?? '')
36
38
  }
37
39
 
38
- export const isLunchboxStandardNode = (node: any): node is Lunch.StandardMeta => {
40
+ export const isLunchboxStandardNode = (
41
+ node: any
42
+ ): node is Lunch.StandardMeta => {
39
43
  return node?.metaType === 'standardMeta'
40
44
  }
41
45
 
42
46
  export const isLunchboxRootNode = (node: any): node is Lunch.RootMeta => {
43
47
  return node.isLunchboxRootNode
44
- }
48
+ }