murow 0.0.72 → 0.0.73

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.
@@ -1 +1 @@
1
- var e=Object.defineProperty;var o=Object.getOwnPropertyDescriptor;var s=Object.getOwnPropertyNames;var l=Object.prototype.hasOwnProperty;var p=(r,t)=>{for(var n in t)e(r,n,{get:t[n],enumerable:!0})},u=(r,t,n,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of s(t))!l.call(r,i)&&i!==n&&e(r,i,{get:()=>t[i],enumerable:!(a=o(t,i))||a.enumerable});return r};var m=r=>u(e({},"__esModule",{value:!0}),r);var h={};p(h,{RafDriver:()=>f});module.exports=m(h);class f{constructor(t){this.update=t;this.last=performance.now();this.running=!1;this.rafId=null;this.loop=()=>{if(!this.running)return;const t=performance.now(),n=(t-this.last)/1e3;this.last=t,this.update(n),requestAnimationFrame(this.loop)}}start(){this.running=!0,this.last=performance.now(),this.rafId=requestAnimationFrame(this.loop)}stop(){this.running=!1,this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null)}}
1
+ var s=Object.defineProperty;var l=Object.getOwnPropertyDescriptor;var o=Object.getOwnPropertyNames;var d=Object.prototype.hasOwnProperty;var u=(t,i)=>{for(var n in i)s(t,n,{get:i[n],enumerable:!0})},h=(t,i,n,r)=>{if(i&&typeof i=="object"||typeof i=="function")for(let e of o(i))!d.call(t,e)&&e!==n&&s(t,e,{get:()=>i[e],enumerable:!(r=l(i,e))||r.enumerable});return t};var m=t=>h(s({},"__esModule",{value:!0}),t);var p={};u(p,{RafDriver:()=>a});module.exports=m(p);class a{constructor(i){this.update=i;this.last=performance.now();this.running=!1;this.rafId=null;this.visibilityHandler=null;this.loop=()=>{if(!this.running)return;const i=performance.now(),n=i-this.last;this.last=i;const r=Math.min(n,a.MAX_DT_MS)/1e3;this.update(r),requestAnimationFrame(this.loop)}}static{this.MAX_DT_MS=250}start(){this.running=!0,this.last=performance.now(),typeof document<"u"&&!this.visibilityHandler&&(this.visibilityHandler=()=>{document.visibilityState==="visible"&&(this.last=performance.now())},document.addEventListener("visibilitychange",this.visibilityHandler)),this.rafId=requestAnimationFrame(this.loop)}stop(){this.running=!1,this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.visibilityHandler&&typeof document<"u"&&(document.removeEventListener("visibilitychange",this.visibilityHandler),this.visibilityHandler=null)}}
@@ -1 +1 @@
1
- var a=Object.defineProperty;var h=Object.getOwnPropertyDescriptor;var b=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var v=(c,i)=>{for(var e in i)a(c,e,{get:i[e],enumerable:!0})},f=(c,i,e,s)=>{if(i&&typeof i=="object"||typeof i=="function")for(let t of b(i))!k.call(c,t)&&t!==e&&a(c,t,{get:()=>i[t],enumerable:!(s=h(i,t))||s.enumerable});return c};var y=c=>f(a({},"__esModule",{value:!0}),c);var l={};v(l,{SparseBatcher:()=>n});module.exports=y(l);class n{constructor(i){this.BUCKET_INITIAL_SIZE=256;this.buckets=new Map;this.bucketSizes=new Uint32Array(n.MAX_BUCKETS);this.activeBuckets=new Uint16Array(n.MAX_BUCKETS);this.sortBuffer=new Uint16Array(n.MAX_BUCKETS);this.activeCount=0;this.capacity=i}static{this.MAX_LAYERS=256}static{this.MAX_SHEETS=64}static{this.MAX_BUCKETS=n.MAX_LAYERS*n.MAX_SHEETS}key(i,e){return i*n.MAX_SHEETS+e}add(i,e,s){const t=this.key(i,e);this.buckets.has(t)||(this.buckets.set(t,new Uint32Array(this.BUCKET_INITIAL_SIZE)),this.activeBuckets[this.activeCount++]=t);const u=this.buckets.get(t);if(this.bucketSizes[t]>=u.length){const r=new Uint32Array(u.length*2);r.set(u),this.buckets.set(t,r)}this.buckets.get(t)[this.bucketSizes[t]++]=s}remove(i,e,s){const t=this.key(i,e),u=this.bucketSizes[t];if(u===0)return;const o=this.buckets.get(t);for(let r=0;r<u;r++)if(o[r]===s){o[r]=o[u-1],this.bucketSizes[t]--;break}if(this.bucketSizes[t]===0){for(let r=0;r<this.activeCount;r++)if(this.activeBuckets[r]===t){this.activeBuckets[r]=this.activeBuckets[--this.activeCount];break}}}each(i){this.sortBuffer.set(this.activeBuckets.subarray(0,this.activeCount));for(let e=1;e<this.activeCount;e++){const s=this.sortBuffer[e];let t=e-1;for(;t>=0&&this.sortBuffer[t]>s;)this.sortBuffer[t+1]=this.sortBuffer[t--];this.sortBuffer[t+1]=s}for(let e=0;e<this.activeCount;e++){const s=this.sortBuffer[e],t=s%n.MAX_SHEETS,u=this.bucketSizes[s];i(t,this.buckets.get(s).subarray(0,u),u)}}getActiveCount(){return this.activeCount}getTotalCount(){let i=0;for(let e=0;e<this.activeCount;e++)i+=this.bucketSizes[this.activeBuckets[e]];return i}clear(){this.buckets.clear(),this.bucketSizes.fill(0),this.activeCount=0}}
1
+ var a=Object.defineProperty;var h=Object.getOwnPropertyDescriptor;var b=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var v=(c,i)=>{for(var e in i)a(c,e,{get:i[e],enumerable:!0})},f=(c,i,e,s)=>{if(i&&typeof i=="object"||typeof i=="function")for(let t of b(i))!k.call(c,t)&&t!==e&&a(c,t,{get:()=>i[t],enumerable:!(s=h(i,t))||s.enumerable});return c};var y=c=>f(a({},"__esModule",{value:!0}),c);var l={};v(l,{SparseBatcher:()=>n});module.exports=y(l);class n{constructor(i){this.BUCKET_INITIAL_SIZE=256;this.buckets=new Map;this.bucketSizes=new Uint32Array(n.MAX_BUCKETS);this.activeBuckets=new Uint16Array(n.MAX_BUCKETS);this.sortBuffer=new Uint16Array(n.MAX_BUCKETS);this.activeCount=0;this.capacity=i}static{this.MAX_LAYERS=256}static{this.MAX_SHEETS=64}static{this.MAX_BUCKETS=n.MAX_LAYERS*n.MAX_SHEETS}key(i,e){return i*n.MAX_SHEETS+e}add(i,e,s){const t=this.key(i,e);this.buckets.has(t)||this.buckets.set(t,new Uint32Array(this.BUCKET_INITIAL_SIZE)),this.bucketSizes[t]===0&&(this.activeBuckets[this.activeCount++]=t);const u=this.buckets.get(t);if(this.bucketSizes[t]>=u.length){const r=new Uint32Array(u.length*2);r.set(u),this.buckets.set(t,r)}this.buckets.get(t)[this.bucketSizes[t]++]=s}remove(i,e,s){const t=this.key(i,e),u=this.bucketSizes[t];if(u===0)return;const o=this.buckets.get(t);for(let r=0;r<u;r++)if(o[r]===s){o[r]=o[u-1],this.bucketSizes[t]--;break}if(this.bucketSizes[t]===0){for(let r=0;r<this.activeCount;r++)if(this.activeBuckets[r]===t){this.activeBuckets[r]=this.activeBuckets[--this.activeCount];break}}}each(i){this.sortBuffer.set(this.activeBuckets.subarray(0,this.activeCount));for(let e=1;e<this.activeCount;e++){const s=this.sortBuffer[e];let t=e-1;for(;t>=0&&this.sortBuffer[t]>s;)this.sortBuffer[t+1]=this.sortBuffer[t--];this.sortBuffer[t+1]=s}for(let e=0;e<this.activeCount;e++){const s=this.sortBuffer[e],t=s%n.MAX_SHEETS,u=this.bucketSizes[s];i(t,this.buckets.get(s).subarray(0,u),u)}}getActiveCount(){return this.activeCount}getTotalCount(){let i=0;for(let e=0;e<this.activeCount;e++)i+=this.bucketSizes[this.activeBuckets[e]];return i}clear(){this.buckets.clear(),this.bucketSizes.fill(0),this.activeCount=0}}
@@ -1 +1 @@
1
- var s=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var p=Object.prototype.hasOwnProperty;var l=(r,e)=>{for(var a in e)s(r,a,{get:e[a],enumerable:!0})},c=(r,e,a,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of d(e))!p.call(r,t)&&t!==a&&s(r,t,{get:()=>e[t],enumerable:!(n=m(e,t))||n.enumerable});return r};var D=r=>c(s({},"__esModule",{value:!0}),r);var x={};l(x,{Base3DRenderer:()=>i});module.exports=D(x);var o=require("./renderer");class i extends o.BaseRenderer{constructor(e,a){super(e,a),this.maxModels=a.maxModels??32}}
1
+ var n=Object.defineProperty;var o=Object.getOwnPropertyDescriptor;var c=Object.getOwnPropertyNames;var d=Object.prototype.hasOwnProperty;var p=(r,e)=>{for(var a in e)n(r,a,{get:e[a],enumerable:!0})},D=(r,e,a,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of c(e))!d.call(r,t)&&t!==a&&n(r,t,{get:()=>e[t],enumerable:!(s=o(e,t))||s.enumerable});return r};var i=r=>D(n({},"__esModule",{value:!0}),r);var R={};p(R,{Base3DRenderer:()=>x});module.exports=i(R);var m=require("./renderer");class x extends m.BaseRenderer{constructor(e,a){super(e,a),this.maxInstances=a.maxInstances??32}}
@@ -1 +1 @@
1
- var a=Object.defineProperty;var S=Object.getOwnPropertyDescriptor;var n=Object.getOwnPropertyNames;var o=Object.prototype.hasOwnProperty;var f=(r,e)=>{for(var s in e)a(r,s,{get:e[s],enumerable:!0})},b=(r,e,s,c)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of n(e))!o.call(r,t)&&t!==s&&a(r,t,{get:()=>e[t],enumerable:!(c=S(e,t))||c.enumerable});return r};var P=r=>b(a({},"__esModule",{value:!0}),r);var u={};f(u,{PrefabBucket:()=>i});module.exports=P(u);var d=require("./index"),p=require("./parsers");class i extends d.BasePrefabBucket{constructor(e){const s=e==="3d"?p.parsers3d:p.parsers2d;super(e,s)}add(e){return super.add(e)}addAll(e){return super.addAll(e)}get(e){return super.get(e)}}
1
+ var f=Object.defineProperty;var P=Object.getOwnPropertyDescriptor;var S=Object.getOwnPropertyNames;var g=Object.prototype.hasOwnProperty;var M=(t,e)=>{for(var s in e)f(t,s,{get:e[s],enumerable:!0})},k=(t,e,s,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of S(e))!g.call(t,r)&&r!==s&&f(t,r,{get:()=>e[r],enumerable:!(o=P(e,r))||o.enumerable});return t};var h=t=>k(f({},"__esModule",{value:!0}),t);var D={};M(D,{PrefabBucket:()=>w});module.exports=h(D);var c=require("./index"),d=require("./parsers"),u=require("../../core/generate-id");class w extends c.BasePrefabBucket{constructor(e){const s=e==="3d"?d.parsers3d:d.parsers2d;super(e,s)}add(e){return super.add(e)}addAll(e){return super.addAll(e)}get(e){return super.get(e)}addGroup(e,s){if(e.includes("."))throw new Error(`PrefabBucket.addGroup: group name '${e}' is invalid - '.' is reserved for group paths`);if(this.groups.has(e))throw new Error(`PrefabBucket.addGroup: duplicate group '${e}'`);const o=[],r=[];for(const n of s){const p=n.id;if(p!==void 0&&p.includes("."))throw new Error(`PrefabBucket.addGroup: part id '${p}' is invalid - '.' is reserved for group paths`);const a=p!==void 0?`${e}.${p}`:(0,u.generateId)({prefix:`${e}.`,size:e.length+1+8}),{offset:i,...b}=n;this.addUnchecked({...b,id:a}),o.push(a),r.push(i?{partId:a,offset:i}:{partId:a})}return super.add({type:"composite",id:e,parts:r}),this.groups.set(e,o),this}getGroup(e){const o=this.groups.get(e);if(!o)throw new Error(`PrefabBucket.getGroup: unknown group '${e}'`);const r=o.map(p=>this.get(p)),n=this;return{prefabs:r,asComposite:()=>n.get(e)}}}
@@ -1 +1 @@
1
- var n=Object.defineProperty;var o=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var p=Object.prototype.hasOwnProperty;var b=(s,e)=>{for(var r in e)n(s,r,{get:e[r],enumerable:!0})},c=(s,e,r,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of d(e))!p.call(s,a)&&a!==r&&n(s,a,{get:()=>e[a],enumerable:!(t=o(e,a))||t.enumerable});return s};var P=s=>c(n({},"__esModule",{value:!0}),s);var h={};b(h,{BasePrefabBucket:()=>u});module.exports=P(h);var f=require("../../core/events");class u{constructor(e,r={}){this.pending=[];this.pendingIds=new Set;this.prefabs=null;this.mode=e,this.parsers=r,this.events=new f.EventSystem({events:["clips-changed"]})}add(e){if(this.prefabs)throw new Error("PrefabBucket: cannot add after load() \u2014 bucket is frozen");if(this.pendingIds.has(e.id))throw new Error(`PrefabBucket: duplicate id '${e.id}'`);return this.pending.push(e),this.pendingIds.add(e.id),this}addAll(e){if(this.prefabs)throw new Error("PrefabBucket: cannot add after load() \u2014 bucket is frozen");const r=new Set;for(const t of e){if(this.pendingIds.has(t.id)||r.has(t.id))throw new Error(`PrefabBucket: duplicate id '${t.id}'`);r.add(t.id)}for(const t of e)this.pending.push(t),this.pendingIds.add(t.id);return this}async load(){if(this.prefabs)return;const e={events:this.events},r=await Promise.all(this.pending.map(a=>{const i=this.parsers[a.type];if(!i)throw new Error(`PrefabBucket: no parser registered for type '${a.type}'`);return i(a,e)})),t=new Map;for(const a of r)t.set(a.id,a);this.prefabs=t,this.pending=[],this.pendingIds.clear()}get(e){if(!this.prefabs)throw new Error(`PrefabBucket: get('${e}') called before load()`);const r=this.prefabs.get(e);if(!r)throw new Error(`PrefabBucket: unknown prefab id '${e}'`);return r}get loaded(){return this.prefabs!==null}get size(){return this.prefabs?this.prefabs.size:this.pending.length}entries(){if(!this.prefabs)throw new Error("PrefabBucket: entries() called before load()");return Array.from(this.prefabs.values())}getAllByType(e){if(!this.prefabs)throw new Error(`PrefabBucket: getAllByType('${e}') called before load()`);const r=[];for(const t of this.prefabs.values())t.type===e&&r.push(t);return r}resetAnimations(){if(this.prefabs)for(const e of this.prefabs.values())e.resetAnimations?.()}}
1
+ var n=Object.defineProperty;var o=Object.getOwnPropertyDescriptor;var f=Object.getOwnPropertyNames;var p=Object.prototype.hasOwnProperty;var c=(s,e)=>{for(var t in e)n(s,t,{get:e[t],enumerable:!0})},b=(s,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of f(e))!p.call(s,a)&&a!==t&&n(s,a,{get:()=>e[a],enumerable:!(r=o(e,a))||r.enumerable});return s};var P=s=>b(n({},"__esModule",{value:!0}),s);var h={};c(h,{BasePrefabBucket:()=>u});module.exports=P(h);var d=require("../../core/events");class u{constructor(e,t={}){this.pending=[];this.pendingIds=new Set;this.prefabs=null;this.groups=new Map;this.mode=e,this.parsers=t,this.events=new d.EventSystem({events:["clips-changed"]})}add(e){if(e.id.includes("."))throw new Error(`PrefabBucket: id '${e.id}' is invalid - '.' is reserved for group paths`);return this.addUnchecked(e)}addUnchecked(e){if(this.prefabs)throw new Error("PrefabBucket: cannot add after load() - bucket is frozen");if(this.pendingIds.has(e.id))throw new Error(`PrefabBucket: duplicate id '${e.id}'`);return this.pending.push(e),this.pendingIds.add(e.id),this}addAll(e){if(this.prefabs)throw new Error("PrefabBucket: cannot add after load() \u2014 bucket is frozen");const t=new Set;for(const r of e){if(r.id.includes("."))throw new Error(`PrefabBucket: id '${r.id}' is invalid - '.' is reserved for group paths`);if(this.pendingIds.has(r.id)||t.has(r.id))throw new Error(`PrefabBucket: duplicate id '${r.id}'`);t.add(r.id)}for(const r of e)this.pending.push(r),this.pendingIds.add(r.id);return this}async load(){if(this.prefabs)return;const e={events:this.events},t=await Promise.all(this.pending.map(a=>{const i=this.parsers[a.type];if(!i)throw new Error(`PrefabBucket: no parser registered for type '${a.type}'`);return i(a,e)})),r=new Map;for(const a of t)r.set(a.id,a);this.prefabs=r,this.pending=[],this.pendingIds.clear()}get(e){if(!this.prefabs)throw new Error(`PrefabBucket: get('${e}') called before load()`);const t=this.prefabs.get(e);if(!t)throw new Error(`PrefabBucket: unknown prefab id '${e}'`);return t}get loaded(){return this.prefabs!==null}get size(){return this.prefabs?this.prefabs.size:this.pending.length}entries(){if(!this.prefabs)throw new Error("PrefabBucket: entries() called before load()");return Array.from(this.prefabs.values())}getAllByType(e){if(!this.prefabs)throw new Error(`PrefabBucket: getAllByType('${e}') called before load()`);const t=[];for(const r of this.prefabs.values())r.type===e&&t.push(r);return t}resetAnimations(){if(this.prefabs)for(const e of this.prefabs.values())e.resetAnimations?.()}}
@@ -1 +1 @@
1
- var u=Object.defineProperty;var b=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var C=(n,e)=>{for(var i in e)u(n,i,{get:e[i],enumerable:!0})},A=(n,e,i,h)=>{if(e&&typeof e=="object"||typeof e=="function")for(let p of v(e))!k.call(n,p)&&p!==i&&u(n,p,{get:()=>e[p],enumerable:!(h=b(e,p))||h.enumerable});return n};var D=n=>A(u({},"__esModule",{value:!0}),n);var x={};C(x,{parsers2d:()=>z,parsers3d:()=>S});module.exports=D(x);var y=require("../gltf/parser"),P=require("../spritesheet/parser");const S={gltf:async(n,e)=>{if(n.type!=="gltf")throw new Error("gltf parser given non-gltf spec");const i=await(0,y.parseGltf)(n.src,{animations:n.animations!==void 0?[...n.animations]:void 0,freezeAnimations:n.freezeAnimations===!0}),h=i.primitives.filter(d=>d.skinned).length,p=i.skin?.data.jointCount??0;let w=0;for(const d of i.primitives)w+=d.positions.length/3;const t={type:"gltf",id:n.id,parsed:i,skinnedPartCount:h,jointCount:p,totalVertexCount:w,metadata:n.metadata??{}},g=n.animations!==void 0?[...n.animations]:i.skin?.animClips.map(d=>d.name)??[];if(g.length>0){const d={};for(const f of g)d[f]=f;t.animations=d,t.animationList=g}if(n.freezeAnimations!==!0){t.loadAnimations=async f=>{const r=i.skin,m=i.source;if(!r||!m)throw new Error(`loadAnimations: prefab '${n.id}' has no source \u2014 was the model skinned and not frozen?`);const l=new Set(r.animClips.map(s=>s.name)),a=[];for(const s of f){if(l.has(s))continue;const o=m.decodeAnimation(s);o&&(r.animClips.push(o),l.add(s),a.push(s))}if(a.length>0){t.animationList||(t.animationList=[]),t.animations||(t.animations={});const s=t.animationList,o=t.animations;for(const c of a)s.push(c),o[c]=c;e.events.emit("clips-changed",{prefabId:n.id,added:a,removed:[]})}},t.unloadAnimations=f=>{const r=i.skin;if(!r)return;const m=new Set(f),l=[];if(r.animClips=r.animClips.filter(a=>m.has(a.name)?(l.push(a.name),!1):!0),l.length>0){const a=t.animationList;if(a)for(let o=a.length-1;o>=0;o--)m.has(a[o])&&a.splice(o,1);const s=t.animations;if(s)for(const o of l)delete s[o];e.events.emit("clips-changed",{prefabId:n.id,added:[],removed:l})}};const d=new Set(g);t.resetAnimations=()=>{const f=i.skin;if(!f)return;const r=[];for(const m of f.animClips)d.has(m.name)||r.push(m.name);r.length>0&&t.unloadAnimations(r)}}return t},grid:n=>{if(n.type!=="grid")throw new Error("grid parser given non-grid spec");return{type:"grid",id:n.id,size:n.size,step:n.step,lineWidth:n.lineWidth,metadata:n.metadata??{}}}},z={spritesheet:async(n,e)=>{if(n.type!=="spritesheet")throw new Error("spritesheet parser given non-spritesheet spec");const i=await(0,P.parseSpritesheet)({image:n.src,frameWidth:n.frameWidth,frameHeight:n.frameHeight,data:n.data});return{type:"spritesheet",id:n.id,parsed:i,frameCount:i.uvs.length,width:i.width,height:i.height,metadata:n.metadata??{}}}};
1
+ var u=Object.defineProperty;var P=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var C=(t,e)=>{for(var n in e)u(t,n,{get:e[n],enumerable:!0})},A=(t,e,n,c)=>{if(e&&typeof e=="object"||typeof e=="function")for(let p of v(e))!k.call(t,p)&&p!==n&&u(t,p,{get:()=>e[p],enumerable:!(c=P(e,p))||c.enumerable});return t};var D=t=>A(u({},"__esModule",{value:!0}),t);var E={};C(E,{parsers2d:()=>S,parsers3d:()=>z});module.exports=D(E);var b=require("../gltf/parser"),w=require("../spritesheet/parser");const z={gltf:async(t,e)=>{if(t.type!=="gltf")throw new Error("gltf parser given non-gltf spec");const n=await(0,b.parseGltf)(t.src,{animations:t.animations!==void 0?[...t.animations]:void 0,freezeAnimations:t.freezeAnimations===!0}),c=n.primitives.filter(d=>d.skinned).length,p=n.skin?.data.jointCount??0;let y=0;for(const d of n.primitives)y+=d.positions.length/3;const i={type:"gltf",id:t.id,parsed:n,skinnedPartCount:c,jointCount:p,totalVertexCount:y,metadata:t.metadata??{}},g=t.animations!==void 0?[...t.animations]:n.skin?.animClips.map(d=>d.name)??[];if(g.length>0){const d={};for(const m of g)d[m]=m;i.animations=d,i.animationList=g}if(t.freezeAnimations!==!0){i.loadAnimations=async m=>{const r=n.skin,f=n.source;if(!r||!f)throw new Error(`loadAnimations: prefab '${t.id}' has no source \u2014 was the model skinned and not frozen?`);const h=new Set(r.animClips.map(s=>s.name)),a=[];for(const s of m){if(h.has(s))continue;const o=f.decodeAnimation(s);o&&(r.animClips.push(o),h.add(s),a.push(s))}if(a.length>0){i.animationList||(i.animationList=[]),i.animations||(i.animations={});const s=i.animationList,o=i.animations;for(const l of a)s.push(l),o[l]=l;e.events.emit("clips-changed",{prefabId:t.id,added:a,removed:[]})}},i.unloadAnimations=m=>{const r=n.skin;if(!r)return;const f=new Set(m),h=[];if(r.animClips=r.animClips.filter(a=>f.has(a.name)?(h.push(a.name),!1):!0),h.length>0){const a=i.animationList;if(a)for(let o=a.length-1;o>=0;o--)f.has(a[o])&&a.splice(o,1);const s=i.animations;if(s)for(const o of h)delete s[o];e.events.emit("clips-changed",{prefabId:t.id,added:[],removed:h})}};const d=new Set(g);i.resetAnimations=()=>{const m=n.skin;if(!m)return;const r=[];for(const f of m.animClips)d.has(f.name)||r.push(f.name);r.length>0&&i.unloadAnimations(r)}}return i},grid:t=>{if(t.type!=="grid")throw new Error("grid parser given non-grid spec");return{type:"grid",id:t.id,size:t.size,step:t.step,lineWidth:t.lineWidth,metadata:t.metadata??{}}},cube:t=>{if(t.type!=="cube")throw new Error("cube parser given non-cube spec");return{type:"cube",id:t.id,size:t.size??1,metadata:t.metadata??{}}},composite:t=>{if(t.type!=="composite")throw new Error("composite parser given non-composite spec");return{type:"composite",id:t.id,parts:t.parts,metadata:t.metadata??{}}}},S={spritesheet:async(t,e)=>{if(t.type!=="spritesheet")throw new Error("spritesheet parser given non-spritesheet spec");const n=await(0,w.parseSpritesheet)({image:t.src,frameWidth:t.frameWidth,frameHeight:t.frameHeight,data:t.data});return{type:"spritesheet",id:t.id,parsed:n,frameCount:n.uvs.length,width:n.width,height:n.height,metadata:t.metadata??{}}}};
@@ -1 +1 @@
1
- var a=Object.defineProperty;var o=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var s=Object.prototype.hasOwnProperty;var l=(n,e,i,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of d(e))!s.call(n,t)&&t!==i&&a(n,t,{get:()=>e[t],enumerable:!(r=o(e,t))||r.enumerable});return n};var p=n=>l(a({},"__esModule",{value:!0}),n);var y={};module.exports=p(y);
1
+ var a=Object.defineProperty;var i=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var s=Object.prototype.hasOwnProperty;var l=(n,e,o,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of d(e))!s.call(n,t)&&t!==o&&a(n,t,{get:()=>e[t],enumerable:!(r=i(e,t))||r.enumerable});return n};var p=n=>l(a({},"__esModule",{value:!0}),n);var y={};module.exports=p(y);
@@ -1 +1 @@
1
- class a{constructor(t){this.update=t;this.last=performance.now();this.running=!1;this.rafId=null;this.loop=()=>{if(!this.running)return;const t=performance.now(),r=(t-this.last)/1e3;this.last=t,this.update(r),requestAnimationFrame(this.loop)}}start(){this.running=!0,this.last=performance.now(),this.rafId=requestAnimationFrame(this.loop)}stop(){this.running=!1,this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null)}}export{a as RafDriver};
1
+ class t{constructor(i){this.update=i;this.last=performance.now();this.running=!1;this.rafId=null;this.visibilityHandler=null;this.loop=()=>{if(!this.running)return;const i=performance.now(),n=i-this.last;this.last=i;const e=Math.min(n,t.MAX_DT_MS)/1e3;this.update(e),requestAnimationFrame(this.loop)}}static{this.MAX_DT_MS=250}start(){this.running=!0,this.last=performance.now(),typeof document<"u"&&!this.visibilityHandler&&(this.visibilityHandler=()=>{document.visibilityState==="visible"&&(this.last=performance.now())},document.addEventListener("visibilitychange",this.visibilityHandler)),this.rafId=requestAnimationFrame(this.loop)}stop(){this.running=!1,this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.visibilityHandler&&typeof document<"u"&&(document.removeEventListener("visibilitychange",this.visibilityHandler),this.visibilityHandler=null)}}export{t as RafDriver};
@@ -1 +1 @@
1
- class n{constructor(i){this.BUCKET_INITIAL_SIZE=256;this.buckets=new Map;this.bucketSizes=new Uint32Array(n.MAX_BUCKETS);this.activeBuckets=new Uint16Array(n.MAX_BUCKETS);this.sortBuffer=new Uint16Array(n.MAX_BUCKETS);this.activeCount=0;this.capacity=i}static{this.MAX_LAYERS=256}static{this.MAX_SHEETS=64}static{this.MAX_BUCKETS=n.MAX_LAYERS*n.MAX_SHEETS}key(i,e){return i*n.MAX_SHEETS+e}add(i,e,r){const t=this.key(i,e);this.buckets.has(t)||(this.buckets.set(t,new Uint32Array(this.BUCKET_INITIAL_SIZE)),this.activeBuckets[this.activeCount++]=t);const u=this.buckets.get(t);if(this.bucketSizes[t]>=u.length){const s=new Uint32Array(u.length*2);s.set(u),this.buckets.set(t,s)}this.buckets.get(t)[this.bucketSizes[t]++]=r}remove(i,e,r){const t=this.key(i,e),u=this.bucketSizes[t];if(u===0)return;const c=this.buckets.get(t);for(let s=0;s<u;s++)if(c[s]===r){c[s]=c[u-1],this.bucketSizes[t]--;break}if(this.bucketSizes[t]===0){for(let s=0;s<this.activeCount;s++)if(this.activeBuckets[s]===t){this.activeBuckets[s]=this.activeBuckets[--this.activeCount];break}}}each(i){this.sortBuffer.set(this.activeBuckets.subarray(0,this.activeCount));for(let e=1;e<this.activeCount;e++){const r=this.sortBuffer[e];let t=e-1;for(;t>=0&&this.sortBuffer[t]>r;)this.sortBuffer[t+1]=this.sortBuffer[t--];this.sortBuffer[t+1]=r}for(let e=0;e<this.activeCount;e++){const r=this.sortBuffer[e],t=r%n.MAX_SHEETS,u=this.bucketSizes[r];i(t,this.buckets.get(r).subarray(0,u),u)}}getActiveCount(){return this.activeCount}getTotalCount(){let i=0;for(let e=0;e<this.activeCount;e++)i+=this.bucketSizes[this.activeBuckets[e]];return i}clear(){this.buckets.clear(),this.bucketSizes.fill(0),this.activeCount=0}}export{n as SparseBatcher};
1
+ class n{constructor(i){this.BUCKET_INITIAL_SIZE=256;this.buckets=new Map;this.bucketSizes=new Uint32Array(n.MAX_BUCKETS);this.activeBuckets=new Uint16Array(n.MAX_BUCKETS);this.sortBuffer=new Uint16Array(n.MAX_BUCKETS);this.activeCount=0;this.capacity=i}static{this.MAX_LAYERS=256}static{this.MAX_SHEETS=64}static{this.MAX_BUCKETS=n.MAX_LAYERS*n.MAX_SHEETS}key(i,e){return i*n.MAX_SHEETS+e}add(i,e,r){const t=this.key(i,e);this.buckets.has(t)||this.buckets.set(t,new Uint32Array(this.BUCKET_INITIAL_SIZE)),this.bucketSizes[t]===0&&(this.activeBuckets[this.activeCount++]=t);const u=this.buckets.get(t);if(this.bucketSizes[t]>=u.length){const s=new Uint32Array(u.length*2);s.set(u),this.buckets.set(t,s)}this.buckets.get(t)[this.bucketSizes[t]++]=r}remove(i,e,r){const t=this.key(i,e),u=this.bucketSizes[t];if(u===0)return;const c=this.buckets.get(t);for(let s=0;s<u;s++)if(c[s]===r){c[s]=c[u-1],this.bucketSizes[t]--;break}if(this.bucketSizes[t]===0){for(let s=0;s<this.activeCount;s++)if(this.activeBuckets[s]===t){this.activeBuckets[s]=this.activeBuckets[--this.activeCount];break}}}each(i){this.sortBuffer.set(this.activeBuckets.subarray(0,this.activeCount));for(let e=1;e<this.activeCount;e++){const r=this.sortBuffer[e];let t=e-1;for(;t>=0&&this.sortBuffer[t]>r;)this.sortBuffer[t+1]=this.sortBuffer[t--];this.sortBuffer[t+1]=r}for(let e=0;e<this.activeCount;e++){const r=this.sortBuffer[e],t=r%n.MAX_SHEETS,u=this.bucketSizes[r];i(t,this.buckets.get(r).subarray(0,u),u)}}getActiveCount(){return this.activeCount}getTotalCount(){let i=0;for(let e=0;e<this.activeCount;e++)i+=this.bucketSizes[this.activeBuckets[e]];return i}clear(){this.buckets.clear(),this.bucketSizes.fill(0),this.activeCount=0}}export{n as SparseBatcher};
@@ -1 +1 @@
1
- import{BaseRenderer as a}from"./renderer";class n extends a{constructor(r,e){super(r,e),this.maxModels=e.maxModels??32}}export{n as Base3DRenderer};
1
+ import{BaseRenderer as a}from"./renderer";class s extends a{constructor(r,e){super(r,e),this.maxInstances=e.maxInstances??32}}export{s as Base3DRenderer};
@@ -1 +1 @@
1
- import{BasePrefabBucket as s}from"./index";import{parsers2d as t,parsers3d as p}from"./parsers";class S extends s{constructor(e){const r=e==="3d"?p:t;super(e,r)}add(e){return super.add(e)}addAll(e){return super.addAll(e)}get(e){return super.get(e)}}export{S as PrefabBucket};
1
+ import{BasePrefabBucket as f}from"./index";import{parsers2d as i,parsers3d as c}from"./parsers";import{generateId as u}from"../../core/generate-id";class M extends f{constructor(e){const t=e==="3d"?c:i;super(e,t)}add(e){return super.add(e)}addAll(e){return super.addAll(e)}get(e){return super.get(e)}addGroup(e,t){if(e.includes("."))throw new Error(`PrefabBucket.addGroup: group name '${e}' is invalid - '.' is reserved for group paths`);if(this.groups.has(e))throw new Error(`PrefabBucket.addGroup: duplicate group '${e}'`);const s=[],o=[];for(const p of t){const r=p.id;if(r!==void 0&&r.includes("."))throw new Error(`PrefabBucket.addGroup: part id '${r}' is invalid - '.' is reserved for group paths`);const n=r!==void 0?`${e}.${r}`:u({prefix:`${e}.`,size:e.length+1+8}),{offset:a,...d}=p;this.addUnchecked({...d,id:n}),s.push(n),o.push(a?{partId:n,offset:a}:{partId:n})}return super.add({type:"composite",id:e,parts:o}),this.groups.set(e,s),this}getGroup(e){const s=this.groups.get(e);if(!s)throw new Error(`PrefabBucket.getGroup: unknown group '${e}'`);const o=s.map(r=>this.get(r)),p=this;return{prefabs:o,asComposite:()=>p.get(e)}}}export{M as PrefabBucket};
@@ -1 +1 @@
1
- import{EventSystem as n}from"../../core/events";class o{constructor(e,t={}){this.pending=[];this.pendingIds=new Set;this.prefabs=null;this.mode=e,this.parsers=t,this.events=new n({events:["clips-changed"]})}add(e){if(this.prefabs)throw new Error("PrefabBucket: cannot add after load() \u2014 bucket is frozen");if(this.pendingIds.has(e.id))throw new Error(`PrefabBucket: duplicate id '${e.id}'`);return this.pending.push(e),this.pendingIds.add(e.id),this}addAll(e){if(this.prefabs)throw new Error("PrefabBucket: cannot add after load() \u2014 bucket is frozen");const t=new Set;for(const r of e){if(this.pendingIds.has(r.id)||t.has(r.id))throw new Error(`PrefabBucket: duplicate id '${r.id}'`);t.add(r.id)}for(const r of e)this.pending.push(r),this.pendingIds.add(r.id);return this}async load(){if(this.prefabs)return;const e={events:this.events},t=await Promise.all(this.pending.map(a=>{const s=this.parsers[a.type];if(!s)throw new Error(`PrefabBucket: no parser registered for type '${a.type}'`);return s(a,e)})),r=new Map;for(const a of t)r.set(a.id,a);this.prefabs=r,this.pending=[],this.pendingIds.clear()}get(e){if(!this.prefabs)throw new Error(`PrefabBucket: get('${e}') called before load()`);const t=this.prefabs.get(e);if(!t)throw new Error(`PrefabBucket: unknown prefab id '${e}'`);return t}get loaded(){return this.prefabs!==null}get size(){return this.prefabs?this.prefabs.size:this.pending.length}entries(){if(!this.prefabs)throw new Error("PrefabBucket: entries() called before load()");return Array.from(this.prefabs.values())}getAllByType(e){if(!this.prefabs)throw new Error(`PrefabBucket: getAllByType('${e}') called before load()`);const t=[];for(const r of this.prefabs.values())r.type===e&&t.push(r);return t}resetAnimations(){if(this.prefabs)for(const e of this.prefabs.values())e.resetAnimations?.()}}export{o as BasePrefabBucket};
1
+ import{EventSystem as n}from"../../core/events";class o{constructor(e,t={}){this.pending=[];this.pendingIds=new Set;this.prefabs=null;this.groups=new Map;this.mode=e,this.parsers=t,this.events=new n({events:["clips-changed"]})}add(e){if(e.id.includes("."))throw new Error(`PrefabBucket: id '${e.id}' is invalid - '.' is reserved for group paths`);return this.addUnchecked(e)}addUnchecked(e){if(this.prefabs)throw new Error("PrefabBucket: cannot add after load() - bucket is frozen");if(this.pendingIds.has(e.id))throw new Error(`PrefabBucket: duplicate id '${e.id}'`);return this.pending.push(e),this.pendingIds.add(e.id),this}addAll(e){if(this.prefabs)throw new Error("PrefabBucket: cannot add after load() \u2014 bucket is frozen");const t=new Set;for(const r of e){if(r.id.includes("."))throw new Error(`PrefabBucket: id '${r.id}' is invalid - '.' is reserved for group paths`);if(this.pendingIds.has(r.id)||t.has(r.id))throw new Error(`PrefabBucket: duplicate id '${r.id}'`);t.add(r.id)}for(const r of e)this.pending.push(r),this.pendingIds.add(r.id);return this}async load(){if(this.prefabs)return;const e={events:this.events},t=await Promise.all(this.pending.map(a=>{const s=this.parsers[a.type];if(!s)throw new Error(`PrefabBucket: no parser registered for type '${a.type}'`);return s(a,e)})),r=new Map;for(const a of t)r.set(a.id,a);this.prefabs=r,this.pending=[],this.pendingIds.clear()}get(e){if(!this.prefabs)throw new Error(`PrefabBucket: get('${e}') called before load()`);const t=this.prefabs.get(e);if(!t)throw new Error(`PrefabBucket: unknown prefab id '${e}'`);return t}get loaded(){return this.prefabs!==null}get size(){return this.prefabs?this.prefabs.size:this.pending.length}entries(){if(!this.prefabs)throw new Error("PrefabBucket: entries() called before load()");return Array.from(this.prefabs.values())}getAllByType(e){if(!this.prefabs)throw new Error(`PrefabBucket: getAllByType('${e}') called before load()`);const t=[];for(const r of this.prefabs.values())r.type===e&&t.push(r);return t}resetAnimations(){if(this.prefabs)for(const e of this.prefabs.values())e.resetAnimations?.()}}export{o as BasePrefabBucket};
@@ -1 +1 @@
1
- import{parseGltf as w}from"../gltf/parser";import{parseSpritesheet as y}from"../spritesheet/parser";const v={gltf:async(n,l)=>{if(n.type!=="gltf")throw new Error("gltf parser given non-gltf spec");const t=await w(n.src,{animations:n.animations!==void 0?[...n.animations]:void 0,freezeAnimations:n.freezeAnimations===!0}),c=t.primitives.filter(o=>o.skinned).length,u=t.skin?.data.jointCount??0;let g=0;for(const o of t.primitives)g+=o.positions.length/3;const i={type:"gltf",id:n.id,parsed:t,skinnedPartCount:c,jointCount:u,totalVertexCount:g,metadata:n.metadata??{}},p=n.animations!==void 0?[...n.animations]:t.skin?.animClips.map(o=>o.name)??[];if(p.length>0){const o={};for(const d of p)o[d]=d;i.animations=o,i.animationList=p}if(n.freezeAnimations!==!0){i.loadAnimations=async d=>{const a=t.skin,f=t.source;if(!a||!f)throw new Error(`loadAnimations: prefab '${n.id}' has no source \u2014 was the model skinned and not frozen?`);const m=new Set(a.animClips.map(r=>r.name)),e=[];for(const r of d){if(m.has(r))continue;const s=f.decodeAnimation(r);s&&(a.animClips.push(s),m.add(r),e.push(r))}if(e.length>0){i.animationList||(i.animationList=[]),i.animations||(i.animations={});const r=i.animationList,s=i.animations;for(const h of e)r.push(h),s[h]=h;l.events.emit("clips-changed",{prefabId:n.id,added:e,removed:[]})}},i.unloadAnimations=d=>{const a=t.skin;if(!a)return;const f=new Set(d),m=[];if(a.animClips=a.animClips.filter(e=>f.has(e.name)?(m.push(e.name),!1):!0),m.length>0){const e=i.animationList;if(e)for(let s=e.length-1;s>=0;s--)f.has(e[s])&&e.splice(s,1);const r=i.animations;if(r)for(const s of m)delete r[s];l.events.emit("clips-changed",{prefabId:n.id,added:[],removed:m})}};const o=new Set(p);i.resetAnimations=()=>{const d=t.skin;if(!d)return;const a=[];for(const f of d.animClips)o.has(f.name)||a.push(f.name);a.length>0&&i.unloadAnimations(a)}}return i},grid:n=>{if(n.type!=="grid")throw new Error("grid parser given non-grid spec");return{type:"grid",id:n.id,size:n.size,step:n.step,lineWidth:n.lineWidth,metadata:n.metadata??{}}}},k={spritesheet:async(n,l)=>{if(n.type!=="spritesheet")throw new Error("spritesheet parser given non-spritesheet spec");const t=await y({image:n.src,frameWidth:n.frameWidth,frameHeight:n.frameHeight,data:n.data});return{type:"spritesheet",id:n.id,parsed:t,frameCount:t.uvs.length,width:t.width,height:t.height,metadata:n.metadata??{}}}};export{k as parsers2d,v as parsers3d};
1
+ import{parseGltf as y}from"../gltf/parser";import{parseSpritesheet as b}from"../spritesheet/parser";const v={gltf:async(t,h)=>{if(t.type!=="gltf")throw new Error("gltf parser given non-gltf spec");const i=await y(t.src,{animations:t.animations!==void 0?[...t.animations]:void 0,freezeAnimations:t.freezeAnimations===!0}),l=i.primitives.filter(o=>o.skinned).length,u=i.skin?.data.jointCount??0;let g=0;for(const o of i.primitives)g+=o.positions.length/3;const n={type:"gltf",id:t.id,parsed:i,skinnedPartCount:l,jointCount:u,totalVertexCount:g,metadata:t.metadata??{}},p=t.animations!==void 0?[...t.animations]:i.skin?.animClips.map(o=>o.name)??[];if(p.length>0){const o={};for(const d of p)o[d]=d;n.animations=o,n.animationList=p}if(t.freezeAnimations!==!0){n.loadAnimations=async d=>{const a=i.skin,m=i.source;if(!a||!m)throw new Error(`loadAnimations: prefab '${t.id}' has no source \u2014 was the model skinned and not frozen?`);const f=new Set(a.animClips.map(r=>r.name)),e=[];for(const r of d){if(f.has(r))continue;const s=m.decodeAnimation(r);s&&(a.animClips.push(s),f.add(r),e.push(r))}if(e.length>0){n.animationList||(n.animationList=[]),n.animations||(n.animations={});const r=n.animationList,s=n.animations;for(const c of e)r.push(c),s[c]=c;h.events.emit("clips-changed",{prefabId:t.id,added:e,removed:[]})}},n.unloadAnimations=d=>{const a=i.skin;if(!a)return;const m=new Set(d),f=[];if(a.animClips=a.animClips.filter(e=>m.has(e.name)?(f.push(e.name),!1):!0),f.length>0){const e=n.animationList;if(e)for(let s=e.length-1;s>=0;s--)m.has(e[s])&&e.splice(s,1);const r=n.animations;if(r)for(const s of f)delete r[s];h.events.emit("clips-changed",{prefabId:t.id,added:[],removed:f})}};const o=new Set(p);n.resetAnimations=()=>{const d=i.skin;if(!d)return;const a=[];for(const m of d.animClips)o.has(m.name)||a.push(m.name);a.length>0&&n.unloadAnimations(a)}}return n},grid:t=>{if(t.type!=="grid")throw new Error("grid parser given non-grid spec");return{type:"grid",id:t.id,size:t.size,step:t.step,lineWidth:t.lineWidth,metadata:t.metadata??{}}},cube:t=>{if(t.type!=="cube")throw new Error("cube parser given non-cube spec");return{type:"cube",id:t.id,size:t.size??1,metadata:t.metadata??{}}},composite:t=>{if(t.type!=="composite")throw new Error("composite parser given non-composite spec");return{type:"composite",id:t.id,parts:t.parts,metadata:t.metadata??{}}}},k={spritesheet:async(t,h)=>{if(t.type!=="spritesheet")throw new Error("spritesheet parser given non-spritesheet spec");const i=await b({image:t.src,frameWidth:t.frameWidth,frameHeight:t.frameHeight,data:t.data});return{type:"spritesheet",id:t.id,parsed:i,frameCount:i.uvs.length,width:i.width,height:i.height,metadata:t.metadata??{}}}};export{k as parsers2d,v as parsers3d};
@@ -18,6 +18,13 @@ import { LoopDriver } from "../driver";
18
18
  */
19
19
  export declare class RafDriver implements LoopDriver {
20
20
  update: (dt: number) => void;
21
+ /**
22
+ * Hard upper bound on a single frame's delta, in ms. Long pauses (tab
23
+ * backgrounded, breakpoint hit, browser throttling) deliver one huge
24
+ * frame on resume. Clamping keeps the engine from trying to "catch up"
25
+ * by replaying that lost time at high speed.
26
+ */
27
+ private static readonly MAX_DT_MS;
21
28
  /**
22
29
  * @param update - Callback invoked each frame with delta time in seconds
23
30
  */
@@ -25,10 +32,13 @@ export declare class RafDriver implements LoopDriver {
25
32
  private last;
26
33
  private running;
27
34
  private rafId;
35
+ private visibilityHandler;
28
36
  /**
29
37
  * Starts the game loop using requestAnimationFrame.
30
38
  *
31
- * Resets timing to prevent large initial delta.
39
+ * Resets timing to prevent large initial delta and installs a
40
+ * visibilitychange handler so the first frame after a hidden tab
41
+ * doesn't deliver a multi-second delta.
32
42
  */
33
43
  start(): void;
34
44
  /**
@@ -38,7 +48,8 @@ export declare class RafDriver implements LoopDriver {
38
48
  /**
39
49
  * Internal loop method that calculates delta time and schedules the next frame.
40
50
  *
41
- * Delta time is provided in seconds.
51
+ * Delta time is provided in seconds, clamped to `MAX_DT_MS` so a paused
52
+ * tab doesn't deliver a multi-second frame.
42
53
  */
43
54
  loop: () => void;
44
55
  }
@@ -4,7 +4,7 @@
4
4
  import { BaseRenderer } from "./renderer";
5
5
  import type { Camera3DState, Renderer3DOptions } from "../types";
6
6
  export declare abstract class Base3DRenderer extends BaseRenderer<Renderer3DOptions> {
7
- readonly maxModels: number;
7
+ readonly maxInstances: number;
8
8
  abstract readonly camera: Camera3DState;
9
9
  constructor(canvas: HTMLCanvasElement, options: Renderer3DOptions);
10
10
  }
@@ -17,7 +17,7 @@
17
17
  * ```
18
18
  */
19
19
  import { BasePrefabBucket, type StringOr } from './index';
20
- import type { Prefab2D, Prefab2DSpec, Prefab3D, Prefab3DSpec, PrefabFor } from './specs';
20
+ import type { CompositePrefab, PartOffset, Prefab2D, Prefab2DSpec, Prefab3D, Prefab3DSpec, PrefabFor } from './specs';
21
21
  type SpecForMode<M> = M extends '3d' ? Prefab3DSpec : Prefab2DSpec;
22
22
  type PrefabUnionForMode<M> = M extends '3d' ? Prefab3D : Prefab2D;
23
23
  /**
@@ -48,8 +48,48 @@ export declare class PrefabBucket<M extends '2d' | '3d' = '3d', Specs extends Re
48
48
  * Accepts any string at runtime but autocompletes known ids.
49
49
  */
50
50
  get<K extends keyof Specs & string, R = PrefabFor<Specs[K]>>(id: StringOr<K>): R;
51
+ /**
52
+ * Register a named group of part specs. Each part is added as a top-level
53
+ * prefab with id `<groupName>.<partId>` (or `<groupName>.<auto-hex>` when
54
+ * the part omitted `id`). A `composite` prefab is also created at id
55
+ * `groupName`, with the parts wired up via their `offset` for spawning.
56
+ *
57
+ * ```ts
58
+ * bucket.addGroup('campfire', [
59
+ * { type: 'gltf', id: 'logs', src: '/logs.glb' },
60
+ * { type: 'cube', id: 'flame', size: 0.3, offset: { position: [0, 0.3, 0] } },
61
+ * { type: 'cube', size: 0.5, offset: { position: [0, 0.8, 0] } }, // auto id
62
+ * ]);
63
+ *
64
+ * await bucket.load();
65
+ *
66
+ * bucket.get('campfire'); // composite (spawnable as one)
67
+ * bucket.get('campfire.logs'); // the part directly
68
+ * bucket.getGroup('campfire').prefabs; // both parts
69
+ * ```
70
+ */
71
+ addGroup(name: string, parts: readonly GroupPartInput<M>[]): this;
72
+ /**
73
+ * Look up a group registered via `addGroup`. Returns the parts in order
74
+ * and an `asComposite()` helper that returns the composite prefab created
75
+ * for the group (equivalent to `bucket.get(groupName)`).
76
+ */
77
+ getGroup(name: string): {
78
+ prefabs: Prefab3D[];
79
+ asComposite(): CompositePrefab;
80
+ };
51
81
  }
82
+ /**
83
+ * Part spec accepted by `addGroup`: a regular 3D spec with `id` optional
84
+ * (auto-generated when absent) and a new `offset` field.
85
+ */
86
+ type GroupPartInput<M extends '2d' | '3d'> = M extends '3d' ? {
87
+ [K in Prefab3DSpec as K['type']]: Omit<K, 'id'> & {
88
+ id?: string;
89
+ offset?: PartOffset;
90
+ };
91
+ }[Prefab3DSpec['type']] : never;
52
92
  /** Convenience aliases for explicit mode typing (e.g. function params). */
53
93
  export type PrefabBucket2D<Specs extends Record<string, Prefab2DSpec> = {}> = PrefabBucket<'2d', Specs>;
54
94
  export type PrefabBucket3D<Specs extends Record<string, Prefab3DSpec> = {}> = PrefabBucket<'3d', Specs>;
55
- export type { GltfPrefab, GltfSpec, GridPrefab, GridSpec, Prefab2D, Prefab2DSpec, Prefab3D, Prefab3DSpec, PrefabFor, SpritesheetPrefab, SpritesheetSpec, } from './specs';
95
+ export type { CompositePrefab, CompositeSpec, CubePrefab, CubeSpec, GltfPrefab, GltfSpec, GridPrefab, GridSpec, PartOffset, Prefab2D, Prefab2DSpec, Prefab3D, Prefab3DSpec, PrefabFor, SpritesheetPrefab, SpritesheetSpec, } from './specs';
@@ -58,17 +58,27 @@ export type PrefabParser<Spec extends PrefabSpecBase = PrefabSpecBase, Prefab ex
58
58
  export type PrefabParserMap<Spec extends PrefabSpecBase, Prefab extends PrefabBase> = Record<string, PrefabParser<Spec, Prefab>>;
59
59
  export declare class BasePrefabBucket<M extends PrefabMode = PrefabMode, Spec extends PrefabSpecBase = PrefabSpecBase, Prefab extends PrefabBase = PrefabBase, Specs extends Record<string, Spec> = {}> {
60
60
  readonly mode: M;
61
- /** Shared notification channel see `PrefabBucketEvents`. */
61
+ /** Shared notification channel - see `PrefabBucketEvents`. */
62
62
  readonly events: EventSystem<PrefabBucketEvents>;
63
63
  private parsers;
64
64
  private pending;
65
65
  private pendingIds;
66
66
  private prefabs;
67
+ /** `groupName -> ordered list of final part ids`. Populated by `addGroup` on subclasses. */
68
+ protected groups: Map<string, string[]>;
67
69
  constructor(mode: M, parsers?: PrefabParserMap<Spec, Prefab>);
68
- /** Add a single spec. Throws if id is a duplicate or if the bucket is already loaded. */
70
+ /** Add a single spec. Throws if id is a duplicate, contains '.', or if the bucket is already loaded. */
69
71
  add<const S extends Spec>(spec: S): BasePrefabBucket<M, Spec, Prefab, Specs & {
70
72
  [K in S['id']]: S;
71
73
  }>;
74
+ /**
75
+ * Internal add that skips the '.' validation. Used by `addGroup` on
76
+ * subclasses to register parts under group-path ids (e.g. `'campfire.logs'`)
77
+ * after the group itself has validated user input.
78
+ */
79
+ protected addUnchecked<const S extends Spec>(spec: S): BasePrefabBucket<M, Spec, Prefab, Specs & {
80
+ [K in S['id']]: S;
81
+ }>;
72
82
  /**
73
83
  * Add multiple specs. Validates the whole batch (ids unique, not loaded) before
74
84
  * committing, so the bucket never lands in a half-applied state.
@@ -53,7 +53,35 @@ export interface GridSpec {
53
53
  /** Optional user-defined sidecar data (scale, speed, gameplay hints, etc). */
54
54
  readonly metadata?: Record<string, unknown>;
55
55
  }
56
- export type Prefab3DSpec = GltfSpec | GridSpec;
56
+ /** Unit cube prefab centered at origin. Use the instance's `scale` to size it. */
57
+ export interface CubeSpec {
58
+ readonly type: 'cube';
59
+ readonly id: string;
60
+ /** Edge length. Defaults to 1. */
61
+ readonly size?: number;
62
+ readonly metadata?: Record<string, unknown>;
63
+ }
64
+ /** Local transform offset applied to a part inside a composite/group at spawn time. */
65
+ export interface PartOffset {
66
+ readonly position?: readonly [number, number, number];
67
+ readonly rotation?: readonly [number, number, number];
68
+ }
69
+ /**
70
+ * A prefab that spawns several other prefabs at fixed local offsets. Created
71
+ * by `bucket.addGroup(...)`; not typically written by hand. Spawning a
72
+ * composite instance spawns one child per part, with each part's offset
73
+ * composed onto the instance's position/rotation.
74
+ */
75
+ export interface CompositeSpec {
76
+ readonly type: 'composite';
77
+ readonly id: string;
78
+ readonly parts: readonly {
79
+ readonly partId: string;
80
+ readonly offset?: PartOffset;
81
+ }[];
82
+ readonly metadata?: Record<string, unknown>;
83
+ }
84
+ export type Prefab3DSpec = GltfSpec | GridSpec | CubeSpec | CompositeSpec;
57
85
  /**
58
86
  * Map a spec's `animations` tuple to a record-keyed-by-name. Used to type
59
87
  * `GltfPrefab.animations` so `prefab.animations.Run` is a string literal
@@ -134,7 +162,22 @@ export interface GridPrefab<S extends GridSpec = GridSpec> {
134
162
  readonly lineWidth: number;
135
163
  readonly metadata: MetadataOf<S>;
136
164
  }
137
- export type Prefab3D = GltfPrefab | GridPrefab;
165
+ export interface CubePrefab<S extends CubeSpec = CubeSpec> {
166
+ readonly type: 'cube';
167
+ readonly id: S['id'];
168
+ readonly size: number;
169
+ readonly metadata: MetadataOf<S>;
170
+ }
171
+ export interface CompositePrefab<S extends CompositeSpec = CompositeSpec> {
172
+ readonly type: 'composite';
173
+ readonly id: S['id'];
174
+ readonly parts: readonly {
175
+ readonly partId: string;
176
+ readonly offset?: PartOffset;
177
+ }[];
178
+ readonly metadata: MetadataOf<S>;
179
+ }
180
+ export type Prefab3D = GltfPrefab | GridPrefab | CubePrefab | CompositePrefab;
138
181
  export interface SpritesheetSpec {
139
182
  readonly type: 'spritesheet';
140
183
  readonly id: string;
@@ -162,5 +205,5 @@ export type Prefab2D = SpritesheetPrefab;
162
205
  * bucket's `get()` so `bucket.get('minion')` returns `GltfPrefab` directly,
163
206
  * not the discriminated union.
164
207
  */
165
- export type PrefabFor<S> = S extends GltfSpec ? GltfPrefab<S> : S extends GridSpec ? GridPrefab<S> : S extends SpritesheetSpec ? SpritesheetPrefab<S> : never;
208
+ export type PrefabFor<S> = S extends GltfSpec ? GltfPrefab<S> : S extends GridSpec ? GridPrefab<S> : S extends CubeSpec ? CubePrefab<S> : S extends CompositeSpec ? CompositePrefab<S> : S extends SpritesheetSpec ? SpritesheetPrefab<S> : never;
166
209
  export {};
@@ -14,10 +14,12 @@ export interface Renderer2DOptions extends RendererOptions {
14
14
  }
15
15
  export interface Renderer3DOptions extends RendererOptions {
16
16
  /**
17
- * Non-skinned instance budget. Optional when a prefab bucket is provided
18
- * the renderer will derive a sensible default from the bucket.
17
+ * How many instances you intend to spawn at once. Sizes both the
18
+ * non-skinned instance pool directly and seeds the default for the
19
+ * skinned-instance budget. Optional when a prefab bucket is provided -
20
+ * the renderer derives a sensible default from the bucket.
19
21
  */
20
- maxModels?: number;
22
+ maxInstances?: number;
21
23
  enableLighting?: boolean;
22
24
  }
23
25
  export interface Camera2DState {