@vctrl/hooks 0.23.0 → 0.25.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.
- package/CHANGELOG.md +49 -0
- package/README.md +8 -8
- package/index.cjs.js +1 -1
- package/index.es.js +2 -2
- package/package.json +4 -10
- package/use-export-model/use-export-model.d.ts +2 -0
- package/use-export-model-efFsSWe-.js +70 -0
- package/use-export-model-yQWV3O3H.cjs +1 -0
- package/use-export-model.cjs.js +1 -1
- package/use-export-model.es.js +1 -1
- package/use-optimize-model/use-optimize-model.d.ts +7 -2
- package/{use-optimize-model-Jie2FUHc.cjs → use-optimize-model-CD5zqTLK.cjs} +1 -1
- package/{use-optimize-model-CweunXiv.js → use-optimize-model-D16-4Zwm.js} +2 -2
- package/use-optimize-model.cjs.js +1 -1
- package/use-optimize-model.es.js +1 -1
- package/use-export-model-8JbUalyf.cjs +0 -1
- package/use-export-model-BXg6L_W5.js +0 -49
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,54 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.25.0](https://github.com/Vectreal/vectreal-platform/compare/hooks-v0.24.1...hooks-v0.25.0) (2026-08-04)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **packages:** match repository.url org casing for npm provenance ([455caa9](https://github.com/Vectreal/vectreal-platform/commit/455caa927ff7a668ea2b6af6a633d9057d69f203))
|
|
9
|
+
* **packages:** match repository.url org casing for npm provenance ([7289fdc](https://github.com/Vectreal/vectreal-platform/commit/7289fdcbc5678588acccaf5b650d62a4d70a7e10))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Dependencies
|
|
13
|
+
|
|
14
|
+
* The following workspace dependencies were updated
|
|
15
|
+
* dependencies
|
|
16
|
+
* @vctrl/core bumped to 0.25.0
|
|
17
|
+
|
|
18
|
+
## [0.24.1](https://github.com/Vectreal/vectreal-platform/compare/hooks-v0.24.0...hooks-v0.24.1) (2026-08-02)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Bug Fixes
|
|
22
|
+
|
|
23
|
+
* **ci:** resolve workspace protocol at publish time and publish via OIDC ([07e4b62](https://github.com/Vectreal/vectreal-platform/commit/07e4b628be16b61f3ddea9c91cc36c052d60bc8f))
|
|
24
|
+
* **ci:** resolve workspace protocol at publish time and publish via OIDC ([03b9305](https://github.com/Vectreal/vectreal-platform/commit/03b9305719b1cf67548f62730493ca6e3a84c823))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Dependencies
|
|
28
|
+
|
|
29
|
+
* The following workspace dependencies were updated
|
|
30
|
+
* dependencies
|
|
31
|
+
* @vctrl/core bumped to 0.24.1
|
|
32
|
+
|
|
33
|
+
## [0.24.0](https://github.com/Vectreal/vectreal-platform/compare/hooks-v0.23.0...hooks-v0.24.0) (2026-08-01)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
### Features
|
|
37
|
+
|
|
38
|
+
* **publisher:** draco-first optimization pipeline and panel rework ([#652](https://github.com/Vectreal/vectreal-platform/issues/652)) ([1161236](https://github.com/Vectreal/vectreal-platform/commit/1161236d0f4b2461b6b4af1ea4069fdf9da6d9c2))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
### Bug Fixes
|
|
42
|
+
|
|
43
|
+
* **publisher:** scene size inflation and stale model reloads ([#653](https://github.com/Vectreal/vectreal-platform/issues/653)) ([9c5da02](https://github.com/Vectreal/vectreal-platform/commit/9c5da02f3f524337b5d715ff5454ac4687fc73e3))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
### Dependencies
|
|
47
|
+
|
|
48
|
+
* The following workspace dependencies were updated
|
|
49
|
+
* dependencies
|
|
50
|
+
* @vctrl/core bumped to 0.24.0
|
|
51
|
+
|
|
3
52
|
## [0.23.0](https://github.com/Vectreal/vectreal-platform/compare/hooks-v0.22.0...hooks-v0.23.0) (2026-07-03)
|
|
4
53
|
|
|
5
54
|
|
package/README.md
CHANGED
|
@@ -120,7 +120,7 @@ export default function App() {
|
|
|
120
120
|
|
|
121
121
|
## `useOptimizeModel`
|
|
122
122
|
|
|
123
|
-
Runs mesh simplification, deduplication, quantization, and normals optimization using [glTF-Transform](https://gltf-transform.dev)
|
|
123
|
+
Runs mesh simplification, deduplication, quantization, and normals optimization using [glTF-Transform](https://gltf-transform.dev), plus texture compression via browser-native OffscreenCanvas encoding. Every pass runs on the calling thread: this package spawns no Web Worker. If you need the work off the main thread, run the hook inside a worker you own, as the Vectreal Platform does.
|
|
124
124
|
|
|
125
125
|
```tsx
|
|
126
126
|
import { useOptimizeModel } from '@vctrl/hooks/use-optimize-model'
|
|
@@ -152,7 +152,6 @@ function Optimizer() {
|
|
|
152
152
|
| ------------------------------------ | ------------------------------------------------------------------ | ---------------------------------------------------------------------- |
|
|
153
153
|
| `load(model)` | `(model: Object3D) => Promise<void>` | Load a Three.js scene into the optimizer |
|
|
154
154
|
| `loadFromServerSceneData(sceneData)` | `Promise<void>` | Initialize optimizer from a server scene payload |
|
|
155
|
-
| `applyOptimization(fn, opts?)` | `<T>(fn?: (opts?: T) => Promise<void>, opts?: T) => Promise<void>` | Apply optimization and sync the optimized model back into loader state |
|
|
156
155
|
| `simplifyOptimization(options?)` | `Promise<void>` | Simplify mesh geometry |
|
|
157
156
|
| `dedupOptimization(options?)` | `Promise<void>` | Deduplicate model data |
|
|
158
157
|
| `quantizeOptimization(options?)` | `Promise<void>` | Quantize vertex attributes |
|
|
@@ -162,6 +161,8 @@ function Optimizer() {
|
|
|
162
161
|
| `report` / `info` | Objects | Optimization metrics and derived stats |
|
|
163
162
|
| `loading` / `error` | State | Optimization status |
|
|
164
163
|
|
|
164
|
+
> `applyOptimization(fn, opts?)` is **not** returned by `useOptimizeModel` directly. It becomes available on the `optimizer` object returned by `useLoadModel(optimizer)` when you pass an optimizer instance in; it runs an optimization step and syncs the result back into loader state (see the example above).
|
|
165
|
+
|
|
165
166
|
### Optimization option types
|
|
166
167
|
|
|
167
168
|
`useOptimizeModel` methods map directly to `@vctrl/core/model-optimizer` option types:
|
|
@@ -180,7 +181,7 @@ Texture compression runs browser-native via `OffscreenCanvas`. No server call is
|
|
|
180
181
|
|
|
181
182
|
## `useExportModel`
|
|
182
183
|
|
|
183
|
-
Exports
|
|
184
|
+
Exports a `ModelFile` to a downloadable file. The hook takes optional `onSaved` and `onError` callbacks; the model itself is passed to the export method as an argument, not read from `ModelProvider` context.
|
|
184
185
|
|
|
185
186
|
```tsx
|
|
186
187
|
import { useExportModel } from '@vctrl/hooks/use-export-model'
|
|
@@ -218,11 +219,10 @@ function ExportButton({ file }: { file: ModelFile | null }) {
|
|
|
218
219
|
|
|
219
220
|
## Peer dependencies
|
|
220
221
|
|
|
221
|
-
| Package
|
|
222
|
-
|
|
|
223
|
-
| `react`
|
|
224
|
-
| `three`
|
|
225
|
-
| `@react-three/fiber` | `^9` |
|
|
222
|
+
| Package | Version |
|
|
223
|
+
| ------- | -------------- |
|
|
224
|
+
| `react` | `^18 \|\| ^19` |
|
|
225
|
+
| `three` | `^0.177` |
|
|
226
226
|
|
|
227
227
|
---
|
|
228
228
|
|
package/index.cjs.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./use-load-model-B8oqou4I.cjs"),t=require("./use-export-model-
|
|
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-CD5zqTLK.cjs");exports.reconstructGltfFiles=e.r,exports.useExportModel=t.t,exports.useLoadModel=e.t,exports.useOptimizeModel=n.t;
|
package/index.es.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { r as e, t } from "./use-load-model-C88r8-HE.js";
|
|
2
|
-
import { t as n } from "./use-export-model-
|
|
3
|
-
import { t as r } from "./use-optimize-model-
|
|
2
|
+
import { t as n } from "./use-export-model-efFsSWe-.js";
|
|
3
|
+
import { t as r } from "./use-optimize-model-D16-4Zwm.js";
|
|
4
4
|
export { e as reconstructGltfFiles, n as useExportModel, t as useLoadModel, r as useOptimizeModel };
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.
|
|
2
|
+
"version": "0.25.0",
|
|
3
3
|
"name": "@vctrl/hooks",
|
|
4
4
|
"description": "vctrl/hooks is a React hooks package designed to simplify 3D model loading and management within React applications. It's part of the vectreal-core ecosystem and is primarily used in the vctrl/viewer React component and the official website application.",
|
|
5
5
|
"bugs": {
|
|
6
|
-
"url": "https://github.com/
|
|
6
|
+
"url": "https://github.com/Vectreal/vectreal-platform/issues"
|
|
7
7
|
},
|
|
8
8
|
"homepage": "https://vectreal.com",
|
|
9
9
|
"keywords": [
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
],
|
|
26
26
|
"repository": {
|
|
27
27
|
"type": "git",
|
|
28
|
-
"url": "git+https://github.com/
|
|
28
|
+
"url": "git+https://github.com/Vectreal/vectreal-platform.git"
|
|
29
29
|
},
|
|
30
30
|
"license": "AGPL-3.0-only",
|
|
31
31
|
"types": "./index.d.ts",
|
|
@@ -62,13 +62,7 @@
|
|
|
62
62
|
"three": "^0.177.0"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@vctrl/core": "^0.
|
|
65
|
+
"@vctrl/core": "^0.25.0",
|
|
66
66
|
"file-saver": "^2.0.5"
|
|
67
|
-
},
|
|
68
|
-
"devDependencies": {
|
|
69
|
-
"@nx/vite": "22.5.0",
|
|
70
|
-
"@vitejs/plugin-react": "^6.0.0",
|
|
71
|
-
"vite": "^7.3.1",
|
|
72
|
-
"vite-plugin-dts": "4.5.3"
|
|
73
67
|
}
|
|
74
68
|
}
|
|
@@ -10,5 +10,7 @@ import { ModelFile } from '../use-load-model';
|
|
|
10
10
|
declare const useExportModel: (onSaved?: () => void, onError?: (error: Error) => void) => {
|
|
11
11
|
handleThreeGltfExport: (file: ModelFile | null, binary: boolean) => Promise<void>;
|
|
12
12
|
handleDocumentGltfExport: (jsonDocument: Document, file: ModelFile | null, binary?: boolean, download?: boolean) => Promise<import('../../../core/src/model-exporter/index.ts').GLTFExportResult | undefined>;
|
|
13
|
+
handleThreeUsdzExport: (file: ModelFile | null) => Promise<void>;
|
|
14
|
+
handleDocumentGlbDracoExport: (jsonDocument: Document, file: ModelFile | null) => Promise<void>;
|
|
13
15
|
};
|
|
14
16
|
export default useExportModel;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { useCallback as e, useRef as t } from "react";
|
|
2
|
+
import { ModelExporter as n } from "@vctrl/core/model-exporter";
|
|
3
|
+
import r from "file-saver";
|
|
4
|
+
//#region packages/hooks/src/use-export-model/use-export-model.ts
|
|
5
|
+
var i = (i, a) => {
|
|
6
|
+
let o = t(new n()), s = e(async (e, t) => {
|
|
7
|
+
let n = e?.model;
|
|
8
|
+
if (!n) {
|
|
9
|
+
console.error("Scene not initialized");
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
let a = o.current, s = e.name.replace(/\.[^/.]+$/, "");
|
|
14
|
+
if (t) {
|
|
15
|
+
let e = await a.exportThreeJSGLB(n, {});
|
|
16
|
+
if (!(e.data instanceof Uint8Array)) throw Error("Expected GLB export to return Uint8Array");
|
|
17
|
+
r.saveAs(new Blob([new Uint8Array(e.data)]), `${s}.glb`);
|
|
18
|
+
} else {
|
|
19
|
+
let e = await a.exportThreeJSGLTF(n), t = await a.createZIPArchive(e, s);
|
|
20
|
+
r.saveAs(new Blob([new Uint8Array(t)]), `${s}.zip`);
|
|
21
|
+
}
|
|
22
|
+
i && i();
|
|
23
|
+
} catch (e) {
|
|
24
|
+
console.error("Export failed:", e), a && a(e);
|
|
25
|
+
}
|
|
26
|
+
}, [i, a]), c = e(async (e) => {
|
|
27
|
+
let t = e?.model;
|
|
28
|
+
if (!t) {
|
|
29
|
+
console.error("Scene not initialized");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
let n = o.current, a = e.name.replace(/\.[^/.]+$/, ""), s = await n.exportThreeJSUSDZ(t);
|
|
34
|
+
r.saveAs(new Blob([new Uint8Array(s.data)]), `${a}.usdz`), i && i();
|
|
35
|
+
} catch (e) {
|
|
36
|
+
console.error("USDZ export failed:", e), a && a(e);
|
|
37
|
+
}
|
|
38
|
+
}, [i, a]), l = e(async (e, t) => {
|
|
39
|
+
try {
|
|
40
|
+
let n = o.current, a = t?.name.replace(/\.[^/.]+$/, "") || "model", s = await n.exportDocumentGLBDraco(e);
|
|
41
|
+
r.saveAs(new Blob([new Uint8Array(s.data)]), `${a}.glb`), i && i();
|
|
42
|
+
} catch (e) {
|
|
43
|
+
console.error("Draco GLB export failed:", e), a && a(e);
|
|
44
|
+
}
|
|
45
|
+
}, [i, a]);
|
|
46
|
+
return {
|
|
47
|
+
handleThreeGltfExport: s,
|
|
48
|
+
handleDocumentGltfExport: e(async function(e, t, n = !1, s = !0) {
|
|
49
|
+
try {
|
|
50
|
+
let a = o.current, c = t?.name.replace(/\.[^/.]+$/, "") || "model";
|
|
51
|
+
if (n) {
|
|
52
|
+
let t = await a.exportDocumentGLB(e);
|
|
53
|
+
r.saveAs(new Blob([new Uint8Array(t.data)]), `${c}.glb`);
|
|
54
|
+
} else {
|
|
55
|
+
let t = await a.exportDocumentGLTF(e);
|
|
56
|
+
if (!s) return t;
|
|
57
|
+
let n = await a.createZIPArchive(t, c);
|
|
58
|
+
r.saveAs(new Blob([new Uint8Array(n)]), `${c}.zip`);
|
|
59
|
+
}
|
|
60
|
+
i && i();
|
|
61
|
+
} catch (e) {
|
|
62
|
+
console.error("Export failed:", e), a && a(e);
|
|
63
|
+
}
|
|
64
|
+
}, [a, i]),
|
|
65
|
+
handleThreeUsdzExport: c,
|
|
66
|
+
handleDocumentGlbDracoExport: l
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
//#endregion
|
|
70
|
+
export { i as t };
|
|
@@ -0,0 +1 @@
|
|
|
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}});
|
package/use-export-model.cjs.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./use-export-model-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./use-export-model-yQWV3O3H.cjs");exports.useExportModel=e.t;
|
package/use-export-model.es.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as e } from "./use-export-model-
|
|
1
|
+
import { t as e } from "./use-export-model-efFsSWe-.js";
|
|
2
2
|
export { e as useExportModel };
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { DedupOptions, NormalsOptions, QuantizeOptions, SimplifyOptions, TextureCompressOptions } from '../../../core/src/model-optimizer/index.ts';
|
|
2
2
|
import { Object3D } from 'three';
|
|
3
|
-
import { ServerSceneData } from '../../../core/src/index.ts';
|
|
3
|
+
import { DracoCompressionReport, ServerSceneData } from '../../../core/src/index.ts';
|
|
4
|
+
/** Optimizer state produced inside the geometry Web Worker. */
|
|
5
|
+
export interface WorkerResultMeta {
|
|
6
|
+
appliedOptimizations: string[];
|
|
7
|
+
dracoReport?: DracoCompressionReport;
|
|
8
|
+
}
|
|
4
9
|
/**
|
|
5
10
|
* Custom React hook for optimizing 3D models using the ModelOptimizer from @vctrl/core.
|
|
6
11
|
*
|
|
@@ -42,7 +47,7 @@ declare const useOptimizeModel: () => {
|
|
|
42
47
|
*/
|
|
43
48
|
load: (model: Object3D) => Promise<void>;
|
|
44
49
|
loadFromServerSceneData: (sceneData: ServerSceneData) => Promise<void>;
|
|
45
|
-
loadFromGlbBuffer: (buffer: Uint8Array) => Promise<void>;
|
|
50
|
+
loadFromGlbBuffer: (buffer: Uint8Array, meta?: WorkerResultMeta) => Promise<void>;
|
|
46
51
|
loadFromGLTFWithAssets: (gltfBytes: Uint8Array, assets: Map<string, Uint8Array>) => Promise<void>;
|
|
47
52
|
/**
|
|
48
53
|
* Retrieves the current model as a binary Uint8Array in glTF (.glb) format.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
let e=require("@vctrl/core"),t=require("react"),n=require("@vctrl/core/model-optimizer");var r={info:null,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.vertices.after??0,primitivesCount:e.stats.triangles.after??0,meshesCount:e.stats.meshes.after??0,textureAssetCount:e.stats.texturesCount.after??0,materialsCount:e.stats.materials.after??0,sceneBytes:e.optimizedSize??0}:{verticesCount:0,primitivesCount:0,meshesCount:0,textureAssetCount:0,materialsCount:0,sceneBytes:0}}var o=e=>{let[n,r]=(0,t.useState)(null),i=e.report;return(0,t.useEffect)(()=>{!n&&i&&r(i)},[n,i]),{info:(0,t.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,t.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,t.useReducer)(i,r),{report:c,error:u,loading:d}=a,{info:f,reset:p}=o(a),m=(0,t.useRef)(new n.ModelOptimizer);(0,t.useEffect)(()=>{let e=m.current;return()=>{e.reset()}},[]);let h=(0,t.useCallback)(async e=>{s({type:`LOAD_START`});try{let t=m.current;await t.loadFromThreeJS(e),s({type:`LOAD_SUCCESS`,payload:{report:await t.getReport()}})}catch(e){s({type:`LOAD_ERROR`,payload:e}),console.error(`Error loading model:`,e)}},[]),g=(0,t.useCallback)(async t=>{s({type:`LOAD_START`});try{let n=m.current,r=new Map;for(let n of Object.values(t.assetData??{}))for(let t of(0,e.buildAssetLookupKeys)(n.fileName))r.set(t,n);let i={},a=t.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 t of o){let n=(0,e.normalizeAssetUri)(t),a=n.split(`/`).pop()||n,o=r.get(t)??r.get(n)??r.get(a);o&&(i[t]=(0,e.toSerializedAssetBytes)(o))}let c={json:t.gltfJson,resources:i};await n.loadFromJSON(c),s({type:`LOAD_SUCCESS`,payload:{report:await n.getReport()}})}catch(e){throw s({type:`LOAD_ERROR`,payload:e}),console.error(`Error loading server scene into optimizer:`,e),e}},[]),_=(0,t.useCallback)(async
|
|
1
|
+
let e=require("@vctrl/core"),t=require("react"),n=require("@vctrl/core/model-optimizer");var r={info:null,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.vertices.after??0,primitivesCount:e.stats.triangles.after??0,meshesCount:e.stats.meshes.after??0,textureAssetCount:e.stats.texturesCount.after??0,materialsCount:e.stats.materials.after??0,sceneBytes:e.optimizedSize??0}:{verticesCount:0,primitivesCount:0,meshesCount:0,textureAssetCount:0,materialsCount:0,sceneBytes:0}}var o=e=>{let[n,r]=(0,t.useState)(null),i=e.report;return(0,t.useEffect)(()=>{!n&&i&&r(i)},[n,i]),{info:(0,t.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,t.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,t.useReducer)(i,r),{report:c,error:u,loading:d}=a,{info:f,reset:p}=o(a),m=(0,t.useRef)(new n.ModelOptimizer);(0,t.useEffect)(()=>{let e=m.current;return()=>{e.reset()}},[]);let h=(0,t.useCallback)(async e=>{s({type:`LOAD_START`});try{let t=m.current;await t.loadFromThreeJS(e),s({type:`LOAD_SUCCESS`,payload:{report:await t.getReport()}})}catch(e){s({type:`LOAD_ERROR`,payload:e}),console.error(`Error loading model:`,e)}},[]),g=(0,t.useCallback)(async t=>{s({type:`LOAD_START`});try{let n=m.current,r=new Map;for(let n of Object.values(t.assetData??{}))for(let t of(0,e.buildAssetLookupKeys)(n.fileName))r.set(t,n);let i={},a=t.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 t of o){let n=(0,e.normalizeAssetUri)(t),a=n.split(`/`).pop()||n,o=r.get(t)??r.get(n)??r.get(a);o&&(i[t]=(0,e.toSerializedAssetBytes)(o))}let c={json:t.gltfJson,resources:i};await n.loadFromJSON(c),s({type:`LOAD_SUCCESS`,payload:{report:await n.getReport()}})}catch(e){throw s({type:`LOAD_ERROR`,payload:e}),console.error(`Error loading server scene into optimizer:`,e),e}},[]),_=(0,t.useCallback)(async(e,t)=>{s({type:`LOAD_START`});try{await m.current.loadFromBuffer(e),m.current.setAppliedOptimizations(t?.appliedOptimizations??[]),m.current.setDracoReport(t?.dracoReport??null),s({type:`LOAD_SUCCESS`,payload:{report:await m.current.getReport()}})}catch(e){throw s({type:`LOAD_ERROR`,payload:e}),console.error(`Error loading GLB buffer into optimizer:`,e),e}},[]),v=(0,t.useCallback)(async(e,t)=>{s({type:`LOAD_START`});try{await m.current.loadFromGLTFWithAssets(e,t),s({type:`LOAD_SUCCESS`,payload:{report:await m.current.getReport()}})}catch(e){throw s({type:`LOAD_ERROR`,payload:e}),console.error(`Error loading GLTF with assets into optimizer:`,e),e}},[]),y=(0,t.useCallback)(async e=>{if(m.current.hasModel())try{await m.current.simplify(e),s({type:`LOAD_SUCCESS`,payload:{report:await m.current.getReport()}})}catch(e){console.error(`Simplification failed:`,e)}},[]),b=(0,t.useCallback)(async e=>{if(m.current.hasModel())try{await m.current.deduplicate(e),s({type:`LOAD_SUCCESS`,payload:{report:await m.current.getReport()}})}catch(e){console.error(`Deduplication failed:`,e)}},[]),x=(0,t.useCallback)(async e=>{if(m.current.hasModel())try{await m.current.quantize(e),s({type:`LOAD_SUCCESS`,payload:{report:await m.current.getReport()}})}catch(e){console.error(`Quantization failed:`,e)}},[]),S=(0,t.useCallback)(async e=>{if(m.current.hasModel())try{await m.current.optimizeNormals(e),s({type:`LOAD_SUCCESS`,payload:{report:await m.current.getReport()}})}catch(e){console.error(`Normals optimization failed:`,e)}},[]),C=(0,t.useCallback)(async e=>{try{await l(m.current,e),s({type:`LOAD_SUCCESS`,payload:{report:await m.current.getReport()}})}catch(e){throw console.error(`Texture optimization failed:`,e),e}},[]),w=(0,t.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,t.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,t.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,"t",{enumerable:!0,get:function(){return u}});
|
|
@@ -191,10 +191,10 @@ var g = async (e, t) => {
|
|
|
191
191
|
payload: e
|
|
192
192
|
}), console.error("Error loading server scene into optimizer:", e), e;
|
|
193
193
|
}
|
|
194
|
-
}, []), S = r(async (e) => {
|
|
194
|
+
}, []), S = r(async (e, t) => {
|
|
195
195
|
c({ type: "LOAD_START" });
|
|
196
196
|
try {
|
|
197
|
-
await y.current.loadFromBuffer(e), c({
|
|
197
|
+
await y.current.loadFromBuffer(e), y.current.setAppliedOptimizations(t?.appliedOptimizations ?? []), y.current.setDracoReport(t?.dracoReport ?? null), c({
|
|
198
198
|
type: "LOAD_SUCCESS",
|
|
199
199
|
payload: { report: await y.current.getReport() }
|
|
200
200
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./use-optimize-model-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./use-optimize-model-CD5zqTLK.cjs");exports.useOptimizeModel=e.t;
|
package/use-optimize-model.es.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as e } from "./use-optimize-model-
|
|
1
|
+
import { t as e } from "./use-optimize-model-D16-4Zwm.js";
|
|
2
2
|
export { e as useOptimizeModel };
|
|
@@ -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);return{handleThreeGltfExport:(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]),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])}};Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return i}});
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { useCallback as e, useRef as t } from "react";
|
|
2
|
-
import { ModelExporter as n } from "@vctrl/core/model-exporter";
|
|
3
|
-
import r from "file-saver";
|
|
4
|
-
//#region packages/hooks/src/use-export-model/use-export-model.ts
|
|
5
|
-
var i = (i, a) => {
|
|
6
|
-
let o = t(new n());
|
|
7
|
-
return {
|
|
8
|
-
handleThreeGltfExport: e(async (e, t) => {
|
|
9
|
-
let n = e?.model;
|
|
10
|
-
if (!n) {
|
|
11
|
-
console.error("Scene not initialized");
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
try {
|
|
15
|
-
let a = o.current, s = e.name.replace(/\.[^/.]+$/, "");
|
|
16
|
-
if (t) {
|
|
17
|
-
let e = await a.exportThreeJSGLB(n, {});
|
|
18
|
-
if (!(e.data instanceof Uint8Array)) throw Error("Expected GLB export to return Uint8Array");
|
|
19
|
-
r.saveAs(new Blob([new Uint8Array(e.data)]), `${s}.glb`);
|
|
20
|
-
} else {
|
|
21
|
-
let e = await a.exportThreeJSGLTF(n), t = await a.createZIPArchive(e, s);
|
|
22
|
-
r.saveAs(new Blob([new Uint8Array(t)]), `${s}.zip`);
|
|
23
|
-
}
|
|
24
|
-
i && i();
|
|
25
|
-
} catch (e) {
|
|
26
|
-
console.error("Export failed:", e), a && a(e);
|
|
27
|
-
}
|
|
28
|
-
}, [i, a]),
|
|
29
|
-
handleDocumentGltfExport: e(async function(e, t, n = !1, s = !0) {
|
|
30
|
-
try {
|
|
31
|
-
let a = o.current, c = t?.name.replace(/\.[^/.]+$/, "") || "model";
|
|
32
|
-
if (n) {
|
|
33
|
-
let t = await a.exportDocumentGLB(e);
|
|
34
|
-
r.saveAs(new Blob([new Uint8Array(t.data)]), `${c}.glb`);
|
|
35
|
-
} else {
|
|
36
|
-
let t = await a.exportDocumentGLTF(e);
|
|
37
|
-
if (!s) return t;
|
|
38
|
-
let n = await a.createZIPArchive(t, c);
|
|
39
|
-
r.saveAs(new Blob([new Uint8Array(n)]), `${c}.zip`);
|
|
40
|
-
}
|
|
41
|
-
i && i();
|
|
42
|
-
} catch (e) {
|
|
43
|
-
console.error("Export failed:", e), a && a(e);
|
|
44
|
-
}
|
|
45
|
-
}, [a, i])
|
|
46
|
-
};
|
|
47
|
-
};
|
|
48
|
-
//#endregion
|
|
49
|
-
export { i as t };
|