@needle-tools/gltf-build-pipeline 1.4.8 → 1.4.10

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 CHANGED
@@ -4,6 +4,13 @@ All notable changes to this package will be documented in this file.
4
4
  The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5
5
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [1.4.10] - 2023-04-23
8
+ - add: NEEDLE_progressive can now load and process already draco compressed meshes
9
+
10
+ ## [1.4.9] - 2023-04-23
11
+ - add: NEEDLE_progressive now have `hash` field for LODs which is generated using the content of the LOD and it's settings that produced it.
12
+ - fix: NEEDLE_progressive catches error when copying mesh with extensions where the copy operation fails
13
+
7
14
  ## [1.4.8] - 2023-04-22
8
15
  - fix: update sharp package to `^0.32.6`
9
16
  - change: NEEDLE_progressive meshes max LOD error increased
@@ -39,6 +39,10 @@ export declare function tryGetMetaFromCache(key: string, ignoreVersion?: boolean
39
39
  * @returns The key as a string.
40
40
  */
41
41
  export declare function getKey(property: ExtensibleProperty): string;
42
+ export declare function getKeyWithHash(property: ExtensibleProperty): {
43
+ key: string;
44
+ hash: number;
45
+ };
42
46
  /**
43
47
  * Get the hash for a property or an object.
44
48
  * @param property The property to get the hash for.
@@ -139,6 +139,15 @@ export function getKey(property) {
139
139
  const key = `${prefix}-${hash}`;
140
140
  return key;
141
141
  }
142
+ export function getKeyWithHash(property) {
143
+ let prefix = "prop";
144
+ if (property instanceof Texture) {
145
+ prefix = "image";
146
+ }
147
+ let hash = getHash(property);
148
+ const key = `${prefix}-${hash}`;
149
+ return { key, hash };
150
+ }
142
151
  /**
143
152
  * Get the hash for a property or an object.
144
153
  * @param property The property to get the hash for.
@@ -7,14 +7,19 @@ export type ProgressiveLoadingInfo = {
7
7
  lods?: Array<LodInfo>;
8
8
  };
9
9
  type LodInfo = {
10
+ /** Relative path to the LOD file */
10
11
  path: string;
12
+ /** Hash of the LOD file. It has been generated using the file content and settings */
13
+ hash: string;
11
14
  };
12
15
  export type ProgressiveTextureModel = ProgressiveLoadingInfo & {
13
16
  lods?: Array<LodInfo & {}>;
14
17
  };
15
18
  export type ProgressiveMeshModel = ProgressiveLoadingInfo & {
19
+ /** LOD0 vertex density. TODO: remove this since we now have a LODs array. This is just for supporting older engine versions */
16
20
  density: number;
17
21
  lods?: Array<LodInfo & {
22
+ /** Mesh vertex density */
18
23
  density: number;
19
24
  vertexCount: number;
20
25
  indexCount: number;
@@ -1,10 +1,11 @@
1
1
  import { BufferUtils, Document, NodeIO, PropertyType } from '@gltf-transform/core';
2
2
  import { SIMPLIFY_DEFAULTS, WELD_DEFAULTS, listTextureSlots, sparse } from '@gltf-transform/functions';
3
+ import draco3d from 'draco3dgltf';
3
4
  import { NEEDLE_PROGRESSIVE_EXTENSION_NAME, NEEDLE_progressive } from "../extensions/NEEDLE_progressive/index.js";
4
5
  import { createOpaqueExtension, currentGenerator, NEEDLE_compression_texture, NEEDLE_mesh_compression, NEEDLE_progressive_mesh_settings, NEEDLE_progressive_texture_settings, registerExtensions as registerDefaultExtensions } from '../extensions/index.js';
5
6
  import path, { basename } from 'path';
6
7
  import { existsSync, readFileSync, rmSync, statSync, writeFileSync } from 'fs';
7
- import { addMetaToCache, addToCache, getHash, getKey, tryGetFromCache, tryGetMetaFromCache } from '../cache/index.js';
8
+ import { addMetaToCache, addToCache, getHash, getKeyWithHash, tryGetFromCache, tryGetMetaFromCache } from '../cache/index.js';
8
9
  import sharp from "sharp";
9
10
  import { foreachGLTF, generateGuid } from '../utils/index.js';
10
11
  import { needle_asset, writeNeedleAsset } from './needle_asset.js';
@@ -49,6 +50,8 @@ async function process(file, opts) {
49
50
  registerExtensions(io);
50
51
  io.registerDependencies({
51
52
  'meshopt.decoder': MeshoptDecoder,
53
+ 'draco3d.decoder': await draco3d.createDecoderModule(),
54
+ 'draco3d.encoder': await draco3d.createEncoderModule(),
52
55
  });
53
56
  const document = await io.read(file).catch((err) => {
54
57
  // Log this message as a warning, it happens when trying to run on a already compressed gltf file (e.g. pre-processed. When logging an error we fail the build which is not desired)
@@ -159,12 +162,14 @@ export function make_progressive_textures(filePath, _options = TEXTURE_RESIZE_DE
159
162
  if (!outPath) {
160
163
  continue;
161
164
  }
165
+ const { key, hash } = getKeyWithHash(texture);
162
166
  const relName = path.relative(directory, outPath);
163
167
  // TODO: this path should be added to the NEEDLE dependencies extension
164
168
  const model = {
165
169
  guid: textureGuid,
166
170
  lods: [
167
171
  {
172
+ hash: hash.toString(),
168
173
  path: relName,
169
174
  }
170
175
  ],
@@ -172,7 +177,6 @@ export function make_progressive_textures(filePath, _options = TEXTURE_RESIZE_DE
172
177
  // for backwards compatibility - this should be removed in the future
173
178
  model["uri"] = relName;
174
179
  texture.setExtension(NEEDLE_PROGRESSIVE_EXTENSION_NAME, progressiveExt.createTexture(model));
175
- const key = getKey(texture);
176
180
  if (opts.useCache) {
177
181
  const cached = tryGetFromCache(key);
178
182
  if (cached) {
@@ -526,6 +530,7 @@ export function make_progressive_meshes(filePath, _options, opts) {
526
530
  lods: generatedLODInfos.map(l => {
527
531
  return {
528
532
  path: l.path,
533
+ hash: l.content_hash.toString(),
529
534
  density: l.density,
530
535
  vertexCount: l.lodMeshInfo?.totalVertexCount,
531
536
  indexCount: l.lodMeshInfo?.totalIndexCount,
@@ -587,21 +592,21 @@ async function createMeshLod(doc, basePath, level, mesh, meshInfo, guid, lodLeve
587
592
  const outPath = basePath + fileName;
588
593
  const primitives = mesh.listPrimitives();
589
594
  let cacheKey = undefined;
595
+ // generate hash for mesh
596
+ let hash = mesh["needle:mesh-hash"];
597
+ if (hash === undefined) {
598
+ hash = getHash(mesh);
599
+ mesh["needle:mesh-hash"] = hash;
600
+ }
601
+ hash += getHash(lodLevel);
590
602
  if (opts.useCache !== false) {
591
- // generate hash for mesh
592
- let hash = mesh["needle:mesh-hash"];
593
- if (hash === undefined) {
594
- hash = getHash(mesh);
595
- mesh["needle:mesh-hash"] = hash;
596
- }
597
- hash += getHash(lodLevel);
598
603
  cacheKey = `mesh_lod_${level}_${hash}`;
599
604
  const cachedValue = tryGetFromCache(cacheKey);
600
605
  const meta = tryGetMetaFromCache(cacheKey);
601
606
  if (cachedValue && meta) {
602
607
  console.log(`[${NAME}] LOD ${level} loaded ${(cachedValue.length / 1024).toFixed()} KB from cache for \"${mesh.getName()}\"`);
603
608
  writeFileSync(outPath, cachedValue);
604
- return { level, path: fileName, success: true, cacheKey: cacheKey, lodMeshInfo: meta.meshInfo, density: meta.density };
609
+ return { level, path: fileName, success: true, cacheKey: cacheKey, content_hash: hash, lodMeshInfo: meta.meshInfo, density: meta.density };
605
610
  }
606
611
  }
607
612
  const newDoc = new Document();
@@ -667,9 +672,14 @@ async function createMeshLod(doc, basePath, level, mesh, meshInfo, guid, lodLeve
667
672
  for (const ext of extensions) {
668
673
  const PropertyClass = ext.constructor;
669
674
  const newProp = new PropertyClass(newDoc.getGraph());
670
- const copy = newProp.copy(ext);
671
- const name = ext.extensionName;
672
- newPrim.setExtension(name, copy);
675
+ try {
676
+ const copy = newProp.copy(ext);
677
+ const name = ext.extensionName;
678
+ newPrim.setExtension(name, copy);
679
+ }
680
+ catch (err) {
681
+ console.error(`WARN: [${NAME}] Failed to copy extension ${ext.extensionName} for mesh LOD ${level} → ${err}`);
682
+ }
673
683
  }
674
684
  }
675
685
  let primitiveIndex = 0;
@@ -687,7 +697,7 @@ async function createMeshLod(doc, basePath, level, mesh, meshInfo, guid, lodLeve
687
697
  const newMeshInformation = getMeshInformation(newMesh);
688
698
  if (newMeshInformation.totalVertexCount < 4) {
689
699
  console.error(`[${NAME}] mesh LOD ${level} is has less than 4 vertices → skipping`);
690
- return { level, path: fileName, success: false, cacheKey: cacheKey, lodMeshInfo: null, density: null, };
700
+ return { level, path: fileName, success: false, cacheKey: cacheKey, content_hash: hash, lodMeshInfo: null, density: null, };
691
701
  }
692
702
  let density = 0;
693
703
  for (const prim of newMesh.listPrimitives()) {
@@ -723,7 +733,7 @@ async function createMeshLod(doc, basePath, level, mesh, meshInfo, guid, lodLeve
723
733
  addToCache(cacheKey, bytes);
724
734
  addMetaToCache(cacheKey, { meshInfo: newMeshInformation, density: density });
725
735
  }
726
- return { level, path: fileName, success: true, lodMeshInfo: newMeshInformation, cacheKey: cacheKey, density: density };
736
+ return { level, path: fileName, success: true, lodMeshInfo: newMeshInformation, cacheKey: cacheKey, content_hash: hash, density: density };
727
737
  }
728
738
  async function simplifyMesh(doc, mesh, prim, index, lodinfo) {
729
739
  const weldOptions = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/gltf-build-pipeline",
3
- "version": "1.4.8",
3
+ "version": "1.4.10",
4
4
  "description": "Pipeline and tools for optimizing gltf files using gltf-transform and compression settings within glTF extensions",
5
5
  "main": "index.mjs",
6
6
  "type": "module",