gltf-pipeline
Advanced tools
Comparing version 3.0.5 to 4.0.1
@@ -79,2 +79,8 @@ #!/usr/bin/env node | ||
}, | ||
keepLegacyExtensions: { | ||
describe: | ||
"When false, materials with KHR_techniques_webgl, KHR_blend, or KHR_materials_common will be converted to PBR.", | ||
type: "boolean", | ||
default: defaults.keepLegacyExtensions, | ||
}, | ||
"draco.compressMeshes": { | ||
@@ -144,3 +150,3 @@ alias: "d", | ||
if (inputExtension !== ".gltf" && inputExtension !== ".glb") { | ||
console.log('Error: unrecognized file extension "' + inputExtension + '".'); | ||
console.log(`Error: unrecognized file extension "${inputExtension}".`); | ||
return; | ||
@@ -160,3 +166,3 @@ } | ||
inputDirectory, | ||
inputName + "-processed" + outputExtension | ||
`${inputName}-processed${outputExtension}` | ||
); | ||
@@ -169,3 +175,3 @@ } | ||
if (outputExtension !== ".gltf" && outputExtension !== ".glb") { | ||
console.log('Error: unrecognized file extension "' + outputExtension + '".'); | ||
console.log(`Error: unrecognized file extension "${outputExtension}".`); | ||
return; | ||
@@ -190,2 +196,3 @@ } | ||
keepUnusedElements: argv.keepUnusedElements, | ||
keepLegacyExtensions: argv.keepLegacyExtensions, | ||
name: outputName, | ||
@@ -192,0 +199,0 @@ dracoOptions: dracoOptions, |
# Change Log | ||
### 4.0.1 - 2023-01-25 | ||
- Fixed crash when writing GLB files above 2GB. [#627](https://github.com/CesiumGS/gltf-pipeline/pull/627) | ||
### 4.0.0 - 2022-08-01 | ||
- Breaking changes | ||
- glTF 1.0 or glTF 2.0 with the `KHR_techniques_webgl`, `KHR_blend`, or `KHR_materials_common` extensions will be converted to glTF 2.0 with PBR materials by default. For the previous behavior pass in `--keepLegacyExtensions`. | ||
### 3.0.5 - 2022-06-30 | ||
@@ -4,0 +13,0 @@ |
@@ -90,4 +90,2 @@ "use strict"; | ||
values.transparency = defaultValue(values.transparency, 1.0); | ||
values.transparent = defaultValue(values.transparent, false); | ||
values.doubleSided = defaultValue(values.doubleSided, false); | ||
@@ -105,2 +103,13 @@ if (technique !== "CONSTANT") { | ||
} | ||
// These actually exist on the extension object, not the values object despite what's shown in the spec | ||
materialsCommon.transparent = defaultValue( | ||
materialsCommon.transparent, | ||
false | ||
); | ||
materialsCommon.doubleSided = defaultValue( | ||
materialsCommon.doubleSided, | ||
false | ||
); | ||
return; | ||
@@ -107,0 +116,0 @@ } |
@@ -94,3 +94,3 @@ "use strict"; | ||
checkRange( | ||
"quantizationBitsValues[" + attributeName + "]", | ||
`quantizationBitsValues[${attributeName}]`, | ||
quantizationBitsValues[attributeName], | ||
@@ -232,3 +232,3 @@ 0, | ||
throw new RuntimeError( | ||
"Error: Failed adding attribute " + semantic | ||
`Error: Failed adding attribute ${semantic}` | ||
); | ||
@@ -235,0 +235,0 @@ } else { |
@@ -65,3 +65,3 @@ "use strict"; | ||
const materialsCommon = material.extensions.KHR_materials_common; | ||
if (defined(materialsCommon)) { | ||
if (defined(materialsCommon) && defined(materialsCommon.values)) { | ||
const diffuse = materialsCommon.values.diffuse; | ||
@@ -68,0 +68,0 @@ const ambient = materialsCommon.values.ambient; |
@@ -184,22 +184,5 @@ "use strict"; | ||
return ( | ||
"Total byte length of all buffers: " + | ||
this.buffersByteLength + | ||
" bytes" + | ||
"\nImages: " + | ||
this.numberOfImages + | ||
"\nDraw calls: " + | ||
this.numberOfDrawCalls + | ||
"\nRendered primitives (e.g., triangles): " + | ||
this.numberOfRenderedPrimitives + | ||
"\nNodes: " + | ||
this.numberOfNodes + | ||
"\nMeshes: " + | ||
this.numberOfMeshes + | ||
"\nMaterials: " + | ||
this.numberOfMaterials + | ||
"\nAnimations: " + | ||
this.numberOfAnimations + | ||
"\nExternal requests (not data uris): " + | ||
this.numberOfExternalRequests | ||
`Total byte length of all buffers: ${this.buffersByteLength} bytes` + | ||
`\nImages: ${this.numberOfImages}\nDraw calls: ${this.numberOfDrawCalls}\nRendered primitives (e.g., triangles): ${this.numberOfRenderedPrimitives}\nNodes: ${this.numberOfNodes}\nMeshes: ${this.numberOfMeshes}\nMaterials: ${this.numberOfMaterials}\nAnimations: ${this.numberOfAnimations}\nExternal requests (not data uris): ${this.numberOfExternalRequests}` | ||
); | ||
}; |
"use strict"; | ||
const BUFFER_MAX_BYTE_LENGTH = require("buffer").constants.MAX_LENGTH; | ||
const FS_WRITE_MAX_LENGTH = 2147479552; // See https://github.com/nodejs/node/issues/35605 | ||
const BUFFER_MAX_LENGTH = require("buffer").constants.MAX_LENGTH; | ||
const BUFFER_MAX_BYTE_LENGTH = Math.min(FS_WRITE_MAX_LENGTH, BUFFER_MAX_LENGTH); | ||
const Cesium = require("cesium"); | ||
@@ -50,3 +52,3 @@ const ForEach = require("./ForEach"); | ||
// Prevent empty meshopt buffer from being merged into main buffer | ||
buffer.extras._pipeline.mergedBufferName = "meshopt-fallback-" + bufferId; | ||
buffer.extras._pipeline.mergedBufferName = `meshopt-fallback-${bufferId}`; | ||
} | ||
@@ -71,3 +73,3 @@ }); | ||
mergedName = defined(mergedName) | ||
? baseBufferName + "-" + mergedName | ||
? `${baseBufferName}-${mergedName}` | ||
: baseBufferName; | ||
@@ -79,3 +81,3 @@ | ||
} | ||
mergedName += "-" + mergedNameCount[mergedName]++; | ||
mergedName += `-${mergedNameCount[mergedName]++}`; | ||
} | ||
@@ -82,0 +84,0 @@ |
@@ -128,3 +128,5 @@ "use strict"; | ||
const uniformName = mappedUniforms[material.technique][parameterName]; | ||
materialExtension.values[uniformName] = value; | ||
if (defined(uniformName)) { | ||
materialExtension.values[uniformName] = value; | ||
} | ||
}); | ||
@@ -131,0 +133,0 @@ |
@@ -132,2 +132,8 @@ "use strict"; | ||
/** | ||
* When false, materials with KHR_techniques_webgl, KHR_blend, or KHR_materials_common will be converted to PBR. | ||
* @type Boolean | ||
* @default false | ||
*/ | ||
keepLegacyExtensions: false, | ||
/** | ||
* Gets or sets whether to compress the meshes using Draco. Adds the KHR_draco_mesh_compression extension. | ||
@@ -134,0 +140,0 @@ * @type Boolean |
@@ -127,3 +127,3 @@ "use strict"; | ||
throw new RuntimeError( | ||
"Draco decoding failed: " + decodingStatus.error_msg() | ||
`Draco decoding failed: ${decodingStatus.error_msg()}` | ||
); | ||
@@ -189,4 +189,5 @@ } | ||
throw new RuntimeError( | ||
"Could not get attribute data for id:" + | ||
attribute.unique_id().toString() | ||
`Could not get attribute data for id:${attribute | ||
.unique_id() | ||
.toString()}` | ||
); | ||
@@ -205,4 +206,5 @@ } | ||
throw new RuntimeError( | ||
"Could not get attribute data for id:" + | ||
attribute.unique_id().toString() | ||
`Could not get attribute data for id:${attribute | ||
.unique_id() | ||
.toString()}` | ||
); | ||
@@ -221,4 +223,5 @@ } | ||
throw new RuntimeError( | ||
"Could not get attribute data for id:" + | ||
attribute.unique_id().toString() | ||
`Could not get attribute data for id:${attribute | ||
.unique_id() | ||
.toString()}` | ||
); | ||
@@ -237,4 +240,5 @@ } | ||
throw new RuntimeError( | ||
"Could not get attribute data for id:" + | ||
attribute.unique_id().toString() | ||
`Could not get attribute data for id:${attribute | ||
.unique_id() | ||
.toString()}` | ||
); | ||
@@ -253,4 +257,5 @@ } | ||
throw new RuntimeError( | ||
"Could not get attribute data for id:" + | ||
attribute.unique_id().toString() | ||
`Could not get attribute data for id:${attribute | ||
.unique_id() | ||
.toString()}` | ||
); | ||
@@ -269,4 +274,5 @@ } | ||
throw new RuntimeError( | ||
"Could not get attribute data for id:" + | ||
attribute.unique_id().toString() | ||
`Could not get attribute data for id:${attribute | ||
.unique_id() | ||
.toString()}` | ||
); | ||
@@ -285,4 +291,5 @@ } | ||
throw new RuntimeError( | ||
"Could not get attribute data for id:" + | ||
attribute.unique_id().toString() | ||
`Could not get attribute data for id:${attribute | ||
.unique_id() | ||
.toString()}` | ||
); | ||
@@ -289,0 +296,0 @@ } |
@@ -13,2 +13,3 @@ "use strict"; | ||
const updateAccessorComponentTypes = require("./updateAccessorComponentTypes"); | ||
const removeExtension = require("./removeExtension"); | ||
@@ -79,2 +80,8 @@ const Cartesian3 = Cesium.Cartesian3; | ||
} | ||
if (!options.keepLegacyExtensions) { | ||
convertTechniquesToPbr(gltf); | ||
convertMaterialsCommonToPbr(gltf); | ||
} | ||
return gltf; | ||
@@ -540,3 +547,3 @@ } | ||
const materialsCommon = extensions.KHR_materials_common; | ||
if (defined(materialsCommon)) { | ||
if (defined(materialsCommon) && defined(materialsCommon.values)) { | ||
ForEach.materialValue(materialsCommon, function (value, name) { | ||
@@ -706,3 +713,3 @@ if (typeof value === "string") { | ||
} else if (!defined(knownSemantics[strippedSemantic])) { | ||
newSemantic = "_" + semantic; | ||
newSemantic = `_${semantic}`; | ||
mappedSemantics[semantic] = newSemantic; | ||
@@ -943,2 +950,16 @@ } | ||
function validatePresentAccessorMinMax(gltf) { | ||
ForEach.accessor(gltf, function (accessor) { | ||
if (defined(accessor.min) || defined(accessor.max)) { | ||
const minMax = findAccessorMinMax(gltf, accessor); | ||
if (defined(accessor.min)) { | ||
accessor.min = minMax.min; | ||
} | ||
if (defined(accessor.max)) { | ||
accessor.max = minMax.max; | ||
} | ||
} | ||
}); | ||
} | ||
function glTF10to20(gltf) { | ||
@@ -969,2 +990,5 @@ gltf.asset = defaultValue(gltf.asset, {}); | ||
requireAnimationAccessorMinMax(gltf); | ||
// When an acccessor has a min- or max, then it is recomputed, to capture the actual | ||
// value, and not use the (possibly imprecise) value from the input | ||
validatePresentAccessorMinMax(gltf); | ||
// buffer.type is unnecessary and should be removed | ||
@@ -989,1 +1013,149 @@ removeBufferType(gltf); | ||
} | ||
// It's not possible to upgrade glTF 1.0 shaders to 2.0 PBR materials in a generic way, | ||
// but we can look for certain uniform names that are commonly found in glTF 1.0 assets | ||
// and create PBR materials out of those. | ||
const baseColorTextureNames = ["u_tex", "u_diffuse", "u_emission"]; | ||
const baseColorFactorNames = ["u_diffuse"]; | ||
function initializePbrMaterial(material) { | ||
material.pbrMetallicRoughness = defined(material.pbrMetallicRoughness) | ||
? material.pbrMetallicRoughness | ||
: {}; | ||
material.pbrMetallicRoughness.roughnessFactor = 1.0; | ||
material.pbrMetallicRoughness.metallicFactor = 0.0; | ||
} | ||
function isTexture(value) { | ||
return defined(value.index); | ||
} | ||
function isVec4(value) { | ||
return Array.isArray(value) && value.length === 4; | ||
} | ||
function srgbToLinear(srgb) { | ||
const linear = new Array(4); | ||
linear[3] = srgb[3]; | ||
for (let i = 0; i < 3; i++) { | ||
const c = srgb[i]; | ||
if (c <= 0.04045) { | ||
// eslint-disable-next-line no-loss-of-precision | ||
linear[i] = srgb[i] * 0.07739938080495356037151702786378; | ||
} else { | ||
linear[i] = Math.pow( | ||
// eslint-disable-next-line no-loss-of-precision | ||
(c + 0.055) * 0.94786729857819905213270142180095, | ||
2.4 | ||
); | ||
} | ||
} | ||
return linear; | ||
} | ||
function convertTechniquesToPbr(gltf) { | ||
// Future work: convert other values like emissive, specular, etc. Only handling diffuse right now. | ||
ForEach.material(gltf, function (material) { | ||
ForEach.materialValue(material, function (value, name) { | ||
if (baseColorTextureNames.indexOf(name) !== -1 && isTexture(value)) { | ||
initializePbrMaterial(material); | ||
material.pbrMetallicRoughness.baseColorTexture = value; | ||
} else if (baseColorFactorNames.indexOf(name) !== -1 && isVec4(value)) { | ||
initializePbrMaterial(material); | ||
material.pbrMetallicRoughness.baseColorFactor = srgbToLinear(value); | ||
} | ||
}); | ||
}); | ||
removeExtension(gltf, "KHR_techniques_webgl"); | ||
removeExtension(gltf, "KHR_blend"); | ||
} | ||
function convertMaterialsCommonToPbr(gltf) { | ||
// Future work: convert KHR_materials_common lights to KHR_lights_punctual | ||
ForEach.material(gltf, function (material) { | ||
const materialsCommon = defaultValue( | ||
material.extensions, | ||
defaultValue.EMPTY_OBJECT | ||
).KHR_materials_common; | ||
if (defined(materialsCommon)) { | ||
const technique = materialsCommon.technique; | ||
if (technique === "CONSTANT") { | ||
// Add the KHR_materials_unlit extension | ||
addExtensionsUsed(gltf, "KHR_materials_unlit"); | ||
material.extensions = defined(material.extensions) | ||
? material.extensions | ||
: {}; | ||
material.extensions["KHR_materials_unlit"] = {}; | ||
} | ||
const values = defined(materialsCommon.values) | ||
? materialsCommon.values | ||
: {}; | ||
const ambient = values.ambient; | ||
const diffuse = values.diffuse; | ||
const emission = values.emission; | ||
const transparency = values.transparency; | ||
// These actually exist on the extension object, not the values object despite what's shown in the spec | ||
const doubleSided = materialsCommon.doubleSided; | ||
const transparent = materialsCommon.transparent; | ||
// Ignore specular and shininess for now because the conversion to PBR | ||
// isn't straightforward and depends on the technique | ||
initializePbrMaterial(material); | ||
if (defined(ambient)) { | ||
if (isVec4(ambient)) { | ||
material.emissiveFactor = ambient.slice(0, 3); | ||
} else if (isTexture(ambient)) { | ||
material.emissiveTexture = ambient; | ||
} | ||
} | ||
if (defined(diffuse)) { | ||
if (isVec4(diffuse)) { | ||
material.pbrMetallicRoughness.baseColorFactor = srgbToLinear(diffuse); | ||
} else if (isTexture(diffuse)) { | ||
material.pbrMetallicRoughness.baseColorTexture = diffuse; | ||
} | ||
} | ||
if (defined(doubleSided)) { | ||
material.doubleSided = doubleSided; | ||
} | ||
if (defined(emission)) { | ||
if (isVec4(emission)) { | ||
material.emissiveFactor = emission.slice(0, 3); | ||
} else if (isTexture(emission)) { | ||
material.emissiveTexture = emission; | ||
} | ||
} | ||
if (defined(transparency)) { | ||
if (defined(material.pbrMetallicRoughness.baseColorFactor)) { | ||
material.pbrMetallicRoughness.baseColorFactor[3] *= transparency; | ||
} else { | ||
material.pbrMetallicRoughness.baseColorFactor = [ | ||
1, | ||
1, | ||
1, | ||
transparency, | ||
]; | ||
} | ||
} | ||
if (defined(transparent)) { | ||
material.alphaMode = transparent ? "BLEND" : "OPAQUE"; | ||
} | ||
} | ||
}); | ||
removeExtension(gltf, "KHR_materials_common"); | ||
} |
@@ -155,3 +155,3 @@ "use strict"; | ||
const mimeType = mime.getType(extension); | ||
object.uri = "data:" + mimeType + ";base64," + source.toString("base64"); | ||
object.uri = `data:${mimeType};base64,${source.toString("base64")}`; | ||
} | ||
@@ -205,3 +205,3 @@ | ||
} | ||
return "buffer" + index; | ||
return `buffer${index}`; | ||
} else if (extension === ".glsl") { | ||
@@ -226,3 +226,3 @@ const programInfo = getProgram(gltf, index); | ||
} | ||
return "image" + index; | ||
return `image${index}`; | ||
} | ||
@@ -243,3 +243,3 @@ | ||
while (defined(options.separateResources[relativePath])) { | ||
relativePath = name + "_" + number + extension; | ||
relativePath = `${name}_${number}${extension}`; | ||
number++; | ||
@@ -246,0 +246,0 @@ } |
{ | ||
"name": "gltf-pipeline", | ||
"version": "3.0.5", | ||
"version": "4.0.1", | ||
"description": "Content pipeline tools for optimizing glTF assets.", | ||
@@ -26,3 +26,3 @@ "license": "Apache-2.0", | ||
"engines": { | ||
"node": ">=6.0.0" | ||
"node": ">=16.0.0" | ||
}, | ||
@@ -43,4 +43,5 @@ "dependencies": { | ||
"eslint": "^8.0.1", | ||
"eslint-config-cesium": "^8.0.1", | ||
"eslint-config-cesium": "^9.0.0", | ||
"eslint-config-prettier": "^8.3.0", | ||
"eslint-plugin-node": "^11.1.0", | ||
"gulp": "^4.0.2", | ||
@@ -63,2 +64,3 @@ "husky": "^4.3.8", | ||
"jsdoc": "jsdoc ./lib -R ./README.md -d doc", | ||
"eslint-fix": "eslint \"./**/*.js\" --fix", | ||
"eslint": "eslint \"./**/*.js\" --cache --quiet", | ||
@@ -80,2 +82,2 @@ "eslint-watch": "gulp eslint-watch", | ||
} | ||
} | ||
} |
@@ -16,3 +16,3 @@ # glTF Pipeline | ||
- Saving buffers/textures as embedded or separate files | ||
- Converting glTF 1.0 models to glTF 2.0 (using the [KHR_techniques_webgl](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_techniques_webgl) and [KHR_blend](https://github.com/KhronosGroup/glTF/pull/1302) extensions) | ||
- Converting glTF 1.0 models to glTF 2.0 | ||
- Applying [Draco](https://github.com/google/draco) mesh compression | ||
@@ -131,2 +131,3 @@ | ||
| `--keepUnusedElements` | Keep unused materials, nodes and meshes. | No, default `false` | | ||
| `--keepLegacyExtensions` | When false, materials with `KHR_techniques_webgl`, `KHR_blend`, or `KHR_materials_common` will be converted to PBR. | No, default `false` | | ||
| `--draco.compressMeshes`, `-d` | Compress the meshes using Draco. Adds the KHR_draco_mesh_compression extension. | No, default `false` | | ||
@@ -133,0 +134,0 @@ | `--draco.compressionLevel` | Draco compression level [0-10], most is 10, least is 0. A value of 0 will apply sequential encoding and preserve face order. | No, default `7` | |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
249972
6147
195
16