├── .appveyor.yml
├── .eslintignore
├── .eslintrc.json
├── .gitattributes
├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── .husky
└── pre-commit
├── .npmignore
├── .npmrc
├── .prettierignore
├── CHANGES.md
├── LICENSE.md
├── README.md
├── ThirdParty.json
├── bin
└── gltf-pipeline.js
├── doc
└── gltf.png
├── gulpfile.js
├── index.js
├── lib
├── FileUrl.js
├── ForEach.js
├── addBuffer.js
├── addDefaults.js
├── addExtensionsRequired.js
├── addExtensionsUsed.js
├── addPipelineExtras.js
├── addToArray.js
├── compressDracoMeshes.js
├── dataUriToBuffer.js
├── findAccessorMinMax.js
├── forEachTextureInMaterial.js
├── getAccessorByteStride.js
├── getBufferPadded.js
├── getComponentReader.js
├── getImageExtension.js
├── getJsonBufferPadded.js
├── getStatistics.js
├── glbToGltf.js
├── gltfToGlb.js
├── mergeBuffers.js
├── moveTechniqueRenderStates.js
├── moveTechniquesToExtension.js
├── numberOfComponentsForType.js
├── parseGlb.js
├── processGlb.js
├── processGltf.js
├── readAccessorPacked.js
├── readResources.js
├── removeDefaults.js
├── removeExtension.js
├── removeExtensionsRequired.js
├── removeExtensionsUsed.js
├── removePipelineExtras.js
├── removeUnusedElements.js
├── replaceWithDecompressedPrimitive.js
├── splitPrimitives.js
├── updateAccessorComponentTypes.js
├── updateVersion.js
├── usesExtension.js
└── writeResources.js
├── package.json
└── specs
├── .eslintrc.json
├── data
├── 1.0
│ ├── box-materials-common
│ │ ├── box-materials-common.bin
│ │ └── box-materials-common.gltf
│ ├── box-textured-binary-separate
│ │ ├── box-textured-binary-separate-fs.glsl
│ │ ├── box-textured-binary-separate-vs.glsl
│ │ ├── box-textured-binary-separate.glb
│ │ └── cesium.png
│ ├── box-textured-binary
│ │ └── box-textured-binary.glb
│ ├── box-textured-embedded
│ │ └── box-textured-embedded.gltf
│ ├── box-textured-materials-common
│ │ ├── CesiumLogoFlat.png
│ │ ├── box-textured-materials-common.bin
│ │ └── box-textured-materials-common.gltf
│ ├── box-textured-separate
│ │ ├── box-textured-separate-fs.glsl
│ │ ├── box-textured-separate-vs.glsl
│ │ ├── box-textured-separate.bin
│ │ ├── box-textured-separate.gltf
│ │ └── cesium.png
│ └── box
│ │ └── box.gltf
└── 2.0
│ ├── box-morph
│ └── box-morph.gltf
│ ├── box-shared-image-references-separate
│ ├── CesiumTexturedBoxTest0FS.glsl
│ ├── CesiumTexturedBoxTest0VS.glsl
│ ├── Image.png
│ ├── box-shared-image-references-separate.bin
│ └── box-shared-image-references-separate.gltf
│ ├── box-shared-image-references
│ └── box-shared-image-references.gltf
│ ├── box-techniques-embedded
│ └── box-techniques-embedded.gltf
│ ├── box-techniques-separate
│ ├── CesiumTexturedBoxTest.bin
│ ├── CesiumTexturedBoxTest0FS.glsl
│ ├── CesiumTexturedBoxTest0VS.glsl
│ ├── Image0001.png
│ └── box-techniques-separate.gltf
│ ├── box-textured-binary-separate
│ ├── box-textured-binary-separate.glb
│ └── cesium.png
│ ├── box-textured-binary
│ └── box-textured-binary.glb
│ ├── box-textured-embedded
│ └── box-textured-embedded.gltf
│ ├── box-textured-separate
│ ├── box-textured-separate.bin
│ ├── box-textured-separate.gltf
│ └── cesium logo.png
│ ├── extensions
│ ├── EXT_meshopt_compression
│ │ ├── meshopt-fallback
│ │ │ ├── meshopt-fallback.bin
│ │ │ ├── meshopt-fallback.fallback.bin
│ │ │ └── meshopt-fallback.gltf
│ │ └── meshopt-no-fallback
│ │ │ ├── meshopt-no-fallback.bin
│ │ │ └── meshopt-no-fallback.gltf
│ └── EXT_texture_webp
│ │ ├── box-textured-embedded
│ │ └── box-textured-embedded.gltf
│ │ └── box-textured-separate
│ │ ├── box-textured-separate.bin
│ │ ├── box-textured-separate.gltf
│ │ ├── box-textured-with-fallback.gltf
│ │ ├── cesium logo.png
│ │ └── cesium logo.webp
│ ├── multiple-boxes
│ └── multiple-boxes.gltf
│ └── triangle-without-indices
│ └── triangle-without-indices.gltf
├── jasmine.json
└── lib
├── ForEachSpec.js
├── addBufferSpec.js
├── addDefaultsSpec.js
├── addExtensionsRequiredSpec.js
├── addExtensionsUsedSpec.js
├── addPipelineExtrasSpec.js
├── addToArraySpec.js
├── compressDracoMeshesSpec.js
├── dataUriToBufferSpec.js
├── findAccessorMinMaxSpec.js
├── getAccessorByteStrideSpec.js
├── getBufferPaddedSpec.js
├── getComponentReaderSpec.js
├── getImageExtensionSpec.js
├── getJsonBufferPaddedSpec.js
├── getStatisticsSpec.js
├── glbToGltfSpec.js
├── gltfToGlbSpec.js
├── mergeBuffersSpec.js
├── moveTechniqueRenderStatesSpec.js
├── moveTechniquesToExtensionSpec.js
├── numberOfComponentsForTypeSpec.js
├── parseGlbSpec.js
├── processGlbSpec.js
├── processGltfSpec.js
├── readAccessorPackedSpec.js
├── readResourcesSpec.js
├── removeDefaultsSpec.js
├── removeExtensionSpec.js
├── removeExtensionsRequiredSpec.js
├── removeExtensionsUsedSpec.js
├── removePipelineExtrasSpec.js
├── removeUnusedElementsSpec.js
├── splitPrimitivesSpec.js
├── updateAccessorComponentTypeSpec.js
├── updateVersionSpec.js
├── usesExtensionSpec.js
└── writeResourcesSpec.js
/.appveyor.yml:
--------------------------------------------------------------------------------
1 | # Builds are whole numbered
2 | version: '{build}'
3 |
4 | clone_depth: 50
5 |
6 | environment:
7 | matrix:
8 | - nodejs_version: "12"
9 |
10 | install:
11 | # Get the latest stable version of Node.js
12 | - ps: Install-Product node $env:nodejs_version
13 | # install modules
14 | - npm install
15 |
16 | test_script:
17 | - node --version
18 | - npm --version
19 | - npm run eslint
20 | - npm run test -- --failTaskOnError --suppressPassed
21 |
22 | # Don't actually build.
23 | build: off
24 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules/**
2 | coverage/**
3 | doc/**
4 | dist/**
5 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["cesium/node"],
3 | "rules": {
4 | "no-unused-vars": ["error", {"args": "none"}]
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 | package.json text eol=lf
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | run-name: build
2 | on:
3 | push:
4 | concurrency:
5 | group: ${{ github.ref }}
6 | cancel-in-progress: true
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 | strategy:
11 | matrix:
12 | node: [ 16, 20 ]
13 | name: Node ${{ matrix.node }}
14 | steps:
15 | - uses: actions/checkout@v3
16 | - uses: actions/setup-node@v3
17 | with:
18 | node-version: ${{ matrix.node }}
19 | - run: npm install
20 | - run: npm run eslint
21 | - run: npm run prettier-check
22 | - run: npm run test -- --failTaskOnError --suppressPassed
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # NPM
2 | node_modules
3 | npm-debug.log
4 | package-lock.json
5 | yarn.lock
6 |
7 | # WebStorm user-specific
8 | .idea/workspace.xml
9 | .idea/tasks.xml
10 |
11 | # VSCode user-specific
12 | .vscode
13 |
14 | # Generate data
15 | .eslintcache
16 | coverage
17 | dist
18 | output
19 | test
20 | *.tgz
21 | .nyc_output
22 | doc/*
23 | !doc/gltf.png
24 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npm run pre-commit
5 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | /.husky
2 | /.vscode
3 | /.idea
4 | /dist
5 | /coverage
6 | /doc/*
7 | /output
8 | /specs
9 | /test
10 | .editorconfig
11 | .eslintcache
12 | .eslintignore
13 | .eslintrc.json
14 | .gitattributes
15 | .nyc_output
16 | .npmignore
17 | .prettierignore
18 | .travis.yml
19 | .appveyor.yml
20 | gulpfile.js
21 | *.tgz
22 | !doc/gltf.png
23 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Ignore everything
2 | *
3 |
4 | # Unignore directories (to all depths) and unignore code in these directories
5 | !bin/**/
6 | !doc/**/
7 | !lib/**/
8 | !specs/**/
9 |
10 | !**/*.js
11 | !**/*.md
12 |
--------------------------------------------------------------------------------
/ThirdParty.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "bluebird",
4 | "license": [
5 | "MIT"
6 | ],
7 | "version": "3.7.2",
8 | "url": "https://www.npmjs.com/package/bluebird"
9 | },
10 | {
11 | "name": "cesium",
12 | "license": [
13 | "Apache-2.0"
14 | ],
15 | "version": "1.109.0",
16 | "url": "https://www.npmjs.com/package/cesium"
17 | },
18 | {
19 | "name": "draco3d",
20 | "license": [
21 | "Apache-2.0"
22 | ],
23 | "version": "1.5.6",
24 | "url": "https://www.npmjs.com/package/draco3d"
25 | },
26 | {
27 | "name": "fs-extra",
28 | "license": [
29 | "MIT"
30 | ],
31 | "version": "11.1.1",
32 | "url": "https://www.npmjs.com/package/fs-extra"
33 | },
34 | {
35 | "name": "mime",
36 | "license": [
37 | "MIT"
38 | ],
39 | "version": "3.0.0",
40 | "url": "https://www.npmjs.com/package/mime"
41 | },
42 | {
43 | "name": "object-hash",
44 | "license": [
45 | "MIT"
46 | ],
47 | "version": "3.0.0",
48 | "url": "https://www.npmjs.com/package/object-hash"
49 | },
50 | {
51 | "name": "yargs",
52 | "license": [
53 | "MIT"
54 | ],
55 | "version": "17.7.2",
56 | "url": "https://www.npmjs.com/package/yargs"
57 | }
58 | ]
--------------------------------------------------------------------------------
/doc/gltf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/gltf-pipeline/cbbff2f212b5aa6163c7152d46ffcaf3a2ff3ac3/doc/gltf.png
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /*eslint-disable n/global-require*/
2 | "use strict";
3 | module.exports = {
4 | getStatistics: require("./lib/getStatistics"),
5 | glbToGltf: require("./lib/glbToGltf"),
6 | gltfToGlb: require("./lib/gltfToGlb"),
7 | processGlb: require("./lib/processGlb"),
8 | processGltf: require("./lib/processGltf"),
9 | removeExtension: require("./lib/removeExtension"),
10 | };
11 |
--------------------------------------------------------------------------------
/lib/FileUrl.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const { Check, RuntimeError } = require("cesium");
4 | const os = require("os");
5 | const path = require("path");
6 | const { domainToUnicode, URL } = require("url");
7 |
8 | module.exports = {
9 | fileURLToPath: fileURLToPath,
10 | pathToFileURL: pathToFileURL,
11 | };
12 |
13 | const isWindows = os.platform() === "win32";
14 | const forwardSlashRegEx = /\//g;
15 |
16 | const CHAR_LOWERCASE_A = 97;
17 | const CHAR_LOWERCASE_Z = 122;
18 | const CHAR_FORWARD_SLASH = 47;
19 | const CHAR_BACKWARD_SLASH = 92;
20 |
21 | const percentRegEx = /%/g;
22 | const backslashRegEx = /\\/g;
23 | const newlineRegEx = /\n/g;
24 | const carriageReturnRegEx = /\r/g;
25 | const tabRegEx = /\t/g;
26 |
27 | // The following function is copied from Node.js implementation of url module
28 | // https://github.com/nodejs/node/blob/7237eaa3353aacf284289c8b59b0a5e0fa5744bb/lib/internal/url.js#L1345-L1383
29 | // pathToFileURL & fileURLToPath were added in 10.12 so we want to maintain ability run under older versions.
30 | function fileURLToPath(path) {
31 | Check.defined("path", path);
32 |
33 | if (typeof path === "string") {
34 | path = new URL(path);
35 | }
36 |
37 | if (path.protocol !== "file:") {
38 | throw new RuntimeError("Expected path.protocol to start with file:");
39 | }
40 | return isWindows ? getPathFromURLWin32(path) : getPathFromURLPosix(path);
41 | }
42 |
43 | function pathToFileURL(filepath) {
44 | let resolved = path.resolve(filepath);
45 | // path.resolve strips trailing slashes so we must add them back
46 | const filePathLast = filepath.charCodeAt(filepath.length - 1);
47 | if (
48 | (filePathLast === CHAR_FORWARD_SLASH ||
49 | (isWindows && filePathLast === CHAR_BACKWARD_SLASH)) &&
50 | resolved[resolved.length - 1] !== path.sep
51 | ) {
52 | resolved += "/";
53 | }
54 | const outURL = new URL("file://");
55 | if (resolved.includes("%")) {
56 | resolved = resolved.replace(percentRegEx, "%25");
57 | }
58 | // in posix, "/" is a valid character in paths
59 | if (!isWindows && resolved.includes("\\")) {
60 | resolved = resolved.replace(backslashRegEx, "%5C");
61 | }
62 | if (resolved.includes("\n")) {
63 | resolved = resolved.replace(newlineRegEx, "%0A");
64 | }
65 | if (resolved.includes("\r")) {
66 | resolved = resolved.replace(carriageReturnRegEx, "%0D");
67 | }
68 | if (resolved.includes("\t")) {
69 | resolved = resolved.replace(tabRegEx, "%09");
70 | }
71 | outURL.pathname = resolved;
72 | return outURL;
73 | }
74 |
75 | function getPathFromURLWin32(url) {
76 | const hostname = url.hostname;
77 | let pathname = url.pathname;
78 | for (let n = 0; n < pathname.length; n++) {
79 | if (pathname[n] === "%") {
80 | const third = pathname.codePointAt(n + 2) | 0x20;
81 | if (
82 | (pathname[n + 1] === "2" && third === 102) || // 2f 2F /
83 | (pathname[n + 1] === "5" && third === 99)
84 | ) {
85 | // 5c 5C \
86 | throw new RuntimeError(
87 | "file URL must not include encoded \\ or / characters",
88 | );
89 | }
90 | }
91 | }
92 | pathname = pathname.replace(forwardSlashRegEx, "\\");
93 | pathname = decodeURIComponent(pathname);
94 | if (hostname !== "") {
95 | // If hostname is set, then we have a UNC path
96 | // Pass the hostname through domainToUnicode just in case
97 | // it is an IDN using punycode encoding. We do not need to worry
98 | // about percent encoding because the URL parser will have
99 | // already taken care of that for us. Note that this only
100 | // causes IDNs with an appropriate `xn--` prefix to be decoded.
101 | return `\\\\${domainToUnicode(hostname)}${pathname}`;
102 | }
103 |
104 | // Otherwise, it's a local path that requires a drive letter
105 | const letter = pathname.codePointAt(1) | 0x20;
106 | const sep = pathname[2];
107 | if (
108 | letter < CHAR_LOWERCASE_A ||
109 | letter > CHAR_LOWERCASE_Z || // a..z A..Z
110 | sep !== ":"
111 | ) {
112 | throw new RuntimeError("file URL must be absolute");
113 | }
114 | return pathname.slice(1);
115 | }
116 |
117 | function getPathFromURLPosix(url) {
118 | if (url.hostname !== "") {
119 | throw new RuntimeError("Invalid platform");
120 | }
121 | const pathname = url.pathname;
122 | for (let n = 0; n < pathname.length; n++) {
123 | if (pathname[n] === "%") {
124 | const third = pathname.codePointAt(n + 2) | 0x20;
125 | if (pathname[n + 1] === "2" && third === 102) {
126 | throw new RuntimeError(
127 | "file URL must not include encoded \\ or / characters",
128 | );
129 | }
130 | }
131 | }
132 | return decodeURIComponent(pathname);
133 | }
134 |
--------------------------------------------------------------------------------
/lib/addBuffer.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const addToArray = require("./addToArray");
3 |
4 | module.exports = addBuffer;
5 |
6 | /**
7 | * Adds buffer to gltf.
8 | *
9 | * @param {object} gltf A javascript object containing a glTF asset.
10 | * @param {Buffer} buffer A Buffer object which will be added to gltf.buffers.
11 | * @returns {number} The bufferView id of the newly added bufferView.
12 | *
13 | * @private
14 | */
15 | function addBuffer(gltf, buffer) {
16 | const newBuffer = {
17 | byteLength: buffer.length,
18 | extras: {
19 | _pipeline: {
20 | source: buffer,
21 | },
22 | },
23 | };
24 | const bufferId = addToArray(gltf.buffers, newBuffer);
25 | const bufferView = {
26 | buffer: bufferId,
27 | byteOffset: 0,
28 | byteLength: buffer.length,
29 | };
30 | return addToArray(gltf.bufferViews, bufferView);
31 | }
32 |
--------------------------------------------------------------------------------
/lib/addExtensionsRequired.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 | const addExtensionsUsed = require("./addExtensionsUsed");
4 | const addToArray = require("./addToArray");
5 |
6 | const defined = Cesium.defined;
7 |
8 | module.exports = addExtensionsRequired;
9 |
10 | /**
11 | * Adds an extension to gltf.extensionsRequired if it does not already exist.
12 | * Initializes extensionsRequired if it is not defined.
13 | *
14 | * @param {object} gltf A javascript object containing a glTF asset.
15 | * @param {string} extension The extension to add.
16 | *
17 | * @private
18 | */
19 | function addExtensionsRequired(gltf, extension) {
20 | let extensionsRequired = gltf.extensionsRequired;
21 | if (!defined(extensionsRequired)) {
22 | extensionsRequired = [];
23 | gltf.extensionsRequired = extensionsRequired;
24 | }
25 | addToArray(extensionsRequired, extension, true);
26 | addExtensionsUsed(gltf, extension);
27 | }
28 |
--------------------------------------------------------------------------------
/lib/addExtensionsUsed.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 | const addToArray = require("./addToArray");
4 |
5 | const defined = Cesium.defined;
6 |
7 | module.exports = addExtensionsUsed;
8 |
9 | /**
10 | * Adds an extension to gltf.extensionsUsed if it does not already exist.
11 | * Initializes extensionsUsed if it is not defined.
12 | *
13 | * @param {object} gltf A javascript object containing a glTF asset.
14 | * @param {string} extension The extension to add.
15 | *
16 | * @private
17 | */
18 | function addExtensionsUsed(gltf, extension) {
19 | let extensionsUsed = gltf.extensionsUsed;
20 | if (!defined(extensionsUsed)) {
21 | extensionsUsed = [];
22 | gltf.extensionsUsed = extensionsUsed;
23 | }
24 | addToArray(extensionsUsed, extension, true);
25 | }
26 |
--------------------------------------------------------------------------------
/lib/addPipelineExtras.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 | const ForEach = require("./ForEach");
4 |
5 | const defined = Cesium.defined;
6 |
7 | module.exports = addPipelineExtras;
8 |
9 | /**
10 | * Adds extras._pipeline to each object that can have extras in the glTF asset.
11 | * This stage runs before updateVersion and handles both glTF 1.0 and glTF 2.0 assets.
12 | *
13 | * @param {object} gltf A javascript object containing a glTF asset.
14 | * @returns {object} The glTF asset with the added pipeline extras.
15 | *
16 | * @private
17 | */
18 | function addPipelineExtras(gltf) {
19 | ForEach.shader(gltf, function (shader) {
20 | addExtras(shader);
21 | });
22 | ForEach.buffer(gltf, function (buffer) {
23 | addExtras(buffer);
24 | });
25 | ForEach.image(gltf, function (image) {
26 | addExtras(image);
27 | });
28 |
29 | addExtras(gltf);
30 |
31 | return gltf;
32 | }
33 |
34 | function addExtras(object) {
35 | object.extras = defined(object.extras) ? object.extras : {};
36 | object.extras._pipeline = defined(object.extras._pipeline)
37 | ? object.extras._pipeline
38 | : {};
39 | }
40 |
--------------------------------------------------------------------------------
/lib/addToArray.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 |
4 | const defaultValue = Cesium.defaultValue;
5 |
6 | module.exports = addToArray;
7 |
8 | /**
9 | * Adds an element to an array and returns the element's index.
10 | *
11 | * @param {Array} array The array to add to.
12 | * @param {object} element The element to add.
13 | * @param {boolean} [checkDuplicates=false] When true
, if a duplicate element is found its index is returned and element
is not added to the array.
14 | *
15 | * @private
16 | */
17 | function addToArray(array, element, checkDuplicates) {
18 | checkDuplicates = defaultValue(checkDuplicates, false);
19 | if (checkDuplicates) {
20 | const index = array.indexOf(element);
21 | if (index > -1) {
22 | return index;
23 | }
24 | }
25 |
26 | array.push(element);
27 | return array.length - 1;
28 | }
29 |
--------------------------------------------------------------------------------
/lib/dataUriToBuffer.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | module.exports = dataUriToBuffer;
3 |
4 | /**
5 | * Read a data uri string into a buffer.
6 | *
7 | * @param {string} dataUri The data uri.
8 | * @returns {Buffer}
9 | *
10 | * @private
11 | */
12 | function dataUriToBuffer(dataUri) {
13 | const data = dataUri.slice(dataUri.indexOf(",") + 1);
14 | if (dataUri.indexOf("base64") >= 0) {
15 | return Buffer.from(data, "base64");
16 | }
17 | return Buffer.from(data, "utf8");
18 | }
19 |
--------------------------------------------------------------------------------
/lib/findAccessorMinMax.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 |
4 | const ComponentDatatype = Cesium.ComponentDatatype;
5 | const defined = Cesium.defined;
6 |
7 | const getAccessorByteStride = require("./getAccessorByteStride");
8 | const getComponentReader = require("./getComponentReader");
9 | const numberOfComponentsForType = require("./numberOfComponentsForType");
10 |
11 | module.exports = findAccessorMinMax;
12 |
13 | /**
14 | * Finds the min and max values of the accessor.
15 | *
16 | * @param {object} gltf A javascript object containing a glTF asset.
17 | * @param {object} accessor The accessor object from the glTF asset to read.
18 | * @returns {{min: Array, max: Array}} min holding the array of minimum values and max holding the array of maximum values.
19 | *
20 | * @private
21 | */
22 | function findAccessorMinMax(gltf, accessor) {
23 | const bufferViews = gltf.bufferViews;
24 | const buffers = gltf.buffers;
25 | const bufferViewId = accessor.bufferView;
26 | const numberOfComponents = numberOfComponentsForType(accessor.type);
27 |
28 | // According to the spec, when bufferView is not defined, accessor must be initialized with zeros
29 | if (!defined(accessor.bufferView)) {
30 | return {
31 | min: new Array(numberOfComponents).fill(0.0),
32 | max: new Array(numberOfComponents).fill(0.0),
33 | };
34 | }
35 |
36 | const min = new Array(numberOfComponents).fill(Number.POSITIVE_INFINITY);
37 | const max = new Array(numberOfComponents).fill(Number.NEGATIVE_INFINITY);
38 |
39 | const bufferView = bufferViews[bufferViewId];
40 | const bufferId = bufferView.buffer;
41 | const buffer = buffers[bufferId];
42 | const source = buffer.extras._pipeline.source;
43 |
44 | const count = accessor.count;
45 | const byteStride = getAccessorByteStride(gltf, accessor);
46 | let byteOffset =
47 | accessor.byteOffset + bufferView.byteOffset + source.byteOffset;
48 | const componentType = accessor.componentType;
49 | const componentTypeByteLength =
50 | ComponentDatatype.getSizeInBytes(componentType);
51 | const dataView = new DataView(source.buffer);
52 | const components = new Array(numberOfComponents);
53 | const componentReader = getComponentReader(componentType);
54 |
55 | for (let i = 0; i < count; i++) {
56 | componentReader(
57 | dataView,
58 | byteOffset,
59 | numberOfComponents,
60 | componentTypeByteLength,
61 | components,
62 | );
63 | for (let j = 0; j < numberOfComponents; j++) {
64 | const value = components[j];
65 | min[j] = Math.min(min[j], value);
66 | max[j] = Math.max(max[j], value);
67 | }
68 | byteOffset += byteStride;
69 | }
70 |
71 | return {
72 | min: min,
73 | max: max,
74 | };
75 | }
76 |
--------------------------------------------------------------------------------
/lib/forEachTextureInMaterial.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 |
4 | const ForEach = require("./ForEach");
5 |
6 | const Check = Cesium.Check;
7 | const defined = Cesium.defined;
8 |
9 | module.exports = forEachTextureInMaterial;
10 |
11 | /**
12 | * Calls the provider handler function on each texture used by the material.
13 | * Mimics the behavior of functions in gltf-pipeline ForEach.
14 | * @param {object} material The glTF material.
15 | * @param {forEachTextureInMaterial~handler} handler Function that is called for each texture in the material.
16 | *
17 | * @private
18 | */
19 | function forEachTextureInMaterial(material, handler) {
20 | Check.typeOf.object("material", material);
21 | Check.defined("handler", handler);
22 |
23 | // Metallic roughness
24 | const pbrMetallicRoughness = material.pbrMetallicRoughness;
25 | if (defined(pbrMetallicRoughness)) {
26 | if (defined(pbrMetallicRoughness.baseColorTexture)) {
27 | const textureInfo = pbrMetallicRoughness.baseColorTexture;
28 | const value = handler(textureInfo.index, textureInfo);
29 | if (defined(value)) {
30 | return value;
31 | }
32 | }
33 | if (defined(pbrMetallicRoughness.metallicRoughnessTexture)) {
34 | const textureInfo = pbrMetallicRoughness.metallicRoughnessTexture;
35 | const value = handler(textureInfo.index, textureInfo);
36 | if (defined(value)) {
37 | return value;
38 | }
39 | }
40 | }
41 |
42 | if (defined(material.extensions)) {
43 | // Spec gloss extension
44 | const pbrSpecularGlossiness =
45 | material.extensions.KHR_materials_pbrSpecularGlossiness;
46 | if (defined(pbrSpecularGlossiness)) {
47 | if (defined(pbrSpecularGlossiness.diffuseTexture)) {
48 | const textureInfo = pbrSpecularGlossiness.diffuseTexture;
49 | const value = handler(textureInfo.index, textureInfo);
50 | if (defined(value)) {
51 | return value;
52 | }
53 | }
54 | if (defined(pbrSpecularGlossiness.specularGlossinessTexture)) {
55 | const textureInfo = pbrSpecularGlossiness.specularGlossinessTexture;
56 | const value = handler(textureInfo.index, textureInfo);
57 | if (defined(value)) {
58 | return value;
59 | }
60 | }
61 | }
62 |
63 | // Materials common extension (may be present in models converted from glTF 1.0)
64 | const materialsCommon = material.extensions.KHR_materials_common;
65 | if (defined(materialsCommon) && defined(materialsCommon.values)) {
66 | const diffuse = materialsCommon.values.diffuse;
67 | const ambient = materialsCommon.values.ambient;
68 | const emission = materialsCommon.values.emission;
69 | const specular = materialsCommon.values.specular;
70 | if (defined(diffuse) && defined(diffuse.index)) {
71 | const value = handler(diffuse.index, diffuse);
72 | if (defined(value)) {
73 | return value;
74 | }
75 | }
76 | if (defined(ambient) && defined(ambient.index)) {
77 | const value = handler(ambient.index, ambient);
78 | if (defined(value)) {
79 | return value;
80 | }
81 | }
82 | if (defined(emission) && defined(emission.index)) {
83 | const value = handler(emission.index, emission);
84 | if (defined(value)) {
85 | return value;
86 | }
87 | }
88 | if (defined(specular) && defined(specular.index)) {
89 | const value = handler(specular.index, specular);
90 | if (defined(value)) {
91 | return value;
92 | }
93 | }
94 | }
95 | }
96 |
97 | // KHR_techniques_webgl extension
98 | const value = ForEach.materialValue(material, function (materialValue) {
99 | if (defined(materialValue.index)) {
100 | const value = handler(materialValue.index, materialValue);
101 | if (defined(value)) {
102 | return value;
103 | }
104 | }
105 | });
106 | if (defined(value)) {
107 | return value;
108 | }
109 |
110 | // Top level textures
111 | if (defined(material.emissiveTexture)) {
112 | const textureInfo = material.emissiveTexture;
113 | const value = handler(textureInfo.index, textureInfo);
114 | if (defined(value)) {
115 | return value;
116 | }
117 | }
118 |
119 | if (defined(material.normalTexture)) {
120 | const textureInfo = material.normalTexture;
121 | const value = handler(textureInfo.index, textureInfo);
122 | if (defined(value)) {
123 | return value;
124 | }
125 | }
126 |
127 | if (defined(material.occlusionTexture)) {
128 | const textureInfo = material.occlusionTexture;
129 | const value = handler(textureInfo.index, textureInfo);
130 | if (defined(value)) {
131 | return value;
132 | }
133 | }
134 | }
135 |
136 | /**
137 | * Function that is called for each texture in the material. If this function returns a value the for each stops and returns that value.
138 | * @callback forEachTextureInMaterial~handler
139 | * @param {number} The texture index.
140 | * @param {object} The texture info object.
141 | *
142 | * @private
143 | */
144 |
--------------------------------------------------------------------------------
/lib/getAccessorByteStride.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 | const numberOfComponentsForType = require("./numberOfComponentsForType");
4 |
5 | const ComponentDatatype = Cesium.ComponentDatatype;
6 | const defined = Cesium.defined;
7 |
8 | module.exports = getAccessorByteStride;
9 |
10 | /**
11 | * Returns the byte stride of the provided accessor.
12 | * If the byteStride is 0, it is calculated based on type and componentType
13 | *
14 | * @param {object} gltf A javascript object containing a glTF asset.
15 | * @param {object} accessor The accessor.
16 | * @returns {number} The byte stride of the accessor.
17 | *
18 | * @private
19 | */
20 | function getAccessorByteStride(gltf, accessor) {
21 | const bufferViewId = accessor.bufferView;
22 | if (defined(bufferViewId)) {
23 | const bufferView = gltf.bufferViews[bufferViewId];
24 | if (defined(bufferView.byteStride) && bufferView.byteStride > 0) {
25 | return bufferView.byteStride;
26 | }
27 | }
28 | return (
29 | ComponentDatatype.getSizeInBytes(accessor.componentType) *
30 | numberOfComponentsForType(accessor.type)
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/lib/getBufferPadded.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | module.exports = getBufferPadded;
3 |
4 | /**
5 | * Pad the buffer to the next 8-byte boundary to ensure proper alignment for the section that follows.
6 | * glTF only requires 4-byte alignment but some extensions like EXT_structural_metadata require 8-byte
7 | * alignment for some buffer views.
8 | *
9 | * @param {Buffer} buffer The buffer.
10 | * @returns {Buffer} The padded buffer.
11 | *
12 | * @private
13 | */
14 | function getBufferPadded(buffer) {
15 | const boundary = 8;
16 | const byteLength = buffer.length;
17 | const remainder = byteLength % boundary;
18 | if (remainder === 0) {
19 | return buffer;
20 | }
21 | const padding = remainder === 0 ? 0 : boundary - remainder;
22 | const emptyBuffer = Buffer.alloc(padding);
23 | return Buffer.concat([buffer, emptyBuffer]);
24 | }
25 |
--------------------------------------------------------------------------------
/lib/getComponentReader.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 |
4 | const ComponentDatatype = Cesium.ComponentDatatype;
5 |
6 | module.exports = getComponentReader;
7 |
8 | /**
9 | * Returns a function to read and convert data from a DataView into an array.
10 | *
11 | * @param {number} componentType Type to convert the data to.
12 | * @returns {ComponentReader} Function that reads and converts data.
13 | *
14 | * @private
15 | */
16 | function getComponentReader(componentType) {
17 | switch (componentType) {
18 | case ComponentDatatype.BYTE:
19 | return function (
20 | dataView,
21 | byteOffset,
22 | numberOfComponents,
23 | componentTypeByteLength,
24 | result,
25 | ) {
26 | for (let i = 0; i < numberOfComponents; ++i) {
27 | result[i] = dataView.getInt8(
28 | byteOffset + i * componentTypeByteLength,
29 | );
30 | }
31 | };
32 | case ComponentDatatype.UNSIGNED_BYTE:
33 | return function (
34 | dataView,
35 | byteOffset,
36 | numberOfComponents,
37 | componentTypeByteLength,
38 | result,
39 | ) {
40 | for (let i = 0; i < numberOfComponents; ++i) {
41 | result[i] = dataView.getUint8(
42 | byteOffset + i * componentTypeByteLength,
43 | );
44 | }
45 | };
46 | case ComponentDatatype.SHORT:
47 | return function (
48 | dataView,
49 | byteOffset,
50 | numberOfComponents,
51 | componentTypeByteLength,
52 | result,
53 | ) {
54 | for (let i = 0; i < numberOfComponents; ++i) {
55 | result[i] = dataView.getInt16(
56 | byteOffset + i * componentTypeByteLength,
57 | true,
58 | );
59 | }
60 | };
61 | case ComponentDatatype.UNSIGNED_SHORT:
62 | return function (
63 | dataView,
64 | byteOffset,
65 | numberOfComponents,
66 | componentTypeByteLength,
67 | result,
68 | ) {
69 | for (let i = 0; i < numberOfComponents; ++i) {
70 | result[i] = dataView.getUint16(
71 | byteOffset + i * componentTypeByteLength,
72 | true,
73 | );
74 | }
75 | };
76 | case ComponentDatatype.INT:
77 | return function (
78 | dataView,
79 | byteOffset,
80 | numberOfComponents,
81 | componentTypeByteLength,
82 | result,
83 | ) {
84 | for (let i = 0; i < numberOfComponents; ++i) {
85 | result[i] = dataView.getInt32(
86 | byteOffset + i * componentTypeByteLength,
87 | true,
88 | );
89 | }
90 | };
91 | case ComponentDatatype.UNSIGNED_INT:
92 | return function (
93 | dataView,
94 | byteOffset,
95 | numberOfComponents,
96 | componentTypeByteLength,
97 | result,
98 | ) {
99 | for (let i = 0; i < numberOfComponents; ++i) {
100 | result[i] = dataView.getUint32(
101 | byteOffset + i * componentTypeByteLength,
102 | true,
103 | );
104 | }
105 | };
106 | case ComponentDatatype.FLOAT:
107 | return function (
108 | dataView,
109 | byteOffset,
110 | numberOfComponents,
111 | componentTypeByteLength,
112 | result,
113 | ) {
114 | for (let i = 0; i < numberOfComponents; ++i) {
115 | result[i] = dataView.getFloat32(
116 | byteOffset + i * componentTypeByteLength,
117 | true,
118 | );
119 | }
120 | };
121 | case ComponentDatatype.DOUBLE:
122 | return function (
123 | dataView,
124 | byteOffset,
125 | numberOfComponents,
126 | componentTypeByteLength,
127 | result,
128 | ) {
129 | for (let i = 0; i < numberOfComponents; ++i) {
130 | result[i] = dataView.getFloat64(
131 | byteOffset + i * componentTypeByteLength,
132 | true,
133 | );
134 | }
135 | };
136 | }
137 | }
138 |
139 | /**
140 | * A callback function that logs messages.
141 | * @callback ComponentReader
142 | *
143 | * @param {DataView} dataView The data view to read from.
144 | * @param {number} byteOffset The byte offset applied when reading from the data view.
145 | * @param {number} numberOfComponents The number of components to read.
146 | * @param {number} componentTypeByteLength The byte length of each component.
147 | * @param {number} result An array storing the components that are read.
148 | *
149 | * @private
150 | */
151 |
--------------------------------------------------------------------------------
/lib/getImageExtension.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 |
4 | const RuntimeError = Cesium.RuntimeError;
5 |
6 | module.exports = getImageExtension;
7 |
8 | /**
9 | * Get the image extension from a Buffer containing image data.
10 | *
11 | * @param {Buffer} data The image data.
12 | * @returns {string} The image extension.
13 | *
14 | * @private
15 | */
16 | function getImageExtension(data) {
17 | const header = data.slice(0, 2);
18 | const webpHeaderRIFFChars = data.slice(0, 4);
19 | const webpHeaderWEBPChars = data.slice(8, 12);
20 |
21 | if (header.equals(Buffer.from([0x42, 0x4d]))) {
22 | return ".bmp";
23 | } else if (header.equals(Buffer.from([0x47, 0x49]))) {
24 | return ".gif";
25 | } else if (header.equals(Buffer.from([0xff, 0xd8]))) {
26 | return ".jpg";
27 | } else if (header.equals(Buffer.from([0x89, 0x50]))) {
28 | return ".png";
29 | } else if (header.equals(Buffer.from([0xab, 0x4b]))) {
30 | return ".ktx2";
31 | } else if (header.equals(Buffer.from([0x73, 0x42]))) {
32 | return ".basis";
33 | } else if (
34 | webpHeaderRIFFChars.equals(Buffer.from([0x52, 0x49, 0x46, 0x46])) &&
35 | webpHeaderWEBPChars.equals(Buffer.from([0x57, 0x45, 0x42, 0x50]))
36 | ) {
37 | // See https://developers.google.com/speed/webp/docs/riff_container#webp_file_header
38 | return ".webp";
39 | }
40 |
41 | throw new RuntimeError("Image data does not have valid header");
42 | }
43 |
--------------------------------------------------------------------------------
/lib/getJsonBufferPadded.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 |
4 | const defaultValue = Cesium.defaultValue;
5 |
6 | module.exports = getJsonBufferPadded;
7 |
8 | /**
9 | * Convert the JSON object to a padded buffer.
10 | *
11 | * Pad the JSON with extra whitespace to fit the next 8-byte boundary. This ensures proper alignment
12 | * for the section that follows. glTF only requires 4-byte alignment but some extensions like
13 | * EXT_structural_metadata require 8-byte alignment for some buffer views.
14 | *
15 | * @param {object} json The JSON object.
16 | * @param {number} [byteOffset=0] The byte offset on which the buffer starts.
17 | * @returns {Buffer} The padded JSON buffer.
18 | *
19 | * @private
20 | */
21 | function getJsonBufferPadded(json, byteOffset) {
22 | byteOffset = defaultValue(byteOffset, 0);
23 | let string = JSON.stringify(json);
24 |
25 | const boundary = 8;
26 | const byteLength = Buffer.byteLength(string);
27 | const remainder = (byteOffset + byteLength) % boundary;
28 | const padding = remainder === 0 ? 0 : boundary - remainder;
29 | let whitespace = "";
30 | for (let i = 0; i < padding; ++i) {
31 | whitespace += " ";
32 | }
33 | string += whitespace;
34 |
35 | return Buffer.from(string);
36 | }
37 |
--------------------------------------------------------------------------------
/lib/getStatistics.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 | const ForEach = require("./ForEach");
4 |
5 | const defined = Cesium.defined;
6 | const isDataUri = Cesium.isDataUri;
7 | const WebGLConstants = Cesium.WebGLConstants;
8 |
9 | module.exports = getStatistics;
10 |
11 | /**
12 | * Returns an object containing the statistics for the glTF asset.
13 | *
14 | * @param {object} gltf A javascript object containing a glTF asset.
15 | * @param {number} [nodeId] If defined, statistics will only process number of draw calls and rendered primitives for the specified node.
16 | * @returns {Statistics} Object containing the statistics of the glTF asset.
17 | *
18 | * @see Statistics
19 | */
20 | function getStatistics(gltf, nodeId) {
21 | const statistics = new Statistics();
22 |
23 | if (defined(nodeId)) {
24 | const nodeDrawStats = getDrawCallStatisticsForNode(gltf, nodeId);
25 | statistics.numberOfDrawCalls = nodeDrawStats.numberOfDrawCalls;
26 | statistics.numberOfRenderedPrimitives =
27 | nodeDrawStats.numberOfRenderedPrimitives;
28 | return statistics;
29 | }
30 |
31 | const drawStats = getDrawCallStatistics(gltf);
32 |
33 | statistics.buffersByteLength = getBuffersByteLength(gltf);
34 | statistics.numberOfImages = defined(gltf.images) ? gltf.images.length : 0;
35 | statistics.numberOfExternalRequests = getNumberOfExternalRequests(gltf);
36 | statistics.numberOfDrawCalls = drawStats.numberOfDrawCalls;
37 | statistics.numberOfRenderedPrimitives = drawStats.numberOfRenderedPrimitives;
38 | statistics.numberOfNodes = defined(gltf.nodes) ? gltf.nodes.length : 0;
39 | statistics.numberOfMeshes = defined(gltf.meshes) ? gltf.meshes.length : 0;
40 | statistics.numberOfMaterials = defined(gltf.materials)
41 | ? gltf.materials.length
42 | : 0;
43 | statistics.numberOfAnimations = defined(gltf.animations)
44 | ? gltf.animations.length
45 | : 0;
46 |
47 | return statistics;
48 | }
49 |
50 | function getBuffersByteLength(gltf) {
51 | let byteLength = 0;
52 | ForEach.buffer(gltf, function (buffer) {
53 | byteLength += buffer.byteLength;
54 | });
55 | return byteLength;
56 | }
57 |
58 | function getNumberOfExternalRequests(gltf) {
59 | let count = 0;
60 | ForEach.buffer(gltf, function (buffer) {
61 | if (defined(buffer.uri) && !isDataUri(buffer.uri)) {
62 | count++;
63 | }
64 | });
65 | ForEach.image(gltf, function (image) {
66 | if (defined(image.uri) && !isDataUri(image.uri)) {
67 | count++;
68 | }
69 | });
70 | ForEach.shader(gltf, function (shader) {
71 | if (defined(shader.uri) && !isDataUri(shader.uri)) {
72 | count++;
73 | }
74 | });
75 | return count;
76 | }
77 |
78 | function getNumberOfRenderedPrimitives(gltf, primitive) {
79 | let count = 0;
80 | if (defined(primitive.indices)) {
81 | count = gltf.accessors[primitive.indices].count;
82 | } else if (defined(primitive.attributes.POSITION)) {
83 | count = gltf.accessors[primitive.attributes.POSITION].count;
84 | }
85 | switch (primitive.mode) {
86 | case WebGLConstants.POINTS:
87 | return count;
88 | case WebGLConstants.LINES:
89 | return count / 2;
90 | case WebGLConstants.LINE_LOOP:
91 | return count;
92 | case WebGLConstants.LINE_STRIP:
93 | return Math.max(count - 1, 0);
94 | case WebGLConstants.TRIANGLES:
95 | return count / 3;
96 | case WebGLConstants.TRIANGLE_STRIP:
97 | case WebGLConstants.TRIANGLE_FAN:
98 | return Math.max(count - 2, 0);
99 | default:
100 | // TRIANGLES
101 | return count / 3;
102 | }
103 | }
104 |
105 | function getDrawCallStatisticsForNode(gltf, nodeId) {
106 | let numberOfDrawCalls = 0;
107 | let numberOfRenderedPrimitives = 0;
108 |
109 | ForEach.nodeInTree(gltf, [nodeId], function (node) {
110 | const mesh = gltf.meshes[node.mesh];
111 | if (defined(mesh)) {
112 | ForEach.meshPrimitive(mesh, function (primitive) {
113 | numberOfDrawCalls++;
114 | numberOfRenderedPrimitives += getNumberOfRenderedPrimitives(
115 | gltf,
116 | primitive,
117 | );
118 | });
119 | }
120 | });
121 |
122 | return {
123 | numberOfDrawCalls: numberOfDrawCalls,
124 | numberOfRenderedPrimitives: numberOfRenderedPrimitives,
125 | };
126 | }
127 |
128 | function getDrawCallStatistics(gltf) {
129 | let numberOfDrawCalls = 0;
130 | let numberOfRenderedPrimitives = 0;
131 |
132 | ForEach.mesh(gltf, function (mesh) {
133 | ForEach.meshPrimitive(mesh, function (primitive) {
134 | numberOfDrawCalls++;
135 | numberOfRenderedPrimitives += getNumberOfRenderedPrimitives(
136 | gltf,
137 | primitive,
138 | );
139 | });
140 | });
141 |
142 | return {
143 | numberOfDrawCalls: numberOfDrawCalls,
144 | numberOfRenderedPrimitives: numberOfRenderedPrimitives,
145 | };
146 | }
147 |
148 | /**
149 | * Contains statistics for a glTF asset.
150 | *
151 | * @property {number} buffersByteLength The total byte length of all buffers.
152 | * @property {number} numberOfImages The number of images in the asset.
153 | * @property {number} numberOfExternalRequests The number of external requests required to fetch the asset data.
154 | * @property {number} numberOfDrawCalls The number of draw calls required to render the asset.
155 | * @property {number} numberOfRenderedPrimitives The total number of rendered primitives in the asset (e.g. triangles).
156 | * @property {number} numberOfNodes The total number of nodes in the asset.
157 | * @property {number} numberOfMeshes The total number of meshes in the asset.
158 | * @property {number} numberOfMaterials The total number of materials in the asset.
159 | * @property {number} numberOfAnimations The total number of animations in the asset.
160 | *
161 | * @constructor
162 | *
163 | * @see getStatistics
164 | */
165 | function Statistics() {
166 | this.buffersByteLength = 0;
167 | this.numberOfImages = 0;
168 | this.numberOfExternalRequests = 0;
169 | this.numberOfDrawCalls = 0;
170 | this.numberOfRenderedPrimitives = 0;
171 | this.numberOfNodes = 0;
172 | this.numberOfMeshes = 0;
173 | this.numberOfMaterials = 0;
174 | this.numberOfAnimations = 0;
175 | }
176 |
177 | /**
178 | * Creates a string listing the statistics along with their descriptions.
179 | *
180 | * @returns {string} A string describing the statistics for the glTF asset.
181 | */
182 | Statistics.prototype.toString = function () {
183 | return (
184 | `Total byte length of all buffers: ${this.buffersByteLength} bytes` +
185 | `\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}`
186 | );
187 | };
188 |
--------------------------------------------------------------------------------
/lib/glbToGltf.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const parseGlb = require("./parseGlb");
3 | const processGltf = require("./processGltf");
4 |
5 | module.exports = glbToGltf;
6 |
7 | /**
8 | * Convert a glb to glTF.
9 | *
10 | * @param {Buffer} glb A buffer containing the glb contents.
11 | * @param {object} [options] The same options object as {@link processGltf}
12 | * @returns {Promise} A promise that resolves to an object containing the glTF and a dictionary containing separate resources.
13 | */
14 | function glbToGltf(glb, options) {
15 | const gltf = parseGlb(glb);
16 | return processGltf(gltf, options);
17 | }
18 |
--------------------------------------------------------------------------------
/lib/gltfToGlb.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 | const getJsonBufferPadded = require("./getJsonBufferPadded");
4 | const processGltf = require("./processGltf");
5 |
6 | const clone = Cesium.clone;
7 | const defined = Cesium.defined;
8 |
9 | module.exports = gltfToGlb;
10 |
11 | /**
12 | * Convert a glTF to glb.
13 | *
14 | * @param {object} gltf A javascript object containing a glTF asset.
15 | * @param {object} [options] The same options object as {@link processGltf}
16 | * @returns {Promise} A promise that resolves to an object containing the glb and a dictionary containing separate resources.
17 | */
18 | function gltfToGlb(gltf, options) {
19 | options = defined(options) ? clone(options) : {};
20 | options.forceMergeBuffers = !options.separate;
21 | options.bufferStorage = {
22 | buffer: undefined,
23 | };
24 |
25 | return processGltf(gltf, options).then(function (results) {
26 | return {
27 | glb: getGlb(results.gltf, options.bufferStorage.buffer),
28 | separateResources: results.separateResources,
29 | };
30 | });
31 | }
32 |
33 | function getGlb(gltf, binaryBuffer) {
34 | const jsonBuffer = getJsonBufferPadded(gltf, 20);
35 |
36 | // Compute glb length: (Global header) + (JSON chunk header) + (JSON chunk) + [(Binary chunk header) + (Binary chunk)]
37 | let glbLength = 12 + 8 + jsonBuffer.length;
38 |
39 | if (defined(binaryBuffer)) {
40 | glbLength += 8 + binaryBuffer.length;
41 | }
42 |
43 | const glb = Buffer.alloc(glbLength);
44 |
45 | // Write binary glTF header (magic, version, length)
46 | let byteOffset = 0;
47 | glb.writeUInt32LE(0x46546c67, byteOffset);
48 | byteOffset += 4;
49 | glb.writeUInt32LE(2, byteOffset);
50 | byteOffset += 4;
51 | glb.writeUInt32LE(glbLength, byteOffset);
52 | byteOffset += 4;
53 |
54 | // Write JSON Chunk header (length, type)
55 | glb.writeUInt32LE(jsonBuffer.length, byteOffset);
56 | byteOffset += 4;
57 | glb.writeUInt32LE(0x4e4f534a, byteOffset); // JSON
58 | byteOffset += 4;
59 |
60 | // Write JSON Chunk
61 | jsonBuffer.copy(glb, byteOffset);
62 | byteOffset += jsonBuffer.length;
63 |
64 | if (defined(binaryBuffer)) {
65 | // Write Binary Chunk header (length, type)
66 | glb.writeUInt32LE(binaryBuffer.length, byteOffset);
67 | byteOffset += 4;
68 | glb.writeUInt32LE(0x004e4942, byteOffset); // BIN
69 | byteOffset += 4;
70 |
71 | // Write Binary Chunk
72 | binaryBuffer.copy(glb, byteOffset);
73 | }
74 |
75 | return glb;
76 | }
77 |
--------------------------------------------------------------------------------
/lib/mergeBuffers.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const FS_WRITE_MAX_LENGTH = 2147479552; // See https://github.com/nodejs/node/issues/35605
3 | const BUFFER_MAX_LENGTH = require("buffer").constants.MAX_LENGTH;
4 | const BUFFER_MAX_BYTE_LENGTH = Math.min(FS_WRITE_MAX_LENGTH, BUFFER_MAX_LENGTH);
5 | const Cesium = require("cesium");
6 | const ForEach = require("./ForEach");
7 |
8 | const defaultValue = Cesium.defaultValue;
9 | const defined = Cesium.defined;
10 |
11 | module.exports = mergeBuffers;
12 |
13 | /**
14 | * Merge all buffers. Buffers with the same extras._pipeline.mergedBufferName will be merged together.
15 | *
16 | * @param {object} gltf A javascript object containing a glTF asset.
17 | * @param {string} [defaultName] The default name of the buffer data files.
18 | * @param {boolean} [force=false] Whether to force merging all buffers.
19 | * @returns {object} The glTF asset with its buffers merged.
20 | *
21 | * @private
22 | */
23 | function mergeBuffers(gltf, defaultName, force) {
24 | let baseBufferName = defaultName;
25 | if (!defined(baseBufferName)) {
26 | ForEach.buffer(gltf, function (buffer) {
27 | baseBufferName = defaultValue(baseBufferName, buffer.name);
28 | });
29 | baseBufferName = defaultValue(baseBufferName, "buffer");
30 | }
31 |
32 | let buffersByteLength = 0;
33 | const emptyBuffers = [];
34 | const emptyBufferIds = [];
35 |
36 | ForEach.buffer(gltf, function (buffer, bufferId) {
37 | const source = buffer.extras._pipeline.source;
38 | if (defined(source)) {
39 | buffersByteLength += source.length;
40 | } else {
41 | emptyBuffers.push(buffer);
42 | emptyBufferIds.push(bufferId);
43 | }
44 |
45 | const extensions = defaultValue(
46 | buffer.extensions,
47 | defaultValue.EMPTY_OBJECT,
48 | );
49 | const meshoptObject = extensions.EXT_meshopt_compression;
50 | if (defined(meshoptObject) && meshoptObject.fallback) {
51 | // Prevent empty meshopt buffer from being merged into main buffer
52 | buffer.extras._pipeline.mergedBufferName = `meshopt-fallback-${bufferId}`;
53 | }
54 | });
55 |
56 | // Don't merge buffers if the merged buffer will exceed the Node limit.
57 | const splitBuffers =
58 | buffersByteLength > mergeBuffers._getBufferMaxByteLength();
59 |
60 | const buffersToMerge = {};
61 | const mergedNameCount = {};
62 |
63 | forEachBufferViewLikeObject(gltf, function (bufferView) {
64 | const buffer = gltf.buffers[bufferView.buffer];
65 | const source = buffer.extras._pipeline.source;
66 | if (!defined(source)) {
67 | return;
68 | }
69 |
70 | let mergedName = buffer.extras._pipeline.mergedBufferName;
71 | mergedName = defined(mergedName)
72 | ? `${baseBufferName}-${mergedName}`
73 | : baseBufferName;
74 |
75 | if (splitBuffers) {
76 | if (!defined(mergedNameCount[mergedName])) {
77 | mergedNameCount[mergedName] = 0;
78 | }
79 | mergedName += `-${mergedNameCount[mergedName]++}`;
80 | }
81 |
82 | if (force) {
83 | mergedName = baseBufferName;
84 | }
85 |
86 | if (!defined(buffersToMerge[mergedName])) {
87 | buffersToMerge[mergedName] = {
88 | buffers: [],
89 | byteLength: 0,
90 | index: Object.keys(buffersToMerge).length,
91 | };
92 | }
93 | const buffers = buffersToMerge[mergedName].buffers;
94 | let byteLength = buffersToMerge[mergedName].byteLength;
95 | const index = buffersToMerge[mergedName].index;
96 |
97 | const sourceBufferViewData = Buffer.from(
98 | source.slice(
99 | bufferView.byteOffset,
100 | bufferView.byteOffset + bufferView.byteLength,
101 | ),
102 | );
103 | const bufferViewPadding = allocateBufferPadding(byteLength);
104 | if (defined(bufferViewPadding)) {
105 | buffers.push(bufferViewPadding);
106 | byteLength += bufferViewPadding.byteLength;
107 | }
108 |
109 | bufferView.byteOffset = byteLength;
110 | bufferView.buffer = index;
111 |
112 | buffers.push(sourceBufferViewData);
113 | byteLength += sourceBufferViewData.byteLength;
114 |
115 | buffersToMerge[mergedName].byteLength = byteLength;
116 | });
117 |
118 | const buffersLength = Object.keys(buffersToMerge).length;
119 | gltf.buffers = new Array(buffersLength);
120 |
121 | for (const mergedName in buffersToMerge) {
122 | if (Object.prototype.hasOwnProperty.call(buffersToMerge, mergedName)) {
123 | const buffers = buffersToMerge[mergedName].buffers;
124 | const byteLength = buffersToMerge[mergedName].byteLength;
125 | const index = buffersToMerge[mergedName].index;
126 | const bufferPadding = allocateBufferPadding(byteLength);
127 | if (defined(bufferPadding)) {
128 | buffers.push(bufferPadding);
129 | }
130 | const mergedSource =
131 | buffers.length > 1 ? Buffer.concat(buffers) : buffers[0];
132 | gltf.buffers[index] = {
133 | name: mergedName,
134 | byteLength: mergedSource.byteLength,
135 | extras: {
136 | _pipeline: {
137 | source: mergedSource,
138 | },
139 | },
140 | };
141 | }
142 | }
143 |
144 | const emptyBuffersLength = emptyBuffers.length;
145 | for (let i = 0; i < emptyBuffersLength; ++i) {
146 | const emptyBuffer = emptyBuffers[i];
147 | const emptyBufferId = emptyBufferIds[i];
148 | const newBufferId = gltf.buffers.length;
149 | forEachBufferViewLikeObject(gltf, function (bufferView) {
150 | if (bufferView.buffer === emptyBufferId) {
151 | bufferView.buffer = newBufferId;
152 | }
153 | });
154 | gltf.buffers.push(emptyBuffer);
155 | }
156 |
157 | return gltf;
158 | }
159 |
160 | function forEachBufferViewLikeObject(gltf, callback) {
161 | ForEach.bufferView(gltf, function (bufferView) {
162 | callback(bufferView);
163 |
164 | const extensions = defaultValue(
165 | bufferView.extensions,
166 | defaultValue.EMPTY_OBJECT,
167 | );
168 | const meshoptObject = extensions.EXT_meshopt_compression;
169 | if (defined(meshoptObject)) {
170 | // The EXT_meshopt_compression object has buffer, byteOffset, and byteLength properties like a bufferView
171 | callback(meshoptObject);
172 | }
173 | });
174 | }
175 |
176 | function allocateBufferPadding(byteLength) {
177 | const boundary = 8;
178 | const remainder = byteLength % boundary;
179 | const padding = remainder === 0 ? 0 : boundary - remainder;
180 | if (padding > 0) {
181 | return Buffer.alloc(padding);
182 | }
183 | return undefined;
184 | }
185 |
186 | // Exposed for testing
187 | mergeBuffers._getBufferMaxByteLength = function () {
188 | return BUFFER_MAX_BYTE_LENGTH;
189 | };
190 |
--------------------------------------------------------------------------------
/lib/moveTechniqueRenderStates.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const Cesium = require("cesium");
4 | const addExtensionsUsed = require("./addExtensionsUsed");
5 | const ForEach = require("./ForEach");
6 |
7 | const defaultValue = Cesium.defaultValue;
8 | const defined = Cesium.defined;
9 | const WebGLConstants = Cesium.WebGLConstants;
10 |
11 | module.exports = moveTechniqueRenderStates;
12 |
13 | const defaultBlendEquation = [WebGLConstants.FUNC_ADD, WebGLConstants.FUNC_ADD];
14 |
15 | const defaultBlendFactors = [
16 | WebGLConstants.ONE,
17 | WebGLConstants.ZERO,
18 | WebGLConstants.ONE,
19 | WebGLConstants.ZERO,
20 | ];
21 |
22 | function isStateEnabled(renderStates, state) {
23 | const enabled = renderStates.enable;
24 | if (!defined(enabled)) {
25 | return false;
26 | }
27 |
28 | return enabled.indexOf(state) > -1;
29 | }
30 |
31 | const supportedBlendFactors = [
32 | WebGLConstants.ZERO,
33 | WebGLConstants.ONE,
34 | WebGLConstants.SRC_COLOR,
35 | WebGLConstants.ONE_MINUS_SRC_COLOR,
36 | WebGLConstants.SRC_ALPHA,
37 | WebGLConstants.ONE_MINUS_SRC_ALPHA,
38 | WebGLConstants.DST_ALPHA,
39 | WebGLConstants.ONE_MINUS_DST_ALPHA,
40 | WebGLConstants.DST_COLOR,
41 | WebGLConstants.ONE_MINUS_DST_COLOR,
42 | ];
43 |
44 | // If any of the blend factors are not supported, return the default
45 | function getSupportedBlendFactors(value, defaultValue) {
46 | if (!defined(value)) {
47 | return defaultValue;
48 | }
49 |
50 | for (let i = 0; i < 4; i++) {
51 | if (supportedBlendFactors.indexOf(value[i]) === -1) {
52 | return defaultValue;
53 | }
54 | }
55 |
56 | return value;
57 | }
58 |
59 | /**
60 | * Move glTF 1.0 technique render states to glTF 2.0 materials properties and KHR_blend extension.
61 | *
62 | * @param {object} gltf A javascript object containing a glTF asset.
63 | * @returns {object} The updated glTF asset.
64 | *
65 | * @private
66 | */
67 | function moveTechniqueRenderStates(gltf) {
68 | const blendingForTechnique = {};
69 | const materialPropertiesForTechnique = {};
70 | const techniquesLegacy = gltf.techniques;
71 | if (!defined(techniquesLegacy)) {
72 | return gltf;
73 | }
74 |
75 | ForEach.technique(gltf, function (techniqueLegacy, techniqueIndex) {
76 | const renderStates = techniqueLegacy.states;
77 | if (defined(renderStates)) {
78 | const materialProperties = (materialPropertiesForTechnique[
79 | techniqueIndex
80 | ] = {});
81 |
82 | // If BLEND is enabled, the material should have alpha mode BLEND
83 | if (isStateEnabled(renderStates, WebGLConstants.BLEND)) {
84 | materialProperties.alphaMode = "BLEND";
85 |
86 | const blendFunctions = renderStates.functions;
87 | if (
88 | defined(blendFunctions) &&
89 | (defined(blendFunctions.blendEquationSeparate) ||
90 | defined(blendFunctions.blendFuncSeparate))
91 | ) {
92 | blendingForTechnique[techniqueIndex] = {
93 | blendEquation: defaultValue(
94 | blendFunctions.blendEquationSeparate,
95 | defaultBlendEquation,
96 | ),
97 | blendFactors: getSupportedBlendFactors(
98 | blendFunctions.blendFuncSeparate,
99 | defaultBlendFactors,
100 | ),
101 | };
102 | }
103 | }
104 |
105 | // If CULL_FACE is not enabled, the material should be doubleSided
106 | if (!isStateEnabled(renderStates, WebGLConstants.CULL_FACE)) {
107 | materialProperties.doubleSided = true;
108 | }
109 |
110 | delete techniqueLegacy.states;
111 | }
112 | });
113 |
114 | if (Object.keys(blendingForTechnique).length > 0) {
115 | if (!defined(gltf.extensions)) {
116 | gltf.extensions = {};
117 | }
118 |
119 | addExtensionsUsed(gltf, "KHR_blend");
120 | }
121 |
122 | ForEach.material(gltf, function (material) {
123 | if (defined(material.technique)) {
124 | const materialProperties =
125 | materialPropertiesForTechnique[material.technique];
126 | ForEach.objectLegacy(materialProperties, function (value, property) {
127 | material[property] = value;
128 | });
129 |
130 | const blending = blendingForTechnique[material.technique];
131 | if (defined(blending)) {
132 | if (!defined(material.extensions)) {
133 | material.extensions = {};
134 | }
135 |
136 | material.extensions.KHR_blend = blending;
137 | }
138 | }
139 | });
140 |
141 | return gltf;
142 | }
143 |
--------------------------------------------------------------------------------
/lib/moveTechniquesToExtension.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const Cesium = require("cesium");
4 | const addExtensionsUsed = require("./addExtensionsUsed");
5 | const addExtensionsRequired = require("./addExtensionsRequired");
6 | const addToArray = require("./addToArray");
7 | const ForEach = require("./ForEach");
8 |
9 | const defined = Cesium.defined;
10 |
11 | module.exports = moveTechniquesToExtension;
12 |
13 | /**
14 | * Move glTF 1.0 material techniques to glTF 2.0 KHR_techniques_webgl extension.
15 | *
16 | * @param {object} gltf A javascript object containing a glTF asset.
17 | * @returns {object} The updated glTF asset.
18 | *
19 | * @private
20 | */
21 | function moveTechniquesToExtension(gltf) {
22 | const techniquesLegacy = gltf.techniques;
23 | const mappedUniforms = {};
24 | const updatedTechniqueIndices = {};
25 | const seenPrograms = {};
26 | if (defined(techniquesLegacy)) {
27 | const extension = {
28 | programs: [],
29 | shaders: [],
30 | techniques: [],
31 | };
32 |
33 | // Some 1.1 models have a glExtensionsUsed property that can be transferred to program.glExtensions
34 | const glExtensions = gltf.glExtensionsUsed;
35 | delete gltf.glExtensionsUsed;
36 |
37 | ForEach.technique(gltf, function (techniqueLegacy, techniqueId) {
38 | const technique = {
39 | name: techniqueLegacy.name,
40 | program: undefined,
41 | attributes: {},
42 | uniforms: {},
43 | };
44 |
45 | let parameterLegacy;
46 | ForEach.techniqueAttribute(
47 | techniqueLegacy,
48 | function (parameterName, attributeName) {
49 | parameterLegacy = techniqueLegacy.parameters[parameterName];
50 | technique.attributes[attributeName] = {
51 | semantic: parameterLegacy.semantic,
52 | };
53 | },
54 | );
55 |
56 | ForEach.techniqueUniform(
57 | techniqueLegacy,
58 | function (parameterName, uniformName) {
59 | parameterLegacy = techniqueLegacy.parameters[parameterName];
60 | technique.uniforms[uniformName] = {
61 | count: parameterLegacy.count,
62 | node: parameterLegacy.node,
63 | type: parameterLegacy.type,
64 | semantic: parameterLegacy.semantic,
65 | value: parameterLegacy.value,
66 | };
67 |
68 | // Store the name of the uniform to update material values.
69 | if (!defined(mappedUniforms[techniqueId])) {
70 | mappedUniforms[techniqueId] = {};
71 | }
72 | mappedUniforms[techniqueId][parameterName] = uniformName;
73 | },
74 | );
75 |
76 | if (!defined(seenPrograms[techniqueLegacy.program])) {
77 | const programLegacy = gltf.programs[techniqueLegacy.program];
78 |
79 | const program = {
80 | name: programLegacy.name,
81 | fragmentShader: undefined,
82 | vertexShader: undefined,
83 | glExtensions: glExtensions,
84 | };
85 |
86 | const fs = gltf.shaders[programLegacy.fragmentShader];
87 | program.fragmentShader = addToArray(extension.shaders, fs, true);
88 |
89 | const vs = gltf.shaders[programLegacy.vertexShader];
90 | program.vertexShader = addToArray(extension.shaders, vs, true);
91 |
92 | technique.program = addToArray(extension.programs, program);
93 | seenPrograms[techniqueLegacy.program] = technique.program;
94 | } else {
95 | technique.program = seenPrograms[techniqueLegacy.program];
96 | }
97 |
98 | // Store the index of the new technique to reference instead.
99 | updatedTechniqueIndices[techniqueId] = addToArray(
100 | extension.techniques,
101 | technique,
102 | );
103 | });
104 |
105 | if (extension.techniques.length > 0) {
106 | if (!defined(gltf.extensions)) {
107 | gltf.extensions = {};
108 | }
109 |
110 | gltf.extensions.KHR_techniques_webgl = extension;
111 | addExtensionsUsed(gltf, "KHR_techniques_webgl");
112 | addExtensionsRequired(gltf, "KHR_techniques_webgl");
113 | }
114 | }
115 |
116 | ForEach.material(gltf, function (material) {
117 | if (defined(material.technique)) {
118 | const materialExtension = {
119 | technique: updatedTechniqueIndices[material.technique],
120 | };
121 |
122 | ForEach.objectLegacy(material.values, function (value, parameterName) {
123 | if (!defined(materialExtension.values)) {
124 | materialExtension.values = {};
125 | }
126 |
127 | const uniformName = mappedUniforms[material.technique][parameterName];
128 | if (defined(uniformName)) {
129 | materialExtension.values[uniformName] = value;
130 | }
131 | });
132 |
133 | if (!defined(material.extensions)) {
134 | material.extensions = {};
135 | }
136 |
137 | material.extensions.KHR_techniques_webgl = materialExtension;
138 | }
139 |
140 | delete material.technique;
141 | delete material.values;
142 | });
143 |
144 | delete gltf.techniques;
145 | delete gltf.programs;
146 | delete gltf.shaders;
147 |
148 | return gltf;
149 | }
150 |
--------------------------------------------------------------------------------
/lib/numberOfComponentsForType.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = numberOfComponentsForType;
4 |
5 | /**
6 | * Utility function for retrieving the number of components in a given type.
7 | *
8 | * @param {string} type glTF type
9 | * @returns {number} The number of components in that type.
10 | *
11 | * @private
12 | */
13 | function numberOfComponentsForType(type) {
14 | switch (type) {
15 | case "SCALAR":
16 | return 1;
17 | case "VEC2":
18 | return 2;
19 | case "VEC3":
20 | return 3;
21 | case "VEC4":
22 | case "MAT2":
23 | return 4;
24 | case "MAT3":
25 | return 9;
26 | case "MAT4":
27 | return 16;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/lib/parseGlb.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 | const addPipelineExtras = require("./addPipelineExtras");
4 | const removeExtensionsUsed = require("./removeExtensionsUsed");
5 |
6 | const defaultValue = Cesium.defaultValue;
7 | const defined = Cesium.defined;
8 | const getMagic = Cesium.getMagic;
9 | const getStringFromTypedArray = Cesium.getStringFromTypedArray;
10 | const RuntimeError = Cesium.RuntimeError;
11 |
12 | const sizeOfUint32 = 4;
13 |
14 | module.exports = parseGlb;
15 |
16 | /**
17 | * Convert a binary glTF to glTF.
18 | *
19 | * The returned glTF has pipeline extras included. The embedded binary data is stored in gltf.buffers[0].extras._pipeline.source.
20 | *
21 | * @param {Buffer} glb The glb data to parse.
22 | * @returns {object} A javascript object containing a glTF asset with pipeline extras included.
23 | *
24 | * @private
25 | */
26 | function parseGlb(glb) {
27 | // Check that the magic string is present
28 | const magic = getMagic(glb);
29 | if (magic !== "glTF") {
30 | throw new RuntimeError("File is not valid binary glTF");
31 | }
32 |
33 | const header = readHeader(glb, 0, 5);
34 | const version = header[1];
35 | if (version !== 1 && version !== 2) {
36 | throw new RuntimeError("Binary glTF version is not 1 or 2");
37 | }
38 |
39 | if (version === 1) {
40 | return parseGlbVersion1(glb, header);
41 | }
42 |
43 | return parseGlbVersion2(glb, header);
44 | }
45 |
46 | function readHeader(glb, byteOffset, count) {
47 | const dataView = new DataView(glb.buffer);
48 | const header = new Array(count);
49 | for (let i = 0; i < count; ++i) {
50 | header[i] = dataView.getUint32(
51 | glb.byteOffset + byteOffset + i * sizeOfUint32,
52 | true,
53 | );
54 | }
55 | return header;
56 | }
57 |
58 | function parseGlbVersion1(glb, header) {
59 | const length = header[2];
60 | const contentLength = header[3];
61 | const contentFormat = header[4];
62 |
63 | // Check that the content format is 0, indicating that it is JSON
64 | if (contentFormat !== 0) {
65 | throw new RuntimeError("Binary glTF scene format is not JSON");
66 | }
67 |
68 | const jsonStart = 20;
69 | const binaryStart = jsonStart + contentLength;
70 |
71 | const contentString = getStringFromTypedArray(glb, jsonStart, contentLength);
72 | const gltf = JSON.parse(contentString);
73 | addPipelineExtras(gltf);
74 |
75 | const binaryBuffer = glb.subarray(binaryStart, length);
76 |
77 | const buffers = gltf.buffers;
78 | if (defined(buffers) && Object.keys(buffers).length > 0) {
79 | // In some older models, the binary glTF buffer is named KHR_binary_glTF
80 | const binaryGltfBuffer = defaultValue(
81 | buffers.binary_glTF,
82 | buffers.KHR_binary_glTF,
83 | );
84 | if (defined(binaryGltfBuffer)) {
85 | binaryGltfBuffer.extras._pipeline.source = binaryBuffer;
86 | delete binaryGltfBuffer.uri;
87 | }
88 | }
89 | // Remove the KHR_binary_glTF extension
90 | removeExtensionsUsed(gltf, "KHR_binary_glTF");
91 | return gltf;
92 | }
93 |
94 | function parseGlbVersion2(glb, header) {
95 | const length = header[2];
96 | let byteOffset = 12;
97 | let gltf;
98 | let binaryBuffer;
99 | while (byteOffset < length) {
100 | const chunkHeader = readHeader(glb, byteOffset, 2);
101 | const chunkLength = chunkHeader[0];
102 | const chunkType = chunkHeader[1];
103 | byteOffset += 8;
104 | const chunkBuffer = glb.subarray(byteOffset, byteOffset + chunkLength);
105 | byteOffset += chunkLength;
106 | // Load JSON chunk
107 | if (chunkType === 0x4e4f534a) {
108 | const jsonString = getStringFromTypedArray(chunkBuffer);
109 | gltf = JSON.parse(jsonString);
110 | addPipelineExtras(gltf);
111 | }
112 | // Load Binary chunk
113 | else if (chunkType === 0x004e4942) {
114 | binaryBuffer = chunkBuffer;
115 | }
116 | }
117 | if (defined(gltf) && defined(binaryBuffer)) {
118 | const buffers = gltf.buffers;
119 | if (defined(buffers) && buffers.length > 0) {
120 | const buffer = buffers[0];
121 | buffer.extras._pipeline.source = binaryBuffer;
122 | }
123 | }
124 | return gltf;
125 | }
126 |
--------------------------------------------------------------------------------
/lib/processGlb.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const parseGlb = require("./parseGlb");
3 | const gltfToGlb = require("./gltfToGlb");
4 |
5 | module.exports = processGlb;
6 |
7 | /**
8 | * Run a glb through the gltf-pipeline.
9 | *
10 | * @param {Buffer} glb A buffer containing the glb contents.
11 | * @param {object} [options] The same options object as {@link processGltf}
12 | * @returns {Promise} A promise that resolves to an object containing the glb and a dictionary containing separate resources.
13 | */
14 | function processGlb(glb, options) {
15 | const gltf = parseGlb(glb);
16 | return gltfToGlb(gltf, options);
17 | }
18 |
--------------------------------------------------------------------------------
/lib/processGltf.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 | const Promise = require("bluebird");
4 | const addDefaults = require("./addDefaults");
5 | const addPipelineExtras = require("./addPipelineExtras");
6 | const getStatistics = require("./getStatistics");
7 | const readResources = require("./readResources");
8 | const removeDefaults = require("./removeDefaults");
9 | const removePipelineExtras = require("./removePipelineExtras");
10 | const removeUnusedElements = require("./removeUnusedElements");
11 | const updateVersion = require("./updateVersion");
12 | const writeResources = require("./writeResources");
13 | const compressDracoMeshes = require("./compressDracoMeshes");
14 |
15 | const clone = Cesium.clone;
16 | const defaultValue = Cesium.defaultValue;
17 | const defined = Cesium.defined;
18 |
19 | module.exports = processGltf;
20 |
21 | /**
22 | * Run a glTF through the gltf-pipeline.
23 | *
24 | * @param {object} gltf A javascript object containing a glTF asset. The glTF is modified in place.
25 | * @param {object} [options] An object with the following properties:
26 | * @param {string} [options.resourceDirectory] The path for reading separate resources.
27 | * @param {string} [options.name] The name of the glTF asset, for writing separate resources.
28 | * @param {boolean} [options.separate = false] Write separate buffers, shaders, and textures instead of embedding them in the glTF.
29 | * @param {boolean} [options.separateTextures = false] Write out separate textures only.
30 | * @param {boolean} [options.stats = false] Print statistics to console for input and output glTF files.
31 | * @param {object} [options.dracoOptions] Options to pass to the compressDracoMeshes stage. If undefined, stage is not run.
32 | * @param {Stage[]} [options.customStages] Custom stages to run on the glTF asset.
33 | * @param {Logger} [options.logger] A callback function for handling logged messages. Defaults to console.log.
34 | * @param {string[]} [options.baseColorTextureNames] Names of uniforms that indicate base color textures.
35 | * @param {string[]} [options.baseColorFactorNames] Names of uniforms that indicate base color factors.
36 | *
37 | * @returns {Promise} A promise that resolves to an object containing the glTF and a dictionary containing separate resources.
38 | */
39 | function processGltf(gltf, options) {
40 | const defaults = processGltf.defaults;
41 | options = defined(options) ? clone(options) : {};
42 | options.separateBuffers = defaultValue(options.separate, defaults.separate);
43 | options.separateShaders = defaultValue(options.separate, defaults.separate);
44 | options.separateTextures =
45 | defaultValue(options.separateTextures, defaults.separateTextures) ||
46 | options.separate;
47 | options.stats = defaultValue(options.stats, defaults.stats);
48 | options.logger = defaultValue(options.logger, getDefaultLogger());
49 | options.separateResources = {};
50 | options.customStages = defaultValue(options.customStages, []);
51 |
52 | const preStages = [
53 | addPipelineExtras,
54 | readResources,
55 | updateVersion,
56 | addDefaults,
57 | ];
58 |
59 | const postStages = [writeResources, removePipelineExtras, removeDefaults];
60 |
61 | const pipelineStages = getStages(options);
62 | const stages = preStages.concat(
63 | options.customStages,
64 | pipelineStages,
65 | postStages,
66 | );
67 |
68 | return Promise.each(stages, function (stage) {
69 | return stage(gltf, options);
70 | }).then(function () {
71 | printStats(gltf, options, true);
72 | return {
73 | gltf: gltf,
74 | separateResources: options.separateResources,
75 | };
76 | });
77 | }
78 |
79 | function printStats(gltf, options, processed) {
80 | if (options.stats) {
81 | options.logger(processed ? "Statistics after:" : "Statistics before:");
82 | options.logger(getStatistics(gltf).toString());
83 | }
84 | }
85 |
86 | function getStages(options) {
87 | const stages = [];
88 | if (defined(options.dracoOptions)) {
89 | stages.push(compressDracoMeshes);
90 | }
91 | if (!options.keepUnusedElements) {
92 | stages.push(function (gltf, options) {
93 | removeUnusedElements(gltf);
94 | });
95 | }
96 | return stages;
97 | }
98 |
99 | function getDefaultLogger() {
100 | return function (message) {
101 | console.log(message);
102 | };
103 | }
104 |
105 | /**
106 | * Default values that will be used when calling processGltf(options) unless specified in the options object.
107 | */
108 | processGltf.defaults = {
109 | /**
110 | * Gets or sets whether to write out separate buffers, shaders, and textures instead of embedding them in the glTF
111 | * @type Boolean
112 | * @default false
113 | */
114 | separate: false,
115 | /**
116 | * Gets or sets whether to write out separate textures only.
117 | * @type Boolean
118 | * @default false
119 | */
120 | separateTextures: false,
121 | /**
122 | * Gets or sets whether to print statistics to console for input and output glTF files.
123 | * @type Boolean
124 | * @default false
125 | */
126 | stats: false,
127 | /**
128 | * Keep unused 'node', 'mesh' and 'material' elements.
129 | * @type Boolean
130 | * @default false
131 | */
132 | keepUnusedElements: false,
133 | /**
134 | * When false, materials with KHR_techniques_webgl, KHR_blend, or KHR_materials_common will be converted to PBR.
135 | * @type Boolean
136 | * @default false
137 | */
138 | keepLegacyExtensions: false,
139 | /**
140 | * Gets or sets whether to compress the meshes using Draco. Adds the KHR_draco_mesh_compression extension.
141 | * @type Boolean
142 | * @default false
143 | */
144 | compressDracoMeshes: false,
145 | };
146 |
147 | /**
148 | * A callback function that logs messages.
149 | * @callback Logger
150 | *
151 | * @param {string} message The message to log.
152 | */
153 |
154 | /**
155 | * A stage that processes a glTF asset.
156 | * @callback Stage
157 | *
158 | * @param {object} gltf The glTF asset.
159 | * @returns {Promise|Object} The glTF asset or a promise that resolves to the glTF asset.
160 | */
161 |
--------------------------------------------------------------------------------
/lib/readAccessorPacked.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 | const getAccessorByteStride = require("./getAccessorByteStride");
4 | const getComponentReader = require("./getComponentReader");
5 | const numberOfComponentsForType = require("./numberOfComponentsForType");
6 |
7 | const ComponentDatatype = Cesium.ComponentDatatype;
8 | const defined = Cesium.defined;
9 |
10 | module.exports = readAccessorPacked;
11 |
12 | /**
13 | * Returns the accessor data in a contiguous array.
14 | *
15 | * @param {object} gltf A javascript object containing a glTF asset.
16 | * @param {object} accessor The accessor.
17 | * @returns {Array} The accessor values in a contiguous array.
18 | *
19 | * @private
20 | */
21 | function readAccessorPacked(gltf, accessor) {
22 | const byteStride = getAccessorByteStride(gltf, accessor);
23 | const componentTypeByteLength = ComponentDatatype.getSizeInBytes(
24 | accessor.componentType,
25 | );
26 | const numberOfComponents = numberOfComponentsForType(accessor.type);
27 | const count = accessor.count;
28 | const values = new Array(numberOfComponents * count);
29 |
30 | if (!defined(accessor.bufferView)) {
31 | return values.fill(0);
32 | }
33 |
34 | const bufferView = gltf.bufferViews[accessor.bufferView];
35 | const source = gltf.buffers[bufferView.buffer].extras._pipeline.source;
36 | let byteOffset =
37 | accessor.byteOffset + bufferView.byteOffset + source.byteOffset;
38 |
39 | const dataView = new DataView(source.buffer);
40 | const components = new Array(numberOfComponents);
41 | const componentReader = getComponentReader(accessor.componentType);
42 |
43 | for (let i = 0; i < count; ++i) {
44 | componentReader(
45 | dataView,
46 | byteOffset,
47 | numberOfComponents,
48 | componentTypeByteLength,
49 | components,
50 | );
51 | for (let j = 0; j < numberOfComponents; ++j) {
52 | values[i * numberOfComponents + j] = components[j];
53 | }
54 | byteOffset += byteStride;
55 | }
56 | return values;
57 | }
58 |
--------------------------------------------------------------------------------
/lib/readResources.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 | const fsExtra = require("fs-extra");
4 | const path = require("path");
5 | const Promise = require("bluebird");
6 | const { URL } = require("url");
7 |
8 | const addPipelineExtras = require("./addPipelineExtras");
9 | const dataUriToBuffer = require("./dataUriToBuffer");
10 | const { fileURLToPath, pathToFileURL } = require("./FileUrl");
11 | const ForEach = require("./ForEach");
12 |
13 | const defined = Cesium.defined;
14 | const defaultValue = Cesium.defaultValue;
15 | const isDataUri = Cesium.isDataUri;
16 | const RuntimeError = Cesium.RuntimeError;
17 |
18 | module.exports = readResources;
19 |
20 | /**
21 | * Read data uris, buffer views, or files referenced by the glTF into buffers.
22 | * The buffer data is placed into extras._pipeline.source for the corresponding object.
23 | * This stage runs before updateVersion and handles both glTF 1.0 and glTF 2.0 assets.
24 | *
25 | * @param {object} gltf A javascript object containing a glTF asset.
26 | * @param {object} [options] Object with the following properties:
27 | * @param {string} [options.resourceDirectory] The path to look in when reading separate files.
28 | * @returns {Promise} A promise that resolves to the glTF asset when all resources are read.
29 | *
30 | * @private
31 | */
32 | function readResources(gltf, options) {
33 | addPipelineExtras(gltf);
34 | options = defaultValue(options, {});
35 |
36 | // Make sure its an absolute path with a trailing separator
37 | options.resourceDirectory = defined(options.resourceDirectory)
38 | ? path.resolve(options.resourceDirectory) + path.sep
39 | : undefined;
40 |
41 | const bufferPromises = [];
42 | const resourcePromises = [];
43 |
44 | ForEach.buffer(gltf, function (buffer) {
45 | bufferPromises.push(readBuffer(gltf, buffer, options));
46 | });
47 |
48 | // Buffers need to be read first because images and shader may resolve to bufferViews
49 | return Promise.all(bufferPromises)
50 | .then(function () {
51 | ForEach.shader(gltf, function (shader) {
52 | resourcePromises.push(readShader(gltf, shader, options));
53 | });
54 | ForEach.image(gltf, function (image) {
55 | resourcePromises.push(readImage(gltf, image, options));
56 | });
57 | return Promise.all(resourcePromises);
58 | })
59 | .then(function () {
60 | return gltf;
61 | });
62 | }
63 |
64 | function readBuffer(gltf, buffer, options) {
65 | return readResource(gltf, buffer, false, options).then(function (data) {
66 | if (defined(data)) {
67 | buffer.extras._pipeline.source = data;
68 | }
69 | });
70 | }
71 |
72 | function readImage(gltf, image, options) {
73 | return readResource(gltf, image, true, options).then(function (data) {
74 | image.extras._pipeline.source = data;
75 | });
76 | }
77 |
78 | function readShader(gltf, shader, options) {
79 | return readResource(gltf, shader, true, options).then(function (data) {
80 | shader.extras._pipeline.source = data.toString();
81 | });
82 | }
83 |
84 | function readResource(gltf, object, saveResourceId, options) {
85 | const uri = object.uri;
86 | delete object.uri; // Don't hold onto the uri, its contents will be stored in extras._pipeline.source
87 |
88 | // Source already exists if the gltf was converted from a glb
89 | const source = object.extras._pipeline.source;
90 | if (defined(source)) {
91 | return Promise.resolve(Buffer.from(source));
92 | }
93 | // Handle reading buffer view from 1.0 glb model
94 | const extensions = object.extensions;
95 | if (defined(extensions)) {
96 | const khrBinaryGltf = extensions.KHR_binary_glTF;
97 | if (defined(khrBinaryGltf)) {
98 | return Promise.resolve(
99 | readBufferView(gltf, khrBinaryGltf.bufferView, object, saveResourceId),
100 | );
101 | }
102 | }
103 | if (defined(object.bufferView)) {
104 | return Promise.resolve(
105 | readBufferView(gltf, object.bufferView, object, saveResourceId),
106 | );
107 | }
108 | if (!defined(uri)) {
109 | return Promise.resolve(undefined);
110 | }
111 | if (isDataUri(uri)) {
112 | return Promise.resolve(dataUriToBuffer(uri));
113 | }
114 | return readFile(object, uri, saveResourceId, options);
115 | }
116 |
117 | function readBufferView(gltf, bufferViewId, object, saveResourceId) {
118 | if (saveResourceId) {
119 | object.extras._pipeline.resourceId = bufferViewId;
120 | }
121 | const bufferView = gltf.bufferViews[bufferViewId];
122 | const buffer = gltf.buffers[bufferView.buffer];
123 | const source = buffer.extras._pipeline.source;
124 | const byteOffset = defaultValue(bufferView.byteOffset, 0);
125 | return source.slice(byteOffset, byteOffset + bufferView.byteLength);
126 | }
127 |
128 | function readFile(object, uri, saveResourceId, options) {
129 | const resourceDirectory = options.resourceDirectory;
130 | const hasResourceDirectory = defined(resourceDirectory);
131 |
132 | // Resolve the URL
133 | let absoluteUrl;
134 | try {
135 | absoluteUrl = new URL(
136 | uri,
137 | hasResourceDirectory ? pathToFileURL(resourceDirectory) : undefined,
138 | );
139 | } catch (error) {
140 | return Promise.reject(
141 | new RuntimeError(
142 | "glTF model references separate files but no resourceDirectory is supplied",
143 | ),
144 | );
145 | }
146 |
147 | // Generate file paths for the resource
148 | const absolutePath = fileURLToPath(absoluteUrl);
149 | const relativePath = hasResourceDirectory
150 | ? path.relative(resourceDirectory, absolutePath)
151 | : path.basename(absolutePath);
152 |
153 | if (!defined(object.name)) {
154 | const extension = path.extname(relativePath);
155 | object.name = path.basename(relativePath, extension);
156 | }
157 |
158 | if (saveResourceId) {
159 | object.extras._pipeline.resourceId = absolutePath;
160 | }
161 |
162 | object.extras._pipeline.absolutePath = absolutePath;
163 | object.extras._pipeline.relativePath = relativePath;
164 | return fsExtra.readFile(absolutePath);
165 | }
166 |
--------------------------------------------------------------------------------
/lib/removeDefaults.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 | const ForEach = require("./ForEach");
4 |
5 | const defined = Cesium.defined;
6 | const Matrix4 = Cesium.Matrix4;
7 |
8 | module.exports = removeDefaults;
9 |
10 | /**
11 | * Remove default values from the glTF. Not exhaustive.
12 | *
13 | * @param {object} gltf A javascript object containing a glTF asset.
14 | * @returns {object} glTF with default values removed.
15 | *
16 | * @private
17 | */
18 | function removeDefaults(gltf) {
19 | ForEach.node(gltf, function (node) {
20 | if (
21 | defined(node.matrix) &&
22 | Matrix4.equals(Matrix4.fromArray(node.matrix), Matrix4.IDENTITY)
23 | ) {
24 | delete node.matrix;
25 | }
26 | });
27 | ForEach.accessor(gltf, function (accessor) {
28 | if (accessor.normalized === false) {
29 | delete accessor.normalized;
30 | }
31 | });
32 | return gltf;
33 | }
34 |
--------------------------------------------------------------------------------
/lib/removeExtension.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 | const ForEach = require("./ForEach");
4 | const removeExtensionsUsed = require("./removeExtensionsUsed");
5 |
6 | const defined = Cesium.defined;
7 |
8 | module.exports = removeExtension;
9 |
10 | /**
11 | * Removes an extension from gltf.extensions, gltf.extensionsUsed, gltf.extensionsRequired, and any other objects in the glTF if it is present.
12 | *
13 | * @param {object} gltf A javascript object containing a glTF asset.
14 | * @param {string} extension The extension to remove.
15 | *
16 | * @returns {*} The extension data removed from gltf.extensions.
17 | */
18 | function removeExtension(gltf, extension) {
19 | removeExtensionsUsed(gltf, extension); // Also removes from extensionsRequired
20 |
21 | if (extension === "CESIUM_RTC") {
22 | removeCesiumRTC(gltf);
23 | }
24 |
25 | return removeExtensionAndTraverse(gltf, extension);
26 | }
27 |
28 | function removeCesiumRTC(gltf) {
29 | ForEach.technique(gltf, function (technique) {
30 | ForEach.techniqueUniform(technique, function (uniform) {
31 | if (uniform.semantic === "CESIUM_RTC_MODELVIEW") {
32 | uniform.semantic = "MODELVIEW";
33 | }
34 | });
35 | });
36 | }
37 |
38 | function removeExtensionAndTraverse(object, extension) {
39 | if (Array.isArray(object)) {
40 | const length = object.length;
41 | for (let i = 0; i < length; ++i) {
42 | removeExtensionAndTraverse(object[i], extension);
43 | }
44 | } else if (
45 | object !== null &&
46 | typeof object === "object" &&
47 | object.constructor === Object
48 | ) {
49 | const extensions = object.extensions;
50 | let extensionData;
51 | if (defined(extensions)) {
52 | extensionData = extensions[extension];
53 | if (defined(extensionData)) {
54 | delete extensions[extension];
55 | if (Object.keys(extensions).length === 0) {
56 | delete object.extensions;
57 | }
58 | }
59 | }
60 | for (const key in object) {
61 | if (Object.prototype.hasOwnProperty.call(object, key)) {
62 | removeExtensionAndTraverse(object[key], extension);
63 | }
64 | }
65 | return extensionData;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/lib/removeExtensionsRequired.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 |
4 | const defined = Cesium.defined;
5 |
6 | module.exports = removeExtensionsRequired;
7 |
8 | /**
9 | * Removes an extension from gltf.extensionsRequired if it is present.
10 | *
11 | * @param {object} gltf A javascript object containing a glTF asset.
12 | * @param {string} extension The extension to remove.
13 | *
14 | * @private
15 | */
16 | function removeExtensionsRequired(gltf, extension) {
17 | const extensionsRequired = gltf.extensionsRequired;
18 | if (defined(extensionsRequired)) {
19 | const index = extensionsRequired.indexOf(extension);
20 | if (index >= 0) {
21 | extensionsRequired.splice(index, 1);
22 | }
23 | if (extensionsRequired.length === 0) {
24 | delete gltf.extensionsRequired;
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lib/removeExtensionsUsed.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 | const removeExtensionsRequired = require("./removeExtensionsRequired");
4 |
5 | const defined = Cesium.defined;
6 |
7 | module.exports = removeExtensionsUsed;
8 |
9 | /**
10 | * Removes an extension from gltf.extensionsUsed and gltf.extensionsRequired if it is present.
11 | *
12 | * @param {object} gltf A javascript object containing a glTF asset.
13 | * @param {string} extension The extension to remove.
14 | *
15 | * @private
16 | */
17 | function removeExtensionsUsed(gltf, extension) {
18 | const extensionsUsed = gltf.extensionsUsed;
19 | if (defined(extensionsUsed)) {
20 | const index = extensionsUsed.indexOf(extension);
21 | if (index >= 0) {
22 | extensionsUsed.splice(index, 1);
23 | }
24 | removeExtensionsRequired(gltf, extension);
25 | if (extensionsUsed.length === 0) {
26 | delete gltf.extensionsUsed;
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/lib/removePipelineExtras.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 | const ForEach = require("./ForEach");
4 |
5 | const defined = Cesium.defined;
6 |
7 | module.exports = removePipelineExtras;
8 |
9 | /**
10 | * Iterate through the objects within the glTF and delete their pipeline extras object.
11 | *
12 | * @param {object} gltf A javascript object containing a glTF asset.
13 | * @returns {object} glTF with no pipeline extras.
14 | *
15 | * @private
16 | */
17 | function removePipelineExtras(gltf) {
18 | ForEach.shader(gltf, function (shader) {
19 | removeExtras(shader);
20 | });
21 | ForEach.buffer(gltf, function (buffer) {
22 | removeExtras(buffer);
23 | });
24 | ForEach.image(gltf, function (image) {
25 | removeExtras(image);
26 | });
27 |
28 | removeExtras(gltf);
29 |
30 | return gltf;
31 | }
32 |
33 | function removeExtras(object) {
34 | if (!defined(object.extras)) {
35 | return;
36 | }
37 |
38 | if (defined(object.extras._pipeline)) {
39 | delete object.extras._pipeline;
40 | }
41 |
42 | if (Object.keys(object.extras).length === 0) {
43 | delete object.extras;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/lib/updateAccessorComponentTypes.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 | const addBuffer = require("./addBuffer");
4 | const ForEach = require("./ForEach");
5 | const readAccessorPacked = require("./readAccessorPacked");
6 |
7 | const ComponentDatatype = Cesium.ComponentDatatype;
8 | const WebGLConstants = Cesium.WebGLConstants;
9 |
10 | module.exports = updateAccessorComponentTypes;
11 |
12 | /**
13 | * Update accessors referenced by JOINTS_0 and WEIGHTS_0 attributes to use correct component types.
14 | *
15 | * @param {object} gltf A javascript object containing a glTF asset.
16 | * @returns {object} The glTF asset with compressed meshes.
17 | *
18 | * @private
19 | */
20 | function updateAccessorComponentTypes(gltf) {
21 | let componentType;
22 | ForEach.accessorWithSemantic(gltf, "JOINTS_0", function (accessorId) {
23 | const accessor = gltf.accessors[accessorId];
24 | componentType = accessor.componentType;
25 | if (componentType === WebGLConstants.BYTE) {
26 | convertType(gltf, accessor, ComponentDatatype.UNSIGNED_BYTE);
27 | } else if (
28 | componentType !== WebGLConstants.UNSIGNED_BYTE &&
29 | componentType !== WebGLConstants.UNSIGNED_SHORT
30 | ) {
31 | convertType(gltf, accessor, ComponentDatatype.UNSIGNED_SHORT);
32 | }
33 | });
34 | ForEach.accessorWithSemantic(gltf, "WEIGHTS_0", function (accessorId) {
35 | const accessor = gltf.accessors[accessorId];
36 | componentType = accessor.componentType;
37 | if (componentType === WebGLConstants.BYTE) {
38 | convertType(gltf, accessor, ComponentDatatype.UNSIGNED_BYTE);
39 | } else if (componentType === WebGLConstants.SHORT) {
40 | convertType(gltf, accessor, ComponentDatatype.UNSIGNED_SHORT);
41 | }
42 | });
43 |
44 | return gltf;
45 | }
46 |
47 | function convertType(gltf, accessor, updatedComponentType) {
48 | const typedArray = ComponentDatatype.createTypedArray(
49 | updatedComponentType,
50 | readAccessorPacked(gltf, accessor),
51 | );
52 | const newBuffer = new Uint8Array(typedArray.buffer);
53 | accessor.bufferView = addBuffer(gltf, newBuffer);
54 | accessor.componentType = updatedComponentType;
55 | accessor.byteOffset = 0;
56 | }
57 |
--------------------------------------------------------------------------------
/lib/usesExtension.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 |
4 | const defined = Cesium.defined;
5 |
6 | module.exports = usesExtension;
7 |
8 | /**
9 | * Checks whether the glTF uses the given extension.
10 | *
11 | * @param {object} gltf A javascript object containing a glTF asset.
12 | * @param {string} extension The name of the extension.
13 | * @returns {boolean} Whether the glTF uses the given extension.
14 | *
15 | * @private
16 | */
17 | function usesExtension(gltf, extension) {
18 | return (
19 | defined(gltf.extensionsUsed) && gltf.extensionsUsed.indexOf(extension) >= 0
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gltf-pipeline",
3 | "version": "4.1.0",
4 | "description": "Content pipeline tools for optimizing glTF assets.",
5 | "license": "Apache-2.0",
6 | "contributors": [
7 | {
8 | "name": "Richard Lee, Cesium GS, Inc., and Contributors",
9 | "url": "https://github.com/CesiumGS/gltf-pipeline/graphs/contributors"
10 | }
11 | ],
12 | "keywords": [
13 | "glTF",
14 | "WebGL"
15 | ],
16 | "homepage": "https://github.com/CesiumGS/gltf-pipeline",
17 | "repository": {
18 | "type": "git",
19 | "url": "git@github.com:CesiumGS/gltf-pipeline.git"
20 | },
21 | "bugs": {
22 | "url": "https://github.com/CesiumGS/gltf-pipeline/issues"
23 | },
24 | "main": "index.js",
25 | "engines": {
26 | "node": ">=16.0.0"
27 | },
28 | "dependencies": {
29 | "bluebird": "^3.7.2",
30 | "cesium": "^1.86.1",
31 | "draco3d": "^1.4.3",
32 | "fs-extra": "^11.0.0",
33 | "mime": "^3.0.0",
34 | "object-hash": "^3.0.0",
35 | "yargs": "^17.2.1"
36 | },
37 | "devDependencies": {
38 | "cloc": "^2.8.0",
39 | "dependency-tree": "^10.0.9",
40 | "eslint": "^8.0.1",
41 | "eslint-config-cesium": "^10.0.1",
42 | "eslint-config-prettier": "^9.0.0",
43 | "eslint-plugin-n": "^16.1.0",
44 | "gulp": "^4.0.2",
45 | "husky": "^8.0.3",
46 | "jasmine": "^5.0.0",
47 | "jasmine-spec-reporter": "^7.0.0",
48 | "jsdoc": "^4.0.0",
49 | "nyc": "^15.1.0",
50 | "prettier": "3.1.1",
51 | "lint-staged": "^15.0.2"
52 | },
53 | "lint-staged": {
54 | "*.(js|ts)": [
55 | "eslint --cache --quiet --fix",
56 | "prettier --write"
57 | ],
58 | "*.!(js|ts)": "prettier --write"
59 | },
60 | "scripts": {
61 | "prepare": "husky install",
62 | "pre-commit": "lint-staged",
63 | "jsdoc": "jsdoc ./lib -R ./README.md -d doc",
64 | "eslint-fix": "eslint \"./**/*.js\" --fix",
65 | "eslint": "eslint \"./**/*.js\" --cache --quiet",
66 | "eslint-watch": "gulp eslint-watch",
67 | "test": "gulp test",
68 | "test-watch": "gulp test-watch",
69 | "coverage": "gulp coverage",
70 | "cloc": "gulp cloc",
71 | "prettier": "prettier --write \"**/*\"",
72 | "prettier-check": "prettier --check \"**/*\"",
73 | "build-cesium": "gulp build-cesium",
74 | "generate-third-party": "gulp generate-third-party"
75 | },
76 | "bin": {
77 | "gltf-pipeline": "./bin/gltf-pipeline.js"
78 | }
79 | }
--------------------------------------------------------------------------------
/specs/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../.eslintrc.json",
3 | "env": {
4 | "jasmine": true
5 | },
6 | "rules": {
7 | "no-restricted-globals": ["error", "fdescribe", "fit"]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/specs/data/1.0/box-materials-common/box-materials-common.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/gltf-pipeline/cbbff2f212b5aa6163c7152d46ffcaf3a2ff3ac3/specs/data/1.0/box-materials-common/box-materials-common.bin
--------------------------------------------------------------------------------
/specs/data/1.0/box-materials-common/box-materials-common.gltf:
--------------------------------------------------------------------------------
1 | {
2 | "accessors": {
3 | "accessor_21": {
4 | "bufferView": "bufferView_29",
5 | "byteOffset": 0,
6 | "byteStride": 0,
7 | "componentType": 5123,
8 | "count": 36,
9 | "type": "SCALAR"
10 | },
11 | "accessor_23": {
12 | "bufferView": "bufferView_30",
13 | "byteOffset": 0,
14 | "byteStride": 12,
15 | "componentType": 5126,
16 | "count": 24,
17 | "max": [
18 | 0.5,
19 | 0.5,
20 | 0.5
21 | ],
22 | "min": [
23 | -0.5,
24 | -0.5,
25 | -0.5
26 | ],
27 | "type": "VEC3"
28 | },
29 | "accessor_25": {
30 | "bufferView": "bufferView_30",
31 | "byteOffset": 288,
32 | "byteStride": 12,
33 | "componentType": 5126,
34 | "count": 24,
35 | "max": [
36 | 1,
37 | 1,
38 | 1
39 | ],
40 | "min": [
41 | -1,
42 | -1,
43 | -1
44 | ],
45 | "type": "VEC3"
46 | }
47 | },
48 | "animations": {},
49 | "asset": {
50 | "generator": "collada2gltf@027f74366341d569dea42e9a68b7104cc3892054",
51 | "premultipliedAlpha": true,
52 | "profile": {
53 | "api": "WebGL",
54 | "version": "1.0.2"
55 | },
56 | "version": "1.0"
57 | },
58 | "bufferViews": {
59 | "bufferView_29": {
60 | "buffer": "Box",
61 | "byteLength": 72,
62 | "byteOffset": 0,
63 | "target": 34963
64 | },
65 | "bufferView_30": {
66 | "buffer": "Box",
67 | "byteLength": 576,
68 | "byteOffset": 72,
69 | "target": 34962
70 | }
71 | },
72 | "buffers": {
73 | "Box": {
74 | "byteLength": 648,
75 | "type": "arraybuffer",
76 | "uri": "box-materials-common.bin"
77 | }
78 | },
79 | "extensionsUsed": [
80 | "KHR_materials_common"
81 | ],
82 | "materials": {
83 | "Effect-Red": {
84 | "extensions": {
85 | "KHR_materials_common": {
86 | "doubleSided": false,
87 | "jointCount": 0,
88 | "technique": "PHONG",
89 | "transparent": false,
90 | "values": {
91 | "diffuse": [
92 | 0.8,
93 | 0,
94 | 0,
95 | 1
96 | ],
97 | "shininess": 256,
98 | "specular": [
99 | 0.2,
100 | 0.2,
101 | 0.2,
102 | 1
103 | ]
104 | }
105 | }
106 | },
107 | "name": "Red"
108 | }
109 | },
110 | "meshes": {
111 | "Geometry-mesh002": {
112 | "name": "Mesh",
113 | "primitives": [
114 | {
115 | "attributes": {
116 | "NORMAL": "accessor_25",
117 | "POSITION": "accessor_23"
118 | },
119 | "indices": "accessor_21",
120 | "material": "Effect-Red",
121 | "mode": 4
122 | }
123 | ]
124 | }
125 | },
126 | "nodes": {
127 | "Geometry-mesh002Node": {
128 | "children": [],
129 | "matrix": [
130 | 1,
131 | 0,
132 | 0,
133 | 0,
134 | 0,
135 | 1,
136 | 0,
137 | 0,
138 | 0,
139 | 0,
140 | 1,
141 | 0,
142 | 0,
143 | 0,
144 | 0,
145 | 1
146 | ],
147 | "meshes": [
148 | "Geometry-mesh002"
149 | ],
150 | "name": "Mesh"
151 | },
152 | "node_1": {
153 | "children": [
154 | "Geometry-mesh002Node"
155 | ],
156 | "matrix": [
157 | 1,
158 | 0,
159 | 0,
160 | 0,
161 | 0,
162 | 0,
163 | -1,
164 | 0,
165 | 0,
166 | 1,
167 | 0,
168 | 0,
169 | 0,
170 | 0,
171 | 0,
172 | 1
173 | ],
174 | "name": "Y_UP_Transform"
175 | }
176 | },
177 | "scene": "defaultScene",
178 | "scenes": {
179 | "defaultScene": {
180 | "nodes": [
181 | "node_1"
182 | ]
183 | }
184 | },
185 | "skins": {}
186 | }
--------------------------------------------------------------------------------
/specs/data/1.0/box-textured-binary-separate/box-textured-binary-separate-fs.glsl:
--------------------------------------------------------------------------------
1 | precision highp float;
2 | varying vec3 v_normal;
3 | varying vec2 v_texcoord0;
4 | uniform sampler2D u_diffuse;
5 | uniform vec4 u_specular;
6 | uniform float u_shininess;
7 | void main(void) {
8 | vec3 normal = normalize(v_normal);
9 | vec4 color = vec4(0., 0., 0., 0.);
10 | vec4 diffuse = vec4(0., 0., 0., 1.);
11 | vec4 specular;
12 | diffuse = texture2D(u_diffuse, v_texcoord0);
13 | specular = u_specular;
14 | diffuse.xyz *= max(dot(normal,vec3(0.,0.,1.)), 0.);
15 | color.xyz += diffuse.xyz;
16 | color = vec4(color.rgb * diffuse.a, diffuse.a);
17 | gl_FragColor = color;
18 | }
19 |
--------------------------------------------------------------------------------
/specs/data/1.0/box-textured-binary-separate/box-textured-binary-separate-vs.glsl:
--------------------------------------------------------------------------------
1 | precision highp float;
2 | attribute vec3 a_position;
3 | attribute vec3 a_normal;
4 | varying vec3 v_normal;
5 | uniform mat3 u_normalMatrix;
6 | uniform mat4 u_modelViewMatrix;
7 | uniform mat4 u_projectionMatrix;
8 | attribute vec2 a_texcoord0;
9 | varying vec2 v_texcoord0;
10 | void main(void) {
11 | vec4 pos = u_modelViewMatrix * vec4(a_position,1.0);
12 | v_normal = u_normalMatrix * a_normal;
13 | v_texcoord0 = a_texcoord0;
14 | gl_Position = u_projectionMatrix * pos;
15 | }
16 |
--------------------------------------------------------------------------------
/specs/data/1.0/box-textured-binary-separate/box-textured-binary-separate.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/gltf-pipeline/cbbff2f212b5aa6163c7152d46ffcaf3a2ff3ac3/specs/data/1.0/box-textured-binary-separate/box-textured-binary-separate.glb
--------------------------------------------------------------------------------
/specs/data/1.0/box-textured-binary-separate/cesium.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/gltf-pipeline/cbbff2f212b5aa6163c7152d46ffcaf3a2ff3ac3/specs/data/1.0/box-textured-binary-separate/cesium.png
--------------------------------------------------------------------------------
/specs/data/1.0/box-textured-binary/box-textured-binary.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/gltf-pipeline/cbbff2f212b5aa6163c7152d46ffcaf3a2ff3ac3/specs/data/1.0/box-textured-binary/box-textured-binary.glb
--------------------------------------------------------------------------------
/specs/data/1.0/box-textured-materials-common/CesiumLogoFlat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/gltf-pipeline/cbbff2f212b5aa6163c7152d46ffcaf3a2ff3ac3/specs/data/1.0/box-textured-materials-common/CesiumLogoFlat.png
--------------------------------------------------------------------------------
/specs/data/1.0/box-textured-materials-common/box-textured-materials-common.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/gltf-pipeline/cbbff2f212b5aa6163c7152d46ffcaf3a2ff3ac3/specs/data/1.0/box-textured-materials-common/box-textured-materials-common.bin
--------------------------------------------------------------------------------
/specs/data/1.0/box-textured-materials-common/box-textured-materials-common.gltf:
--------------------------------------------------------------------------------
1 | {
2 | "accessors": {
3 | "accessor_21": {
4 | "bufferView": "bufferView_29",
5 | "byteOffset": 0,
6 | "byteStride": 0,
7 | "componentType": 5123,
8 | "count": 36,
9 | "type": "SCALAR"
10 | },
11 | "accessor_23": {
12 | "bufferView": "bufferView_30",
13 | "byteOffset": 0,
14 | "byteStride": 12,
15 | "componentType": 5126,
16 | "count": 24,
17 | "max": [
18 | 0.5,
19 | 0.5,
20 | 0.5
21 | ],
22 | "min": [
23 | -0.5,
24 | -0.5,
25 | -0.5
26 | ],
27 | "type": "VEC3"
28 | },
29 | "accessor_25": {
30 | "bufferView": "bufferView_30",
31 | "byteOffset": 288,
32 | "byteStride": 12,
33 | "componentType": 5126,
34 | "count": 24,
35 | "max": [
36 | 1,
37 | 1,
38 | 1
39 | ],
40 | "min": [
41 | -1,
42 | -1,
43 | -1
44 | ],
45 | "type": "VEC3"
46 | },
47 | "accessor_27": {
48 | "bufferView": "bufferView_30",
49 | "byteOffset": 576,
50 | "byteStride": 8,
51 | "componentType": 5126,
52 | "count": 24,
53 | "max": [
54 | 6,
55 | 1
56 | ],
57 | "min": [
58 | 0,
59 | 0
60 | ],
61 | "type": "VEC2"
62 | }
63 | },
64 | "animations": {},
65 | "asset": {
66 | "generator": "collada2gltf@027f74366341d569dea42e9a68b7104cc3892054",
67 | "premultipliedAlpha": true,
68 | "profile": {
69 | "api": "WebGL",
70 | "version": "1.0.2"
71 | },
72 | "version": "1.0"
73 | },
74 | "bufferViews": {
75 | "bufferView_29": {
76 | "buffer": "BoxTextured",
77 | "byteLength": 72,
78 | "byteOffset": 0,
79 | "target": 34963
80 | },
81 | "bufferView_30": {
82 | "buffer": "BoxTextured",
83 | "byteLength": 768,
84 | "byteOffset": 72,
85 | "target": 34962
86 | }
87 | },
88 | "buffers": {
89 | "BoxTextured": {
90 | "byteLength": 840,
91 | "type": "arraybuffer",
92 | "uri": "box-textured-materials-common.bin"
93 | }
94 | },
95 | "extensionsUsed": [
96 | "KHR_materials_common"
97 | ],
98 | "images": {
99 | "Image0001": {
100 | "name": "Image0001",
101 | "uri": "CesiumLogoFlat.png"
102 | }
103 | },
104 | "materials": {
105 | "Effect-Texture": {
106 | "extensions": {
107 | "KHR_materials_common": {
108 | "doubleSided": false,
109 | "jointCount": 0,
110 | "technique": "PHONG",
111 | "transparent": false,
112 | "values": {
113 | "diffuse": "texture_Image0001",
114 | "shininess": 256,
115 | "specular": [
116 | 0.2,
117 | 0.2,
118 | 0.2,
119 | 1
120 | ]
121 | }
122 | }
123 | },
124 | "name": "Texture"
125 | }
126 | },
127 | "meshes": {
128 | "Geometry-mesh002": {
129 | "name": "Mesh",
130 | "primitives": [
131 | {
132 | "attributes": {
133 | "NORMAL": "accessor_25",
134 | "POSITION": "accessor_23",
135 | "TEXCOORD_0": "accessor_27"
136 | },
137 | "indices": "accessor_21",
138 | "material": "Effect-Texture",
139 | "mode": 4
140 | }
141 | ]
142 | }
143 | },
144 | "nodes": {
145 | "Geometry-mesh002Node": {
146 | "children": [],
147 | "matrix": [
148 | 1,
149 | 0,
150 | 0,
151 | 0,
152 | 0,
153 | 1,
154 | 0,
155 | 0,
156 | 0,
157 | 0,
158 | 1,
159 | 0,
160 | 0,
161 | 0,
162 | 0,
163 | 1
164 | ],
165 | "meshes": [
166 | "Geometry-mesh002"
167 | ],
168 | "name": "Mesh"
169 | },
170 | "groupLocator030Node": {
171 | "children": [
172 | "txtrLocator026Node"
173 | ],
174 | "matrix": [
175 | 1,
176 | 0,
177 | 0,
178 | 0,
179 | 0,
180 | 1,
181 | 0,
182 | 0,
183 | 0,
184 | 0,
185 | 1,
186 | 0,
187 | 0,
188 | 0,
189 | 0,
190 | 1
191 | ],
192 | "name": "Texture_Group"
193 | },
194 | "node_3": {
195 | "children": [
196 | "Geometry-mesh002Node",
197 | "groupLocator030Node"
198 | ],
199 | "matrix": [
200 | 1,
201 | 0,
202 | 0,
203 | 0,
204 | 0,
205 | 0,
206 | -1,
207 | 0,
208 | 0,
209 | 1,
210 | 0,
211 | 0,
212 | 0,
213 | 0,
214 | 0,
215 | 1
216 | ],
217 | "name": "Y_UP_Transform"
218 | },
219 | "txtrLocator026Node": {
220 | "children": [],
221 | "matrix": [
222 | 1,
223 | 0,
224 | 0,
225 | 0,
226 | 0,
227 | 1,
228 | 0,
229 | 0,
230 | 0,
231 | 0,
232 | 1,
233 | 0,
234 | 0,
235 | 0,
236 | 0,
237 | 1
238 | ],
239 | "name": "Cesium_Logo_Flat__Image___Texture_"
240 | }
241 | },
242 | "samplers": {
243 | "sampler_0": {
244 | "magFilter": 9729,
245 | "minFilter": 9987,
246 | "wrapS": 10497,
247 | "wrapT": 10497
248 | }
249 | },
250 | "scene": "defaultScene",
251 | "scenes": {
252 | "defaultScene": {
253 | "nodes": [
254 | "node_3"
255 | ]
256 | }
257 | },
258 | "skins": {},
259 | "textures": {
260 | "texture_Image0001": {
261 | "format": 6408,
262 | "internalFormat": 6408,
263 | "sampler": "sampler_0",
264 | "source": "Image0001",
265 | "target": 3553,
266 | "type": 5121
267 | }
268 | }
269 | }
--------------------------------------------------------------------------------
/specs/data/1.0/box-textured-separate/box-textured-separate-fs.glsl:
--------------------------------------------------------------------------------
1 | precision highp float;
2 | varying vec3 v_normal;
3 | varying vec2 v_texcoord0;
4 | uniform sampler2D u_diffuse;
5 | uniform vec4 u_specular;
6 | uniform float u_shininess;
7 | void main(void) {
8 | vec3 normal = normalize(v_normal);
9 | vec4 color = vec4(0., 0., 0., 0.);
10 | vec4 diffuse = vec4(0., 0., 0., 1.);
11 | vec4 specular;
12 | diffuse = texture2D(u_diffuse, v_texcoord0);
13 | specular = u_specular;
14 | diffuse.xyz *= max(dot(normal,vec3(0.,0.,1.)), 0.);
15 | color.xyz += diffuse.xyz;
16 | color = vec4(color.rgb * diffuse.a, diffuse.a);
17 | gl_FragColor = color;
18 | }
19 |
--------------------------------------------------------------------------------
/specs/data/1.0/box-textured-separate/box-textured-separate-vs.glsl:
--------------------------------------------------------------------------------
1 | precision highp float;
2 | attribute vec3 a_position;
3 | attribute vec3 a_normal;
4 | varying vec3 v_normal;
5 | uniform mat3 u_normalMatrix;
6 | uniform mat4 u_modelViewMatrix;
7 | uniform mat4 u_projectionMatrix;
8 | attribute vec2 a_texcoord0;
9 | varying vec2 v_texcoord0;
10 | void main(void) {
11 | vec4 pos = u_modelViewMatrix * vec4(a_position,1.0);
12 | v_normal = u_normalMatrix * a_normal;
13 | v_texcoord0 = a_texcoord0;
14 | gl_Position = u_projectionMatrix * pos;
15 | }
16 |
--------------------------------------------------------------------------------
/specs/data/1.0/box-textured-separate/box-textured-separate.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/gltf-pipeline/cbbff2f212b5aa6163c7152d46ffcaf3a2ff3ac3/specs/data/1.0/box-textured-separate/box-textured-separate.bin
--------------------------------------------------------------------------------
/specs/data/1.0/box-textured-separate/cesium.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/gltf-pipeline/cbbff2f212b5aa6163c7152d46ffcaf3a2ff3ac3/specs/data/1.0/box-textured-separate/cesium.png
--------------------------------------------------------------------------------
/specs/data/2.0/box-shared-image-references-separate/CesiumTexturedBoxTest0FS.glsl:
--------------------------------------------------------------------------------
1 | precision highp float;
2 | varying vec3 v_normal;
3 | varying vec2 v_texcoord0;
4 | uniform sampler2D u_diffuse;
5 | uniform vec4 u_specular;
6 | uniform float u_shininess;
7 | void main(void) {
8 | vec3 normal = normalize(v_normal);
9 | vec4 color = vec4(0., 0., 0., 0.);
10 | vec4 diffuse = vec4(0., 0., 0., 1.);
11 | vec4 specular;
12 | diffuse = texture2D(u_diffuse, v_texcoord0);
13 | specular = u_specular;
14 | diffuse.xyz *= max(dot(normal,vec3(0.,0.,1.)), 0.);
15 | color.xyz += diffuse.xyz;
16 | color = vec4(color.rgb * diffuse.a, diffuse.a);
17 | gl_FragColor = color;
18 | }
19 |
--------------------------------------------------------------------------------
/specs/data/2.0/box-shared-image-references-separate/CesiumTexturedBoxTest0VS.glsl:
--------------------------------------------------------------------------------
1 | precision highp float;
2 | attribute vec3 a_position;
3 | attribute vec3 a_normal;
4 | varying vec3 v_normal;
5 | uniform mat3 u_normalMatrix;
6 | uniform mat4 u_modelViewMatrix;
7 | uniform mat4 u_projectionMatrix;
8 | attribute vec2 a_texcoord0;
9 | varying vec2 v_texcoord0;
10 | void main(void) {
11 | vec4 pos = u_modelViewMatrix * vec4(a_position,1.0);
12 | v_normal = u_normalMatrix * a_normal;
13 | v_texcoord0 = a_texcoord0;
14 | gl_Position = u_projectionMatrix * pos;
15 | }
16 |
--------------------------------------------------------------------------------
/specs/data/2.0/box-shared-image-references-separate/Image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/gltf-pipeline/cbbff2f212b5aa6163c7152d46ffcaf3a2ff3ac3/specs/data/2.0/box-shared-image-references-separate/Image.png
--------------------------------------------------------------------------------
/specs/data/2.0/box-shared-image-references-separate/box-shared-image-references-separate.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/gltf-pipeline/cbbff2f212b5aa6163c7152d46ffcaf3a2ff3ac3/specs/data/2.0/box-shared-image-references-separate/box-shared-image-references-separate.bin
--------------------------------------------------------------------------------
/specs/data/2.0/box-shared-image-references-separate/box-shared-image-references-separate.gltf:
--------------------------------------------------------------------------------
1 | {
2 | "accessors": [
3 | {
4 | "bufferView": 2,
5 | "byteOffset": 0,
6 | "componentType": 5123,
7 | "count": 36,
8 | "type": "SCALAR",
9 | "name": "accessor_indices"
10 | },
11 | {
12 | "bufferView": 0,
13 | "byteOffset": 0,
14 | "componentType": 5126,
15 | "count": 24,
16 | "max": [
17 | 0.5,
18 | 0.5,
19 | 0.5
20 | ],
21 | "min": [
22 | -0.5,
23 | -0.5,
24 | -0.5
25 | ],
26 | "type": "VEC3",
27 | "name": "accessor_positions"
28 | },
29 | {
30 | "bufferView": 0,
31 | "byteOffset": 288,
32 | "componentType": 5126,
33 | "count": 24,
34 | "max": [
35 | 1,
36 | 1,
37 | 1
38 | ],
39 | "min": [
40 | -1,
41 | -1,
42 | -1
43 | ],
44 | "type": "VEC3",
45 | "name": "accessor_normals"
46 | },
47 | {
48 | "bufferView": 1,
49 | "byteOffset": 0,
50 | "componentType": 5126,
51 | "count": 24,
52 | "max": [
53 | 6,
54 | 1
55 | ],
56 | "min": [
57 | 0,
58 | 0
59 | ],
60 | "type": "VEC2",
61 | "name": "accessor_texcoord"
62 | }
63 | ],
64 | "asset": {
65 | "generator": "collada2gltf@ceec062e3d5793f2f249f53cbd843aee382ad40b",
66 | "version": "2.0"
67 | },
68 | "bufferViews": [
69 | {
70 | "buffer": 0,
71 | "byteLength": 576,
72 | "byteOffset": 0,
73 | "target": 34962,
74 | "name": "bufferView_0",
75 | "byteStride": 12
76 | },
77 | {
78 | "buffer": 0,
79 | "byteLength": 192,
80 | "byteOffset": 576,
81 | "target": 34962,
82 | "name": "bufferView_0",
83 | "byteStride": 8
84 | },
85 | {
86 | "buffer": 0,
87 | "byteLength": 72,
88 | "byteOffset": 768,
89 | "target": 34963,
90 | "name": "bufferView_1"
91 | }
92 | ],
93 | "buffers": [
94 | {
95 | "name": "binary_glTF",
96 | "byteLength": 840,
97 | "uri": "box-shared-image-references-separate.bin"
98 | }
99 | ],
100 | "images": [
101 | {
102 | "mimeType": "image/png",
103 | "uri": "Image.png"
104 | },
105 | {
106 | "mimeType": "image/png",
107 | "uri": "Image.png"
108 | }
109 | ],
110 | "materials": [
111 | {
112 | "name": "Texture",
113 | "extensions": {
114 | "KHR_techniques_webgl": {
115 | "technique": 0,
116 | "values": {
117 | "u_diffuse": {
118 | "index": 0,
119 | "texCoord": 0
120 | },
121 | "u_shininess": 256,
122 | "u_specular": [
123 | 0.2,
124 | 0.2,
125 | 0.2,
126 | 1
127 | ]
128 | }
129 | }
130 | },
131 | "emissiveFactor": [
132 | 0,
133 | 0,
134 | 0
135 | ],
136 | "alphaMode": "OPAQUE",
137 | "doubleSided": false
138 | }
139 | ],
140 | "meshes": [
141 | {
142 | "name": "Mesh",
143 | "primitives": [
144 | {
145 | "attributes": {
146 | "NORMAL": 2,
147 | "POSITION": 1,
148 | "TEXCOORD_0": 3
149 | },
150 | "indices": 0,
151 | "material": 0,
152 | "mode": 4
153 | }
154 | ]
155 | }
156 | ],
157 | "nodes": [
158 | {
159 | "name": "rootNode",
160 | "mesh": 0
161 | }
162 | ],
163 | "samplers": [
164 | {
165 | "magFilter": 9729,
166 | "minFilter": 9987,
167 | "wrapS": 10497,
168 | "wrapT": 10497,
169 | "name": "sampler_0"
170 | }
171 | ],
172 | "scene": 0,
173 | "scenes": [
174 | {
175 | "nodes": [
176 | 0
177 | ],
178 | "name": "defaultScene"
179 | }
180 | ],
181 | "textures": [
182 | {
183 | "sampler": 0,
184 | "source": 0,
185 | "name": "texture_Image0001"
186 | },
187 | {
188 | "sampler": 0,
189 | "source": 1,
190 | "name": "texture_Image0002"
191 | }
192 | ],
193 | "extensionsRequired": [
194 | "KHR_techniques_webgl"
195 | ],
196 | "extensions": {
197 | "KHR_techniques_webgl": {
198 | "programs": [
199 | {
200 | "name": "program_0",
201 | "fragmentShader": 0,
202 | "vertexShader": 1
203 | }
204 | ],
205 | "shaders": [
206 | {
207 | "type": 35632,
208 | "name": "CesiumTexturedBoxTest0FS",
209 | "uri": "CesiumTexturedBoxTest0FS.glsl"
210 | },
211 | {
212 | "type": 35633,
213 | "name": "CesiumTexturedBoxTest0VS",
214 | "uri": "CesiumTexturedBoxTest0VS.glsl"
215 | }
216 | ],
217 | "techniques": [
218 | {
219 | "name": "technique0",
220 | "program": 0,
221 | "attributes": {
222 | "a_normal": {
223 | "semantic": "NORMAL"
224 | },
225 | "a_position": {
226 | "semantic": "POSITION"
227 | },
228 | "a_texcoord0": {
229 | "semantic": "TEXCOORD_0"
230 | }
231 | },
232 | "uniforms": {
233 | "u_diffuse": {
234 | "type": 35678
235 | },
236 | "u_modelViewMatrix": {
237 | "type": 35676,
238 | "semantic": "MODELVIEW"
239 | },
240 | "u_normalMatrix": {
241 | "type": 35675,
242 | "semantic": "MODELVIEWINVERSETRANSPOSE"
243 | },
244 | "u_projectionMatrix": {
245 | "type": 35676,
246 | "semantic": "PROJECTION"
247 | },
248 | "u_shininess": {
249 | "type": 5126
250 | },
251 | "u_specular": {
252 | "type": 35666
253 | }
254 | }
255 | }
256 | ]
257 | }
258 | },
259 | "extensionsUsed": [
260 | "KHR_techniques_webgl"
261 | ]
262 | }
263 |
--------------------------------------------------------------------------------
/specs/data/2.0/box-techniques-separate/CesiumTexturedBoxTest.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/gltf-pipeline/cbbff2f212b5aa6163c7152d46ffcaf3a2ff3ac3/specs/data/2.0/box-techniques-separate/CesiumTexturedBoxTest.bin
--------------------------------------------------------------------------------
/specs/data/2.0/box-techniques-separate/CesiumTexturedBoxTest0FS.glsl:
--------------------------------------------------------------------------------
1 | precision highp float;
2 | varying vec3 v_normal;
3 | varying vec2 v_texcoord0;
4 | uniform sampler2D u_diffuse;
5 | uniform vec4 u_specular;
6 | uniform float u_shininess;
7 | void main(void) {
8 | vec3 normal = normalize(v_normal);
9 | vec4 color = vec4(0., 0., 0., 0.);
10 | vec4 diffuse = vec4(0., 0., 0., 1.);
11 | vec4 specular;
12 | diffuse = texture2D(u_diffuse, v_texcoord0);
13 | specular = u_specular;
14 | diffuse.xyz *= max(dot(normal,vec3(0.,0.,1.)), 0.);
15 | color.xyz += diffuse.xyz;
16 | color = vec4(color.rgb * diffuse.a, diffuse.a);
17 | gl_FragColor = color;
18 | }
19 |
--------------------------------------------------------------------------------
/specs/data/2.0/box-techniques-separate/CesiumTexturedBoxTest0VS.glsl:
--------------------------------------------------------------------------------
1 | precision highp float;
2 | attribute vec3 a_position;
3 | attribute vec3 a_normal;
4 | varying vec3 v_normal;
5 | uniform mat3 u_normalMatrix;
6 | uniform mat4 u_modelViewMatrix;
7 | uniform mat4 u_projectionMatrix;
8 | attribute vec2 a_texcoord0;
9 | varying vec2 v_texcoord0;
10 | void main(void) {
11 | vec4 pos = u_modelViewMatrix * vec4(a_position,1.0);
12 | v_normal = u_normalMatrix * a_normal;
13 | v_texcoord0 = a_texcoord0;
14 | gl_Position = u_projectionMatrix * pos;
15 | }
16 |
--------------------------------------------------------------------------------
/specs/data/2.0/box-techniques-separate/Image0001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/gltf-pipeline/cbbff2f212b5aa6163c7152d46ffcaf3a2ff3ac3/specs/data/2.0/box-techniques-separate/Image0001.png
--------------------------------------------------------------------------------
/specs/data/2.0/box-techniques-separate/box-techniques-separate.gltf:
--------------------------------------------------------------------------------
1 | {
2 | "accessors": [
3 | {
4 | "bufferView": 2,
5 | "byteOffset": 0,
6 | "componentType": 5123,
7 | "count": 36,
8 | "type": "SCALAR",
9 | "name": "accessor_indices",
10 | "min": [
11 | 0
12 | ],
13 | "max": [
14 | 23
15 | ]
16 | },
17 | {
18 | "bufferView": 0,
19 | "byteOffset": 0,
20 | "componentType": 5126,
21 | "count": 24,
22 | "max": [
23 | 0.5,
24 | 0.5,
25 | 0.5
26 | ],
27 | "min": [
28 | -0.5,
29 | -0.5,
30 | -0.5
31 | ],
32 | "type": "VEC3",
33 | "name": "accessor_positions"
34 | },
35 | {
36 | "bufferView": 0,
37 | "byteOffset": 288,
38 | "componentType": 5126,
39 | "count": 24,
40 | "max": [
41 | 1,
42 | 1,
43 | 1
44 | ],
45 | "min": [
46 | -1,
47 | -1,
48 | -1
49 | ],
50 | "type": "VEC3",
51 | "name": "accessor_normals"
52 | },
53 | {
54 | "bufferView": 1,
55 | "byteOffset": 0,
56 | "componentType": 5126,
57 | "count": 24,
58 | "max": [
59 | 6,
60 | 1
61 | ],
62 | "min": [
63 | 0,
64 | 0
65 | ],
66 | "type": "VEC2",
67 | "name": "accessor_texcoord"
68 | }
69 | ],
70 | "asset": {
71 | "generator": "collada2gltf@ceec062e3d5793f2f249f53cbd843aee382ad40b",
72 | "version": "2.0"
73 | },
74 | "bufferViews": [
75 | {
76 | "buffer": 0,
77 | "byteLength": 576,
78 | "byteOffset": 0,
79 | "target": 34962,
80 | "name": "bufferView_0",
81 | "byteStride": 12
82 | },
83 | {
84 | "buffer": 0,
85 | "byteLength": 192,
86 | "byteOffset": 576,
87 | "target": 34962,
88 | "name": "bufferView_0",
89 | "byteStride": 8
90 | },
91 | {
92 | "buffer": 0,
93 | "byteLength": 72,
94 | "byteOffset": 768,
95 | "target": 34963,
96 | "name": "bufferView_1"
97 | }
98 | ],
99 | "buffers": [
100 | {
101 | "name": "CesiumTexturedBoxTest",
102 | "byteLength": 840,
103 | "uri": "CesiumTexturedBoxTest.bin"
104 | }
105 | ],
106 | "images": [
107 | {
108 | "name": "Image0001",
109 | "uri": "Image0001.png"
110 | }
111 | ],
112 | "materials": [
113 | {
114 | "name": "Texture",
115 | "extensions": {
116 | "KHR_techniques_webgl": {
117 | "technique": 0,
118 | "values": {
119 | "u_diffuse": {
120 | "index": 0,
121 | "texCoord": 0
122 | },
123 | "u_shininess": 256,
124 | "u_specular": [
125 | 0.2,
126 | 0.2,
127 | 0.2,
128 | 1
129 | ]
130 | }
131 | }
132 | },
133 | "emissiveFactor": [
134 | 0,
135 | 0,
136 | 0
137 | ],
138 | "alphaMode": "OPAQUE",
139 | "doubleSided": false
140 | }
141 | ],
142 | "meshes": [
143 | {
144 | "name": "Mesh",
145 | "primitives": [
146 | {
147 | "attributes": {
148 | "NORMAL": 2,
149 | "POSITION": 1,
150 | "TEXCOORD_0": 3
151 | },
152 | "indices": 0,
153 | "material": 0,
154 | "mode": 4
155 | }
156 | ]
157 | }
158 | ],
159 | "nodes": [
160 | {
161 | "name": "rootNode",
162 | "mesh": 0
163 | }
164 | ],
165 | "samplers": [
166 | {
167 | "magFilter": 9729,
168 | "minFilter": 9987,
169 | "wrapS": 10497,
170 | "wrapT": 10497,
171 | "name": "sampler_0"
172 | }
173 | ],
174 | "scene": 0,
175 | "scenes": [
176 | {
177 | "nodes": [
178 | 0
179 | ],
180 | "name": "defaultScene"
181 | }
182 | ],
183 | "textures": [
184 | {
185 | "sampler": 0,
186 | "source": 0,
187 | "name": "texture_Image0001"
188 | }
189 | ],
190 | "extensionsUsed": [
191 | "KHR_techniques_webgl"
192 | ],
193 | "extensionsRequired": [
194 | "KHR_techniques_webgl"
195 | ],
196 | "extensions": {
197 | "KHR_techniques_webgl": {
198 | "programs": [
199 | {
200 | "name": "program_0",
201 | "fragmentShader": 0,
202 | "vertexShader": 1
203 | }
204 | ],
205 | "shaders": [
206 | {
207 | "type": 35632,
208 | "name": "CesiumTexturedBoxTest0FS",
209 | "uri": "CesiumTexturedBoxTest0FS.glsl"
210 | },
211 | {
212 | "type": 35633,
213 | "name": "CesiumTexturedBoxTest0VS",
214 | "uri": "CesiumTexturedBoxTest0VS.glsl"
215 | }
216 | ],
217 | "techniques": [
218 | {
219 | "name": "technique0",
220 | "program": 0,
221 | "attributes": {
222 | "a_normal": {
223 | "semantic": "NORMAL"
224 | },
225 | "a_position": {
226 | "semantic": "POSITION"
227 | },
228 | "a_texcoord0": {
229 | "semantic": "TEXCOORD_0"
230 | }
231 | },
232 | "uniforms": {
233 | "u_diffuse": {
234 | "type": 35678
235 | },
236 | "u_modelViewMatrix": {
237 | "type": 35676,
238 | "semantic": "MODELVIEW"
239 | },
240 | "u_normalMatrix": {
241 | "type": 35675,
242 | "semantic": "MODELVIEWINVERSETRANSPOSE"
243 | },
244 | "u_projectionMatrix": {
245 | "type": 35676,
246 | "semantic": "PROJECTION"
247 | },
248 | "u_shininess": {
249 | "type": 5126
250 | },
251 | "u_specular": {
252 | "type": 35666
253 | }
254 | }
255 | }
256 | ]
257 | }
258 | }
259 | }
260 |
--------------------------------------------------------------------------------
/specs/data/2.0/box-textured-binary-separate/box-textured-binary-separate.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/gltf-pipeline/cbbff2f212b5aa6163c7152d46ffcaf3a2ff3ac3/specs/data/2.0/box-textured-binary-separate/box-textured-binary-separate.glb
--------------------------------------------------------------------------------
/specs/data/2.0/box-textured-binary-separate/cesium.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/gltf-pipeline/cbbff2f212b5aa6163c7152d46ffcaf3a2ff3ac3/specs/data/2.0/box-textured-binary-separate/cesium.png
--------------------------------------------------------------------------------
/specs/data/2.0/box-textured-binary/box-textured-binary.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/gltf-pipeline/cbbff2f212b5aa6163c7152d46ffcaf3a2ff3ac3/specs/data/2.0/box-textured-binary/box-textured-binary.glb
--------------------------------------------------------------------------------
/specs/data/2.0/box-textured-separate/box-textured-separate.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/gltf-pipeline/cbbff2f212b5aa6163c7152d46ffcaf3a2ff3ac3/specs/data/2.0/box-textured-separate/box-textured-separate.bin
--------------------------------------------------------------------------------
/specs/data/2.0/box-textured-separate/box-textured-separate.gltf:
--------------------------------------------------------------------------------
1 | {
2 | "asset": {
3 | "generator": "COLLADA2GLTF",
4 | "version": "2.0"
5 | },
6 | "scene": 0,
7 | "scenes": [
8 | {
9 | "nodes": [
10 | 0
11 | ]
12 | }
13 | ],
14 | "nodes": [
15 | {
16 | "children": [
17 | 1
18 | ],
19 | "matrix": [
20 | 1,
21 | 0,
22 | 0,
23 | 0,
24 | 0,
25 | 0,
26 | -1,
27 | 0,
28 | 0,
29 | 1,
30 | 0,
31 | 0,
32 | 0,
33 | 0,
34 | 0,
35 | 1
36 | ]
37 | },
38 | {
39 | "mesh": 0
40 | }
41 | ],
42 | "meshes": [
43 | {
44 | "primitives": [
45 | {
46 | "attributes": {
47 | "NORMAL": 1,
48 | "POSITION": 2,
49 | "TEXCOORD_0": 3
50 | },
51 | "indices": 0,
52 | "mode": 4,
53 | "material": 0
54 | }
55 | ],
56 | "name": "Mesh"
57 | }
58 | ],
59 | "accessors": [
60 | {
61 | "bufferView": 0,
62 | "byteOffset": 0,
63 | "componentType": 5123,
64 | "count": 36,
65 | "max": [
66 | 23
67 | ],
68 | "min": [
69 | 0
70 | ],
71 | "type": "SCALAR"
72 | },
73 | {
74 | "bufferView": 1,
75 | "byteOffset": 0,
76 | "componentType": 5126,
77 | "count": 24,
78 | "max": [
79 | 1,
80 | 1,
81 | 1
82 | ],
83 | "min": [
84 | -1,
85 | -1,
86 | -1
87 | ],
88 | "type": "VEC3"
89 | },
90 | {
91 | "bufferView": 1,
92 | "byteOffset": 288,
93 | "componentType": 5126,
94 | "count": 24,
95 | "max": [
96 | 0.5,
97 | 0.5,
98 | 0.5
99 | ],
100 | "min": [
101 | -0.5,
102 | -0.5,
103 | -0.5
104 | ],
105 | "type": "VEC3"
106 | },
107 | {
108 | "bufferView": 2,
109 | "byteOffset": 0,
110 | "componentType": 5126,
111 | "count": 24,
112 | "max": [
113 | 6,
114 | 1
115 | ],
116 | "min": [
117 | 0,
118 | 0
119 | ],
120 | "type": "VEC2"
121 | }
122 | ],
123 | "materials": [
124 | {
125 | "pbrMetallicRoughness": {
126 | "baseColorTexture": {
127 | "index": 0,
128 | "texCoord": 0
129 | },
130 | "metallicFactor": 0,
131 | "baseColorFactor": [
132 | 1,
133 | 1,
134 | 1,
135 | 1
136 | ],
137 | "roughnessFactor": 1
138 | },
139 | "name": "Texture",
140 | "emissiveFactor": [
141 | 0,
142 | 0,
143 | 0
144 | ],
145 | "alphaMode": "OPAQUE",
146 | "doubleSided": false
147 | }
148 | ],
149 | "textures": [
150 | {
151 | "sampler": 0,
152 | "source": 0
153 | }
154 | ],
155 | "images": [
156 | {
157 | "uri": "cesium%20logo.png"
158 | }
159 | ],
160 | "samplers": [
161 | {
162 | "magFilter": 9729,
163 | "minFilter": 9986,
164 | "wrapS": 10497,
165 | "wrapT": 10497
166 | }
167 | ],
168 | "bufferViews": [
169 | {
170 | "buffer": 0,
171 | "byteOffset": 768,
172 | "byteLength": 72,
173 | "target": 34963
174 | },
175 | {
176 | "buffer": 0,
177 | "byteOffset": 0,
178 | "byteLength": 576,
179 | "byteStride": 12,
180 | "target": 34962
181 | },
182 | {
183 | "buffer": 0,
184 | "byteOffset": 576,
185 | "byteLength": 192,
186 | "byteStride": 8,
187 | "target": 34962
188 | }
189 | ],
190 | "buffers": [
191 | {
192 | "byteLength": 840,
193 | "uri": "box-textured-separate.bin"
194 | }
195 | ]
196 | }
197 |
--------------------------------------------------------------------------------
/specs/data/2.0/box-textured-separate/cesium logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/gltf-pipeline/cbbff2f212b5aa6163c7152d46ffcaf3a2ff3ac3/specs/data/2.0/box-textured-separate/cesium logo.png
--------------------------------------------------------------------------------
/specs/data/2.0/extensions/EXT_meshopt_compression/meshopt-fallback/meshopt-fallback.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/gltf-pipeline/cbbff2f212b5aa6163c7152d46ffcaf3a2ff3ac3/specs/data/2.0/extensions/EXT_meshopt_compression/meshopt-fallback/meshopt-fallback.bin
--------------------------------------------------------------------------------
/specs/data/2.0/extensions/EXT_meshopt_compression/meshopt-fallback/meshopt-fallback.fallback.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/gltf-pipeline/cbbff2f212b5aa6163c7152d46ffcaf3a2ff3ac3/specs/data/2.0/extensions/EXT_meshopt_compression/meshopt-fallback/meshopt-fallback.fallback.bin
--------------------------------------------------------------------------------
/specs/data/2.0/extensions/EXT_meshopt_compression/meshopt-fallback/meshopt-fallback.gltf:
--------------------------------------------------------------------------------
1 | {
2 | "buffers": [
3 | {
4 | "uri": "meshopt-fallback.bin",
5 | "byteLength": 468
6 | },
7 | {
8 | "uri": "meshopt-fallback.fallback.bin",
9 | "byteLength": 960,
10 | "extensions": {
11 | "EXT_meshopt_compression": {
12 | "fallback": true
13 | }
14 | }
15 | }
16 | ],
17 | "asset": {
18 | "version": "2.0",
19 | "generator": "gltfpack 0.16"
20 | },
21 | "extensionsUsed": [
22 | "KHR_mesh_quantization",
23 | "EXT_meshopt_compression"
24 | ],
25 | "extensionsRequired": [
26 | "KHR_mesh_quantization"
27 | ],
28 | "bufferViews": [
29 | {
30 | "buffer": 1,
31 | "byteOffset": 0,
32 | "byteLength": 400,
33 | "byteStride": 8,
34 | "target": 34962,
35 | "extensions": {
36 | "EXT_meshopt_compression": {
37 | "buffer": 0,
38 | "byteOffset": 0,
39 | "byteLength": 265,
40 | "byteStride": 8,
41 | "mode": "ATTRIBUTES",
42 | "count": 50
43 | }
44 | }
45 | },
46 | {
47 | "buffer": 1,
48 | "byteOffset": 400,
49 | "byteLength": 200,
50 | "byteStride": 4,
51 | "target": 34962,
52 | "extensions": {
53 | "EXT_meshopt_compression": {
54 | "buffer": 0,
55 | "byteOffset": 268,
56 | "byteLength": 117,
57 | "byteStride": 4,
58 | "mode": "ATTRIBUTES",
59 | "filter": "OCTAHEDRAL",
60 | "count": 50
61 | }
62 | }
63 | },
64 | {
65 | "buffer": 1,
66 | "byteOffset": 600,
67 | "byteLength": 360,
68 | "target": 34963,
69 | "extensions": {
70 | "EXT_meshopt_compression": {
71 | "buffer": 0,
72 | "byteOffset": 388,
73 | "byteLength": 78,
74 | "byteStride": 2,
75 | "mode": "TRIANGLES",
76 | "count": 180
77 | }
78 | }
79 | }
80 | ],
81 | "accessors": [
82 | {
83 | "bufferView": 0,
84 | "byteOffset": 0,
85 | "componentType": 5123,
86 | "count": 24,
87 | "type": "VEC3",
88 | "min": [
89 | 0,
90 | 0,
91 | 0
92 | ],
93 | "max": [
94 | 16383,
95 | 16383,
96 | 16383
97 | ]
98 | },
99 | {
100 | "bufferView": 1,
101 | "byteOffset": 0,
102 | "componentType": 5120,
103 | "count": 24,
104 | "type": "VEC3",
105 | "normalized": true
106 | },
107 | {
108 | "bufferView": 2,
109 | "byteOffset": 0,
110 | "componentType": 5123,
111 | "count": 36,
112 | "type": "SCALAR"
113 | },
114 | {
115 | "bufferView": 0,
116 | "byteOffset": 192,
117 | "componentType": 5123,
118 | "count": 26,
119 | "type": "VEC3",
120 | "min": [
121 | 1315,
122 | 1315,
123 | 1315
124 | ],
125 | "max": [
126 | 15068,
127 | 15068,
128 | 15068
129 | ]
130 | },
131 | {
132 | "bufferView": 1,
133 | "byteOffset": 96,
134 | "componentType": 5120,
135 | "count": 26,
136 | "type": "VEC3",
137 | "normalized": true
138 | },
139 | {
140 | "bufferView": 2,
141 | "byteOffset": 72,
142 | "componentType": 5123,
143 | "count": 144,
144 | "type": "SCALAR"
145 | }
146 | ],
147 | "materials": [
148 | {
149 | "name": "Material.002",
150 | "pbrMetallicRoughness": {
151 | "baseColorFactor": [
152 | 0.800000072,
153 | 0.00122899958,
154 | 0,
155 | 1
156 | ],
157 | "metallicFactor": 0,
158 | "roughnessFactor": 0.5
159 | },
160 | "doubleSided": true
161 | },
162 | {
163 | "name": "Material.001",
164 | "pbrMetallicRoughness": {
165 | "baseColorFactor": [
166 | 0.800000012,
167 | 0.800000012,
168 | 0.800000012,
169 | 1
170 | ],
171 | "metallicFactor": 0,
172 | "roughnessFactor": 0.5
173 | },
174 | "doubleSided": true
175 | }
176 | ],
177 | "meshes": [
178 | {
179 | "primitives": [
180 | {
181 | "attributes": {
182 | "POSITION": 0,
183 | "NORMAL": 1
184 | },
185 | "mode": 4,
186 | "indices": 2,
187 | "material": 0
188 | }
189 | ]
190 | },
191 | {
192 | "primitives": [
193 | {
194 | "attributes": {
195 | "POSITION": 3,
196 | "NORMAL": 4
197 | },
198 | "mode": 4,
199 | "indices": 5,
200 | "material": 1
201 | }
202 | ]
203 | }
204 | ],
205 | "nodes": [
206 | {
207 | "mesh": 0,
208 | "translation": [
209 | -1,
210 | -1,
211 | -1
212 | ],
213 | "scale": [
214 | 0.000122077763,
215 | 0.000122077763,
216 | 0.000122077763
217 | ]
218 | },
219 | {
220 | "mesh": 1,
221 | "translation": [
222 | -1,
223 | -1,
224 | -1
225 | ],
226 | "scale": [
227 | 0.000122077763,
228 | 0.000122077763,
229 | 0.000122077763
230 | ]
231 | },
232 | {
233 | "name": "Cube",
234 | "children": [
235 | 0
236 | ]
237 | },
238 | {
239 | "name": "Cube.001",
240 | "translation": [
241 | 0,
242 | 2.87217259,
243 | 0
244 | ],
245 | "children": [
246 | 1
247 | ]
248 | }
249 | ],
250 | "scenes": [
251 | {
252 | "name": "Scene",
253 | "nodes": [
254 | 2,
255 | 3
256 | ]
257 | }
258 | ],
259 | "scene": 0
260 | }
261 |
--------------------------------------------------------------------------------
/specs/data/2.0/extensions/EXT_meshopt_compression/meshopt-no-fallback/meshopt-no-fallback.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/gltf-pipeline/cbbff2f212b5aa6163c7152d46ffcaf3a2ff3ac3/specs/data/2.0/extensions/EXT_meshopt_compression/meshopt-no-fallback/meshopt-no-fallback.bin
--------------------------------------------------------------------------------
/specs/data/2.0/extensions/EXT_meshopt_compression/meshopt-no-fallback/meshopt-no-fallback.gltf:
--------------------------------------------------------------------------------
1 | {
2 | "buffers": [
3 | {
4 | "uri": "meshopt-no-fallback.bin",
5 | "byteLength": 468
6 | },
7 | {
8 | "byteLength": 960
9 | }
10 | ],
11 | "asset": {
12 | "version": "2.0",
13 | "generator": "gltfpack 0.16"
14 | },
15 | "extensionsUsed": [
16 | "KHR_mesh_quantization",
17 | "EXT_meshopt_compression"
18 | ],
19 | "extensionsRequired": [
20 | "KHR_mesh_quantization",
21 | "EXT_meshopt_compression"
22 | ],
23 | "bufferViews": [
24 | {
25 | "buffer": 1,
26 | "byteOffset": 0,
27 | "byteLength": 400,
28 | "byteStride": 8,
29 | "target": 34962,
30 | "extensions": {
31 | "EXT_meshopt_compression": {
32 | "buffer": 0,
33 | "byteOffset": 0,
34 | "byteLength": 265,
35 | "byteStride": 8,
36 | "mode": "ATTRIBUTES",
37 | "count": 50
38 | }
39 | }
40 | },
41 | {
42 | "buffer": 1,
43 | "byteOffset": 400,
44 | "byteLength": 200,
45 | "byteStride": 4,
46 | "target": 34962,
47 | "extensions": {
48 | "EXT_meshopt_compression": {
49 | "buffer": 0,
50 | "byteOffset": 268,
51 | "byteLength": 117,
52 | "byteStride": 4,
53 | "mode": "ATTRIBUTES",
54 | "filter": "OCTAHEDRAL",
55 | "count": 50
56 | }
57 | }
58 | },
59 | {
60 | "buffer": 1,
61 | "byteOffset": 600,
62 | "byteLength": 360,
63 | "target": 34963,
64 | "extensions": {
65 | "EXT_meshopt_compression": {
66 | "buffer": 0,
67 | "byteOffset": 388,
68 | "byteLength": 78,
69 | "byteStride": 2,
70 | "mode": "TRIANGLES",
71 | "count": 180
72 | }
73 | }
74 | }
75 | ],
76 | "accessors": [
77 | {
78 | "bufferView": 0,
79 | "byteOffset": 0,
80 | "componentType": 5123,
81 | "count": 24,
82 | "type": "VEC3",
83 | "min": [
84 | 0,
85 | 0,
86 | 0
87 | ],
88 | "max": [
89 | 16383,
90 | 16383,
91 | 16383
92 | ]
93 | },
94 | {
95 | "bufferView": 1,
96 | "byteOffset": 0,
97 | "componentType": 5120,
98 | "count": 24,
99 | "type": "VEC3",
100 | "normalized": true
101 | },
102 | {
103 | "bufferView": 2,
104 | "byteOffset": 0,
105 | "componentType": 5123,
106 | "count": 36,
107 | "type": "SCALAR"
108 | },
109 | {
110 | "bufferView": 0,
111 | "byteOffset": 192,
112 | "componentType": 5123,
113 | "count": 26,
114 | "type": "VEC3",
115 | "min": [
116 | 1315,
117 | 1315,
118 | 1315
119 | ],
120 | "max": [
121 | 15068,
122 | 15068,
123 | 15068
124 | ]
125 | },
126 | {
127 | "bufferView": 1,
128 | "byteOffset": 96,
129 | "componentType": 5120,
130 | "count": 26,
131 | "type": "VEC3",
132 | "normalized": true
133 | },
134 | {
135 | "bufferView": 2,
136 | "byteOffset": 72,
137 | "componentType": 5123,
138 | "count": 144,
139 | "type": "SCALAR"
140 | }
141 | ],
142 | "materials": [
143 | {
144 | "name": "Material.002",
145 | "pbrMetallicRoughness": {
146 | "baseColorFactor": [
147 | 0.800000072,
148 | 0.00122899958,
149 | 0,
150 | 1
151 | ],
152 | "metallicFactor": 0,
153 | "roughnessFactor": 0.5
154 | },
155 | "doubleSided": true
156 | },
157 | {
158 | "name": "Material.001",
159 | "pbrMetallicRoughness": {
160 | "baseColorFactor": [
161 | 0.800000012,
162 | 0.800000012,
163 | 0.800000012,
164 | 1
165 | ],
166 | "metallicFactor": 0,
167 | "roughnessFactor": 0.5
168 | },
169 | "doubleSided": true
170 | }
171 | ],
172 | "meshes": [
173 | {
174 | "primitives": [
175 | {
176 | "attributes": {
177 | "POSITION": 0,
178 | "NORMAL": 1
179 | },
180 | "mode": 4,
181 | "indices": 2,
182 | "material": 0
183 | }
184 | ]
185 | },
186 | {
187 | "primitives": [
188 | {
189 | "attributes": {
190 | "POSITION": 3,
191 | "NORMAL": 4
192 | },
193 | "mode": 4,
194 | "indices": 5,
195 | "material": 1
196 | }
197 | ]
198 | }
199 | ],
200 | "nodes": [
201 | {
202 | "mesh": 0,
203 | "translation": [
204 | -1,
205 | -1,
206 | -1
207 | ],
208 | "scale": [
209 | 0.000122077763,
210 | 0.000122077763,
211 | 0.000122077763
212 | ]
213 | },
214 | {
215 | "mesh": 1,
216 | "translation": [
217 | -1,
218 | -1,
219 | -1
220 | ],
221 | "scale": [
222 | 0.000122077763,
223 | 0.000122077763,
224 | 0.000122077763
225 | ]
226 | },
227 | {
228 | "name": "Cube",
229 | "children": [
230 | 0
231 | ]
232 | },
233 | {
234 | "name": "Cube.001",
235 | "translation": [
236 | 0,
237 | 2.87217259,
238 | 0
239 | ],
240 | "children": [
241 | 1
242 | ]
243 | }
244 | ],
245 | "scenes": [
246 | {
247 | "name": "Scene",
248 | "nodes": [
249 | 2,
250 | 3
251 | ]
252 | }
253 | ],
254 | "scene": 0
255 | }
256 |
--------------------------------------------------------------------------------
/specs/data/2.0/extensions/EXT_texture_webp/box-textured-separate/box-textured-separate.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/gltf-pipeline/cbbff2f212b5aa6163c7152d46ffcaf3a2ff3ac3/specs/data/2.0/extensions/EXT_texture_webp/box-textured-separate/box-textured-separate.bin
--------------------------------------------------------------------------------
/specs/data/2.0/extensions/EXT_texture_webp/box-textured-separate/box-textured-separate.gltf:
--------------------------------------------------------------------------------
1 | {
2 | "asset": {
3 | "generator": "COLLADA2GLTF",
4 | "version": "2.0"
5 | },
6 | "extensionsUsed": [
7 | "EXT_texture_webp"
8 | ],
9 | "extensionsRequired": [
10 | "EXT_texture_webp"
11 | ],
12 | "scene": 0,
13 | "scenes": [
14 | {
15 | "nodes": [
16 | 0
17 | ]
18 | }
19 | ],
20 | "nodes": [
21 | {
22 | "children": [
23 | 1
24 | ],
25 | "matrix": [
26 | 1,
27 | 0,
28 | 0,
29 | 0,
30 | 0,
31 | 0,
32 | -1,
33 | 0,
34 | 0,
35 | 1,
36 | 0,
37 | 0,
38 | 0,
39 | 0,
40 | 0,
41 | 1
42 | ]
43 | },
44 | {
45 | "mesh": 0
46 | }
47 | ],
48 | "meshes": [
49 | {
50 | "primitives": [
51 | {
52 | "attributes": {
53 | "NORMAL": 1,
54 | "POSITION": 2,
55 | "TEXCOORD_0": 3
56 | },
57 | "indices": 0,
58 | "mode": 4,
59 | "material": 0
60 | }
61 | ],
62 | "name": "Mesh"
63 | }
64 | ],
65 | "accessors": [
66 | {
67 | "bufferView": 0,
68 | "byteOffset": 0,
69 | "componentType": 5123,
70 | "count": 36,
71 | "max": [
72 | 23
73 | ],
74 | "min": [
75 | 0
76 | ],
77 | "type": "SCALAR"
78 | },
79 | {
80 | "bufferView": 1,
81 | "byteOffset": 0,
82 | "componentType": 5126,
83 | "count": 24,
84 | "max": [
85 | 1,
86 | 1,
87 | 1
88 | ],
89 | "min": [
90 | -1,
91 | -1,
92 | -1
93 | ],
94 | "type": "VEC3"
95 | },
96 | {
97 | "bufferView": 1,
98 | "byteOffset": 288,
99 | "componentType": 5126,
100 | "count": 24,
101 | "max": [
102 | 0.5,
103 | 0.5,
104 | 0.5
105 | ],
106 | "min": [
107 | -0.5,
108 | -0.5,
109 | -0.5
110 | ],
111 | "type": "VEC3"
112 | },
113 | {
114 | "bufferView": 2,
115 | "byteOffset": 0,
116 | "componentType": 5126,
117 | "count": 24,
118 | "max": [
119 | 6,
120 | 1
121 | ],
122 | "min": [
123 | 0,
124 | 0
125 | ],
126 | "type": "VEC2"
127 | }
128 | ],
129 | "materials": [
130 | {
131 | "pbrMetallicRoughness": {
132 | "baseColorTexture": {
133 | "index": 0,
134 | "texCoord": 0
135 | },
136 | "metallicFactor": 0,
137 | "baseColorFactor": [
138 | 1,
139 | 1,
140 | 1,
141 | 1
142 | ],
143 | "roughnessFactor": 1
144 | },
145 | "name": "Texture",
146 | "emissiveFactor": [
147 | 0,
148 | 0,
149 | 0
150 | ],
151 | "alphaMode": "OPAQUE",
152 | "doubleSided": false
153 | }
154 | ],
155 | "textures": [
156 | {
157 | "sampler": 0,
158 | "extensions": {
159 | "EXT_texture_webp": {
160 | "source": 0
161 | }
162 | }
163 | }
164 | ],
165 | "images": [
166 | {
167 | "uri": "cesium%20logo.webp"
168 | }
169 | ],
170 | "samplers": [
171 | {
172 | "magFilter": 9729,
173 | "minFilter": 9986,
174 | "wrapS": 10497,
175 | "wrapT": 10497
176 | }
177 | ],
178 | "bufferViews": [
179 | {
180 | "buffer": 0,
181 | "byteOffset": 768,
182 | "byteLength": 72,
183 | "target": 34963
184 | },
185 | {
186 | "buffer": 0,
187 | "byteOffset": 0,
188 | "byteLength": 576,
189 | "byteStride": 12,
190 | "target": 34962
191 | },
192 | {
193 | "buffer": 0,
194 | "byteOffset": 576,
195 | "byteLength": 192,
196 | "byteStride": 8,
197 | "target": 34962
198 | }
199 | ],
200 | "buffers": [
201 | {
202 | "byteLength": 840,
203 | "uri": "box-textured-separate.bin"
204 | }
205 | ]
206 | }
207 |
--------------------------------------------------------------------------------
/specs/data/2.0/extensions/EXT_texture_webp/box-textured-separate/box-textured-with-fallback.gltf:
--------------------------------------------------------------------------------
1 | {
2 | "asset": {
3 | "generator": "COLLADA2GLTF",
4 | "version": "2.0"
5 | },
6 | "extensionsUsed": [
7 | "EXT_texture_webp"
8 | ],
9 | "scene": 0,
10 | "scenes": [
11 | {
12 | "nodes": [
13 | 0
14 | ]
15 | }
16 | ],
17 | "nodes": [
18 | {
19 | "children": [
20 | 1
21 | ],
22 | "matrix": [
23 | 1,
24 | 0,
25 | 0,
26 | 0,
27 | 0,
28 | 0,
29 | -1,
30 | 0,
31 | 0,
32 | 1,
33 | 0,
34 | 0,
35 | 0,
36 | 0,
37 | 0,
38 | 1
39 | ]
40 | },
41 | {
42 | "mesh": 0
43 | }
44 | ],
45 | "meshes": [
46 | {
47 | "primitives": [
48 | {
49 | "attributes": {
50 | "NORMAL": 1,
51 | "POSITION": 2,
52 | "TEXCOORD_0": 3
53 | },
54 | "indices": 0,
55 | "mode": 4,
56 | "material": 0
57 | }
58 | ],
59 | "name": "Mesh"
60 | }
61 | ],
62 | "accessors": [
63 | {
64 | "bufferView": 0,
65 | "byteOffset": 0,
66 | "componentType": 5123,
67 | "count": 36,
68 | "max": [
69 | 23
70 | ],
71 | "min": [
72 | 0
73 | ],
74 | "type": "SCALAR"
75 | },
76 | {
77 | "bufferView": 1,
78 | "byteOffset": 0,
79 | "componentType": 5126,
80 | "count": 24,
81 | "max": [
82 | 1,
83 | 1,
84 | 1
85 | ],
86 | "min": [
87 | -1,
88 | -1,
89 | -1
90 | ],
91 | "type": "VEC3"
92 | },
93 | {
94 | "bufferView": 1,
95 | "byteOffset": 288,
96 | "componentType": 5126,
97 | "count": 24,
98 | "max": [
99 | 0.5,
100 | 0.5,
101 | 0.5
102 | ],
103 | "min": [
104 | -0.5,
105 | -0.5,
106 | -0.5
107 | ],
108 | "type": "VEC3"
109 | },
110 | {
111 | "bufferView": 2,
112 | "byteOffset": 0,
113 | "componentType": 5126,
114 | "count": 24,
115 | "max": [
116 | 6,
117 | 1
118 | ],
119 | "min": [
120 | 0,
121 | 0
122 | ],
123 | "type": "VEC2"
124 | }
125 | ],
126 | "materials": [
127 | {
128 | "pbrMetallicRoughness": {
129 | "baseColorTexture": {
130 | "index": 0,
131 | "texCoord": 0
132 | },
133 | "metallicFactor": 0,
134 | "baseColorFactor": [
135 | 1,
136 | 1,
137 | 1,
138 | 1
139 | ],
140 | "roughnessFactor": 1
141 | },
142 | "name": "Texture",
143 | "emissiveFactor": [
144 | 0,
145 | 0,
146 | 0
147 | ],
148 | "alphaMode": "OPAQUE",
149 | "doubleSided": false
150 | }
151 | ],
152 | "textures": [
153 | {
154 | "sampler": 0,
155 | "source": 1,
156 | "extensions": {
157 | "EXT_texture_webp": {
158 | "source": 0
159 | }
160 | }
161 | }
162 | ],
163 | "images": [
164 | {
165 | "uri": "cesium%20logo.webp"
166 | },
167 | {
168 | "uri": "cesium%20logo.png"
169 | }
170 | ],
171 | "samplers": [
172 | {
173 | "magFilter": 9729,
174 | "minFilter": 9986,
175 | "wrapS": 10497,
176 | "wrapT": 10497
177 | }
178 | ],
179 | "bufferViews": [
180 | {
181 | "buffer": 0,
182 | "byteOffset": 768,
183 | "byteLength": 72,
184 | "target": 34963
185 | },
186 | {
187 | "buffer": 0,
188 | "byteOffset": 0,
189 | "byteLength": 576,
190 | "byteStride": 12,
191 | "target": 34962
192 | },
193 | {
194 | "buffer": 0,
195 | "byteOffset": 576,
196 | "byteLength": 192,
197 | "byteStride": 8,
198 | "target": 34962
199 | }
200 | ],
201 | "buffers": [
202 | {
203 | "byteLength": 840,
204 | "uri": "box-textured-separate.bin"
205 | }
206 | ]
207 | }
208 |
--------------------------------------------------------------------------------
/specs/data/2.0/extensions/EXT_texture_webp/box-textured-separate/cesium logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/gltf-pipeline/cbbff2f212b5aa6163c7152d46ffcaf3a2ff3ac3/specs/data/2.0/extensions/EXT_texture_webp/box-textured-separate/cesium logo.png
--------------------------------------------------------------------------------
/specs/data/2.0/extensions/EXT_texture_webp/box-textured-separate/cesium logo.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/gltf-pipeline/cbbff2f212b5aa6163c7152d46ffcaf3a2ff3ac3/specs/data/2.0/extensions/EXT_texture_webp/box-textured-separate/cesium logo.webp
--------------------------------------------------------------------------------
/specs/data/2.0/triangle-without-indices/triangle-without-indices.gltf:
--------------------------------------------------------------------------------
1 | {
2 | "scenes" : [
3 | {
4 | "nodes" : [ 0 ]
5 | }
6 | ],
7 |
8 | "nodes" : [
9 | {
10 | "mesh" : 0
11 | }
12 | ],
13 |
14 | "meshes" : [
15 | {
16 | "primitives" : [ {
17 | "attributes" : {
18 | "POSITION" : 0
19 | }
20 | } ]
21 | }
22 | ],
23 |
24 | "buffers" : [
25 | {
26 | "uri" : "data:application/octet-stream;base64,AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAA",
27 | "byteLength" : 36
28 | }
29 | ],
30 | "bufferViews" : [
31 | {
32 | "buffer" : 0,
33 | "byteOffset" : 0,
34 | "byteLength" : 36,
35 | "target" : 34962
36 | }
37 | ],
38 | "accessors" : [
39 | {
40 | "bufferView" : 0,
41 | "byteOffset" : 0,
42 | "componentType" : 5126,
43 | "count" : 3,
44 | "type" : "VEC3",
45 | "max" : [ 1.0, 1.0, 0.0 ],
46 | "min" : [ 0.0, 0.0, 0.0 ]
47 | }
48 | ],
49 |
50 | "asset" : {
51 | "version" : "2.0"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/specs/jasmine.json:
--------------------------------------------------------------------------------
1 | {
2 | "spec_dir": "specs",
3 | "spec_files": [
4 | "**/*Spec.js"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/specs/lib/addBufferSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const addBuffer = require("../../lib/addBuffer");
3 |
4 | describe("addBuffer", () => {
5 | it("adds buffer to gltf and returns its bufferView id", () => {
6 | const gltf = {
7 | buffers: [],
8 | bufferViews: [],
9 | };
10 | const buffer0 = Buffer.alloc(100);
11 | const buffer1 = Buffer.alloc(200);
12 |
13 | expect(addBuffer(gltf, buffer0)).toBe(0);
14 | expect(addBuffer(gltf, buffer1)).toBe(1);
15 | expect(gltf.buffers.length).toBe(2);
16 | expect(gltf.bufferViews.length).toBe(2);
17 | expect(gltf.buffers[0].extras._pipeline.source).toEqual(buffer0);
18 | expect(gltf.buffers[1].extras._pipeline.source).toEqual(buffer1);
19 | expect(gltf.bufferViews[0].buffer).toBe(0);
20 | expect(gltf.bufferViews[1].buffer).toBe(1);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/specs/lib/addExtensionsRequiredSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const addExtensionsRequired = require("../../lib/addExtensionsRequired");
3 |
4 | describe("addExtensionsRequired", () => {
5 | it("adds an extension to extensionsRequired", () => {
6 | const gltf = {};
7 | addExtensionsRequired(gltf, "KHR_materials_pbrSpecularGlossiness");
8 | addExtensionsRequired(gltf, "KHR_draco_mesh_compression");
9 | addExtensionsRequired(gltf, "KHR_draco_mesh_compression"); // Test adding duplicate
10 | expect(gltf.extensionsRequired).toEqual([
11 | "KHR_materials_pbrSpecularGlossiness",
12 | "KHR_draco_mesh_compression",
13 | ]);
14 | expect(gltf.extensionsUsed).toEqual([
15 | "KHR_materials_pbrSpecularGlossiness",
16 | "KHR_draco_mesh_compression",
17 | ]);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/specs/lib/addExtensionsUsedSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const addExtensionsUsed = require("../../lib/addExtensionsUsed");
3 |
4 | describe("addExtensionsUsed", () => {
5 | it("adds an extension to extensionsUsed", () => {
6 | const gltf = {};
7 | addExtensionsUsed(gltf, "KHR_materials_pbrSpecularGlossiness");
8 | addExtensionsUsed(gltf, "KHR_draco_mesh_compression");
9 | addExtensionsUsed(gltf, "KHR_draco_mesh_compression"); // Test adding duplicate
10 | expect(gltf.extensionsUsed).toEqual([
11 | "KHR_materials_pbrSpecularGlossiness",
12 | "KHR_draco_mesh_compression",
13 | ]);
14 | expect(gltf.extensionsRequired).toBeUndefined();
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/specs/lib/addPipelineExtrasSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 | const addPipelineExtras = require("../../lib/addPipelineExtras");
4 |
5 | const WebGLConstants = Cesium.WebGLConstants;
6 |
7 | describe("addPipelineExtras", () => {
8 | it("adds pipeline extras to glTF 1.0 assets", () => {
9 | const gltf = {
10 | buffers: {
11 | sampleBuffer0: {
12 | byteLength: 100,
13 | },
14 | },
15 | shaders: {
16 | sample0VS: {
17 | type: WebGLConstants.VERTEX_SHADER,
18 | uri: "data:,",
19 | },
20 | },
21 | };
22 | const gltfWithExtras = addPipelineExtras(gltf);
23 | expect(
24 | gltfWithExtras.buffers["sampleBuffer0"].extras._pipeline,
25 | ).toBeDefined();
26 | expect(gltfWithExtras.shaders["sample0VS"].extras._pipeline).toBeDefined();
27 | });
28 |
29 | it("adds pipeline extras to glTF 2.0 assets", () => {
30 | const gltf = {
31 | buffers: [
32 | {
33 | byteLength: 100,
34 | },
35 | ],
36 | extensions: {
37 | KHR_techniques_webgl: {
38 | shaders: [
39 | {
40 | type: WebGLConstants.VERTEX_SHADER,
41 | uri: "data:,",
42 | },
43 | ],
44 | },
45 | },
46 | extensionsRequired: ["KHR_techniques_webgl"],
47 | extensionsUsed: ["KHR_techniques_webgl"],
48 | };
49 | const gltfWithExtras = addPipelineExtras(gltf);
50 | expect(gltfWithExtras.buffers[0].extras._pipeline).toBeDefined();
51 | expect(
52 | gltfWithExtras.extensions.KHR_techniques_webgl.shaders[0].extras
53 | ._pipeline,
54 | ).toBeDefined();
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/specs/lib/addToArraySpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const addToArray = require("../../lib/addToArray");
3 |
4 | describe("addToArray", () => {
5 | it("adds item to array and returns its index", () => {
6 | const gltf = {
7 | buffers: [],
8 | };
9 | const buffer0 = {
10 | byteLength: 100,
11 | };
12 | const buffer1 = {
13 | byteLength: 200,
14 | };
15 | expect(addToArray(gltf.buffers, buffer0)).toBe(0);
16 | expect(addToArray(gltf.buffers, buffer1)).toBe(1);
17 | expect(gltf.buffers).toEqual([buffer0, buffer1]);
18 | });
19 |
20 | it("returns index of duplicate element when checkDuplicates is true", () => {
21 | const gltf = {
22 | buffers: [],
23 | };
24 | const buffer0 = {
25 | byteLength: 100,
26 | };
27 | const buffer1 = {
28 | byteLength: 200,
29 | };
30 | expect(addToArray(gltf.buffers, buffer0, true)).toBe(0);
31 | expect(addToArray(gltf.buffers, buffer1, true)).toBe(1);
32 | expect(addToArray(gltf.buffers, buffer0, true)).toBe(0);
33 | expect(gltf.buffers.length).toBe(2);
34 | expect(addToArray(gltf.buffers, buffer0)).toBe(2);
35 | expect(gltf.buffers.length).toBe(3);
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/specs/lib/dataUriToBufferSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const dataUriToBuffer = require("../../lib/dataUriToBuffer");
3 |
4 | describe("dataUriToBuffer", () => {
5 | it("converts base64 data uri to buffer", () => {
6 | const buffer = Buffer.from([103, 108, 84, 70]);
7 | const dataUri = `data:application/octet-stream;base64,${buffer.toString(
8 | "base64",
9 | )}`;
10 | expect(dataUriToBuffer(dataUri)).toEqual(buffer);
11 | });
12 |
13 | it("converts utf8 data uri to buffer", () => {
14 | const buffer = Buffer.from([103, 108, 84, 70]);
15 | const dataUri = `data:text/plain;charset=utf-8,${buffer.toString("utf8")}`;
16 | expect(dataUriToBuffer(dataUri)).toEqual(buffer);
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/specs/lib/findAccessorMinMaxSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const findAccessorMinMax = require("../../lib/findAccessorMinMax");
3 | const readResources = require("../../lib/readResources");
4 |
5 | const contiguousData = [
6 | -1.0, -2.0, -3.0, 3.0, 2.0, 1.0, 0.0, 0.0, 0.0, 0.5, -0.5, 0.5,
7 | ];
8 |
9 | const nan = Number.NaN;
10 | const nonContiguousData = [
11 | -1.0,
12 | 1.0,
13 | -1.0,
14 | nan,
15 | nan,
16 | nan,
17 | 0.0,
18 | 0.0,
19 | 0.0,
20 | nan,
21 | nan,
22 | nan,
23 | 3.0,
24 | 2.0,
25 | 1.0,
26 | nan,
27 | nan,
28 | nan,
29 | -1.0,
30 | -2.0,
31 | -3.0,
32 | nan,
33 | nan,
34 | nan,
35 | ];
36 |
37 | function createGltf(elements, byteStride) {
38 | const buffer = Buffer.from(new Float32Array(elements).buffer);
39 | const byteLength = buffer.length;
40 | const dataUri = `data:application/octet-stream;base64,${buffer.toString(
41 | "base64",
42 | )}`;
43 | const gltf = {
44 | asset: {
45 | version: "2.0",
46 | },
47 | accessors: [
48 | {
49 | bufferView: 0,
50 | byteOffset: 0,
51 | componentType: 5126,
52 | count: 4,
53 | type: "VEC3",
54 | },
55 | ],
56 | bufferViews: [
57 | {
58 | buffer: 0,
59 | byteOffset: 0,
60 | byteLength: byteLength,
61 | byteStride: byteStride,
62 | },
63 | ],
64 | buffers: [
65 | {
66 | uri: dataUri,
67 | byteLength: byteLength,
68 | },
69 | ],
70 | };
71 | return readResources(gltf);
72 | }
73 |
74 | describe("findAccessorMinMax", () => {
75 | it("finds the min and max of an accessor", async () => {
76 | const gltf = await createGltf(contiguousData, 12);
77 | const expectedMin = [-1.0, -2.0, -3.0];
78 | const expectedMax = [3.0, 2.0, 1.0];
79 | const minMax = findAccessorMinMax(gltf, gltf.accessors[0]);
80 | expect(minMax.min).toEqual(expectedMin);
81 | expect(minMax.max).toEqual(expectedMax);
82 | });
83 |
84 | it("finds the min and max in a non-contiguous accessor", async () => {
85 | const gltf = await createGltf(nonContiguousData, 24);
86 | const expectedMin = [-1.0, -2.0, -3.0];
87 | const expectedMax = [3.0, 2.0, 1.0];
88 | const minMax = findAccessorMinMax(gltf, gltf.accessors[0]);
89 | expect(minMax.min).toEqual(expectedMin);
90 | expect(minMax.max).toEqual(expectedMax);
91 | });
92 | });
93 |
--------------------------------------------------------------------------------
/specs/lib/getAccessorByteStrideSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 | const getAccessorByteStride = require("../../lib/getAccessorByteStride");
4 |
5 | const WebGLConstants = Cesium.WebGLConstants;
6 |
7 | describe("getAccessorByteStride", () => {
8 | it("gets accessor byte stride", () => {
9 | const gltf = {
10 | accessors: [
11 | {
12 | componentType: WebGLConstants.FLOAT,
13 | count: 24,
14 | type: "VEC3",
15 | min: [-1.0, -1.0, -1.0],
16 | max: [1.0, 1.0, 1.0],
17 | },
18 | {
19 | bufferView: 0,
20 | componentType: WebGLConstants.FLOAT,
21 | count: 24,
22 | type: "VEC3",
23 | min: [-1.0, -1.0, -1.0],
24 | max: [1.0, 1.0, 1.0],
25 | },
26 | {
27 | bufferView: 1,
28 | componentType: WebGLConstants.FLOAT,
29 | count: 24,
30 | type: "VEC3",
31 | min: [-1.0, -1.0, -1.0],
32 | max: [1.0, 1.0, 1.0],
33 | },
34 | {
35 | componentType: WebGLConstants.FLOAT,
36 | count: 24,
37 | type: "VEC2",
38 | min: [0.0, 0.0],
39 | max: [1.0, 1.0],
40 | },
41 | {
42 | componentType: WebGLConstants.INT,
43 | count: 36,
44 | type: "SCALAR",
45 | min: [0],
46 | max: [24],
47 | },
48 | ],
49 | bufferViews: [
50 | {
51 | buffer: 0,
52 | byteLength: 288,
53 | byteOffset: 0,
54 | },
55 | {
56 | buffer: 0,
57 | byteLength: 288,
58 | byteOffset: 288,
59 | byteStride: 32,
60 | },
61 | ],
62 | };
63 |
64 | expect(getAccessorByteStride(gltf, gltf.accessors[0])).toBe(12);
65 | expect(getAccessorByteStride(gltf, gltf.accessors[1])).toBe(12);
66 | expect(getAccessorByteStride(gltf, gltf.accessors[2])).toBe(32);
67 | expect(getAccessorByteStride(gltf, gltf.accessors[3])).toBe(8);
68 | expect(getAccessorByteStride(gltf, gltf.accessors[4])).toBe(4);
69 | });
70 | });
71 |
--------------------------------------------------------------------------------
/specs/lib/getBufferPaddedSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const getBufferPadded = require("../../lib/getBufferPadded");
3 |
4 | describe("getBufferPadded", () => {
5 | it("gets buffer padded to 8 bytes", () => {
6 | let buffer = Buffer.alloc(0);
7 | let bufferPadded = getBufferPadded(buffer);
8 | expect(bufferPadded.length).toBe(0);
9 |
10 | buffer = Buffer.from([1]);
11 | bufferPadded = getBufferPadded(buffer);
12 | expect(bufferPadded.length).toBe(8);
13 | expect(bufferPadded.readUInt8(0)).toBe(1);
14 | expect(bufferPadded.readUInt8(1)).toBe(0);
15 | expect(bufferPadded.readUInt8(2)).toBe(0);
16 | expect(bufferPadded.readUInt8(3)).toBe(0);
17 | expect(bufferPadded.readUInt8(4)).toBe(0);
18 | expect(bufferPadded.readUInt8(5)).toBe(0);
19 | expect(bufferPadded.readUInt8(6)).toBe(0);
20 | expect(bufferPadded.readUInt8(7)).toBe(0);
21 |
22 | // Does not allocate a new buffer when buffer length is already aligned to 8 bytes
23 | buffer = Buffer.alloc(8);
24 | bufferPadded = getBufferPadded(buffer);
25 | expect(bufferPadded).toBe(buffer);
26 |
27 | buffer = Buffer.alloc(70);
28 | bufferPadded = getBufferPadded(buffer);
29 | expect(bufferPadded.length).toBe(72);
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/specs/lib/getComponentReaderSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 | const getComponentReader = require("../../lib/getComponentReader");
4 |
5 | const ComponentDatatype = Cesium.ComponentDatatype;
6 |
7 | function testComponentReader(componentType) {
8 | const typedArray = ComponentDatatype.createTypedArray(
9 | componentType,
10 | [0, 1, 2],
11 | );
12 | const dataView = new DataView(typedArray.buffer);
13 | const componentTypeByteLength =
14 | ComponentDatatype.getSizeInBytes(componentType);
15 | const componentReader = getComponentReader(componentType);
16 | const byteOffset = componentTypeByteLength;
17 | const numberOfComponents = 2;
18 | const result = new Array(numberOfComponents);
19 | componentReader(
20 | dataView,
21 | byteOffset,
22 | numberOfComponents,
23 | componentTypeByteLength,
24 | result,
25 | );
26 | expect(result).toEqual([1, 2]);
27 | }
28 |
29 | describe("getComponentReader", () => {
30 | it("reads values", () => {
31 | testComponentReader(ComponentDatatype.BYTE);
32 | testComponentReader(ComponentDatatype.UNSIGNED_BYTE);
33 | testComponentReader(ComponentDatatype.SHORT);
34 | testComponentReader(ComponentDatatype.UNSIGNED_SHORT);
35 | testComponentReader(ComponentDatatype.INT);
36 | testComponentReader(ComponentDatatype.UNSIGNED_INT);
37 | testComponentReader(ComponentDatatype.FLOAT);
38 | testComponentReader(ComponentDatatype.DOUBLE);
39 | });
40 | });
41 |
--------------------------------------------------------------------------------
/specs/lib/getImageExtensionSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const { RuntimeError } = require("cesium");
4 |
5 | const dataUriToBuffer = require("../../lib/dataUriToBuffer");
6 | const getImageExtension = require("../../lib/getImageExtension");
7 |
8 | const pngData = dataUriToBuffer(
9 | "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAIAAAAmkwkpAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gQcDxwOcoRpqQAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAAFElEQVQI12P8//8/AwwwMSAB3BwAlm4DBdoYksUAAAAASUVORK5CYII=",
10 | );
11 | const gifData = dataUriToBuffer(
12 | "data:image/gif;base64,R0lGODdhBAAEAIAAAP///////ywAAAAABAAEAAACBISPCQUAOw==",
13 | );
14 | const jpgData = dataUriToBuffer(
15 | "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD//gATQ3JlYXRlZCB3aXRoIEdJTVD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAEAAQDAREAAhEBAxEB/8QAFAABAAAAAAAAAAAAAAAAAAAACP/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAVSf/8QAFBABAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQABBQJ//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPwF//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwF//8QAFBABAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQAGPwJ//8QAFBABAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQABPyF//9oADAMBAAIAAwAAABCf/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPxB//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPxB//8QAFBABAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQABPxB//9k=",
16 | );
17 | const bmpData = dataUriToBuffer(
18 | "data:image/bmp;base64,Qk1mAAAAAAAAADYAAAAoAAAABAAAAAQAAAABABgAAAAAADAAAAATCwAAEwsAAAAAAAAAAAAA////////////////////////////////////////////////////////////////",
19 | );
20 | const ktx2Data = dataUriToBuffer(
21 | "data:image/ktx2:base64,q0tUWCAyMLsNChoKAAAAAAEAAABAAAAAQAAAAAAAAAAAAAAAAQAAAAEAAAABAAAAaAAAACwAAACUAAAAdAAAAAgBAAAAAAAA7QEAAAAAAAD1AgAAAAAAAPgAAAAAAAAAAAAAAAAAAAAsAAAAAAAAAAIAKACjAQIAAwMAAAAAAAAAAAAAAAA/AAAAAAAAAAAA/////xIAAABLVFhvcmllbnRhdGlvbgByZAAAAFUAAABLVFh3cml0ZXIAdG9rdHggdjQuMC5iZXRhMi41My5nZjgzZDI4NDUuZGlydHkgLyBsaWJrdHggdjQuMC5iZXRhMi41MS5nM2EzMWQ0ZGEuZGlydHkAAAAAHQBfAEcAAAAcAQAAYgAAAAAAAAAAAAAAAAAAAPgAAAAAAAAAAAAAACDAGwABAIBgclixPD5qPIA3EAEAAAJpL1qO+wKACQAAAAAAAAQCEAEAAABACMSHxYxC5qRfZDX/tDYozqTrsZwcLKFPOj8HAAjmamgUQQAA4G9md89jvYMNJJDEE0erNdUVV1pMAlpY6II/hRiwULCxywNMUggi6NVWCjb+FBYpBRcLawsfwLyBDyAEEpo8f13mE8N3+MzWN8aG0uInpReLBNfESNpLUhJjn6b0UYqK6fRJOijLPA7LtfXfpawtHOM5wslUOelTK5qmmCyvsvvBs9vv7W4xDJxWwcfD2u9chVzP234bZjCbzdBt9+rlcHEnF9Uqax51Mg7aopqvbDx89+Bm8yd0dfjyNdzXnWaTS7dwHQMKYiRu4hcUupGEncw9fArUMQahsqgFgqPLjReLeCFOmjpQB/VCqMhnZzzd2+xeHe83j+sRN/Cff4yZ8Q0Cxnpj/0d75e6dw3y0evn6/C0AwdsAARAGgie2geTzWDtGyh9KgpX8WJb+BoILSDZI+sB3AA8ABEAQhjVfWhkigV6/gQJxMCXBAADD9G9HsB/tCHQCO40d4H1eHXIIwiGiqaHGIEjX+AaACQAAAAAAEMRAAPxNflpzjZbtuGAsfCOdQtK6+1SN1+ekY6j7t75MEIfKzChq81nw7nJCXzrVkyZprvXxESlhruXkPYHimL3u9o3k57aC4TXqLc9ZhbxM8VrYjcZ4NtT2+55ZPICCeHUISHGmLmU+/LNbHx0jlpkCmnpHFAZSaHnCJ49gGUlMs7+K4PcBQ9KG618GBGDlxTULGQlp8NdIpU8tdzAFkCSPVU20irfukO/YSpScsCHeCglbH2ljLGR+7McSAoyLd9j0k06gjHZdYiPGpi0n6fX7PCd22juv/0qvP5YIoHnrGZEXMiFaw9chnA2C8POpNi0EQu/lwCZoILII",
22 | );
23 | const basisData = dataUriToBuffer(
24 | "data:image/basis;base64,c0ITAE0AAA9wAAAAqewBAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAQBkAAAAKAAAAQCMAAAABQAAkQAAACsAAABNAAAAAAAAAAAAAAAAAAAAAAEAAQABAAEAvAAAAAEAAACPUwHABAAAAAAAAAICmAgAAAAAAEASBAATAAAAAAAAiABgAgAAAAAAABFUVVVVBQDBRAAAAAAAAPJfLQCYAAAAAAAAQAgAEwACAAAAAIgBwAQAAAAAAAACCAAA",
25 | );
26 | const textData = dataUriToBuffer("data:text/plain;charset=utf-8,randomtext");
27 |
28 | describe("getImageExtension", () => {
29 | it("gets image extension from buffer", () => {
30 | expect(getImageExtension(pngData)).toBe(".png");
31 | expect(getImageExtension(gifData)).toBe(".gif");
32 | expect(getImageExtension(jpgData)).toBe(".jpg");
33 | expect(getImageExtension(bmpData)).toBe(".bmp");
34 | expect(getImageExtension(ktx2Data)).toBe(".ktx2");
35 | expect(getImageExtension(basisData)).toBe(".basis");
36 | });
37 |
38 | it("throws error if buffer does not contain image data", () => {
39 | let thrownError;
40 | try {
41 | getImageExtension(textData);
42 | } catch (e) {
43 | thrownError = e;
44 | }
45 | expect(thrownError).toEqual(
46 | new RuntimeError("Image data does not have valid header"),
47 | );
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/specs/lib/getJsonBufferPaddedSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const getJsonBufferPadded = require("../../lib/getJsonBufferPadded");
3 |
4 | describe("getJsonBufferPadded", () => {
5 | it("get json buffer padded to 8 bytes", () => {
6 | const gltf = {
7 | asset: {
8 | version: "2.0",
9 | },
10 | };
11 | const string = JSON.stringify(gltf);
12 | expect(string.length).toBe(27);
13 | const bufferPadded = getJsonBufferPadded(gltf);
14 | expect(bufferPadded.length).toBe(32);
15 | expect(bufferPadded.readUInt8(27)).toBe(32); // Space
16 | expect(bufferPadded.readUInt8(28)).toBe(32);
17 | expect(bufferPadded.readUInt8(29)).toBe(32);
18 | expect(bufferPadded.readUInt8(30)).toBe(32);
19 | expect(bufferPadded.readUInt8(31)).toBe(32);
20 | });
21 |
22 | it("get json buffer padded to 8 bytes relative to byte offset", () => {
23 | const gltf = {
24 | asset: {
25 | version: "2.0",
26 | },
27 | };
28 | const string = JSON.stringify(gltf);
29 | expect(string.length).toBe(27);
30 | const bufferPadded = getJsonBufferPadded(gltf, 20);
31 | expect(bufferPadded.length).toBe(28);
32 | expect(bufferPadded.readUInt8(27)).toBe(32); // Space
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/specs/lib/getStatisticsSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const getStatistics = require("../../lib/getStatistics");
3 |
4 | describe("getStatistics", () => {
5 | const gltf = {
6 | accessors: [
7 | {
8 | componentType: 5123,
9 | type: "SCALAR",
10 | count: 1,
11 | },
12 | {
13 | componentType: 5123,
14 | type: "SCALAR",
15 | count: 2,
16 | },
17 | {
18 | componentType: 5123,
19 | type: "SCALAR",
20 | count: 6,
21 | },
22 | {
23 | componentType: 5126,
24 | type: "VEC3",
25 | count: 6,
26 | },
27 | ],
28 | buffers: [
29 | {
30 | byteLength: 140,
31 | },
32 | {
33 | byteLength: 120,
34 | uri: "buffer.bin",
35 | },
36 | ],
37 | images: [
38 | {
39 | uri: "image.png",
40 | },
41 | {
42 | uri: "data:image/png;",
43 | },
44 | {
45 | uri: "image2.png",
46 | },
47 | ],
48 | meshes: [
49 | {
50 | primitives: [
51 | {
52 | indices: 0,
53 | mode: 0, // POINTS
54 | },
55 | {
56 | indices: 1,
57 | mode: 1, // LINES
58 | },
59 | {
60 | attributes: {
61 | POSITION: 3,
62 | },
63 | },
64 | ],
65 | },
66 | {
67 | primitives: [
68 | {
69 | indices: 2,
70 | mode: 4, // TRIANGLES
71 | },
72 | ],
73 | },
74 | ],
75 | materials: [{}, {}],
76 | animations: [{}, {}, {}],
77 | nodes: [
78 | {
79 | name: "rootNode",
80 | mesh: 0,
81 | },
82 | ],
83 | };
84 |
85 | it("returns statistics for a gltf", () => {
86 | const statistics = getStatistics(gltf);
87 | expect(statistics.buffersByteLength).toEqual(260);
88 | expect(statistics.numberOfImages).toEqual(3);
89 | expect(statistics.numberOfExternalRequests).toEqual(3);
90 | expect(statistics.numberOfDrawCalls).toEqual(4);
91 | expect(statistics.numberOfRenderedPrimitives).toEqual(6);
92 | expect(statistics.numberOfNodes).toEqual(1);
93 | expect(statistics.numberOfMeshes).toEqual(2);
94 | expect(statistics.numberOfMaterials).toEqual(2);
95 | expect(statistics.numberOfAnimations).toEqual(3);
96 | });
97 |
98 | it("returns draw call statistics for a gltf node", () => {
99 | const statistics = getStatistics(gltf, 0);
100 | expect(statistics.numberOfDrawCalls).toEqual(3);
101 | expect(statistics.numberOfRenderedPrimitives).toEqual(4);
102 | });
103 | });
104 |
--------------------------------------------------------------------------------
/specs/lib/glbToGltfSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const fsExtra = require("fs-extra");
3 | const glbToGltf = require("../../lib/glbToGltf");
4 |
5 | const glbPath = "specs/data/2.0/box-textured-binary/box-textured-binary.glb";
6 |
7 | describe("glbToGltf", () => {
8 | it("glbToGltf", async () => {
9 | spyOn(console, "log");
10 | const glb = fsExtra.readFileSync(glbPath);
11 | const options = {
12 | separate: true,
13 | stats: true,
14 | };
15 | const results = await glbToGltf(glb, options);
16 | expect(results.gltf).toBeDefined();
17 | expect(results.separateResources).toBeDefined();
18 | expect(results.gltf.buffers.length).toBe(1);
19 | expect(console.log).toHaveBeenCalled();
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/specs/lib/gltfToGlbSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const fsExtra = require("fs-extra");
3 | const path = require("path");
4 |
5 | const gltfToGlb = require("../../lib/gltfToGlb");
6 | const parseGlb = require("../../lib/parseGlb");
7 |
8 | const gltfPath =
9 | "specs/data/2.0/box-textured-embedded/box-textured-embedded.gltf";
10 | const gltfMeshoptFallbackPath =
11 | "specs/data/2.0/extensions/EXT_meshopt_compression/meshopt-fallback/meshopt-fallback.gltf";
12 |
13 | describe("gltfToGlb", () => {
14 | it("gltfToGlb", async () => {
15 | spyOn(console, "log");
16 | const gltf = fsExtra.readJsonSync(gltfPath);
17 | const options = {
18 | separateTextures: true,
19 | stats: true,
20 | };
21 | const results = await gltfToGlb(gltf, options);
22 | const glb = results.glb;
23 | const separateResources = results.separateResources;
24 | expect(Buffer.isBuffer(glb)).toBe(true);
25 | expect(Object.keys(separateResources).length).toBe(1);
26 | expect(console.log).toHaveBeenCalled();
27 |
28 | // Header + JSON header + JSON content + binary header + binary content
29 | const glbLength = glb.readUInt32LE(8);
30 | const jsonChunkLength = glb.readUInt32LE(12);
31 | const binaryChunkLength = glb.readUInt32LE(12 + 8 + jsonChunkLength);
32 | const expectedLength = 12 + 8 + jsonChunkLength + 8 + binaryChunkLength;
33 | expect(glbLength).toBe(expectedLength);
34 | expect(glb.length).toBe(expectedLength);
35 | });
36 |
37 | it("gltfToGlb with separate resources", async () => {
38 | spyOn(console, "log");
39 | const gltf = fsExtra.readJsonSync(gltfPath);
40 | const options = {
41 | separate: true,
42 | stats: true,
43 | };
44 | const results = await gltfToGlb(gltf, options);
45 | const glb = results.glb;
46 | const separateResources = results.separateResources;
47 | expect(Buffer.isBuffer(glb)).toBe(true);
48 | expect(Object.keys(separateResources).length).toBe(2);
49 | expect(console.log).toHaveBeenCalled();
50 |
51 | // Header + JSON header + JSON content. No binary header or content.
52 | const glbLength = glb.readUInt32LE(8);
53 | const jsonChunkLength = glb.readUInt32LE(12);
54 | const expectedLength = 12 + 8 + jsonChunkLength;
55 | expect(glbLength).toBe(expectedLength);
56 | expect(glb.length).toBe(expectedLength);
57 | });
58 |
59 | it("gltfToGlb processes glTF with EXT_meshopt_compression extension.", async () => {
60 | const gltf = fsExtra.readJsonSync(gltfMeshoptFallbackPath);
61 | const options = {
62 | resourceDirectory: path.dirname(gltfMeshoptFallbackPath),
63 | };
64 | const results = await gltfToGlb(gltf, options);
65 | expect(results.glb).toBeDefined();
66 |
67 | const processedGltf = parseGlb(results.glb);
68 |
69 | expect(processedGltf).toBeDefined();
70 | expect(processedGltf.buffers.length).toBe(1);
71 |
72 | const buffer0 = processedGltf.buffers[0];
73 |
74 | const bufferView0 = processedGltf.bufferViews[0];
75 | const bufferView1 = processedGltf.bufferViews[1];
76 | const bufferView2 = processedGltf.bufferViews[2];
77 |
78 | const meshoptObject0 = bufferView0.extensions.EXT_meshopt_compression;
79 | const meshoptObject1 = bufferView1.extensions.EXT_meshopt_compression;
80 | const meshoptObject2 = bufferView2.extensions.EXT_meshopt_compression;
81 |
82 | expect(buffer0.byteLength).toBe(1432);
83 | expect(buffer0.uri).not.toBeDefined();
84 |
85 | expect(bufferView0.buffer).toBe(0);
86 | expect(bufferView0.byteOffset).toBe(0);
87 | expect(bufferView0.byteLength).toBe(400);
88 | expect(meshoptObject0.buffer).toBe(0);
89 | expect(meshoptObject0.byteOffset).toBe(400);
90 | expect(meshoptObject0.byteLength).toBe(265);
91 |
92 | expect(bufferView1.buffer).toBe(0);
93 | expect(bufferView1.byteOffset).toBe(672);
94 | expect(bufferView1.byteLength).toBe(200);
95 | expect(meshoptObject1.buffer).toBe(0);
96 | expect(meshoptObject1.byteOffset).toBe(872);
97 | expect(meshoptObject1.byteLength).toBe(117);
98 |
99 | expect(bufferView2.buffer).toBe(0);
100 | expect(bufferView2.byteOffset).toBe(992);
101 | expect(bufferView2.byteLength).toBe(360);
102 | expect(meshoptObject2.buffer).toBe(0);
103 | expect(meshoptObject2.byteOffset).toBe(1352);
104 | expect(meshoptObject2.byteLength).toBe(78);
105 | });
106 |
107 | it("gltfToGlb processes glTF with EXT_meshopt_compression extension with separate resources.", async () => {
108 | const gltf = fsExtra.readJsonSync(gltfMeshoptFallbackPath);
109 | const options = {
110 | separate: true,
111 | resourceDirectory: path.dirname(gltfMeshoptFallbackPath),
112 | };
113 | const results = await gltfToGlb(gltf, options);
114 | expect(results.glb).toBeDefined();
115 |
116 | const processedGltf = parseGlb(results.glb);
117 |
118 | expect(processedGltf).toBeDefined();
119 | expect(processedGltf.buffers.length).toBe(2);
120 |
121 | const buffer0 = processedGltf.buffers[0];
122 | const buffer1 = processedGltf.buffers[1];
123 |
124 | const bufferView0 = gltf.bufferViews[0];
125 | const bufferView1 = gltf.bufferViews[1];
126 | const bufferView2 = gltf.bufferViews[2];
127 |
128 | const meshoptObject0 = bufferView0.extensions.EXT_meshopt_compression;
129 | const meshoptObject1 = bufferView1.extensions.EXT_meshopt_compression;
130 | const meshoptObject2 = bufferView2.extensions.EXT_meshopt_compression;
131 |
132 | expect(buffer0.byteLength).toBe(960);
133 | expect(buffer0.uri).toBe("meshopt-fallback-meshopt-fallback-1.bin");
134 | expect(buffer1.byteLength).toBe(472);
135 | expect(buffer1.uri).toBe("meshopt-fallback.bin");
136 |
137 | expect(bufferView0.buffer).toBe(0);
138 | expect(bufferView0.byteOffset).toBe(0);
139 | expect(bufferView0.byteLength).toBe(400);
140 | expect(meshoptObject0.buffer).toBe(1);
141 | expect(meshoptObject0.byteOffset).toBe(0);
142 | expect(meshoptObject0.byteLength).toBe(265);
143 |
144 | expect(bufferView1.buffer).toBe(0);
145 | expect(bufferView1.byteOffset).toBe(400);
146 | expect(bufferView1.byteLength).toBe(200);
147 | expect(meshoptObject1.buffer).toBe(1);
148 | expect(meshoptObject1.byteOffset).toBe(272);
149 | expect(meshoptObject1.byteLength).toBe(117);
150 |
151 | expect(bufferView2.buffer).toBe(0);
152 | expect(bufferView2.byteOffset).toBe(600);
153 | expect(bufferView2.byteLength).toBe(360);
154 | expect(meshoptObject2.buffer).toBe(1);
155 | expect(meshoptObject2.byteOffset).toBe(392);
156 | expect(meshoptObject2.byteLength).toBe(78);
157 | });
158 | });
159 |
--------------------------------------------------------------------------------
/specs/lib/moveTechniquesToExtensionSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 | const moveTechniquesToExtension = require("../../lib/moveTechniquesToExtension");
4 |
5 | const WebGLConstants = Cesium.WebGLConstants;
6 | describe("moveTechniquesToExtension", () => {
7 | it("moves techniques, shaders, and programs to extension", () => {
8 | const gltf = {
9 | programs: {
10 | program_0: {
11 | attributes: ["a_normal", "a_position", "a_texcoord0"],
12 | fragmentShader: "BoxTextured0FS",
13 | vertexShader: "BoxTextured0VS",
14 | },
15 | program_1: {
16 | attributes: ["a_normal", "a_position", "a_texcoord0"],
17 | fragmentShader: "BoxTextured1FS",
18 | vertexShader: "BoxTextured0VS",
19 | },
20 | },
21 | shaders: {
22 | BoxTextured0FS: {
23 | type: WebGLConstants.FRAGMENT_SHADER,
24 | uri: "BoxTextured0FS.glsl",
25 | },
26 | BoxTextured0VS: {
27 | type: WebGLConstants.VERTEX_SHADER,
28 | uri: "BoxTextured0VS.glsl",
29 | },
30 | BoxTextured1FS: {
31 | type: WebGLConstants.FRAGMENT_SHADER,
32 | uri: "BoxTextured1FS.glsl",
33 | },
34 | },
35 | techniques: {
36 | technique0: {
37 | attributes: {
38 | a_normal: "normal",
39 | a_position: "position",
40 | a_texcoord0: "texcoord0",
41 | },
42 | parameters: {
43 | diffuse: {
44 | type: WebGLConstants.SAMPLER_2D,
45 | },
46 | modelViewMatrix: {
47 | semantic: "MODELVIEW",
48 | type: WebGLConstants.FLOAT_MAT4,
49 | },
50 | normal: {
51 | semantic: "NORMAL",
52 | type: WebGLConstants.FLOAT_VEC3,
53 | },
54 | normalMatrix: {
55 | semantic: "MODELVIEWINVERSETRANSPOSE",
56 | type: WebGLConstants.FLOAT_MAT3,
57 | },
58 | position: {
59 | semantic: "POSITION",
60 | type: WebGLConstants.FLOAT_VEC3,
61 | },
62 | projectionMatrix: {
63 | semantic: "PROJECTION",
64 | type: WebGLConstants.FLOAT_MAT4,
65 | },
66 | shininess: {
67 | type: WebGLConstants.FLOAT,
68 | },
69 | specular: {
70 | type: WebGLConstants.FLOAT_VEC4,
71 | },
72 | texcoord0: {
73 | semantic: "TEXCOORD_0",
74 | type: WebGLConstants.FLOAT_VEC2,
75 | },
76 | },
77 | program: "program_0",
78 | states: {
79 | enable: [WebGLConstants.DEPTH_TEST, WebGLConstants.CULL_FACE],
80 | },
81 | uniforms: {
82 | u_diffuse: "diffuse",
83 | u_modelViewMatrix: "modelViewMatrix",
84 | u_normalMatrix: "normalMatrix",
85 | u_projectionMatrix: "projectionMatrix",
86 | u_shininess: "shininess",
87 | u_specular: "specular",
88 | },
89 | },
90 | technique1: {
91 | program: "program_1",
92 | },
93 | technique2: {
94 | parameters: {
95 | diffuse: {
96 | type: WebGLConstants.FLOAT_VEC4,
97 | },
98 | },
99 | program: "program_0",
100 | uniforms: {
101 | u_diffuse: "diffuse",
102 | },
103 | },
104 | },
105 | materials: [
106 | {
107 | name: "Texture",
108 | technique: "technique0",
109 | values: {
110 | diffuse: "texture_Image0001",
111 | shininess: 256,
112 | specular: [0.2, 0.2, 0.2, 1],
113 | },
114 | },
115 | {
116 | name: "Color",
117 | technique: "technique2",
118 | values: {
119 | diffuse: [0.2, 0.2, 0.2, 1],
120 | },
121 | },
122 | ],
123 | };
124 |
125 | const gltfWithTechniquesWebgl = moveTechniquesToExtension(gltf);
126 | expect(gltfWithTechniquesWebgl.extensions).toBeDefined();
127 | const techniques = gltfWithTechniquesWebgl.extensions.KHR_techniques_webgl;
128 | expect(techniques).toBeDefined();
129 | expect(techniques.techniques.length).toBe(3);
130 |
131 | const technique = techniques.techniques[0];
132 | const attributes = technique.attributes;
133 | expect(attributes).toBeDefined();
134 | expect(attributes.a_position.semantic).toBe("POSITION");
135 |
136 | const uniforms = technique.uniforms;
137 | expect(uniforms).toBeDefined();
138 | expect(uniforms.u_modelViewMatrix.semantic).toBe("MODELVIEW");
139 | expect(uniforms.u_modelViewMatrix.type).toBe(WebGLConstants.FLOAT_MAT4);
140 |
141 | expect(technique.program).toBe(0);
142 | expect(technique.parameters).toBeUndefined();
143 | expect(technique.states).toBeUndefined();
144 |
145 | expect(techniques.programs.length).toBe(2);
146 | const program = techniques.programs[technique.program];
147 | expect(program).toBeDefined();
148 |
149 | expect(techniques.shaders.length).toBe(3);
150 | expect(techniques.shaders[program.fragmentShader].type).toBe(
151 | WebGLConstants.FRAGMENT_SHADER,
152 | );
153 | expect(techniques.shaders[program.vertexShader].type).toBe(
154 | WebGLConstants.VERTEX_SHADER,
155 | );
156 |
157 | expect(gltfWithTechniquesWebgl.techniques).toBeUndefined();
158 | expect(gltfWithTechniquesWebgl.programs).toBeUndefined();
159 | expect(gltfWithTechniquesWebgl.shaders).toBeUndefined();
160 |
161 | const material = gltf.materials[0];
162 | expect(material.extensions).toBeDefined();
163 | const materialTechniques = material.extensions.KHR_techniques_webgl;
164 | expect(materialTechniques).toBeDefined();
165 | expect(materialTechniques.technique).toBe(0);
166 | expect(materialTechniques.values.u_shininess).toBe(256);
167 | expect(materialTechniques.values.u_diffuse).toBe("texture_Image0001");
168 |
169 | expect(material.technique).toBeUndefined();
170 | expect(material.values).toBeUndefined();
171 |
172 | const technique2 = techniques.techniques[1];
173 | const program2 = techniques.programs[technique2.program];
174 | expect(program2.vertexShader).toBe(program.vertexShader);
175 | expect(program2.fragmentShader).not.toBe(program.fragmentShader);
176 |
177 | const technique3 = techniques.techniques[2];
178 | expect(technique3.program).toBe(0);
179 | expect(technique3.uniforms.u_diffuse.type).toBe(WebGLConstants.FLOAT_VEC4);
180 | expect(
181 | gltf.materials[1].extensions.KHR_techniques_webgl.values.u_diffuse,
182 | ).toEqual([0.2, 0.2, 0.2, 1.0]);
183 | });
184 | });
185 |
--------------------------------------------------------------------------------
/specs/lib/numberOfComponentsForTypeSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const numberOfComponentsForType = require("../../lib/numberOfComponentsForType");
3 |
4 | describe("numberOfComponentsForType", () => {
5 | it("numberOfComponentsForType", () => {
6 | expect(numberOfComponentsForType("SCALAR")).toBe(1);
7 | expect(numberOfComponentsForType("VEC2")).toBe(2);
8 | expect(numberOfComponentsForType("VEC3")).toBe(3);
9 | expect(numberOfComponentsForType("VEC4")).toBe(4);
10 | expect(numberOfComponentsForType("MAT2")).toBe(4);
11 | expect(numberOfComponentsForType("MAT3")).toBe(9);
12 | expect(numberOfComponentsForType("MAT4")).toBe(16);
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/specs/lib/parseGlbSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const { RuntimeError } = require("cesium");
4 |
5 | const parseGlb = require("../../lib/parseGlb");
6 | const removePipelineExtras = require("../../lib/removePipelineExtras");
7 |
8 | describe("parseGlb", () => {
9 | it("throws an error with invalid magic", () => {
10 | const glb = Buffer.alloc(20);
11 | glb.write("NOPE", 0);
12 |
13 | let thrownError;
14 | try {
15 | parseGlb(glb);
16 | } catch (e) {
17 | thrownError = e;
18 | }
19 | expect(thrownError).toEqual(
20 | new RuntimeError("File is not valid binary glTF"),
21 | );
22 | });
23 |
24 | it("throws an error if version is not 1 or 2", () => {
25 | const glb = Buffer.alloc(20);
26 | glb.write("glTF", 0);
27 | glb.writeUInt32LE(3, 4);
28 |
29 | let thrownError;
30 | try {
31 | parseGlb(glb);
32 | } catch (e) {
33 | thrownError = e;
34 | }
35 | expect(thrownError).toEqual(
36 | new RuntimeError("Binary glTF version is not 1 or 2"),
37 | );
38 | });
39 |
40 | describe("1.0", () => {
41 | it("throws an error if content format is not JSON", () => {
42 | const glb = Buffer.alloc(20);
43 | glb.write("glTF", 0);
44 | glb.writeUInt32LE(1, 4);
45 | glb.writeUInt32LE(20, 8);
46 | glb.writeUInt32LE(0, 12);
47 | glb.writeUInt32LE(1, 16);
48 |
49 | let thrownError;
50 | try {
51 | parseGlb(glb);
52 | } catch (e) {
53 | thrownError = e;
54 | }
55 | expect(thrownError).toEqual(
56 | new RuntimeError("Binary glTF scene format is not JSON"),
57 | );
58 | });
59 |
60 | it("loads binary glTF", () => {
61 | const binaryData = Buffer.from([0, 1, 2, 3, 4, 5]);
62 | const gltf = {
63 | bufferViews: {
64 | imageBufferView: {
65 | byteLength: 0,
66 | },
67 | shaderBufferView: {
68 | byteLength: 0,
69 | },
70 | },
71 | buffers: {
72 | binary_glTF: {
73 | byteLength: binaryData.length,
74 | uri: "data:,",
75 | },
76 | },
77 | images: {
78 | image: {
79 | extensions: {
80 | KHR_binary_glTF: {
81 | bufferView: "imageBufferView",
82 | mimeType: "image/jpg",
83 | },
84 | },
85 | },
86 | },
87 | shaders: {
88 | shader: {
89 | extensions: {
90 | KHR_binary_glTF: {
91 | bufferView: "shaderBufferView",
92 | },
93 | },
94 | },
95 | },
96 | extensionsUsed: ["KHR_binary_glTF"],
97 | };
98 | let gltfString = JSON.stringify(gltf);
99 | while (gltfString.length % 4 !== 0) {
100 | gltfString += " ";
101 | }
102 | const glb = Buffer.alloc(20 + gltfString.length + binaryData.length);
103 | glb.write("glTF", 0);
104 | glb.writeUInt32LE(1, 4);
105 | glb.writeUInt32LE(20 + gltfString.length + binaryData.length, 8);
106 | glb.writeUInt32LE(gltfString.length, 12);
107 | glb.writeUInt32LE(0, 16);
108 | glb.write(gltfString, 20);
109 | binaryData.copy(glb, 20 + gltfString.length);
110 |
111 | const parsedGltf = parseGlb(glb);
112 | expect(parsedGltf.extensionsUsed).toBeUndefined();
113 | const buffer = parsedGltf.buffers.binary_glTF;
114 | for (let i = 0; i < binaryData.length; i++) {
115 | expect(buffer.extras._pipeline.source[i]).toEqual(binaryData[i]);
116 | expect(buffer.uri).toBeUndefined();
117 | }
118 |
119 | const image = parsedGltf.images.image;
120 | expect(image.extensions.KHR_binary_glTF).toBeDefined();
121 | expect(image.extensions.KHR_binary_glTF.bufferView).toBe(
122 | "imageBufferView",
123 | );
124 | expect(image.extensions.KHR_binary_glTF.mimeType).toBe("image/jpg");
125 | const shader = parsedGltf.shaders.shader;
126 | expect(shader.extensions.KHR_binary_glTF).toBeDefined();
127 | expect(shader.extensions.KHR_binary_glTF.bufferView).toBe(
128 | "shaderBufferView",
129 | );
130 | });
131 | });
132 |
133 | describe("2.0", () => {
134 | it("loads binary glTF", () => {
135 | let i;
136 | const binaryData = Buffer.from([0, 1, 2, 3, 4, 5]);
137 | const gltf = {
138 | asset: {
139 | version: "2.0",
140 | },
141 | buffers: [
142 | {
143 | byteLength: binaryData.length,
144 | },
145 | ],
146 | images: [
147 | {
148 | bufferView: 0,
149 | mimeType: "image/jpg",
150 | },
151 | ],
152 | };
153 | let gltfString = JSON.stringify(gltf);
154 | while (gltfString.length % 4 !== 0) {
155 | gltfString += " ";
156 | }
157 | const glb = Buffer.alloc(28 + gltfString.length + binaryData.length);
158 | glb.write("glTF", 0);
159 | glb.writeUInt32LE(2, 4);
160 | glb.writeUInt32LE(12 + 8 + gltfString.length + 8 + binaryData.length, 8);
161 | glb.writeUInt32LE(gltfString.length, 12);
162 | glb.writeUInt32LE(0x4e4f534a, 16);
163 | glb.write(gltfString, 20);
164 | glb.writeUInt32LE(binaryData.length, 20 + gltfString.length);
165 | glb.writeUInt32LE(0x004e4942, 24 + gltfString.length);
166 | binaryData.copy(glb, 28 + gltfString.length);
167 |
168 | const parsedGltf = parseGlb(glb);
169 | const buffer = parsedGltf.buffers[0];
170 | for (i = 0; i < binaryData.length; i++) {
171 | expect(buffer.extras._pipeline.source[i]).toEqual(binaryData[i]);
172 | }
173 | removePipelineExtras(parsedGltf);
174 | expect(parsedGltf).toEqual(gltf);
175 | });
176 | });
177 | });
178 |
--------------------------------------------------------------------------------
/specs/lib/processGlbSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const fsExtra = require("fs-extra");
3 | const processGlb = require("../../lib/processGlb");
4 |
5 | const glbPath = "specs/data/2.0/box-textured-binary/box-textured-binary.glb";
6 |
7 | describe("processGlb", () => {
8 | it("processGlb", async () => {
9 | spyOn(console, "log");
10 | const glb = fsExtra.readFileSync(glbPath);
11 | const options = {
12 | separate: true,
13 | stats: true,
14 | };
15 | const results = await processGlb(glb, options);
16 | expect(Buffer.isBuffer(results.glb)).toBe(true);
17 | expect(results.separateResources).toBeDefined();
18 | expect(console.log).toHaveBeenCalled();
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/specs/lib/readAccessorPackedSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const readAccessorPacked = require("../../lib/readAccessorPacked");
3 | const readResources = require("../../lib/readResources");
4 |
5 | const contiguousData = [
6 | -1.0, 1.0, -1.0, 0.0, 0.0, 0.0, 3.0, 2.0, 1.0, -1.0, -2.0, -3.0,
7 | ];
8 |
9 | const nan = Number.NaN;
10 | const nonContiguousData = [
11 | -1.0,
12 | 1.0,
13 | -1.0,
14 | nan,
15 | nan,
16 | nan,
17 | 0.0,
18 | 0.0,
19 | 0.0,
20 | nan,
21 | nan,
22 | nan,
23 | 3.0,
24 | 2.0,
25 | 1.0,
26 | nan,
27 | nan,
28 | nan,
29 | -1.0,
30 | -2.0,
31 | -3.0,
32 | nan,
33 | nan,
34 | nan,
35 | ];
36 |
37 | function createGltf(elements, byteStride) {
38 | const buffer = Buffer.from(new Float32Array(elements).buffer);
39 | const byteLength = buffer.length;
40 | const dataUri = `data:application/octet-stream;base64,${buffer.toString(
41 | "base64",
42 | )}`;
43 | const gltf = {
44 | accessors: [
45 | {
46 | bufferView: 0,
47 | byteOffset: 0,
48 | componentType: 5126,
49 | count: 4,
50 | type: "VEC3",
51 | },
52 | ],
53 | bufferViews: [
54 | {
55 | buffer: 0,
56 | byteOffset: 0,
57 | byteLength: byteLength,
58 | byteStride: byteStride,
59 | },
60 | ],
61 | buffers: [
62 | {
63 | uri: dataUri,
64 | byteLength: byteLength,
65 | },
66 | ],
67 | };
68 | return readResources(gltf);
69 | }
70 |
71 | describe("readAccessorPacked", () => {
72 | it("reads contiguous accessor", async () => {
73 | const gltf = await createGltf(contiguousData, 12);
74 | expect(readAccessorPacked(gltf, gltf.accessors[0])).toEqual(contiguousData);
75 | });
76 |
77 | it("reads non-contiguous accessor", async () => {
78 | const gltf = await createGltf(nonContiguousData, 24);
79 | expect(readAccessorPacked(gltf, gltf.accessors[0])).toEqual(contiguousData);
80 | });
81 |
82 | it("reads accessor that does not have a buffer view", () => {
83 | const gltf = {
84 | accessors: [
85 | {
86 | componentType: 5126,
87 | count: 4,
88 | type: "VEC3",
89 | },
90 | ],
91 | };
92 | const expected = new Array(12).fill(0);
93 | expect(readAccessorPacked(gltf, gltf.accessors[0])).toEqual(expected);
94 | });
95 | });
96 |
--------------------------------------------------------------------------------
/specs/lib/readResourcesSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 | const fsExtra = require("fs-extra");
4 | const path = require("path");
5 | const ForEach = require("../../lib/ForEach");
6 | const parseGlb = require("../../lib/parseGlb");
7 | const readResources = require("../../lib/readResources");
8 |
9 | const RuntimeError = Cesium.RuntimeError;
10 |
11 | const boxTexturedSeparate1Path =
12 | "specs/data/1.0/box-textured-separate/box-textured-separate.gltf";
13 | const boxTexturedBinarySeparate1Path =
14 | "specs/data/1.0/box-textured-binary-separate/box-textured-binary-separate.glb";
15 | const boxTexturedBinary1Path =
16 | "specs/data/1.0/box-textured-binary/box-textured-binary.glb";
17 | const boxTexturedEmbedded1Path =
18 | "specs/data/1.0/box-textured-embedded/box-textured-embedded.gltf";
19 | const boxTexturedSeparate2Path =
20 | "specs/data/2.0/box-textured-separate/box-textured-separate.gltf";
21 | const boxTexturedBinarySeparate2Path =
22 | "specs/data/2.0/box-textured-binary-separate/box-textured-binary-separate.glb";
23 | const boxTexturedBinary2Path =
24 | "specs/data/2.0/box-textured-binary/box-textured-binary.glb";
25 | const boxTexturedEmbedded2Path =
26 | "specs/data/2.0/box-textured-embedded/box-textured-embedded.gltf";
27 |
28 | function readGltf(gltfPath, binary) {
29 | if (binary) {
30 | const glb = fsExtra.readFileSync(gltfPath);
31 | return parseGlb(glb);
32 | }
33 | return fsExtra.readJsonSync(gltfPath);
34 | }
35 |
36 | function checkPaths(object, resourceDirectory) {
37 | const pipelineExtras = object.extras._pipeline;
38 | const absolutePath = pipelineExtras.absolutePath;
39 | const relativePath = pipelineExtras.relativePath;
40 | expect(path.basename(relativePath)).toBe(relativePath);
41 | expect(absolutePath).toBe(path.join(resourceDirectory, relativePath));
42 | expect(object.name).toBe(
43 | path.basename(relativePath, path.extname(relativePath)),
44 | );
45 | }
46 |
47 | async function readsResources(gltfPath, binary, separate) {
48 | const gltf = readGltf(gltfPath, binary);
49 | const resourceDirectory = path.resolve(path.dirname(gltfPath));
50 | const options = {
51 | resourceDirectory: resourceDirectory,
52 | };
53 | await readResources(gltf, options);
54 | ForEach.shader(gltf, (shader) => {
55 | const shaderText = shader.extras._pipeline.source;
56 | expect(typeof shaderText === "string").toBe(true);
57 | expect(shaderText.length).toBeGreaterThan(0);
58 | expect(shader.uri).toBeUndefined();
59 | if (separate) {
60 | checkPaths(shader, resourceDirectory);
61 | }
62 | });
63 | ForEach.image(gltf, (image) => {
64 | const imageSource = image.extras._pipeline.source;
65 | expect(Buffer.isBuffer(imageSource)).toBe(true);
66 | expect(image.uri).toBeUndefined();
67 | if (separate) {
68 | checkPaths(image, resourceDirectory);
69 | }
70 | });
71 | ForEach.buffer(gltf, (buffer) => {
72 | const bufferSource = buffer.extras._pipeline.source;
73 | expect(Buffer.isBuffer(bufferSource)).toBe(true);
74 | expect(buffer.uri).toBeUndefined();
75 | if (separate && !binary) {
76 | checkPaths(buffer, resourceDirectory);
77 | }
78 | });
79 | }
80 |
81 | describe("readResources", () => {
82 | it("reads separate resources from 1.0 model", async () => {
83 | await readsResources(boxTexturedSeparate1Path, false, true);
84 | });
85 |
86 | it("reads separate resources from 1.0 glb", async () => {
87 | await readsResources(boxTexturedBinarySeparate1Path, true, true);
88 | });
89 |
90 | it("reads embedded resources from 1.0 model", async () => {
91 | await readsResources(boxTexturedEmbedded1Path, false, false);
92 | });
93 |
94 | it("reads resources from 1.0 glb", async () => {
95 | await readsResources(boxTexturedBinary1Path, true, false);
96 | });
97 |
98 | it("reads separate resources from model", async () => {
99 | await readsResources(boxTexturedSeparate2Path, false, true);
100 | });
101 |
102 | it("reads separate resources from glb", async () => {
103 | await readsResources(boxTexturedBinarySeparate2Path, true, true);
104 | });
105 |
106 | it("reads embedded resources from model", async () => {
107 | await readsResources(boxTexturedEmbedded2Path, false, false);
108 | });
109 |
110 | it("reads resources from glb", async () => {
111 | await readsResources(boxTexturedBinary2Path, true, false);
112 | });
113 |
114 | it("rejects if gltf contains separate resources but no resource directory is supplied", async () => {
115 | const gltf = readGltf(boxTexturedSeparate2Path);
116 |
117 | let thrownError;
118 | try {
119 | await readResources(gltf);
120 | } catch (e) {
121 | thrownError = e;
122 | }
123 | expect(thrownError).toEqual(
124 | new RuntimeError(
125 | "glTF model references separate files but no resourceDirectory is supplied",
126 | ),
127 | );
128 | });
129 | });
130 |
--------------------------------------------------------------------------------
/specs/lib/removeDefaultsSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const removeDefaults = require("../../lib/removeDefaults");
3 |
4 | describe("removeDefaults", () => {
5 | it("removeDefaults", () => {
6 | const gltf = {
7 | nodes: [
8 | {
9 | matrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
10 | },
11 | {
12 | matrix: [2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
13 | },
14 | ],
15 | accessors: [
16 | {
17 | bufferView: 0,
18 | normalized: false,
19 | },
20 | ],
21 | };
22 | removeDefaults(gltf);
23 | expect(gltf.nodes[0].matrix).toBeUndefined();
24 | expect(gltf.nodes[1].matrix).toBeDefined();
25 | expect(gltf.accessors[0].normalized).toBeUndefined();
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/specs/lib/removeExtensionSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 | const removeExtension = require("../../lib/removeExtension");
4 |
5 | const WebGLConstants = Cesium.WebGLConstants;
6 |
7 | describe("removeExtension", () => {
8 | it("removes extension", () => {
9 | const gltf = {
10 | extensionsRequired: ["extension1", "extension2", "extension3"],
11 | extensionsUsed: ["extension1", "extension2", "extension3"],
12 | extensions: {
13 | extension1: {
14 | value: 9,
15 | },
16 | extension2: [0, 1, 2],
17 | },
18 | materials: [
19 | {
20 | baseColorFactor: [1.0, 0.0, 0.0, 1.0],
21 | extensions: {
22 | extension1: {
23 | value: 10,
24 | },
25 | },
26 | },
27 | {
28 | baseColorFactor: [0.0, 0.0, 1.0, 1.0],
29 | extensions: {
30 | extension1: {
31 | value: 11,
32 | },
33 | },
34 | },
35 | ],
36 | cameras: [
37 | {
38 | extensions: {
39 | extension1: {
40 | value: 9,
41 | },
42 | extension2: [3, 4, 5],
43 | },
44 | },
45 | ],
46 | };
47 | const extension1 = removeExtension(gltf, "extension1");
48 | expect(gltf.extensionsRequired).toEqual(["extension2", "extension3"]);
49 | expect(gltf.extensionsUsed).toEqual(["extension2", "extension3"]);
50 | expect(gltf.extensions).toEqual({
51 | extension2: [0, 1, 2],
52 | });
53 | expect(gltf.materials[0].extensions).toBeUndefined();
54 | expect(gltf.materials[1].extensions).toBeUndefined();
55 | expect(gltf.cameras[0].extensions).toEqual({
56 | extension2: [3, 4, 5],
57 | });
58 | expect(extension1).toEqual({
59 | value: 9,
60 | });
61 |
62 | const extension2 = removeExtension(gltf, "extension2");
63 | expect(gltf.extensionsRequired).toEqual(["extension3"]);
64 | expect(gltf.extensionsUsed).toEqual(["extension3"]);
65 | expect(gltf.extensions).toBeUndefined();
66 | expect(gltf.materials[0].extensions).toBeUndefined();
67 | expect(gltf.materials[1].extensions).toBeUndefined();
68 | expect(gltf.cameras[0].extensions).toBeUndefined();
69 | expect(extension2).toEqual([0, 1, 2]);
70 |
71 | const extension3 = removeExtension(gltf, "extension3");
72 | expect(gltf.extensionsRequired).toBeUndefined();
73 | expect(gltf.extensionsUsed).toBeUndefined();
74 | expect(gltf.extensions).toBeUndefined();
75 | expect(gltf.materials[0].extensions).toBeUndefined();
76 | expect(gltf.materials[1].extensions).toBeUndefined();
77 | expect(gltf.cameras[0].extensions).toBeUndefined();
78 | expect(extension3).toBeUndefined();
79 |
80 | const emptyGltf = {};
81 | removeExtension(gltf, "extension1");
82 | expect(emptyGltf).toEqual({});
83 | });
84 |
85 | it("removes CESIUM_RTC extension", () => {
86 | const gltf = {
87 | extensionsRequired: ["CESIUM_RTC", "KHR_techniques_webgl"],
88 | extensionsUsed: ["CESIUM_RTC", "KHR_techniques_webgl"],
89 | extensions: {
90 | CESIUM_RTC: {
91 | center: [1.0, 2.0, 3.0],
92 | },
93 | KHR_techniques_webgl: {
94 | techniques: [
95 | {
96 | uniforms: {
97 | u_modelViewMatrix: {
98 | type: WebGLConstants.FLOAT_MAT4,
99 | semantic: "CESIUM_RTC_MODELVIEW",
100 | },
101 | },
102 | },
103 | ],
104 | },
105 | },
106 | };
107 | const extension = removeExtension(gltf, "CESIUM_RTC");
108 | expect(extension).toEqual({
109 | center: [1.0, 2.0, 3.0],
110 | });
111 | expect(gltf.extensionsRequired).toEqual(["KHR_techniques_webgl"]);
112 | expect(gltf.extensionsUsed).toEqual(["KHR_techniques_webgl"]);
113 | expect(
114 | gltf.extensions.KHR_techniques_webgl.techniques[0].uniforms
115 | .u_modelViewMatrix.semantic,
116 | ).toBe("MODELVIEW");
117 | });
118 | });
119 |
--------------------------------------------------------------------------------
/specs/lib/removeExtensionsRequiredSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const removeExtensionsRequired = require("../../lib/removeExtensionsRequired");
3 |
4 | describe("removeExtensionsRequired", () => {
5 | it("removes extension from extensionsRequired", () => {
6 | const gltf = {
7 | extensionsRequired: ["extension1", "extension2"],
8 | extensionsUsed: ["extension1", "extension2"],
9 | };
10 | removeExtensionsRequired(gltf, "extension1");
11 | expect(gltf.extensionsRequired).toEqual(["extension2"]);
12 | removeExtensionsRequired(gltf, "extension2");
13 | expect(gltf.extensionsRequired).toBeUndefined();
14 | expect(gltf.extensionsUsed).toEqual(["extension1", "extension2"]);
15 |
16 | const emptyGltf = {};
17 | removeExtensionsRequired(gltf, "extension1");
18 | expect(emptyGltf).toEqual({});
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/specs/lib/removeExtensionsUsedSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const removeExtensionsUsed = require("../../lib/removeExtensionsUsed");
3 |
4 | describe("removeExtensionsUsed", () => {
5 | it("removes extension from extensionsUsed", () => {
6 | const gltf = {
7 | extensionsRequired: ["extension1", "extension2"],
8 | extensionsUsed: ["extension1", "extension2"],
9 | };
10 | removeExtensionsUsed(gltf, "extension1");
11 | expect(gltf.extensionsRequired).toEqual(["extension2"]);
12 | expect(gltf.extensionsUsed).toEqual(["extension2"]);
13 |
14 | removeExtensionsUsed(gltf, "extension2");
15 | expect(gltf.extensionsRequired).toBeUndefined();
16 | expect(gltf.extensionsUsed).toBeUndefined();
17 |
18 | const emptyGltf = {};
19 | removeExtensionsUsed(gltf, "extension1");
20 | expect(emptyGltf).toEqual({});
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/specs/lib/removePipelineExtrasSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 | const addPipelineExtras = require("../../lib/addPipelineExtras");
4 | const removePipelineExtras = require("../../lib/removePipelineExtras");
5 |
6 | const WebGLConstants = Cesium.WebGLConstants;
7 |
8 | describe("removePipelineExtras", () => {
9 | it("removes pipeline extras", () => {
10 | const gltf = {
11 | buffers: [
12 | {
13 | byteLength: 100,
14 | },
15 | ],
16 | extensions: {
17 | KHR_techniques_webgl: {
18 | shaders: [
19 | {
20 | type: WebGLConstants.VERTEX_SHADER,
21 | uri: "data:,",
22 | },
23 | ],
24 | },
25 | },
26 | };
27 | const gltfWithExtrasRemoved = removePipelineExtras(addPipelineExtras(gltf));
28 | expect(gltfWithExtrasRemoved.buffers[0].extras).toBeUndefined();
29 | expect(
30 | gltfWithExtrasRemoved.extensions.KHR_techniques_webgl.shaders[0].extras,
31 | ).toBeUndefined();
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/specs/lib/updateAccessorComponentTypeSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const Cesium = require("cesium");
3 | const readResources = require("../../lib/readResources");
4 | const updateAccessorComponentTypes = require("../../lib/updateAccessorComponentTypes");
5 |
6 | const WebGLConstants = Cesium.WebGLConstants;
7 |
8 | let buffer;
9 |
10 | describe("updateAccessorComponentTypes", () => {
11 | beforeAll(() => {
12 | // Note: TypedArray constructors initialize all elements to zero
13 | const byteBuffer = Buffer.from(new Int8Array(96).buffer);
14 | const floatBuffer = Buffer.from(new Float32Array(96).buffer);
15 | const unsignedShortBuffer = Buffer.from(new Uint16Array(96).buffer);
16 | const source = Buffer.concat([
17 | byteBuffer,
18 | floatBuffer,
19 | unsignedShortBuffer,
20 | ]);
21 | const byteLength = source.length;
22 | const dataUri = `data:application/octet-stream;base64,${source.toString(
23 | "base64",
24 | )}`;
25 | buffer = {
26 | uri: dataUri,
27 | byteLength: byteLength,
28 | };
29 | });
30 |
31 | it("converts joints accessor types", async () => {
32 | const gltf = {
33 | meshes: [
34 | {
35 | primitives: [
36 | {
37 | attributes: {
38 | JOINTS_0: 0,
39 | },
40 | },
41 | {
42 | attributes: {
43 | JOINTS_0: 1,
44 | },
45 | },
46 | {
47 | attributes: {
48 | JOINTS_0: 2,
49 | },
50 | },
51 | ],
52 | },
53 | ],
54 | accessors: [
55 | {
56 | bufferView: 0,
57 | componentType: WebGLConstants.BYTE,
58 | count: 24,
59 | type: "VEC4",
60 | },
61 | {
62 | bufferView: 1,
63 | componentType: WebGLConstants.FLOAT,
64 | byteOffset: 12,
65 | count: 24,
66 | type: "VEC4",
67 | },
68 | {
69 | bufferView: 2,
70 | componentType: WebGLConstants.UNSIGNED_SHORT,
71 | count: 24,
72 | type: "VEC4",
73 | },
74 | ],
75 | bufferViews: [
76 | {
77 | buffer: 0,
78 | byteOffset: 0,
79 | byteLength: 96,
80 | },
81 | {
82 | buffer: 0,
83 | byteOffset: 96,
84 | byteLength: 384,
85 | },
86 | {
87 | buffer: 0,
88 | byteOffset: 480,
89 | byteLength: 192,
90 | },
91 | ],
92 | buffers: [buffer],
93 | };
94 |
95 | await readResources(gltf);
96 | updateAccessorComponentTypes(gltf);
97 |
98 | expect(gltf.accessors.length).toBe(3);
99 | expect(gltf.bufferViews.length).toBe(5);
100 | expect(gltf.buffers.length).toBe(3);
101 |
102 | expect(gltf.accessors[0].componentType).toBe(WebGLConstants.UNSIGNED_BYTE);
103 | expect(gltf.accessors[0].bufferView).toBe(3);
104 | expect(gltf.bufferViews[3].buffer).toBe(1);
105 | expect(gltf.bufferViews[3].byteLength).toBe(96);
106 |
107 | expect(gltf.accessors[1].componentType).toBe(WebGLConstants.UNSIGNED_SHORT);
108 | expect(gltf.accessors[1].bufferView).toBe(4);
109 | expect(gltf.accessors[1].byteOffset).toBe(0);
110 | expect(gltf.bufferViews[4].buffer).toBe(2);
111 | expect(gltf.bufferViews[4].byteLength).toBe(192);
112 |
113 | expect(gltf.accessors[2].componentType).toBe(WebGLConstants.UNSIGNED_SHORT);
114 | expect(gltf.accessors[2].bufferView).toBe(2);
115 | });
116 |
117 | it("converts weights accessor types", async () => {
118 | const gltf = {
119 | meshes: [
120 | {
121 | primitives: [
122 | {
123 | attributes: {
124 | WEIGHTS_0: 0,
125 | },
126 | },
127 | {
128 | attributes: {
129 | WEIGHTS_0: 1,
130 | },
131 | },
132 | {
133 | attributes: {
134 | WEIGHTS_0: 2,
135 | },
136 | },
137 | ],
138 | },
139 | ],
140 | accessors: [
141 | {
142 | bufferView: 0,
143 | componentType: WebGLConstants.FLOAT,
144 | count: 24,
145 | type: "VEC4",
146 | },
147 | {
148 | bufferView: 1,
149 | componentType: WebGLConstants.BYTE,
150 | byteOffset: 12,
151 | count: 24,
152 | type: "VEC4",
153 | },
154 | {
155 | bufferView: 2,
156 | componentType: WebGLConstants.SHORT,
157 | count: 24,
158 | type: "VEC4",
159 | },
160 | ],
161 | bufferViews: [
162 | {
163 | buffer: 0,
164 | byteLength: 12,
165 | },
166 | {
167 | buffer: 0,
168 | byteOffset: 12,
169 | byteLength: 12,
170 | },
171 | {
172 | buffer: 0,
173 | byteLength: 12,
174 | },
175 | ],
176 | buffers: [buffer],
177 | };
178 |
179 | await readResources(gltf);
180 | updateAccessorComponentTypes(gltf);
181 |
182 | expect(gltf.accessors.length).toBe(3);
183 | expect(gltf.bufferViews.length).toBe(5);
184 | expect(gltf.buffers.length).toBe(3);
185 |
186 | expect(gltf.accessors[0].componentType).toBe(WebGLConstants.FLOAT);
187 | expect(gltf.accessors[0].bufferView).toBe(0);
188 |
189 | expect(gltf.accessors[1].componentType).toBe(WebGLConstants.UNSIGNED_BYTE);
190 | expect(gltf.accessors[1].bufferView).toBe(3);
191 | expect(gltf.accessors[1].byteOffset).toBe(0);
192 | expect(gltf.bufferViews[3].buffer).toBe(1);
193 | expect(gltf.bufferViews[3].byteLength).toBe(96);
194 |
195 | expect(gltf.accessors[2].componentType).toBe(WebGLConstants.UNSIGNED_SHORT);
196 | expect(gltf.accessors[2].bufferView).toBe(4);
197 | expect(gltf.bufferViews[4].buffer).toBe(2);
198 | expect(gltf.bufferViews[4].byteLength).toBe(192);
199 | });
200 | });
201 |
--------------------------------------------------------------------------------
/specs/lib/usesExtensionSpec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const usesExtension = require("../../lib/usesExtension");
3 |
4 | describe("usesExtension", () => {
5 | it("uses extension", () => {
6 | const gltf = {
7 | extensionsUsed: ["extension1", "extension2"],
8 | };
9 | expect(usesExtension(gltf, "extension1")).toBe(true);
10 | expect(usesExtension(gltf, "extension2")).toBe(true);
11 | expect(usesExtension(gltf, "extension3")).toBe(false);
12 |
13 | const emptyGltf = {};
14 | expect(usesExtension(emptyGltf, "extension1")).toBe(false);
15 | });
16 | });
17 |
--------------------------------------------------------------------------------