@vctrl/hooks 0.25.1 → 1.0.0

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.
Files changed (46) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +110 -67
  3. package/index.cjs +1 -0
  4. package/index.d.ts +2 -1
  5. package/index.es.js +4 -4
  6. package/package.json +18 -10
  7. package/{chunk-CMqjfN_6.cjs → rolldown-runtime-BocRIvOZ.cjs} +1 -1
  8. package/use-export-model/use-export-model.d.ts +1 -1
  9. package/use-export-model-CORoAKbf.cjs +1 -0
  10. package/{use-export-model-efFsSWe-.js → use-export-model-DXesLypI.js} +1 -1
  11. package/use-export-model.cjs +1 -0
  12. package/use-export-model.es.js +1 -1
  13. package/use-load-model/error-helpers.d.ts +5 -0
  14. package/use-load-model/file-loaders.d.ts +11 -12
  15. package/use-load-model/index.d.ts +1 -1
  16. package/use-load-model/load-context.d.ts +17 -0
  17. package/use-load-model/optimizer-ingest.d.ts +8 -0
  18. package/use-load-model/optimizer-integration.d.ts +3 -4
  19. package/use-load-model/scene-loaders.d.ts +15 -0
  20. package/use-load-model/state.d.ts +14 -14
  21. package/use-load-model/types.d.ts +154 -131
  22. package/use-load-model/use-load-model.d.ts +12 -8
  23. package/use-load-model/utils/index.d.ts +1 -1
  24. package/use-load-model/utils/reconstruct-files.d.ts +27 -5
  25. package/use-load-model/utils/resolve-scene-payload.d.ts +9 -0
  26. package/use-load-model-CRDVzrPC.js +635 -0
  27. package/use-load-model-CZCi6bPM.cjs +1 -0
  28. package/use-load-model.cjs +1 -0
  29. package/use-load-model.es.js +1 -1
  30. package/use-optimize-model/index.d.ts +1 -0
  31. package/use-optimize-model/types.d.ts +3 -1
  32. package/use-optimize-model/use-optimize-model.d.ts +8 -5
  33. package/use-optimize-model-Fwew6qD8.cjs +1 -0
  34. package/{use-optimize-model-BCyM6An0.js → use-optimize-model-sNXkORNg.js} +93 -76
  35. package/use-optimize-model.cjs +1 -0
  36. package/use-optimize-model.es.js +2 -2
  37. package/utils/server-communication.d.ts +0 -5
  38. package/index.cjs.js +0 -1
  39. package/use-export-model-yQWV3O3H.cjs +0 -1
  40. package/use-export-model.cjs.js +0 -1
  41. package/use-load-model/event-system.d.ts +0 -7
  42. package/use-load-model-B8oqou4I.cjs +0 -1
  43. package/use-load-model-C88r8-HE.js +0 -585
  44. package/use-load-model.cjs.js +0 -1
  45. package/use-optimize-model-YAGNlhUN.cjs +0 -1
  46. package/use-optimize-model.cjs.js +0 -1
@@ -22,18 +22,21 @@ export interface WorkerResultMeta {
22
22
  * - Progress tracking and error handling
23
23
  * - Optimization reports with before/after metrics
24
24
  *
25
+ * Every method runs on the calling thread. To move the geometry passes off the main
26
+ * thread, run this hook inside a worker you own and hand the result back through
27
+ * `loadFromGlbBuffer(bytes, meta)`, as the Vectreal Platform does.
28
+ *
25
29
  * @example
26
30
  * const optimizer = useOptimizeModel()
27
31
  *
28
32
  * // Load a GLB into the optimizer
29
33
  * await optimizer.loadFromGlbBuffer(glbBytes)
30
34
  *
31
- * // Geometry optimizations run via Web Worker (use-optimization-process.ts)
32
- * // Texture compression runs browser-native via texturesOptimization()
33
- * await optimizer.texturesOptimization({ targetFormat: 'webp', quality: 0.8 })
35
+ * // quality is a 0-100 scale
36
+ * await optimizer.texturesOptimization({ targetFormat: 'webp', quality: 80 })
34
37
  *
35
- * // Sync result back to the Three.js viewer
36
- * await optimizer.applyOptimization()
38
+ * // Syncing the result back into the viewer is `applyOptimization`, which lives on
39
+ * // the object useLoadModel(optimizer) returns, not on this hook.
37
40
  *
38
41
  * @returns Object containing optimization methods, state, and report data
39
42
  */
@@ -0,0 +1 @@
1
+ let e=require("react"),t=require("@vctrl/core"),n=require("@vctrl/core/model-optimizer");var r={error:null,loading:!1,report:null},i=(e,t)=>{switch(t.type){case`LOAD_START`:return{...e,loading:!0,error:null};case`LOAD_SUCCESS`:return{...e,loading:!1,report:t.payload.report};case`LOAD_ERROR`:return{...e,loading:!1,error:t.payload};case`RESET`:return{...r};default:return e}};function a(e){return e?{verticesCount:e.stats.verticesCount.after??0,primitivesCount:e.stats.primitivesCount.after??0,meshesCount:e.stats.meshesCount.after??0,textureAssetCount:e.stats.texturesCount.after??0,materialsCount:e.stats.materialsCount.after??0,sceneBytes:e.optimizedSize??0}:{verticesCount:0,primitivesCount:0,meshesCount:0,textureAssetCount:0,materialsCount:0,sceneBytes:0}}var o=t=>{let[n,r]=(0,e.useState)(null),i=t.report;return(0,e.useEffect)(()=>{!n&&i&&r(i)},[n,i]),{info:(0,e.useMemo)(()=>{let e=a(n),t=a(i);return{initial:{verticesCount:e.verticesCount,primitivesCount:e.primitivesCount,meshesCount:e.meshesCount,texturesCount:e.textureAssetCount,sceneBytes:e.sceneBytes},optimized:{verticesCount:t.verticesCount,primitivesCount:t.primitivesCount,meshesCount:t.meshesCount,texturesCount:t.textureAssetCount,sceneBytes:t.sceneBytes},improvement:{verticesCount:e.verticesCount-t.verticesCount,primitivesCount:e.primitivesCount-t.primitivesCount,meshesCount:e.meshesCount-t.meshesCount,texturesCount:e.textureAssetCount-t.textureAssetCount,sceneBytes:e.sceneBytes-t.sceneBytes}}},[i,n]),reset:(0,e.useCallback)(()=>{r(null)},[])}};async function s(e,t){let n=new Blob([e]),r=await createImageBitmap(n),{width:i,height:a}=r;if(t.maxWidth||t.maxHeight){let e=t.maxWidth?t.maxWidth/i:1/0,n=t.maxHeight?t.maxHeight/a:1/0,r=Math.min(e,n,1);r<1&&(i=Math.round(i*r),a=Math.round(a*r))}let o=new OffscreenCanvas(i,a),s=o.getContext(`2d`);if(!s)throw r.close(),Error(`OffscreenCanvas 2D context unavailable`);s.drawImage(r,0,0,i,a),r.close();let c=t.format===`webp`?`image/webp`:t.format===`jpeg`?`image/jpeg`:`image/png`,l=await o.convertToBlob({type:c,quality:t.quality/100});return new Uint8Array(await l.arrayBuffer())}function c(){return function(e){let t=e instanceof Uint8Array?e:new Uint8Array(e),n=`webp`,r=80,i,a,o={resize(e,t,n){return i=e,a=t,o},webp(e){return n=`webp`,typeof e?.quality==`number`&&(r=e.quality),o},jpeg(e){return n=`jpeg`,typeof e?.quality==`number`&&(r=e.quality),o},png(e){return n=`png`,o},toFormat(e,t){return(e===`webp`||e===`jpeg`||e===`png`)&&(n=e),typeof t?.quality==`number`&&(r=t.quality),o},async toBuffer(){return s(t,{format:n,quality:r,maxWidth:i,maxHeight:a})},async metadata(){let e=new Blob([t]),n=await createImageBitmap(e),r={width:n.width,height:n.height,format:`unknown`};return n.close(),r}};return o}}var l=async(e,t)=>{e.hasModel()&&await e.compressTextures({...t,encoder:c()})},u=()=>{let[a,s]=(0,e.useReducer)(i,r),{report:c,error:u,loading:d}=a,{info:f,reset:p}=o(a),m=(0,e.useRef)(new n.ModelOptimizer);(0,e.useEffect)(()=>{let e=m.current;return()=>{e.reset()}},[]);let h=(0,e.useCallback)(async e=>{s({type:`LOAD_START`});try{let t=m.current;await t.loadFromThreeJS(e);let n=await t.getReport();s({type:`LOAD_SUCCESS`,payload:{report:n}})}catch(e){s({type:`LOAD_ERROR`,payload:e}),console.error(`Error loading model:`,e)}},[]),g=(0,e.useCallback)(async e=>{if(s({type:`LOAD_START`}),!e.gltfJson)throw Error(`Cannot optimize a scene loaded from a published GLB: it has no glTF document.`);try{let n=m.current,r=new Map;for(let n of Object.values(e.assetData??{}))for(let e of(0,t.buildAssetLookupKeys)(n.fileName))r.set(e,n);let i={},a=e.gltfJson,o=[...(a.images??[]).map(e=>e.uri).filter(e=>!!e&&!e.startsWith(`data:`)),...(a.buffers??[]).map(e=>e.uri).filter(e=>!!e&&!e.startsWith(`data:`))];for(let e of o){let n=(0,t.normalizeAssetUri)(e),a=n.split(`/`).pop()||n,o=r.get(e)??r.get(n)??r.get(a);o&&(i[e]=(0,t.toSerializedAssetBytes)(o))}let c={json:e.gltfJson,resources:i};await n.loadFromJSON(c);let l=await n.getReport();s({type:`LOAD_SUCCESS`,payload:{report:l}})}catch(e){throw s({type:`LOAD_ERROR`,payload:e}),console.error(`Error loading server scene into optimizer:`,e),e}},[]),_=(0,e.useCallback)(async(e,t,n)=>{s({type:`LOAD_START`});try{let r=n?.preserveBaseline?m.current.getBaseline():null;await m.current.loadFromBuffer(e),r&&m.current.setBaseline(r),m.current.setAppliedOptimizations(t?.appliedOptimizations??[]),m.current.setDracoReport(t?.dracoReport??null);let i=await m.current.getReport();s({type:`LOAD_SUCCESS`,payload:{report:i}})}catch(e){throw s({type:`LOAD_ERROR`,payload:e}),console.error(`Error loading GLB buffer into optimizer:`,e),e}},[]),v=(0,e.useCallback)(async(e,t)=>{s({type:`LOAD_START`});try{await m.current.loadFromGLTFWithAssets(e,t);let n=await m.current.getReport();s({type:`LOAD_SUCCESS`,payload:{report:n}})}catch(e){throw s({type:`LOAD_ERROR`,payload:e}),console.error(`Error loading GLTF with assets into optimizer:`,e),e}},[]),y=(0,e.useCallback)(async e=>{if(m.current.hasModel())try{await m.current.simplify(e);let t=await m.current.getReport();s({type:`LOAD_SUCCESS`,payload:{report:t}})}catch(e){console.error(`Simplification failed:`,e)}},[]),b=(0,e.useCallback)(async e=>{if(m.current.hasModel())try{await m.current.deduplicate(e);let t=await m.current.getReport();s({type:`LOAD_SUCCESS`,payload:{report:t}})}catch(e){console.error(`Deduplication failed:`,e)}},[]),x=(0,e.useCallback)(async e=>{if(m.current.hasModel())try{await m.current.quantize(e);let t=await m.current.getReport();s({type:`LOAD_SUCCESS`,payload:{report:t}})}catch(e){console.error(`Quantization failed:`,e)}},[]),S=(0,e.useCallback)(async e=>{if(m.current.hasModel())try{await m.current.optimizeNormals(e);let t=await m.current.getReport();s({type:`LOAD_SUCCESS`,payload:{report:t}})}catch(e){console.error(`Normals optimization failed:`,e)}},[]),C=(0,e.useCallback)(async e=>{try{await l(m.current,e);let t=await m.current.getReport();s({type:`LOAD_SUCCESS`,payload:{report:t}})}catch(e){throw console.error(`Texture optimization failed:`,e),e}},[]),w=(0,e.useCallback)(async()=>{if(!m.current.hasModel())return null;try{return await m.current.export()}catch(e){return console.error(`Error getting model binary:`,e),null}},[]),T=(0,e.useCallback)(()=>m.current.hasModel()?m.current.document:null,[]);return{load:h,loadFromServerSceneData:g,loadFromGlbBuffer:_,loadFromGLTFWithAssets:v,getModel:w,isReady:!!T()&&!d,_getDocument:T,reset:(0,e.useCallback)(()=>{m.current.reset(),s({type:`RESET`}),p()},[p]),error:u,loading:d,report:c,info:f,simplifyOptimization:y,dedupOptimization:b,quantizeOptimization:x,normalsOptimization:S,texturesOptimization:C}};Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return u}});
@@ -1,9 +1,8 @@
1
- import { buildAssetLookupKeys as e, normalizeAssetUri as t, toSerializedAssetBytes as n } from "@vctrl/core";
2
- import { useCallback as r, useEffect as i, useMemo as a, useReducer as o, useRef as s, useState as c } from "react";
1
+ import { useCallback as e, useEffect as t, useMemo as n, useReducer as r, useRef as i, useState as a } from "react";
2
+ import { buildAssetLookupKeys as o, normalizeAssetUri as s, toSerializedAssetBytes as c } from "@vctrl/core";
3
3
  import { ModelOptimizer as l } from "@vctrl/core/model-optimizer";
4
4
  //#region packages/hooks/src/use-optimize-model/state.ts
5
5
  var u = {
6
- info: null,
7
6
  error: null,
8
7
  loading: !1,
9
8
  report: null
@@ -32,11 +31,11 @@ var u = {
32
31
  //#region packages/hooks/src/use-optimize-model/use-calc-optimization-info.ts
33
32
  function f(e) {
34
33
  return e ? {
35
- verticesCount: e.stats.vertices.after ?? 0,
36
- primitivesCount: e.stats.triangles.after ?? 0,
37
- meshesCount: e.stats.meshes.after ?? 0,
34
+ verticesCount: e.stats.verticesCount.after ?? 0,
35
+ primitivesCount: e.stats.primitivesCount.after ?? 0,
36
+ meshesCount: e.stats.meshesCount.after ?? 0,
38
37
  textureAssetCount: e.stats.texturesCount.after ?? 0,
39
- materialsCount: e.stats.materials.after ?? 0,
38
+ materialsCount: e.stats.materialsCount.after ?? 0,
40
39
  sceneBytes: e.optimizedSize ?? 0
41
40
  } : {
42
41
  verticesCount: 0,
@@ -47,13 +46,13 @@ function f(e) {
47
46
  sceneBytes: 0
48
47
  };
49
48
  }
50
- var p = (e) => {
51
- let [t, n] = c(null), o = e.report;
52
- return i(() => {
53
- !t && o && n(o);
54
- }, [t, o]), {
55
- info: a(() => {
56
- let e = f(t), n = f(o);
49
+ var p = (r) => {
50
+ let [i, o] = a(null), s = r.report;
51
+ return t(() => {
52
+ !i && s && o(s);
53
+ }, [i, s]), {
54
+ info: n(() => {
55
+ let e = f(i), t = f(s);
57
56
  return {
58
57
  initial: {
59
58
  verticesCount: e.verticesCount,
@@ -63,23 +62,23 @@ var p = (e) => {
63
62
  sceneBytes: e.sceneBytes
64
63
  },
65
64
  optimized: {
66
- verticesCount: n.verticesCount,
67
- primitivesCount: n.primitivesCount,
68
- meshesCount: n.meshesCount,
69
- texturesCount: n.textureAssetCount,
70
- sceneBytes: n.sceneBytes
65
+ verticesCount: t.verticesCount,
66
+ primitivesCount: t.primitivesCount,
67
+ meshesCount: t.meshesCount,
68
+ texturesCount: t.textureAssetCount,
69
+ sceneBytes: t.sceneBytes
71
70
  },
72
71
  improvement: {
73
- verticesCount: e.verticesCount - n.verticesCount,
74
- primitivesCount: e.primitivesCount - n.primitivesCount,
75
- meshesCount: e.meshesCount - n.meshesCount,
76
- texturesCount: e.textureAssetCount - n.textureAssetCount,
77
- sceneBytes: e.sceneBytes - n.sceneBytes
72
+ verticesCount: e.verticesCount - t.verticesCount,
73
+ primitivesCount: e.primitivesCount - t.primitivesCount,
74
+ meshesCount: e.meshesCount - t.meshesCount,
75
+ texturesCount: e.textureAssetCount - t.textureAssetCount,
76
+ sceneBytes: e.sceneBytes - t.sceneBytes
78
77
  }
79
78
  };
80
- }, [o, t]),
81
- reset: r(() => {
82
- n(null);
79
+ }, [s, i]),
80
+ reset: e(() => {
81
+ o(null);
83
82
  }, [])
84
83
  };
85
84
  };
@@ -146,131 +145,149 @@ var g = async (e, t) => {
146
145
  encoder: h()
147
146
  });
148
147
  }, _ = () => {
149
- let [a, c] = o(d, u), { report: f, error: m, loading: h } = a, { info: _, reset: v } = p(a), y = s(new l());
150
- i(() => {
148
+ let [n, a] = r(d, u), { report: f, error: m, loading: h } = n, { info: _, reset: v } = p(n), y = i(new l());
149
+ t(() => {
151
150
  let e = y.current;
152
151
  return () => {
153
152
  e.reset();
154
153
  };
155
154
  }, []);
156
- let b = r(async (e) => {
157
- c({ type: "LOAD_START" });
155
+ let b = e(async (e) => {
156
+ a({ type: "LOAD_START" });
158
157
  try {
159
158
  let t = y.current;
160
- await t.loadFromThreeJS(e), c({
159
+ await t.loadFromThreeJS(e);
160
+ let n = await t.getReport();
161
+ a({
161
162
  type: "LOAD_SUCCESS",
162
- payload: { report: await t.getReport() }
163
+ payload: { report: n }
163
164
  });
164
165
  } catch (e) {
165
- c({
166
+ a({
166
167
  type: "LOAD_ERROR",
167
168
  payload: e
168
169
  }), console.error("Error loading model:", e);
169
170
  }
170
- }, []), x = r(async (r) => {
171
- c({ type: "LOAD_START" });
171
+ }, []), x = e(async (e) => {
172
+ if (a({ type: "LOAD_START" }), !e.gltfJson) throw Error("Cannot optimize a scene loaded from a published GLB: it has no glTF document.");
172
173
  try {
173
- let i = y.current, a = /* @__PURE__ */ new Map();
174
- for (let t of Object.values(r.assetData ?? {})) for (let n of e(t.fileName)) a.set(n, t);
175
- let o = {}, s = r.gltfJson, l = [...(s.images ?? []).map((e) => e.uri).filter((e) => !!e && !e.startsWith("data:")), ...(s.buffers ?? []).map((e) => e.uri).filter((e) => !!e && !e.startsWith("data:"))];
174
+ let t = y.current, n = /* @__PURE__ */ new Map();
175
+ for (let t of Object.values(e.assetData ?? {})) for (let e of o(t.fileName)) n.set(e, t);
176
+ let r = {}, i = e.gltfJson, l = [...(i.images ?? []).map((e) => e.uri).filter((e) => !!e && !e.startsWith("data:")), ...(i.buffers ?? []).map((e) => e.uri).filter((e) => !!e && !e.startsWith("data:"))];
176
177
  for (let e of l) {
177
- let r = t(e), i = r.split("/").pop() || r, s = a.get(e) ?? a.get(r) ?? a.get(i);
178
- s && (o[e] = n(s));
178
+ let t = s(e), i = t.split("/").pop() || t, a = n.get(e) ?? n.get(t) ?? n.get(i);
179
+ a && (r[e] = c(a));
179
180
  }
180
181
  let u = {
181
- json: r.gltfJson,
182
- resources: o
182
+ json: e.gltfJson,
183
+ resources: r
183
184
  };
184
- await i.loadFromJSON(u), c({
185
+ await t.loadFromJSON(u);
186
+ let d = await t.getReport();
187
+ a({
185
188
  type: "LOAD_SUCCESS",
186
- payload: { report: await i.getReport() }
189
+ payload: { report: d }
187
190
  });
188
191
  } catch (e) {
189
- throw c({
192
+ throw a({
190
193
  type: "LOAD_ERROR",
191
194
  payload: e
192
195
  }), console.error("Error loading server scene into optimizer:", e), e;
193
196
  }
194
- }, []), S = r(async (e, t, n) => {
195
- c({ type: "LOAD_START" });
197
+ }, []), S = e(async (e, t, n) => {
198
+ a({ type: "LOAD_START" });
196
199
  try {
197
200
  let r = n?.preserveBaseline ? y.current.getBaseline() : null;
198
- await y.current.loadFromBuffer(e), r && y.current.setBaseline(r), y.current.setAppliedOptimizations(t?.appliedOptimizations ?? []), y.current.setDracoReport(t?.dracoReport ?? null), c({
201
+ await y.current.loadFromBuffer(e), r && y.current.setBaseline(r), y.current.setAppliedOptimizations(t?.appliedOptimizations ?? []), y.current.setDracoReport(t?.dracoReport ?? null);
202
+ let i = await y.current.getReport();
203
+ a({
199
204
  type: "LOAD_SUCCESS",
200
- payload: { report: await y.current.getReport() }
205
+ payload: { report: i }
201
206
  });
202
207
  } catch (e) {
203
- throw c({
208
+ throw a({
204
209
  type: "LOAD_ERROR",
205
210
  payload: e
206
211
  }), console.error("Error loading GLB buffer into optimizer:", e), e;
207
212
  }
208
- }, []), C = r(async (e, t) => {
209
- c({ type: "LOAD_START" });
213
+ }, []), C = e(async (e, t) => {
214
+ a({ type: "LOAD_START" });
210
215
  try {
211
- await y.current.loadFromGLTFWithAssets(e, t), c({
216
+ await y.current.loadFromGLTFWithAssets(e, t);
217
+ let n = await y.current.getReport();
218
+ a({
212
219
  type: "LOAD_SUCCESS",
213
- payload: { report: await y.current.getReport() }
220
+ payload: { report: n }
214
221
  });
215
222
  } catch (e) {
216
- throw c({
223
+ throw a({
217
224
  type: "LOAD_ERROR",
218
225
  payload: e
219
226
  }), console.error("Error loading GLTF with assets into optimizer:", e), e;
220
227
  }
221
- }, []), w = r(async (e) => {
228
+ }, []), w = e(async (e) => {
222
229
  if (y.current.hasModel()) try {
223
- await y.current.simplify(e), c({
230
+ await y.current.simplify(e);
231
+ let t = await y.current.getReport();
232
+ a({
224
233
  type: "LOAD_SUCCESS",
225
- payload: { report: await y.current.getReport() }
234
+ payload: { report: t }
226
235
  });
227
236
  } catch (e) {
228
237
  console.error("Simplification failed:", e);
229
238
  }
230
- }, []), T = r(async (e) => {
239
+ }, []), T = e(async (e) => {
231
240
  if (y.current.hasModel()) try {
232
- await y.current.deduplicate(e), c({
241
+ await y.current.deduplicate(e);
242
+ let t = await y.current.getReport();
243
+ a({
233
244
  type: "LOAD_SUCCESS",
234
- payload: { report: await y.current.getReport() }
245
+ payload: { report: t }
235
246
  });
236
247
  } catch (e) {
237
248
  console.error("Deduplication failed:", e);
238
249
  }
239
- }, []), E = r(async (e) => {
250
+ }, []), E = e(async (e) => {
240
251
  if (y.current.hasModel()) try {
241
- await y.current.quantize(e), c({
252
+ await y.current.quantize(e);
253
+ let t = await y.current.getReport();
254
+ a({
242
255
  type: "LOAD_SUCCESS",
243
- payload: { report: await y.current.getReport() }
256
+ payload: { report: t }
244
257
  });
245
258
  } catch (e) {
246
259
  console.error("Quantization failed:", e);
247
260
  }
248
- }, []), D = r(async (e) => {
261
+ }, []), D = e(async (e) => {
249
262
  if (y.current.hasModel()) try {
250
- await y.current.optimizeNormals(e), c({
263
+ await y.current.optimizeNormals(e);
264
+ let t = await y.current.getReport();
265
+ a({
251
266
  type: "LOAD_SUCCESS",
252
- payload: { report: await y.current.getReport() }
267
+ payload: { report: t }
253
268
  });
254
269
  } catch (e) {
255
270
  console.error("Normals optimization failed:", e);
256
271
  }
257
- }, []), O = r(async (e) => {
272
+ }, []), O = e(async (e) => {
258
273
  try {
259
- await g(y.current, e), c({
274
+ await g(y.current, e);
275
+ let t = await y.current.getReport();
276
+ a({
260
277
  type: "LOAD_SUCCESS",
261
- payload: { report: await y.current.getReport() }
278
+ payload: { report: t }
262
279
  });
263
280
  } catch (e) {
264
281
  throw console.error("Texture optimization failed:", e), e;
265
282
  }
266
- }, []), k = r(async () => {
283
+ }, []), k = e(async () => {
267
284
  if (!y.current.hasModel()) return null;
268
285
  try {
269
286
  return await y.current.export();
270
287
  } catch (e) {
271
288
  return console.error("Error getting model binary:", e), null;
272
289
  }
273
- }, []), A = r(() => y.current.hasModel() ? y.current.document : null, []);
290
+ }, []), A = e(() => y.current.hasModel() ? y.current.document : null, []);
274
291
  return {
275
292
  load: b,
276
293
  loadFromServerSceneData: x,
@@ -279,8 +296,8 @@ var g = async (e, t) => {
279
296
  getModel: k,
280
297
  isReady: !!A() && !h,
281
298
  _getDocument: A,
282
- reset: r(() => {
283
- y.current.reset(), c({ type: "RESET" }), v();
299
+ reset: e(() => {
300
+ y.current.reset(), a({ type: "RESET" }), v();
284
301
  }, [v]),
285
302
  error: m,
286
303
  loading: h,
@@ -294,4 +311,4 @@ var g = async (e, t) => {
294
311
  };
295
312
  };
296
313
  //#endregion
297
- export { _ as t };
314
+ export { h as n, _ as t };
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./use-optimize-model-Fwew6qD8.cjs");exports.createBrowserTextureEncoder=e.n,exports.useOptimizeModel=e.t;
@@ -1,2 +1,2 @@
1
- import { t as e } from "./use-optimize-model-BCyM6An0.js";
2
- export { e as useOptimizeModel };
1
+ import { n as e, t } from "./use-optimize-model-sNXkORNg.js";
2
+ export { e as createBrowserTextureEncoder, t as useOptimizeModel };
@@ -48,11 +48,6 @@ export interface ServerRequestConfig {
48
48
  */
49
49
  export declare class ServerCommunicationService {
50
50
  private static extractApiData;
51
- /**
52
- * Creates default server options with required endpoint.
53
- * Merges provided options with defaults.
54
- */
55
- static createDefaultServerOptions(serverOptions?: ServerOptions): ServerOptions & Required<Pick<ServerOptions, 'endpoint'>>;
56
51
  /**
57
52
  * Creates request headers for server communication.
58
53
  * Includes authentication token if apiKey is provided.
package/index.cjs.js DELETED
@@ -1 +0,0 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./use-load-model-B8oqou4I.cjs"),t=require("./use-export-model-yQWV3O3H.cjs"),n=require("./use-optimize-model-YAGNlhUN.cjs");exports.reconstructGltfFiles=e.r,exports.useExportModel=t.t,exports.useLoadModel=e.t,exports.useOptimizeModel=n.t;
@@ -1 +0,0 @@
1
- const e=require("./chunk-CMqjfN_6.cjs");let t=require("react"),n=require("@vctrl/core/model-exporter"),r=require("file-saver");r=e.t(r,1);var i=(e,i)=>{let a=(0,t.useRef)(new n.ModelExporter),o=(0,t.useCallback)(async(t,n)=>{let o=t?.model;if(!o){console.error(`Scene not initialized`);return}try{let i=a.current,s=t.name.replace(/\.[^/.]+$/,``);if(n){let e=await i.exportThreeJSGLB(o,{});if(!(e.data instanceof Uint8Array))throw Error(`Expected GLB export to return Uint8Array`);r.default.saveAs(new Blob([new Uint8Array(e.data)]),`${s}.glb`)}else{let e=await i.exportThreeJSGLTF(o),t=await i.createZIPArchive(e,s);r.default.saveAs(new Blob([new Uint8Array(t)]),`${s}.zip`)}e&&e()}catch(e){console.error(`Export failed:`,e),i&&i(e)}},[e,i]),s=(0,t.useCallback)(async t=>{let n=t?.model;if(!n){console.error(`Scene not initialized`);return}try{let i=a.current,o=t.name.replace(/\.[^/.]+$/,``),s=await i.exportThreeJSUSDZ(n);r.default.saveAs(new Blob([new Uint8Array(s.data)]),`${o}.usdz`),e&&e()}catch(e){console.error(`USDZ export failed:`,e),i&&i(e)}},[e,i]),c=(0,t.useCallback)(async(t,n)=>{try{let i=a.current,o=n?.name.replace(/\.[^/.]+$/,``)||`model`,s=await i.exportDocumentGLBDraco(t);r.default.saveAs(new Blob([new Uint8Array(s.data)]),`${o}.glb`),e&&e()}catch(e){console.error(`Draco GLB export failed:`,e),i&&i(e)}},[e,i]);return{handleThreeGltfExport:o,handleDocumentGltfExport:(0,t.useCallback)(async function(t,n,o=!1,s=!0){try{let i=a.current,c=n?.name.replace(/\.[^/.]+$/,``)||`model`;if(o){let e=await i.exportDocumentGLB(t);r.default.saveAs(new Blob([new Uint8Array(e.data)]),`${c}.glb`)}else{let e=await i.exportDocumentGLTF(t);if(!s)return e;let n=await i.createZIPArchive(e,c);r.default.saveAs(new Blob([new Uint8Array(n)]),`${c}.zip`)}e&&e()}catch(e){console.error(`Export failed:`,e),i&&i(e)}},[i,e]),handleThreeUsdzExport:s,handleDocumentGlbDracoExport:c}};Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return i}});
@@ -1 +0,0 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./use-export-model-yQWV3O3H.cjs");exports.useExportModel=e.t;
@@ -1,7 +0,0 @@
1
- import { EventData, EventHandler, EventTypes } from './types';
2
- declare const eventSystem: {
3
- emit<T extends EventTypes>(event: T, data?: EventData[T]): void;
4
- on<T extends EventTypes>(event: T, handler?: EventHandler<T>): void;
5
- off<T extends EventTypes>(event: T, handler?: EventHandler<T>): void;
6
- };
7
- export default eventSystem;
@@ -1 +0,0 @@
1
- let e=require("@vctrl/core"),t=require("@vctrl/core/model-loader"),n=require("react");var r=({code:e,message:t,recoverable:n,source:r,cause:i,context:a})=>({code:e,message:t,recoverable:n,source:r,cause:i,context:a}),i=e=>e instanceof Error&&e.message?e.message:typeof e==`string`&&e.trim().length>0?e:`Unknown error`,a=(e,t,n)=>{let a=i(e);return r({code:a.includes(`missing required referenced assets`)?`missing_assets`:t,message:a,recoverable:!0,source:`local-upload`,cause:e,context:n})},o=(e,t)=>{let n=i(e),a=`server_load_failed`;return n.includes(`Server responded with 404`)?a=`not_found`:(n.includes(`Server responded with 402`)||n.includes(`Server responded with 403`))&&(a=`quota_exceeded`),r({code:a,message:n,recoverable:a!==`not_found`,source:`server-load`,cause:e,context:{sceneId:t}})},s={},c={emit(e,t){let n=s[e];console.debug(`Event emitted: ${e}`,t),n&&n.forEach(e=>e(t))},on(e,t){s[e]||(s[e]=[]),s[e].push(t)},off(e,t){let n=s[e];n&&(s[e]=n.filter(e=>e!==t))}},l=t=>{try{return(0,e.normalizeAssetUri)(t)}catch{return t}},u=e=>{let t=new Set,n=e,r=Array.isArray(n.images)?n.images:[];for(let e of r)typeof e.uri==`string`&&!e.uri.startsWith(`data:`)&&t.add(e.uri);let i=Array.isArray(n.buffers)?n.buffers:[];for(let e of i)typeof e.uri==`string`&&!e.uri.startsWith(`data:`)&&t.add(e.uri);return t},d=async(t,n)=>{let r=JSON.parse(await t.text()),i=u(r),a={},o=new Map,s=new Map,c=new Set,d=async e=>{let t=s.get(e);if(t)return t;let n=new Uint8Array(await e.arrayBuffer());return s.set(e,n),n};for(let t of n){for(let n of(0,e.buildAssetLookupKeys)(t.name))o.set(n,t);if(t.webkitRelativePath)for(let n of(0,e.buildAssetLookupKeys)(t.webkitRelativePath))o.set(n,t)}let f=[];for(let e of i){let t=l(e),n=t.split(`/`).pop()||t,r=o.get(e)||o.get(t)||o.get(n);if(!r){f.push(e);continue}c.add(r);let i=await d(r);a[t]={data:Array.from(i),fileName:t,mimeType:r.type||`application/octet-stream`}}if(f.length>0)throw Error(`Scene payload is missing required referenced assets: ${f.slice(0,5).join(`, `)}`);for(let[e,t]of n.entries()){if(c.has(t))continue;let n=l(t.name),r=n.split(`/`).pop()||n,i=await d(t);a[`extra-${e}-${r}`]={data:Array.from(i),fileName:r,mimeType:t.type||`application/octet-stream`}}return{gltfJson:r,assetData:a}},f=async(e,n,r,i,o)=>{try{let a={model:(await r.loadToThreeJS(e)).scene,type:n,name:e.name};if(o({type:`set-file`,payload:a}),o({type:`set-file-loading`,payload:!1}),i&&n!==t.ModelFileTypes.usdz){let t=new Uint8Array(await e.arrayBuffer());await i.loadFromGlbBuffer(t)}return a}catch(t){return console.error(`Error loading binary model:`,t),o({type:`set-file-loading`,payload:!1}),c.emit(`load-error`,a(t,`binary_load_failed`,{fileName:e.name,fileType:n,fileSize:e.size})),null}},p=async(e,n,r,i,o,s)=>{try{let{sourcePackageBytes:a,textureBytes:c}=await s(e,n),l=await r.loadGLTFWithAssetsToThreeJS(e,n),u={model:l.scene,type:t.ModelFileTypes.gltf,name:e.name,sourcePackageBytes:a,sourceTextureBytes:c};if(o({type:`set-file`,payload:u}),o({type:`set-file-loading`,payload:!1}),i)try{let t=await d(e,n);await i.loadFromServerSceneData(t)}catch(t){console.warn(`Failed to initialize optimizer from source GLTF payload; trying direct asset load.`,t);try{if(`loadFromGLTFWithAssets`in i){let t=new Uint8Array(await e.arrayBuffer()),r=new Map;for(let e of n){let t=new Uint8Array(await e.arrayBuffer());r.set(e.name,t),e.webkitRelativePath&&r.set(e.webkitRelativePath,t)}await i.loadFromGLTFWithAssets(t,r)}else throw Error(`loadFromGLTFWithAssets not available`)}catch(e){console.warn(`Direct asset load also failed; falling back to Three.js scene import. Texture names may be generic.`,e),await i.load(l.scene)}}return u}catch(t){return console.error(`Error loading GLTF model:`,t),console.error(`GLTF file:`,e.name),console.error(`Asset files:`,n.map(e=>e.name)),o({type:`set-file-loading`,payload:!1}),c.emit(`load-error`,a(t,`gltf_load_failed`,{fileName:e.name,fileSize:e.size,assetCount:n.length})),null}};function m(e,r,i,a){let o=(0,n.useCallback)(async(n,o)=>{if(!e){console.warn(`Optimizer is not available`);return}try{n&&await n(o);let s=await e.getModel();if(!s){console.warn(`No optimized model available after optimization`);return}let c=s instanceof Uint8Array?s.slice():new Uint8Array(s).slice(),l=new File([c],i?.name||`optimized_model.glb`,{type:`model/gltf-binary`});r({type:`set-file`,payload:{model:(await a.loadToThreeJS(l)).scene,type:t.ModelFileTypes.glb,name:l.name}})}catch(e){console.error(`Optimization failed:`,e)}},[e,a,i,r]);if(!e)return null;let s=e.isReady,c=!!i?.model&&!s;return{...e,isReady:s,isPreparing:c,applyOptimization:o,reset:e?.reset,error:e?.error,loading:e?.loading}}var h={file:null,isFileLoading:!1,progress:0,supportedFileTypes:[t.ModelFileTypes.gltf,t.ModelFileTypes.glb,t.ModelFileTypes.usdz]};function g(e,t){switch(t.type){case`set-file`:return{...e,file:t.payload};case`set-file-loading`:return{...e,isFileLoading:t.payload};case`set-progress`:return{...e,progress:t.payload};case`reset-state`:return{...h};default:return e}}async function _(e){let t=[];async function*n(e){let t=e;for await(let[,e]of t)e.kind===`file`?yield await e.getFile():e.kind===`directory`&&(yield*n(e))}for await(let r of n(e))t.push(r);return t}function v(t){let n=[];if(t.assetData&&typeof t.assetData==`object`)for(let[,r]of Object.entries(t.assetData)){let{fileName:t,mimeType:i}=r;if(t===e.PERSISTED_BAKE_FILENAME)continue;let a=(0,e.toSerializedAssetBytes)(r),o=new Uint8Array(a.byteLength);o.set(a);let s=new Blob([o],{type:i}),c=new File([s],t,{type:i});n.push(c)}let r=JSON.stringify(t.gltfJson),i=new Blob([r],{type:`model/gltf+json`}),a=t.meta?.name,o=a?`${a}.gltf`:`scene.gltf`;return[new File([i],o,{type:`model/gltf+json`}),...n]}var y=(t,n)=>{let r=(0,e.normalizeAssetUri)(t),i=r.split(`/`).pop()||r;for(let t of n){let n=(0,e.buildAssetLookupKeys)(t.fileName);if(n.has(r)||n.has(i))return t.size}return 0},b=e=>{let t=new Set,n=new Set,r=e,i=Array.isArray(r.images)?r.images:[];for(let e of i)typeof e.uri==`string`&&!e.uri.startsWith(`data:`)&&n.add(e.uri);let a=Array.isArray(r.buffers)?r.buffers:[];for(let e of a)typeof e.uri==`string`&&!e.uri.startsWith(`data:`)&&t.add(e.uri);return{bufferUris:t,imageUris:n}};async function x(e,t){let n=await e.text(),r;try{r=JSON.parse(n)}catch{return{sourcePackageBytes:e.size,textureBytes:0}}let i=t.map(e=>({fileName:e.name,size:e.size})),{bufferUris:a,imageUris:o}=b(r),s=Array.from(a).reduce((e,t)=>e+y(t,i),0),c=Array.from(o).reduce((e,t)=>e+y(t,i),0);return{sourcePackageBytes:e.size+s+c,textureBytes:c}}function S(t){let n=t.gltfJson,r=Object.values(t.assetData||{}).map(t=>({fileName:t.fileName,size:(0,e.getSerializedAssetByteSize)(t.data)})),i=new TextEncoder().encode(JSON.stringify(n)).byteLength,{bufferUris:a,imageUris:o}=b(n),s=Array.from(a).reduce((e,t)=>e+y(t,r),0),c=Array.from(o).reduce((e,t)=>e+y(t,r),0);return{sourcePackageBytes:i+s+c,textureBytes:c}}function C(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function w(t){let n=C(t.settings)?t.settings:{},r=(0,e.normalizeCameraSettings)(t.camera??n.camera);return{...n,bounds:t.bounds??n.bounds,camera:r,interactions:(0,e.normalizeSceneInteractions)(n.interactions,{camera:r}),controls:t.controls??n.controls,environment:t.environment??n.environment,shadows:t.shadows??n.shadows}}function T(e){let t=new Set,n=e,r=Array.isArray(n.images)?n.images:[];for(let e of r)typeof e.uri==`string`&&!e.uri.startsWith(`data:`)&&t.add(e.uri);let i=Array.isArray(n.buffers)?n.buffers:[];for(let e of i)typeof e.uri==`string`&&!e.uri.startsWith(`data:`)&&t.add(e.uri);return t}function E(t){for(let[n,r]of Object.entries(t)){if(!r||typeof r!=`object`)throw Error(`Scene payload contains invalid asset entry for ${n}`);if(typeof r.fileName!=`string`||r.fileName.trim().length===0)throw Error(`Scene payload asset ${n} is missing a valid fileName`);if(typeof r.mimeType!=`string`||r.mimeType.trim().length===0)throw Error(`Scene payload asset ${n} is missing a valid mimeType`);if(typeof r.data==`string`&&r.encoding===`base64`&&!(0,e.isValidBase64)(r.data))throw Error(`Scene payload asset ${r.fileName} has invalid base64 data`)}}function D(t,n){let r=T(t);if(r.size===0)return;let i=new Set;for(let t of Object.values(n))for(let n of(0,e.buildAssetLookupKeys)(t.fileName))i.add(n);let a=[];for(let t of r){let n=(0,e.normalizeAssetUri)(t),r=n.split(`/`).pop()||n;!i.has(t)&&!i.has(n)&&!i.has(r)&&a.push(t)}if(a.length>0)throw Error(`Scene payload is missing required referenced assets: ${a.slice(0,5).join(`, `)}`)}function O(e){if(!C(e.gltfJson))throw Error(`Scene payload is missing a valid glTF document`);let t=e.assetData??{};return E(t),D(e.gltfJson,t),{meta:e.meta,gltfJson:e.gltfJson,assetData:t,...w(e)}}async function k(e,t={}){let n=Object.entries(e);if(n.length===0)return t.onProgress?.(1),{};let r=n.every(([,e])=>e.byteSize!=null),i=r?n.reduce((e,[,t])=>e+(t.byteSize??0),0):0,a=0,o=0,s={},c=new AbortController;return await Promise.all(n.map(async([e,l])=>{try{let u=await fetch(l.url,{headers:t.headers,credentials:`same-origin`,signal:c.signal});if(!u.ok)throw c.abort(),Error(`Failed to fetch scene asset ${l.fileName}: ${u.status}`);s[e]={data:new Uint8Array(await u.arrayBuffer()),fileName:l.fileName,mimeType:l.mimeType},o+=1,a+=l.byteSize??0,t.onProgress?.(r&&i>0?a/i:o/n.length)}catch(e){throw c.abort(),e}})),s}async function A(e,t){try{let n=await fetch(e,{method:`GET`,headers:{Accept:`application/json`,...t}});if(!n.ok)return null;let r=await n.json(),i=r.data??r;return!i||typeof i!=`object`||!i.gltfJson||!i.assetRefs&&!i.assetData?null:i}catch{return null}}async function j(e,t,n){let r=new FormData;r.append(`action`,`get-scene-settings`),r.append(`sceneId`,n);let i=await fetch(e,{method:`POST`,headers:t,body:r});if(!i.ok)throw Error(`Server responded with ${i.status} ${i.statusText}`);let a=await i.json();return a.data??a}function M(r){let i=(0,n.useRef)(!1),a=(0,n.useRef)(null),[s,l]=(0,n.useReducer)(g,h),u=(0,n.useMemo)(()=>{let e=new t.ModelLoader;return e.onProgress(e=>{l({type:`set-progress`,payload:e.progress}),e.progress!==100&&c.emit(`load-progress`,e.progress)}),e},[]),d=(0,n.useCallback)(e=>{l({type:`set-progress`,payload:e}),c.emit(`load-progress`,e)},[]),y=(0,n.useCallback)(()=>{l({type:`reset-state`}),c.emit(`load-reset`)},[]),b=(0,n.useCallback)((e,t)=>e.find(e=>e.name.endsWith(`.`+t)),[]),C=(0,n.useCallback)(async e=>{if(e.length===0)return;let n=b(e,t.ModelFileTypes.gltf),o=b(e,t.ModelFileTypes.glb),s=b(e,t.ModelFileTypes.usdz),d=[n,o,s].filter(Boolean);if(d.length>1){c.emit(`multiple-models`,d);return}let m=e.filter(e=>e!==n&&e!==o&&e!==s),h=()=>{i.current=!0,a.current&&(c.emit(`load-complete`,a.current),i.current=!1)};if(n){let e=await p(n,m,u,r,l,x);e&&(a.current=e),h()}else if(o){let e=await f(o,t.ModelFileTypes.glb,u,r,l);e&&(a.current=e),h()}else if(s){let e=await f(s,t.ModelFileTypes.usdz,u,r,l);e&&(a.current=e),h()}else c.emit(`not-loaded-files`,e),l({type:`set-file-loading`,payload:!1})},[b,u,r]),w=(0,n.useCallback)(async e=>{let t=[];c.emit(`load-start`),l({type:`reset-state`}),l({type:`set-file-loading`,payload:!0}),d(0),i.current=!1;for(let n of e)if(n instanceof File)t.push(n);else if(`kind`in n&&n.kind===`directory`){let e=await _(n);t.push(...e)}await C(t)},[C,d]),T=(0,n.useCallback)(async n=>{let{sceneData:i,sceneId:o}=n;if(d(40),n.parseMode===`direct`&&i.gltfJson){let n=new Map;for(let t of Object.values(i.assetData??{}))t.fileName!==e.PERSISTED_BAKE_FILENAME&&n.set(t.fileName,(0,e.toSerializedAssetBytes)(t));let{sourcePackageBytes:r,textureBytes:s}=S(i);d(60);let c={model:(await u.parseGLTFJsonToThreeJS(i.gltfJson,n)).scene,type:t.ModelFileTypes.gltf,name:i.meta?.name||`scene`,sourcePackageBytes:r,sourceTextureBytes:s};return l({type:`set-file`,payload:c}),l({type:`set-file-loading`,payload:!1}),a.current=c,d(100),{file:c,sceneId:o,...i}}let s=v(i),{sourcePackageBytes:c,textureBytes:f}=S(i);d(60);let p=s[0],m=s.slice(1),h={model:(await u.loadGLTFWithAssetsToThreeJS(p,m)).scene,type:t.ModelFileTypes.gltf,name:p.name,sourcePackageBytes:c,sourceTextureBytes:f};return l({type:`set-file`,payload:h}),l({type:`set-file-loading`,payload:!1}),a.current=h,r&&await r.loadFromServerSceneData(i),d(100),{file:h,sceneId:o,...i}},[u,r,d]),E=(0,n.useCallback)(async e=>{let{sceneId:t,serverOptions:n,parseMode:r}=e;try{c.emit(`server-load-start`,t),l({type:`reset-state`}),l({type:`set-file-loading`,payload:!0}),d(0);let e=n?.endpoint??`/api/scenes/${t}`,i=n?.apiKey?{Authorization:`Bearer ${n.apiKey}`,...n.headers}:{...n?.headers},a=await A(e,i);if(a?.assetRefs&&!a.assetData){d(10);let e=await k(a.assetRefs,{headers:i,onProgress:e=>d(10+Math.round(e*30))});a={...a,assetData:e}}a||=await j(e,i,t);let o=await T({sceneId:t,sceneData:O(a),parseMode:r});return c.emit(`server-load-complete`,o),o}catch(e){console.error(`Server scene loading failed:`,e),l({type:`set-file-loading`,payload:!1});let n=o(e,t);throw c.emit(`server-load-error`,n),n}},[T,d]),D=m(r,l,s.file,u);return{...s,on:c.on,off:c.off,load:w,loadFromData:T,loadFromServer:E,reset:y,optimizer:D}}Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return k}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return v}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return M}});