├── .eslintignore
├── .eslintrc.json
├── .gitattributes
├── .gitignore
├── .npmignore
├── .npmrc
├── CHANGES.md
├── LICENSE.md
├── README.md
├── ThirdParty.json
├── bin
└── 3d-tiles-samples-generator.ts
├── conf.json
├── data
├── animated_box.glb
├── box.glb
├── building.glb
├── dragon_high.glb
├── dragon_low.glb
├── dragon_medium.glb
├── house
│ ├── door0.gltf
│ ├── door1.gltf
│ ├── door2.gltf
│ ├── door3.gltf
│ ├── doorknob0.gltf
│ ├── doorknob1.gltf
│ ├── doorknob2.gltf
│ ├── doorknob3.gltf
│ ├── roof.gltf
│ └── wall.gltf
├── red_box.glb
├── textured_box.glb
├── textured_box_separate
│ ├── bricks.jpg
│ └── textured_box.glb
├── tree.glb
├── tree_billboard.glb
└── wood_red.jpg
├── gulpfile.js
├── index.js
├── jest.config.js
├── lib
├── Extensions.ts
├── Material.ts
├── Mesh.ts
├── addMeshToGltf.ts
├── arguments.ts
├── atLeastN.ts
├── attribute.ts
├── bufferUtil.ts
├── calculateBufferPadding.ts
├── calculateFilenameExt.ts
├── calculateMinMax.ts
├── colorTypes.ts
├── compositeSamplesNext.ts
├── constants.ts
├── createB3dm.js
├── createBatchTableHierarchy.ts
├── createBuilding.ts
├── createBuildingsTile.ts
├── createByteBuffer.ts
├── createCmpt.js
├── createConstantAttribute.ts
├── createEXTMeshInstancing.ts
├── createFeatureHierarchySubExtension.ts
├── createFeatureMetadataExtension.ts
├── createGlb.js
├── createGltf.js
├── createGltfFromPnts.js
├── createI3dm.js
├── createInstancesTile.ts
├── createPnts.js
├── createPointCloudTile.ts
├── createTilesetJsonSingle.ts
├── featureHierarchyClass.ts
├── featureMetadata.ts
├── featureMetadataExtension.ts
├── featureMetadataType.ts
├── featureMetatableUtilsNext.ts
├── getBufferPadded.js
├── getJsonBufferPadded.js
├── getMinMax.js
├── getProperties.js
├── gltfFromUri.ts
├── gltfType.ts
├── gltfUtil.ts
├── initializeGltf.ts
├── instanceSamplesNext.ts
├── instanceUtilsNext.ts
├── ioUtil.ts
├── meshView.ts
├── modifyImageUri.ts
├── sampleOutput.ts
├── samplesNext.ts
├── saveBinary.js
├── saveJson.js
├── tilesNextExtension.ts
├── tilesetJson.ts
├── tilesetSamplesNext.ts
├── tilesetUtilsNext.ts
├── typeConversion.js
├── typeSize.ts
├── utility.ts
└── xor.ts
├── package.json
├── test
├── .eslintrc.json
├── bin
│ ├── createConstantAttribute.test.ts
│ ├── createFeatureMetadataExtensionSpec.test.ts
│ ├── createFeatureMetadataExtensionSpec.ts
│ ├── createGltfFromPntsSpec.test.ts
│ └── getQuaternionNormalsSpec.test.ts
└── data
│ └── triangle.gltf
└── tsconfig.json
/.eslintignore:
--------------------------------------------------------------------------------
1 | dist/
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "cesium/node"
3 | }
4 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 | package.json text eol=lf
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | /coverage
2 | /output
3 | /.idea
4 | .eslintrc.json
5 | .gitattributes
6 | .npmignore
7 | gulpfile.js
8 | *.tgz
9 | *.zip
10 | .eslintcache
11 | .nyc_output
12 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 |
--------------------------------------------------------------------------------
/CHANGES.md:
--------------------------------------------------------------------------------
1 | Change Log
2 | ==========
3 |
4 | ### 0.1.0 - 2017-02-07
5 |
6 | * Initial release.
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 3D Tiles Samples Generator
2 |
3 | > **A note about the repository structure**
4 | >
5 | > This repository was originally part of the `3d-tiles-validator` repository, which contained multiple projects. Now, these project are maintained in separate repositories:
6 | >
7 | > - The `3d-tiles-validator` can be found in [the `3d-tiles-validator` repository](https://github.com/CesiumGS/3d-tiles-validator)
8 | > - The `3d-tiles-tools` can be found in [the `3d-tiles-tools` repository](https://github.com/CesiumGS/3d-tiles-tools)
9 | >
10 |
11 |
12 | The tilesets generated here are included in [3d-tiles-samples](https://github.com/CesiumGS/3d-tiles-samples) and [Cesium](https://github.com/CesiumGS/cesium).
13 |
14 | ## Instructions
15 |
16 | Clone this repo and install [Node.js](http://nodejs.org/). From this directory, run:
17 |
18 | ```
19 | npm install
20 |
21 | npm run build
22 |
23 | cd dist/
24 |
25 | node bin/3d-tiles-samples-generator.js
26 | ```
27 |
28 | This commands generates a set of tilesets and saves them in a folder called `output`. The `Batched`, `Composite`, `Instanced`, `PointCloud`, and `Tilesets` folders may be copied directly to CesiumJS's `Specs/Data/Cesium3DTiles/` folder for testing with CesiumJS. The tilesets in the `Samples` folder may be copied to the `tilesets` folder in `3d-tiles-samples`.
29 |
30 | Run the tests:
31 | ```
32 | npm run test
33 | ```
34 | To run ESLint on the entire codebase, run:
35 | ```
36 | npm run eslint
37 | ```
38 | To run ESLint automatically when a file is saved, run the following and leave it open in a console window:
39 | ```
40 | npm run eslint-watch
41 | ```
42 |
43 | ## Auto Recompilation
44 | You can use
45 | ```
46 | npm run watch
47 | ```
48 |
49 | to automatically recompile your changes while editing.
50 |
51 | ## License
52 |
53 | Tilesets generated by this tool are licensed under [CC0](https://creativecommons.org/share-your-work/public-domain/cc0/) with the following exceptions:
54 |
55 | * `TilesetWithRequestVolume` is licensed under a [Creative Commons Attribution 3.0 Unported License](https://creativecommons.org/licenses/by/3.0/). The building model was created by Richard Edwards: http://www.blendswap.com/blends/view/45211.
56 |
57 | ## Contributions
58 |
59 | Pull requests are appreciated! Please use the same [Contributor License Agreement (CLA)](https://github.com/CesiumGS/cesium/blob/main/CONTRIBUTING.md) and [Coding Guide](https://github.com/CesiumGS/cesium/blob/main/Documentation/Contributors/CodingGuide/README.md) used for [CesiumJS](https://cesium.com/cesiumjs/).
60 |
61 | ---
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/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.93.0",
16 | "url": "https://www.npmjs.com/package/cesium"
17 | },
18 | {
19 | "name": "draco3d",
20 | "license": [
21 | "Apache-2.0"
22 | ],
23 | "version": "1.3.6",
24 | "url": "https://www.npmjs.com/package/draco3d"
25 | },
26 | {
27 | "name": "fs-extra",
28 | "license": [
29 | "MIT"
30 | ],
31 | "version": "9.1.0",
32 | "url": "https://www.npmjs.com/package/fs-extra"
33 | },
34 | {
35 | "name": "gltf-pipeline",
36 | "license": [
37 | "Apache-2.0"
38 | ],
39 | "version": "3.0.4",
40 | "url": "https://www.npmjs.com/package/gltf-pipeline"
41 | },
42 | {
43 | "name": "mime",
44 | "license": [
45 | "MIT"
46 | ],
47 | "version": "2.6.0",
48 | "url": "https://www.npmjs.com/package/mime"
49 | },
50 | {
51 | "name": "simplex-noise",
52 | "license": [
53 | "MIT"
54 | ],
55 | "version": "2.4.0",
56 | "url": "https://www.npmjs.com/package/simplex-noise"
57 | }
58 | ]
--------------------------------------------------------------------------------
/conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "tags": {
3 | "allowUnknownTags": false
4 | },
5 | "source": {
6 | "include": ["lib"],
7 | "includePattern": ".+\\.js(doc)?$",
8 | "excludePattern": "(^|\\/|\\\\)_"
9 | },
10 | "templates": {
11 | "cleverLinks": true,
12 | "default": {
13 | "outputSourceFiles": false
14 | },
15 | "sourceUrl": "https://github.com/CesiumGS/3d-tiles-validator/blob/{version}/lib/{filename}"
16 | },
17 | "opts": {
18 | "destination": "doc",
19 | "recurse": true
20 | }
21 | }
--------------------------------------------------------------------------------
/data/animated_box.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/3d-tiles-samples-generator/2f79f88e572caf80759a1262c67d039c7c0ac530/data/animated_box.glb
--------------------------------------------------------------------------------
/data/box.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/3d-tiles-samples-generator/2f79f88e572caf80759a1262c67d039c7c0ac530/data/box.glb
--------------------------------------------------------------------------------
/data/building.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/3d-tiles-samples-generator/2f79f88e572caf80759a1262c67d039c7c0ac530/data/building.glb
--------------------------------------------------------------------------------
/data/dragon_high.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/3d-tiles-samples-generator/2f79f88e572caf80759a1262c67d039c7c0ac530/data/dragon_high.glb
--------------------------------------------------------------------------------
/data/dragon_low.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/3d-tiles-samples-generator/2f79f88e572caf80759a1262c67d039c7c0ac530/data/dragon_low.glb
--------------------------------------------------------------------------------
/data/dragon_medium.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/3d-tiles-samples-generator/2f79f88e572caf80759a1262c67d039c7c0ac530/data/dragon_medium.glb
--------------------------------------------------------------------------------
/data/house/door0.gltf:
--------------------------------------------------------------------------------
1 | {
2 | "accessors": [
3 | {
4 | "bufferView": 1,
5 | "byteOffset": 0,
6 | "componentType": 5123,
7 | "count": 72,
8 | "type": "SCALAR",
9 | "min": [
10 | 0
11 | ],
12 | "max": [
13 | 45
14 | ]
15 | },
16 | {
17 | "bufferView": 0,
18 | "byteOffset": 0,
19 | "componentType": 5126,
20 | "count": 46,
21 | "min": [
22 | -0.3696739971637726,
23 | 0,
24 | -1.0869100093841553
25 | ],
26 | "max": [
27 | 0.3696739971637726,
28 | 1.3130379915237427,
29 | -0.9400410056114197
30 | ],
31 | "type": "VEC3"
32 | },
33 | {
34 | "bufferView": 0,
35 | "byteOffset": 552,
36 | "componentType": 5126,
37 | "count": 46,
38 | "type": "VEC3",
39 | "min": [
40 | -1,
41 | -1,
42 | -1
43 | ],
44 | "max": [
45 | 1,
46 | 1,
47 | 1
48 | ]
49 | }
50 | ],
51 | "asset": {
52 | "generator": "OBJ2GLTF",
53 | "version": "2.0"
54 | },
55 | "buffers": [
56 | {
57 | "byteLength": 1248,
58 | "uri": "data:application/octet-stream;base64,6UW9vqERqD+HpnC/6UW9vqERqD/eH4u/6UW9vgAAAADeH4u/6UW9vgAAAACHpnC/6UW9vqERqD/eH4u/6UW9PqERqD/eH4u/7fVePhXjND/eH4u/7fVePpmCkz/eH4u/6UW9vgAAAADeH4u/6UW9PgAAAADeH4u/6UW9PqERqD/eH4u/6UW9PqERqD+HpnC/6UW9PgAAAACHpnC/6UW9PgAAAADeH4u/6UW9PqERqD+HpnC/6UW9vqERqD+HpnC/6UW9vgAAAACHpnC/6UW9PgAAAACHpnC/6UW9vgAAAACHpnC/6UW9vgAAAADeH4u/6UW9PgAAAADeH4u/6UW9PgAAAACHpnC/6UW9PqERqD+HpnC/6UW9PqERqD/eH4u/6UW9vqERqD/eH4u/6UW9vqERqD+HpnC/hpJpvpmCkz987YG/hpJpvhXjND987YG/hpJpvhXjND/eH4u/hpJpvpmCkz/eH4u/7fVePpmCkz987YG/7fVePhXjND987YG/hpJpvhXjND987YG/hpJpvpmCkz987YG/hpJpvhXjND987YG/7fVePhXjND987YG/7fVePhXjND/eH4u/hpJpvhXjND/eH4u/7fVePpmCkz987YG/hpJpvpmCkz987YG/hpJpvpmCkz/eH4u/7fVePpmCkz/eH4u/7fVePhXjND/eH4u/7fVePhXjND987YG/7fVePpmCkz987YG/7fVePpmCkz/eH4u/AACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAgAAAAAAAAIC/AAAAgAAAAAAAAIC/AAAAgAAAAAAAAIC/AAAAgAAAAAAAAIC/AAAAgAAAAAAAAIC/AAAAgAAAAAAAAIC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAgAAAAAAAAIC/AAAAgAAAAAAAAIC/AAAAgAAAAAAAAIC/AAAAgAAAAAAAAIC/AAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAABAAIAAAACAAMABAAFAAYABAAGAAcABQAIAAQABQAJAAgACgALAAwACgAMAA0ADgAPABAADgAQABEAEgATABQAEgAUABUAFgAXABgAFgAYABkAGgAbABwAGgAcAB0AHgAfACAAHgAgACEAIgAjACQAIgAkACUAJgAnACgAJgAoACkAKgArACwAKgAsAC0A"
59 | }
60 | ],
61 | "bufferViews": [
62 | {
63 | "buffer": 0,
64 | "byteLength": 1104,
65 | "byteOffset": 0,
66 | "target": 34962,
67 | "byteStride": 12
68 | },
69 | {
70 | "buffer": 0,
71 | "byteLength": 144,
72 | "byteOffset": 1104,
73 | "target": 34963
74 | }
75 | ],
76 | "materials": [
77 | {
78 | "pbrMetallicRoughness": {
79 | "baseColorFactor": [
80 | 0.45435,
81 | 0.245963,
82 | 0.171126,
83 | 1
84 | ],
85 | "metallicFactor": 0,
86 | "roughnessFactor": 1
87 | },
88 | "emissiveFactor": [
89 | 0,
90 | 0,
91 | 0
92 | ],
93 | "alphaMode": "OPAQUE",
94 | "doubleSided": false
95 | }
96 | ],
97 | "meshes": [
98 | {
99 | "primitives": [
100 | {
101 | "attributes": {
102 | "POSITION": 1,
103 | "NORMAL": 2
104 | },
105 | "indices": 0,
106 | "material": 0,
107 | "mode": 4
108 | }
109 | ]
110 | }
111 | ],
112 | "nodes": [
113 | {
114 | "mesh": 0
115 | }
116 | ],
117 | "scene": 0,
118 | "scenes": [
119 | {
120 | "nodes": [
121 | 0
122 | ]
123 | }
124 | ]
125 | }
126 |
--------------------------------------------------------------------------------
/data/house/door1.gltf:
--------------------------------------------------------------------------------
1 | {
2 | "accessors": [
3 | {
4 | "bufferView": 1,
5 | "byteOffset": 0,
6 | "componentType": 5123,
7 | "count": 72,
8 | "type": "SCALAR",
9 | "min": [
10 | 0
11 | ],
12 | "max": [
13 | 45
14 | ]
15 | },
16 | {
17 | "bufferView": 0,
18 | "byteOffset": 0,
19 | "componentType": 5126,
20 | "count": 46,
21 | "min": [
22 | 0.9400410056114197,
23 | 0,
24 | -0.3696739971637726
25 | ],
26 | "max": [
27 | 1.0869100093841553,
28 | 1.3130379915237427,
29 | 0.3696739971637726
30 | ],
31 | "type": "VEC3"
32 | },
33 | {
34 | "bufferView": 0,
35 | "byteOffset": 552,
36 | "componentType": 5126,
37 | "count": 46,
38 | "type": "VEC3",
39 | "min": [
40 | -1,
41 | -1,
42 | -1
43 | ],
44 | "max": [
45 | 1,
46 | 1,
47 | 1
48 | ]
49 | }
50 | ],
51 | "asset": {
52 | "generator": "OBJ2GLTF",
53 | "version": "2.0"
54 | },
55 | "buffers": [
56 | {
57 | "byteLength": 1248,
58 | "uri": "data:application/octet-stream;base64,h6ZwP6ERqD/pRb2+3h+LP6ERqD/pRb2+3h+LPwAAAADpRb2+h6ZwPwAAAADpRb2+3h+LP6ERqD/pRb2+3h+LP6ERqD/pRb0+3h+LPxXjND/t9V4+3h+LP5mCkz/t9V4+3h+LPwAAAADpRb2+3h+LPwAAAADpRb0+3h+LP6ERqD/pRb0+h6ZwP6ERqD/pRb0+h6ZwPwAAAADpRb0+3h+LPwAAAADpRb0+h6ZwP6ERqD/pRb0+h6ZwP6ERqD/pRb2+h6ZwPwAAAADpRb2+h6ZwPwAAAADpRb0+h6ZwPwAAAADpRb2+3h+LPwAAAADpRb2+3h+LPwAAAADpRb0+h6ZwPwAAAADpRb0+h6ZwP6ERqD/pRb0+3h+LP6ERqD/pRb0+3h+LP6ERqD/pRb2+h6ZwP6ERqD/pRb2+fO2BP5mCkz+Gkmm+fO2BPxXjND+Gkmm+3h+LPxXjND+Gkmm+3h+LP5mCkz+Gkmm+fO2BP5mCkz/t9V4+fO2BPxXjND/t9V4+fO2BPxXjND+Gkmm+fO2BP5mCkz+Gkmm+fO2BPxXjND+Gkmm+fO2BPxXjND/t9V4+3h+LPxXjND/t9V4+3h+LPxXjND+Gkmm+fO2BP5mCkz/t9V4+fO2BP5mCkz+Gkmm+3h+LP5mCkz+Gkmm+3h+LP5mCkz/t9V4+3h+LPxXjND/t9V4+fO2BPxXjND/t9V4+fO2BP5mCkz/t9V4+3h+LP5mCkz/t9V4+AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAgAAAAAAAAIA/AAAAgAAAAAAAAIA/AAAAgAAAAAAAAIA/AAAAgAAAAAAAAIA/AACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAgAAAAAAAAIA/AAAAgAAAAAAAAIA/AAAAgAAAAAAAAIA/AAAAgAAAAAAAAIA/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAABAAIAAAACAAMABAAFAAYABAAGAAcABQAIAAQABQAJAAgACgALAAwACgAMAA0ADgAPABAADgAQABEAEgATABQAEgAUABUAFgAXABgAFgAYABkAGgAbABwAGgAcAB0AHgAfACAAHgAgACEAIgAjACQAIgAkACUAJgAnACgAJgAoACkAKgArACwAKgAsAC0A"
59 | }
60 | ],
61 | "bufferViews": [
62 | {
63 | "buffer": 0,
64 | "byteLength": 1104,
65 | "byteOffset": 0,
66 | "target": 34962,
67 | "byteStride": 12
68 | },
69 | {
70 | "buffer": 0,
71 | "byteLength": 144,
72 | "byteOffset": 1104,
73 | "target": 34963
74 | }
75 | ],
76 | "materials": [
77 | {
78 | "pbrMetallicRoughness": {
79 | "baseColorFactor": [
80 | 0.45435,
81 | 0.245963,
82 | 0.171126,
83 | 1
84 | ],
85 | "metallicFactor": 0,
86 | "roughnessFactor": 1
87 | },
88 | "emissiveFactor": [
89 | 0,
90 | 0,
91 | 0
92 | ],
93 | "alphaMode": "OPAQUE",
94 | "doubleSided": false
95 | }
96 | ],
97 | "meshes": [
98 | {
99 | "primitives": [
100 | {
101 | "attributes": {
102 | "POSITION": 1,
103 | "NORMAL": 2
104 | },
105 | "indices": 0,
106 | "material": 0,
107 | "mode": 4
108 | }
109 | ]
110 | }
111 | ],
112 | "nodes": [
113 | {
114 | "mesh": 0
115 | }
116 | ],
117 | "scene": 0,
118 | "scenes": [
119 | {
120 | "nodes": [
121 | 0
122 | ]
123 | }
124 | ]
125 | }
126 |
--------------------------------------------------------------------------------
/data/house/door2.gltf:
--------------------------------------------------------------------------------
1 | {
2 | "accessors": [
3 | {
4 | "bufferView": 1,
5 | "byteOffset": 0,
6 | "componentType": 5123,
7 | "count": 72,
8 | "type": "SCALAR",
9 | "min": [
10 | 0
11 | ],
12 | "max": [
13 | 45
14 | ]
15 | },
16 | {
17 | "bufferView": 0,
18 | "byteOffset": 0,
19 | "componentType": 5126,
20 | "count": 46,
21 | "min": [
22 | -0.3696739971637726,
23 | 0,
24 | 0.9400410056114197
25 | ],
26 | "max": [
27 | 0.3696739971637726,
28 | 1.3130379915237427,
29 | 1.0869100093841553
30 | ],
31 | "type": "VEC3"
32 | },
33 | {
34 | "bufferView": 0,
35 | "byteOffset": 552,
36 | "componentType": 5126,
37 | "count": 46,
38 | "type": "VEC3",
39 | "min": [
40 | -1,
41 | -1,
42 | -1
43 | ],
44 | "max": [
45 | 1,
46 | 1,
47 | 1
48 | ]
49 | }
50 | ],
51 | "asset": {
52 | "generator": "OBJ2GLTF",
53 | "version": "2.0"
54 | },
55 | "buffers": [
56 | {
57 | "byteLength": 1248,
58 | "uri": "data:application/octet-stream;base64,6UW9PqERqD+HpnA/6UW9PqERqD/eH4s/6UW9PgAAAADeH4s/6UW9PgAAAACHpnA/6UW9PqERqD/eH4s/6UW9vqERqD/eH4s/7fVevhXjND/eH4s/7fVevpmCkz/eH4s/6UW9PgAAAADeH4s/6UW9vgAAAADeH4s/6UW9vqERqD/eH4s/6UW9vqERqD+HpnA/6UW9vgAAAACHpnA/6UW9vgAAAADeH4s/6UW9vqERqD+HpnA/6UW9PqERqD+HpnA/6UW9PgAAAACHpnA/6UW9vgAAAACHpnA/6UW9PgAAAACHpnA/6UW9PgAAAADeH4s/6UW9vgAAAADeH4s/6UW9vgAAAACHpnA/6UW9vqERqD+HpnA/6UW9vqERqD/eH4s/6UW9PqERqD/eH4s/6UW9PqERqD+HpnA/hpJpPpmCkz987YE/hpJpPhXjND987YE/hpJpPhXjND/eH4s/hpJpPpmCkz/eH4s/7fVevpmCkz987YE/7fVevhXjND987YE/hpJpPhXjND987YE/hpJpPpmCkz987YE/hpJpPhXjND987YE/7fVevhXjND987YE/7fVevhXjND/eH4s/hpJpPhXjND/eH4s/7fVevpmCkz987YE/hpJpPpmCkz987YE/hpJpPpmCkz/eH4s/7fVevpmCkz/eH4s/7fVevhXjND/eH4s/7fVevhXjND987YE/7fVevpmCkz987YE/7fVevpmCkz/eH4s/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAACAvwAAAAAAAACAAACAvwAAAAAAAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAACAvwAAAAAAAACAAACAvwAAAAAAAACAAACAvwAAAAAAAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAABAAIAAAACAAMABAAFAAYABAAGAAcABQAIAAQABQAJAAgACgALAAwACgAMAA0ADgAPABAADgAQABEAEgATABQAEgAUABUAFgAXABgAFgAYABkAGgAbABwAGgAcAB0AHgAfACAAHgAgACEAIgAjACQAIgAkACUAJgAnACgAJgAoACkAKgArACwAKgAsAC0A"
59 | }
60 | ],
61 | "bufferViews": [
62 | {
63 | "buffer": 0,
64 | "byteLength": 1104,
65 | "byteOffset": 0,
66 | "target": 34962,
67 | "byteStride": 12
68 | },
69 | {
70 | "buffer": 0,
71 | "byteLength": 144,
72 | "byteOffset": 1104,
73 | "target": 34963
74 | }
75 | ],
76 | "materials": [
77 | {
78 | "pbrMetallicRoughness": {
79 | "baseColorFactor": [
80 | 0.45435,
81 | 0.245963,
82 | 0.171126,
83 | 1
84 | ],
85 | "metallicFactor": 0,
86 | "roughnessFactor": 1
87 | },
88 | "emissiveFactor": [
89 | 0,
90 | 0,
91 | 0
92 | ],
93 | "alphaMode": "OPAQUE",
94 | "doubleSided": false
95 | }
96 | ],
97 | "meshes": [
98 | {
99 | "primitives": [
100 | {
101 | "attributes": {
102 | "POSITION": 1,
103 | "NORMAL": 2
104 | },
105 | "indices": 0,
106 | "material": 0,
107 | "mode": 4
108 | }
109 | ]
110 | }
111 | ],
112 | "nodes": [
113 | {
114 | "mesh": 0
115 | }
116 | ],
117 | "scene": 0,
118 | "scenes": [
119 | {
120 | "nodes": [
121 | 0
122 | ]
123 | }
124 | ]
125 | }
126 |
--------------------------------------------------------------------------------
/data/house/door3.gltf:
--------------------------------------------------------------------------------
1 | {
2 | "accessors": [
3 | {
4 | "bufferView": 1,
5 | "byteOffset": 0,
6 | "componentType": 5123,
7 | "count": 72,
8 | "type": "SCALAR",
9 | "min": [
10 | 0
11 | ],
12 | "max": [
13 | 45
14 | ]
15 | },
16 | {
17 | "bufferView": 0,
18 | "byteOffset": 0,
19 | "componentType": 5126,
20 | "count": 46,
21 | "min": [
22 | -1.0869100093841553,
23 | 0,
24 | -0.3696739971637726
25 | ],
26 | "max": [
27 | -0.9400410056114197,
28 | 1.3130379915237427,
29 | 0.3696739971637726
30 | ],
31 | "type": "VEC3"
32 | },
33 | {
34 | "bufferView": 0,
35 | "byteOffset": 552,
36 | "componentType": 5126,
37 | "count": 46,
38 | "type": "VEC3",
39 | "min": [
40 | -1,
41 | -1,
42 | -1
43 | ],
44 | "max": [
45 | 1,
46 | 1,
47 | 1
48 | ]
49 | }
50 | ],
51 | "asset": {
52 | "generator": "OBJ2GLTF",
53 | "version": "2.0"
54 | },
55 | "buffers": [
56 | {
57 | "byteLength": 1248,
58 | "uri": "data:application/octet-stream;base64,h6Zwv6ERqD/pRb0+3h+Lv6ERqD/pRb0+3h+LvwAAAADpRb0+h6ZwvwAAAADpRb0+3h+Lv6ERqD/pRb0+3h+Lv6ERqD/pRb2+3h+LvxXjND/t9V6+3h+Lv5mCkz/t9V6+3h+LvwAAAADpRb0+3h+LvwAAAADpRb2+3h+Lv6ERqD/pRb2+h6Zwv6ERqD/pRb2+h6ZwvwAAAADpRb2+3h+LvwAAAADpRb2+h6Zwv6ERqD/pRb2+h6Zwv6ERqD/pRb0+h6ZwvwAAAADpRb0+h6ZwvwAAAADpRb2+h6ZwvwAAAADpRb0+3h+LvwAAAADpRb0+3h+LvwAAAADpRb2+h6ZwvwAAAADpRb2+h6Zwv6ERqD/pRb2+3h+Lv6ERqD/pRb2+3h+Lv6ERqD/pRb0+h6Zwv6ERqD/pRb0+fO2Bv5mCkz+Gkmk+fO2BvxXjND+Gkmk+3h+LvxXjND+Gkmk+3h+Lv5mCkz+Gkmk+fO2Bv5mCkz/t9V6+fO2BvxXjND/t9V6+fO2BvxXjND+Gkmk+fO2Bv5mCkz+Gkmk+fO2BvxXjND+Gkmk+fO2BvxXjND/t9V6+3h+LvxXjND/t9V6+3h+LvxXjND+Gkmk+fO2Bv5mCkz/t9V6+fO2Bv5mCkz+Gkmk+3h+Lv5mCkz+Gkmk+3h+Lv5mCkz/t9V6+3h+LvxXjND/t9V6+fO2BvxXjND/t9V6+fO2Bv5mCkz/t9V6+3h+Lv5mCkz/t9V6+AAAAgAAAAAAAAIA/AAAAgAAAAAAAAIA/AAAAgAAAAAAAAIA/AAAAgAAAAAAAAIA/AACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAgAAAAAAAAIA/AAAAgAAAAAAAAIA/AAAAgAAAAAAAAIA/AAAAgAAAAAAAAIA/AAABAAIAAAACAAMABAAFAAYABAAGAAcABQAIAAQABQAJAAgACgALAAwACgAMAA0ADgAPABAADgAQABEAEgATABQAEgAUABUAFgAXABgAFgAYABkAGgAbABwAGgAcAB0AHgAfACAAHgAgACEAIgAjACQAIgAkACUAJgAnACgAJgAoACkAKgArACwAKgAsAC0A"
59 | }
60 | ],
61 | "bufferViews": [
62 | {
63 | "buffer": 0,
64 | "byteLength": 1104,
65 | "byteOffset": 0,
66 | "target": 34962,
67 | "byteStride": 12
68 | },
69 | {
70 | "buffer": 0,
71 | "byteLength": 144,
72 | "byteOffset": 1104,
73 | "target": 34963
74 | }
75 | ],
76 | "materials": [
77 | {
78 | "pbrMetallicRoughness": {
79 | "baseColorFactor": [
80 | 0.45435,
81 | 0.245963,
82 | 0.171126,
83 | 1
84 | ],
85 | "metallicFactor": 0,
86 | "roughnessFactor": 1
87 | },
88 | "emissiveFactor": [
89 | 0,
90 | 0,
91 | 0
92 | ],
93 | "alphaMode": "OPAQUE",
94 | "doubleSided": false
95 | }
96 | ],
97 | "meshes": [
98 | {
99 | "primitives": [
100 | {
101 | "attributes": {
102 | "POSITION": 1,
103 | "NORMAL": 2
104 | },
105 | "indices": 0,
106 | "material": 0,
107 | "mode": 4
108 | }
109 | ]
110 | }
111 | ],
112 | "nodes": [
113 | {
114 | "mesh": 0
115 | }
116 | ],
117 | "scene": 0,
118 | "scenes": [
119 | {
120 | "nodes": [
121 | 0
122 | ]
123 | }
124 | ]
125 | }
126 |
--------------------------------------------------------------------------------
/data/house/doorknob0.gltf:
--------------------------------------------------------------------------------
1 | {
2 | "accessors": [
3 | {
4 | "bufferView": 1,
5 | "byteOffset": 0,
6 | "componentType": 5123,
7 | "count": 672,
8 | "type": "SCALAR",
9 | "min": [
10 | 0
11 | ],
12 | "max": [
13 | 113
14 | ]
15 | },
16 | {
17 | "bufferView": 0,
18 | "byteOffset": 0,
19 | "componentType": 5126,
20 | "count": 114,
21 | "min": [
22 | -0.2817629873752594,
23 | 0.5537300109863281,
24 | -1.1363259553909302
25 | ],
26 | "max": [
27 | -0.1847810000181198,
28 | 0.6507120132446289,
29 | -1.0393439531326294
30 | ],
31 | "type": "VEC3"
32 | },
33 | {
34 | "bufferView": 0,
35 | "byteOffset": 1368,
36 | "componentType": 5126,
37 | "count": 114,
38 | "type": "VEC3",
39 | "min": [
40 | -1,
41 | -1,
42 | -1
43 | ],
44 | "max": [
45 | 1,
46 | 1,
47 | 1
48 | ]
49 | }
50 | ],
51 | "asset": {
52 | "generator": "OBJ2GLTF",
53 | "version": "2.0"
54 | },
55 | "buffers": [
56 | {
57 | "byteLength": 4080,
58 | "uri": "data:application/octet-stream;base64,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/mDOviFIar8AAAAA+6m+vglJar+W5R2+MwIqv1v4Mb+y2Yy+AAABAAIAAAACAAMABAAAAAMABQAGAAAABQAAAAQABgABAAAAAQAHAAgAAQAIAAIACQAHAAEABgAJAAEAAgAIAAoAAgAKAAsAAwACAAsABAADAAwADAADAA0AAwALAA0ADgAEAAwADwAFAAQADwAEAA4AEAARAAUAEAAFAA8AEQAGAAUAEgAJAAYAEQASAAYAEgATAAkAEwAUAAkACQAUAAcABwAVAAgAFAAVAAcACAAVAAoACgAVABYACwAKABYADQALABcACwAWABcADAANABgADQAXABkAGAANABkADgAMABoAGgAMABgAGwAOABoAHAAPAA4AHAAOABsAHQAQAA8AHQAPABwAHgARABAAHwAeABAAHwAQAB0AHgAgABEAIAASABEAIQATABIAIAAhABIAIQAiABMAIgAjABMAEwAjABQAIwAVABQAJAAVACMAIgAkACMAJQAkACIAJgAlACIAJgAiACEAJwAmACEAJwAhACAAKAAnACAAKAAgAB4AKQAoAB4AKQAeAB8AKgApAB8AKgAfACsAKwAfAB0AKwAdACwALAAdABwALAAcAC0ALQAcABsALgArACwALgAsAC0ALgAqACsALgAvACoALwApACoAMAAoACkALwAwACkAMAAxACgAMQAnACgAMgAmACcAMQAyACcAMgAzACYAMwAlACYANAA1ACUAMwA0ACUAJQA1ACQANQAVACQANgAVADUANAA2ADUANwA4ADQANwA0ADMAOAA2ADQAOQA3ADMAOQAzADIAOgA5ADIAOgAyADEAOwA6ADEAOwAxADAAPAA7ADAAPAAwAC8ALgA8AC8APQA7ADwALgA9ADwAPQA+ADsAPgA6ADsAPwA5ADoAPgA/ADoAPwBAADkAQAA3ADkAQQA4ADcAQABBADcAQgBDADgAQQBCADgAOABDADYAQwAVADYAQgBEAEMARAAVAEMARQBEAEIARgBFAEIARgBCAEEARwBGAEEARwBBAEAASABHAEAASABAAD8ASQBIAD8ASQA/AD4ASgBJAD4ASgA+AD0ALgBKAD0ALgBLAEoASwBJAEoATABIAEkASwBMAEkATABNAEgATQBHAEgATgBGAEcATQBOAEcATwBFAEYATgBPAEYATwBQAEUAUABRAEUARQBRAEQAUQAVAEQAUgAVAFEAUABSAFEAUwBUAFAAUwBQAE8AVABSAFAAVQBTAE8AVQBPAE4AVgBVAE4AVgBOAE0AVwBWAE0AVwBNAEwAWABXAEwAWABMAEsALgBYAEsAWQBXAFgALgBZAFgAWQBaAFcAWgBWAFcAWwBVAFYAWgBbAFYAXABTAFUAWwBcAFUAXABdAFMAXQBUAFMAXgBfAFQAXQBeAFQAVABfAFIAXwAVAFIAFgAVAGAAYAAVAGEAYQAVAF8AXgBhAF8AYgBhAF4AYwBiAF4AYwBeAF0AZABjAF0AZABdAFwAZQBkAFwAZQBcAFsAZgBlAFsAZgBbAFoAZwBmAFoAZwBaAFkALgBnAFkALgBoAGcAaABmAGcAaQBlAGYAaABpAGYAagBkAGUAaQBqAGUAagBrAGQAawBjAGQAGQBiAGMAawAZAGMAGQAXAGIAFwBgAGIAYgBgAGEAFwAWAGAAGAAZAGsAbAAYAGsAbABrAGoAbQBsAGoAbQBqAGkAbgBtAGkAbgBpAGgALgBuAGgALgBvAHAALgBwAG4ALgAtAG8AcABtAG4AcQBsAG0AcABxAG0AcQAaAGwAGgAYAGwAGwAaAHEAbwAbAHEAbwBxAHAALQAbAG8A"
59 | }
60 | ],
61 | "bufferViews": [
62 | {
63 | "buffer": 0,
64 | "byteLength": 2736,
65 | "byteOffset": 0,
66 | "target": 34962,
67 | "byteStride": 12
68 | },
69 | {
70 | "buffer": 0,
71 | "byteLength": 1344,
72 | "byteOffset": 2736,
73 | "target": 34963
74 | }
75 | ],
76 | "materials": [
77 | {
78 | "pbrMetallicRoughness": {
79 | "baseColorFactor": [
80 | 0.8,
81 | 0.24657,
82 | 0.026813,
83 | 1
84 | ],
85 | "metallicFactor": 0,
86 | "roughnessFactor": 1
87 | },
88 | "emissiveFactor": [
89 | 0,
90 | 0,
91 | 0
92 | ],
93 | "alphaMode": "OPAQUE",
94 | "doubleSided": false
95 | }
96 | ],
97 | "meshes": [
98 | {
99 | "primitives": [
100 | {
101 | "attributes": {
102 | "POSITION": 1,
103 | "NORMAL": 2
104 | },
105 | "indices": 0,
106 | "material": 0,
107 | "mode": 4
108 | }
109 | ]
110 | }
111 | ],
112 | "nodes": [
113 | {
114 | "mesh": 0
115 | }
116 | ],
117 | "scene": 0,
118 | "scenes": [
119 | {
120 | "nodes": [
121 | 0
122 | ]
123 | }
124 | ]
125 | }
126 |
--------------------------------------------------------------------------------
/data/house/doorknob1.gltf:
--------------------------------------------------------------------------------
1 | {
2 | "accessors": [
3 | {
4 | "bufferView": 1,
5 | "byteOffset": 0,
6 | "componentType": 5123,
7 | "count": 672,
8 | "type": "SCALAR",
9 | "min": [
10 | 0
11 | ],
12 | "max": [
13 | 113
14 | ]
15 | },
16 | {
17 | "bufferView": 0,
18 | "byteOffset": 0,
19 | "componentType": 5126,
20 | "count": 114,
21 | "min": [
22 | 1.0393439531326294,
23 | 0.5537300109863281,
24 | -0.2817629873752594
25 | ],
26 | "max": [
27 | 1.1363259553909302,
28 | 0.6507120132446289,
29 | -0.1847810000181198
30 | ],
31 | "type": "VEC3"
32 | },
33 | {
34 | "bufferView": 0,
35 | "byteOffset": 1368,
36 | "componentType": 5126,
37 | "count": 114,
38 | "type": "VEC3",
39 | "min": [
40 | -1,
41 | -1,
42 | -1
43 | ],
44 | "max": [
45 | 1,
46 | 1,
47 | 1
48 | ]
49 | }
50 | ],
51 | "asset": {
52 | "generator": "OBJ2GLTF",
53 | "version": "2.0"
54 | },
55 | "buffers": [
56 | {
57 | "byteLength": 4080,
58 | "uri": "data:application/octet-stream;base64,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AAAAABBHar/UZc6+luUdPglJar/7qb6+stmMPlv4Mb8zAiq/AAABAAIAAAACAAMABAAAAAMABQAGAAAABQAAAAQABgABAAAAAQAHAAgAAQAIAAIACQAHAAEABgAJAAEAAgAIAAoAAgAKAAsAAwACAAsABAADAAwADAADAA0AAwALAA0ADgAEAAwADwAFAAQADwAEAA4AEAARAAUAEAAFAA8AEQAGAAUAEgAJAAYAEQASAAYAEgATAAkAEwAUAAkACQAUAAcABwAVAAgAFAAVAAcACAAVAAoACgAVABYACwAKABYADQALABcACwAWABcADAANABgADQAXABkAGAANABkADgAMABoAGgAMABgAGwAOABoAHAAPAA4AHAAOABsAHQAQAA8AHQAPABwAHgARABAAHwAeABAAHwAQAB0AHgAgABEAIAASABEAIQATABIAIAAhABIAIQAiABMAIgAjABMAEwAjABQAIwAVABQAJAAVACMAIgAkACMAJQAkACIAJgAlACIAJgAiACEAJwAmACEAJwAhACAAKAAnACAAKAAgAB4AKQAoAB4AKQAeAB8AKgApAB8AKgAfACsAKwAfAB0AKwAdACwALAAdABwALAAcAC0ALQAcABsALgArACwALgAsAC0ALgAqACsALgAvACoALwApACoAMAAoACkALwAwACkAMAAxACgAMQAnACgAMgAmACcAMQAyACcAMgAzACYAMwAlACYANAA1ACUAMwA0ACUAJQA1ACQANQAVACQANgAVADUANAA2ADUANwA4ADQANwA0ADMAOAA2ADQAOQA3ADMAOQAzADIAOgA5ADIAOgAyADEAOwA6ADEAOwAxADAAPAA7ADAAPAAwAC8ALgA8AC8APQA7ADwALgA9ADwAPQA+ADsAPgA6ADsAPwA5ADoAPgA/ADoAPwBAADkAQAA3ADkAQQA4ADcAQABBADcAQgBDADgAQQBCADgAOABDADYAQwAVADYAQgBEAEMARAAVAEMARQBEAEIARgBFAEIARgBCAEEARwBGAEEARwBBAEAASABHAEAASABAAD8ASQBIAD8ASQA/AD4ASgBJAD4ASgA+AD0ALgBKAD0ALgBLAEoASwBJAEoATABIAEkASwBMAEkATABNAEgATQBHAEgATgBGAEcATQBOAEcATwBFAEYATgBPAEYATwBQAEUAUABRAEUARQBRAEQAUQAVAEQAUgAVAFEAUABSAFEAUwBUAFAAUwBQAE8AVABSAFAAVQBTAE8AVQBPAE4AVgBVAE4AVgBOAE0AVwBWAE0AVwBNAEwAWABXAEwAWABMAEsALgBYAEsAWQBXAFgALgBZAFgAWQBaAFcAWgBWAFcAWwBVAFYAWgBbAFYAXABTAFUAWwBcAFUAXABdAFMAXQBUAFMAXgBfAFQAXQBeAFQAVABfAFIAXwAVAFIAFgAVAGAAYAAVAGEAYQAVAF8AXgBhAF8AYgBhAF4AYwBiAF4AYwBeAF0AZABjAF0AZABdAFwAZQBkAFwAZQBcAFsAZgBlAFsAZgBbAFoAZwBmAFoAZwBaAFkALgBnAFkALgBoAGcAaABmAGcAaQBlAGYAaABpAGYAagBkAGUAaQBqAGUAagBrAGQAawBjAGQAGQBiAGMAawAZAGMAGQAXAGIAFwBgAGIAYgBgAGEAFwAWAGAAGAAZAGsAbAAYAGsAbABrAGoAbQBsAGoAbQBqAGkAbgBtAGkAbgBpAGgALgBuAGgALgBvAHAALgBwAG4ALgAtAG8AcABtAG4AcQBsAG0AcABxAG0AcQAaAGwAGgAYAGwAGwAaAHEAbwAbAHEAbwBxAHAALQAbAG8A"
59 | }
60 | ],
61 | "bufferViews": [
62 | {
63 | "buffer": 0,
64 | "byteLength": 2736,
65 | "byteOffset": 0,
66 | "target": 34962,
67 | "byteStride": 12
68 | },
69 | {
70 | "buffer": 0,
71 | "byteLength": 1344,
72 | "byteOffset": 2736,
73 | "target": 34963
74 | }
75 | ],
76 | "materials": [
77 | {
78 | "pbrMetallicRoughness": {
79 | "baseColorFactor": [
80 | 0.8,
81 | 0.24657,
82 | 0.026813,
83 | 1
84 | ],
85 | "metallicFactor": 0,
86 | "roughnessFactor": 1
87 | },
88 | "emissiveFactor": [
89 | 0,
90 | 0,
91 | 0
92 | ],
93 | "alphaMode": "OPAQUE",
94 | "doubleSided": false
95 | }
96 | ],
97 | "meshes": [
98 | {
99 | "primitives": [
100 | {
101 | "attributes": {
102 | "POSITION": 1,
103 | "NORMAL": 2
104 | },
105 | "indices": 0,
106 | "material": 0,
107 | "mode": 4
108 | }
109 | ]
110 | }
111 | ],
112 | "nodes": [
113 | {
114 | "mesh": 0
115 | }
116 | ],
117 | "scene": 0,
118 | "scenes": [
119 | {
120 | "nodes": [
121 | 0
122 | ]
123 | }
124 | ]
125 | }
126 |
--------------------------------------------------------------------------------
/data/house/doorknob2.gltf:
--------------------------------------------------------------------------------
1 | {
2 | "accessors": [
3 | {
4 | "bufferView": 1,
5 | "byteOffset": 0,
6 | "componentType": 5123,
7 | "count": 672,
8 | "type": "SCALAR",
9 | "min": [
10 | 0
11 | ],
12 | "max": [
13 | 113
14 | ]
15 | },
16 | {
17 | "bufferView": 0,
18 | "byteOffset": 0,
19 | "componentType": 5126,
20 | "count": 114,
21 | "min": [
22 | 0.1847810000181198,
23 | 0.5537300109863281,
24 | 1.0393439531326294
25 | ],
26 | "max": [
27 | 0.2817629873752594,
28 | 0.6507120132446289,
29 | 1.1363259553909302
30 | ],
31 | "type": "VEC3"
32 | },
33 | {
34 | "bufferView": 0,
35 | "byteOffset": 1368,
36 | "componentType": 5126,
37 | "count": 114,
38 | "type": "VEC3",
39 | "min": [
40 | -1,
41 | -1,
42 | -1
43 | ],
44 | "max": [
45 | 1,
46 | 1,
47 | 1
48 | ]
49 | }
50 | ],
51 | "asset": {
52 | "generator": "OBJ2GLTF",
53 | "version": "2.0"
54 | },
55 | "buffers": [
56 | {
57 | "byteLength": 4080,
58 | "uri": "data:application/octet-stream;base64,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/mDOPiFIar8AAAAA+6m+PglJar+W5R0+MwIqP1v4Mb+y2Yw+AAABAAIAAAACAAMABAAAAAMABQAGAAAABQAAAAQABgABAAAAAQAHAAgAAQAIAAIACQAHAAEABgAJAAEAAgAIAAoAAgAKAAsAAwACAAsABAADAAwADAADAA0AAwALAA0ADgAEAAwADwAFAAQADwAEAA4AEAARAAUAEAAFAA8AEQAGAAUAEgAJAAYAEQASAAYAEgATAAkAEwAUAAkACQAUAAcABwAVAAgAFAAVAAcACAAVAAoACgAVABYACwAKABYADQALABcACwAWABcADAANABgADQAXABkAGAANABkADgAMABoAGgAMABgAGwAOABoAHAAPAA4AHAAOABsAHQAQAA8AHQAPABwAHgARABAAHwAeABAAHwAQAB0AHgAgABEAIAASABEAIQATABIAIAAhABIAIQAiABMAIgAjABMAEwAjABQAIwAVABQAJAAVACMAIgAkACMAJQAkACIAJgAlACIAJgAiACEAJwAmACEAJwAhACAAKAAnACAAKAAgAB4AKQAoAB4AKQAeAB8AKgApAB8AKgAfACsAKwAfAB0AKwAdACwALAAdABwALAAcAC0ALQAcABsALgArACwALgAsAC0ALgAqACsALgAvACoALwApACoAMAAoACkALwAwACkAMAAxACgAMQAnACgAMgAmACcAMQAyACcAMgAzACYAMwAlACYANAA1ACUAMwA0ACUAJQA1ACQANQAVACQANgAVADUANAA2ADUANwA4ADQANwA0ADMAOAA2ADQAOQA3ADMAOQAzADIAOgA5ADIAOgAyADEAOwA6ADEAOwAxADAAPAA7ADAAPAAwAC8ALgA8AC8APQA7ADwALgA9ADwAPQA+ADsAPgA6ADsAPwA5ADoAPgA/ADoAPwBAADkAQAA3ADkAQQA4ADcAQABBADcAQgBDADgAQQBCADgAOABDADYAQwAVADYAQgBEAEMARAAVAEMARQBEAEIARgBFAEIARgBCAEEARwBGAEEARwBBAEAASABHAEAASABAAD8ASQBIAD8ASQA/AD4ASgBJAD4ASgA+AD0ALgBKAD0ALgBLAEoASwBJAEoATABIAEkASwBMAEkATABNAEgATQBHAEgATgBGAEcATQBOAEcATwBFAEYATgBPAEYATwBQAEUAUABRAEUARQBRAEQAUQAVAEQAUgAVAFEAUABSAFEAUwBUAFAAUwBQAE8AVABSAFAAVQBTAE8AVQBPAE4AVgBVAE4AVgBOAE0AVwBWAE0AVwBNAEwAWABXAEwAWABMAEsALgBYAEsAWQBXAFgALgBZAFgAWQBaAFcAWgBWAFcAWwBVAFYAWgBbAFYAXABTAFUAWwBcAFUAXABdAFMAXQBUAFMAXgBfAFQAXQBeAFQAVABfAFIAXwAVAFIAFgAVAGAAYAAVAGEAYQAVAF8AXgBhAF8AYgBhAF4AYwBiAF4AYwBeAF0AZABjAF0AZABdAFwAZQBkAFwAZQBcAFsAZgBlAFsAZgBbAFoAZwBmAFoAZwBaAFkALgBnAFkALgBoAGcAaABmAGcAaQBlAGYAaABpAGYAagBkAGUAaQBqAGUAagBrAGQAawBjAGQAGQBiAGMAawAZAGMAGQAXAGIAFwBgAGIAYgBgAGEAFwAWAGAAGAAZAGsAbAAYAGsAbABrAGoAbQBsAGoAbQBqAGkAbgBtAGkAbgBpAGgALgBuAGgALgBvAHAALgBwAG4ALgAtAG8AcABtAG4AcQBsAG0AcABxAG0AcQAaAGwAGgAYAGwAGwAaAHEAbwAbAHEAbwBxAHAALQAbAG8A"
59 | }
60 | ],
61 | "bufferViews": [
62 | {
63 | "buffer": 0,
64 | "byteLength": 2736,
65 | "byteOffset": 0,
66 | "target": 34962,
67 | "byteStride": 12
68 | },
69 | {
70 | "buffer": 0,
71 | "byteLength": 1344,
72 | "byteOffset": 2736,
73 | "target": 34963
74 | }
75 | ],
76 | "materials": [
77 | {
78 | "pbrMetallicRoughness": {
79 | "baseColorFactor": [
80 | 0.8,
81 | 0.24657,
82 | 0.026813,
83 | 1
84 | ],
85 | "metallicFactor": 0,
86 | "roughnessFactor": 1
87 | },
88 | "emissiveFactor": [
89 | 0,
90 | 0,
91 | 0
92 | ],
93 | "alphaMode": "OPAQUE",
94 | "doubleSided": false
95 | }
96 | ],
97 | "meshes": [
98 | {
99 | "primitives": [
100 | {
101 | "attributes": {
102 | "POSITION": 1,
103 | "NORMAL": 2
104 | },
105 | "indices": 0,
106 | "material": 0,
107 | "mode": 4
108 | }
109 | ]
110 | }
111 | ],
112 | "nodes": [
113 | {
114 | "mesh": 0
115 | }
116 | ],
117 | "scene": 0,
118 | "scenes": [
119 | {
120 | "nodes": [
121 | 0
122 | ]
123 | }
124 | ]
125 | }
126 |
--------------------------------------------------------------------------------
/data/house/doorknob3.gltf:
--------------------------------------------------------------------------------
1 | {
2 | "accessors": [
3 | {
4 | "bufferView": 1,
5 | "byteOffset": 0,
6 | "componentType": 5123,
7 | "count": 672,
8 | "type": "SCALAR",
9 | "min": [
10 | 0
11 | ],
12 | "max": [
13 | 113
14 | ]
15 | },
16 | {
17 | "bufferView": 0,
18 | "byteOffset": 0,
19 | "componentType": 5126,
20 | "count": 114,
21 | "min": [
22 | -1.1363259553909302,
23 | 0.5537300109863281,
24 | 0.1847810000181198
25 | ],
26 | "max": [
27 | -1.0393439531326294,
28 | 0.6507120132446289,
29 | 0.2817629873752594
30 | ],
31 | "type": "VEC3"
32 | },
33 | {
34 | "bufferView": 0,
35 | "byteOffset": 1368,
36 | "componentType": 5126,
37 | "count": 114,
38 | "type": "VEC3",
39 | "min": [
40 | -1,
41 | -1,
42 | -1
43 | ],
44 | "max": [
45 | 1,
46 | 1,
47 | 1
48 | ]
49 | }
50 | ],
51 | "asset": {
52 | "generator": "OBJ2GLTF",
53 | "version": "2.0"
54 | },
55 | "buffers": [
56 | {
57 | "byteLength": 4080,
58 | "uri": "data:application/octet-stream;base64,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AAAAABBHar/UZc4+luUdvglJar/7qb4+stmMvlv4Mb8zAio/AAABAAIAAAACAAMABAAAAAMABQAGAAAABQAAAAQABgABAAAAAQAHAAgAAQAIAAIACQAHAAEABgAJAAEAAgAIAAoAAgAKAAsAAwACAAsABAADAAwADAADAA0AAwALAA0ADgAEAAwADwAFAAQADwAEAA4AEAARAAUAEAAFAA8AEQAGAAUAEgAJAAYAEQASAAYAEgATAAkAEwAUAAkACQAUAAcABwAVAAgAFAAVAAcACAAVAAoACgAVABYACwAKABYADQALABcACwAWABcADAANABgADQAXABkAGAANABkADgAMABoAGgAMABgAGwAOABoAHAAPAA4AHAAOABsAHQAQAA8AHQAPABwAHgARABAAHwAeABAAHwAQAB0AHgAgABEAIAASABEAIQATABIAIAAhABIAIQAiABMAIgAjABMAEwAjABQAIwAVABQAJAAVACMAIgAkACMAJQAkACIAJgAlACIAJgAiACEAJwAmACEAJwAhACAAKAAnACAAKAAgAB4AKQAoAB4AKQAeAB8AKgApAB8AKgAfACsAKwAfAB0AKwAdACwALAAdABwALAAcAC0ALQAcABsALgArACwALgAsAC0ALgAqACsALgAvACoALwApACoAMAAoACkALwAwACkAMAAxACgAMQAnACgAMgAmACcAMQAyACcAMgAzACYAMwAlACYANAA1ACUAMwA0ACUAJQA1ACQANQAVACQANgAVADUANAA2ADUANwA4ADQANwA0ADMAOAA2ADQAOQA3ADMAOQAzADIAOgA5ADIAOgAyADEAOwA6ADEAOwAxADAAPAA7ADAAPAAwAC8ALgA8AC8APQA7ADwALgA9ADwAPQA+ADsAPgA6ADsAPwA5ADoAPgA/ADoAPwBAADkAQAA3ADkAQQA4ADcAQABBADcAQgBDADgAQQBCADgAOABDADYAQwAVADYAQgBEAEMARAAVAEMARQBEAEIARgBFAEIARgBCAEEARwBGAEEARwBBAEAASABHAEAASABAAD8ASQBIAD8ASQA/AD4ASgBJAD4ASgA+AD0ALgBKAD0ALgBLAEoASwBJAEoATABIAEkASwBMAEkATABNAEgATQBHAEgATgBGAEcATQBOAEcATwBFAEYATgBPAEYATwBQAEUAUABRAEUARQBRAEQAUQAVAEQAUgAVAFEAUABSAFEAUwBUAFAAUwBQAE8AVABSAFAAVQBTAE8AVQBPAE4AVgBVAE4AVgBOAE0AVwBWAE0AVwBNAEwAWABXAEwAWABMAEsALgBYAEsAWQBXAFgALgBZAFgAWQBaAFcAWgBWAFcAWwBVAFYAWgBbAFYAXABTAFUAWwBcAFUAXABdAFMAXQBUAFMAXgBfAFQAXQBeAFQAVABfAFIAXwAVAFIAFgAVAGAAYAAVAGEAYQAVAF8AXgBhAF8AYgBhAF4AYwBiAF4AYwBeAF0AZABjAF0AZABdAFwAZQBkAFwAZQBcAFsAZgBlAFsAZgBbAFoAZwBmAFoAZwBaAFkALgBnAFkALgBoAGcAaABmAGcAaQBlAGYAaABpAGYAagBkAGUAaQBqAGUAagBrAGQAawBjAGQAGQBiAGMAawAZAGMAGQAXAGIAFwBgAGIAYgBgAGEAFwAWAGAAGAAZAGsAbAAYAGsAbABrAGoAbQBsAGoAbQBqAGkAbgBtAGkAbgBpAGgALgBuAGgALgBvAHAALgBwAG4ALgAtAG8AcABtAG4AcQBsAG0AcABxAG0AcQAaAGwAGgAYAGwAGwAaAHEAbwAbAHEAbwBxAHAALQAbAG8A"
59 | }
60 | ],
61 | "bufferViews": [
62 | {
63 | "buffer": 0,
64 | "byteLength": 2736,
65 | "byteOffset": 0,
66 | "target": 34962,
67 | "byteStride": 12
68 | },
69 | {
70 | "buffer": 0,
71 | "byteLength": 1344,
72 | "byteOffset": 2736,
73 | "target": 34963
74 | }
75 | ],
76 | "materials": [
77 | {
78 | "pbrMetallicRoughness": {
79 | "baseColorFactor": [
80 | 0.8,
81 | 0.24657,
82 | 0.026813,
83 | 1
84 | ],
85 | "metallicFactor": 0,
86 | "roughnessFactor": 1
87 | },
88 | "emissiveFactor": [
89 | 0,
90 | 0,
91 | 0
92 | ],
93 | "alphaMode": "OPAQUE",
94 | "doubleSided": false
95 | }
96 | ],
97 | "meshes": [
98 | {
99 | "primitives": [
100 | {
101 | "attributes": {
102 | "POSITION": 1,
103 | "NORMAL": 2
104 | },
105 | "indices": 0,
106 | "material": 0,
107 | "mode": 4
108 | }
109 | ]
110 | }
111 | ],
112 | "nodes": [
113 | {
114 | "mesh": 0
115 | }
116 | ],
117 | "scene": 0,
118 | "scenes": [
119 | {
120 | "nodes": [
121 | 0
122 | ]
123 | }
124 | ]
125 | }
126 |
--------------------------------------------------------------------------------
/data/house/roof.gltf:
--------------------------------------------------------------------------------
1 | {
2 | "accessors": [
3 | {
4 | "bufferView": 1,
5 | "byteOffset": 0,
6 | "componentType": 5123,
7 | "count": 18,
8 | "type": "SCALAR",
9 | "min": [
10 | 0
11 | ],
12 | "max": [
13 | 15
14 | ]
15 | },
16 | {
17 | "bufferView": 0,
18 | "byteOffset": 0,
19 | "componentType": 5126,
20 | "count": 16,
21 | "min": [
22 | -1,
23 | 2,
24 | -1
25 | ],
26 | "max": [
27 | 1,
28 | 2.979520082473755,
29 | 1
30 | ],
31 | "type": "VEC3"
32 | },
33 | {
34 | "bufferView": 0,
35 | "byteOffset": 192,
36 | "componentType": 5126,
37 | "count": 16,
38 | "type": "VEC3",
39 | "min": [
40 | -0.6997694373130798,
41 | -1,
42 | -0.6997694373130798
43 | ],
44 | "max": [
45 | 0.6997694373130798,
46 | 0.7143687605857849,
47 | 0.6997694373130798
48 | ]
49 | }
50 | ],
51 | "asset": {
52 | "generator": "OBJ2GLTF",
53 | "version": "2.0"
54 | },
55 | "buffers": [
56 | {
57 | "byteLength": 420,
58 | "uri": "data:application/octet-stream;base64,AAAAAHWwPkAAAAAAAACAvwAAAEAAAIC/AACAvwAAAEAAAIA/AAAAAHWwPkAAAAAAAACAPwAAAEAAAIC/AACAvwAAAEAAAIC/AAAAAHWwPkAAAAAAAACAPwAAAEAAAIA/AACAPwAAAEAAAIC/AAAAAHWwPkAAAAAAAACAvwAAAEAAAIA/AACAPwAAAEAAAIA/AACAvwAAAEAAAIA/AACAvwAAAEAAAIC/AACAPwAAAEAAAIC/AACAPwAAAEAAAIA/FyQzv9/gNj8AAAAAFyQzv9/gNj8AAAAAFyQzv9/gNj8AAAAAAAAAAN/gNj8XJDO/AAAAAN/gNj8XJDO/AAAAAN/gNj8XJDO/FyQzP9/gNj8AAAAAFyQzP9/gNj8AAAAAFyQzP9/gNj8AAAAAAAAAAN/gNj8XJDM/AAAAAN/gNj8XJDM/AAAAAN/gNj8XJDM/AAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAABAAIAAwAEAAUABgAHAAgACQAKAAsADAANAA4ADAAOAA8A"
59 | }
60 | ],
61 | "bufferViews": [
62 | {
63 | "buffer": 0,
64 | "byteLength": 384,
65 | "byteOffset": 0,
66 | "target": 34962,
67 | "byteStride": 12
68 | },
69 | {
70 | "buffer": 0,
71 | "byteLength": 36,
72 | "byteOffset": 384,
73 | "target": 34963
74 | }
75 | ],
76 | "materials": [
77 | {
78 | "pbrMetallicRoughness": {
79 | "baseColorFactor": [
80 | 0.64,
81 | 0.204491,
82 | 0.219181,
83 | 1
84 | ],
85 | "metallicFactor": 0,
86 | "roughnessFactor": 1
87 | },
88 | "emissiveFactor": [
89 | 0,
90 | 0,
91 | 0
92 | ],
93 | "alphaMode": "OPAQUE",
94 | "doubleSided": false
95 | }
96 | ],
97 | "meshes": [
98 | {
99 | "primitives": [
100 | {
101 | "attributes": {
102 | "POSITION": 1,
103 | "NORMAL": 2
104 | },
105 | "indices": 0,
106 | "material": 0,
107 | "mode": 4
108 | }
109 | ]
110 | }
111 | ],
112 | "nodes": [
113 | {
114 | "mesh": 0
115 | }
116 | ],
117 | "scene": 0,
118 | "scenes": [
119 | {
120 | "nodes": [
121 | 0
122 | ]
123 | }
124 | ]
125 | }
126 |
--------------------------------------------------------------------------------
/data/house/wall.gltf:
--------------------------------------------------------------------------------
1 | {
2 | "accessors": [
3 | {
4 | "bufferView": 1,
5 | "byteOffset": 0,
6 | "componentType": 5123,
7 | "count": 36,
8 | "type": "SCALAR",
9 | "min": [
10 | 0
11 | ],
12 | "max": [
13 | 23
14 | ]
15 | },
16 | {
17 | "bufferView": 0,
18 | "byteOffset": 0,
19 | "componentType": 5126,
20 | "count": 24,
21 | "min": [
22 | -1,
23 | 0,
24 | -1
25 | ],
26 | "max": [
27 | 1,
28 | 2,
29 | 1
30 | ],
31 | "type": "VEC3"
32 | },
33 | {
34 | "bufferView": 0,
35 | "byteOffset": 288,
36 | "componentType": 5126,
37 | "count": 24,
38 | "type": "VEC3",
39 | "min": [
40 | -1,
41 | -1,
42 | -1
43 | ],
44 | "max": [
45 | 1,
46 | 1,
47 | 1
48 | ]
49 | }
50 | ],
51 | "asset": {
52 | "generator": "OBJ2GLTF",
53 | "version": "2.0"
54 | },
55 | "buffers": [
56 | {
57 | "byteLength": 648,
58 | "uri": "data:application/octet-stream;base64,AACAvwAAAEAAAIA/AACAvwAAAEAAAIC/AACAvwAAAAAAAIC/AACAvwAAAAAAAIA/AACAvwAAAEAAAIC/AACAPwAAAEAAAIC/AACAPwAAAAAAAIC/AACAvwAAAAAAAIC/AACAPwAAAEAAAIC/AACAPwAAAEAAAIA/AACAPwAAAAAAAIA/AACAPwAAAAAAAIC/AACAPwAAAEAAAIA/AACAvwAAAEAAAIA/AACAvwAAAAAAAIA/AACAPwAAAAAAAIA/AACAvwAAAAAAAIA/AACAvwAAAAAAAIC/AACAPwAAAAAAAIC/AACAPwAAAAAAAIA/AACAPwAAAEAAAIA/AACAPwAAAEAAAIC/AACAvwAAAEAAAIC/AACAvwAAAEAAAIA/AACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAABAAIAAAACAAMABAAFAAYABAAGAAcACAAJAAoACAAKAAsADAANAA4ADAAOAA8AEAARABIAEAASABMAFAAVABYAFAAWABcA"
59 | }
60 | ],
61 | "bufferViews": [
62 | {
63 | "buffer": 0,
64 | "byteLength": 576,
65 | "byteOffset": 0,
66 | "target": 34962,
67 | "byteStride": 12
68 | },
69 | {
70 | "buffer": 0,
71 | "byteLength": 72,
72 | "byteOffset": 576,
73 | "target": 34963
74 | }
75 | ],
76 | "materials": [
77 | {
78 | "pbrMetallicRoughness": {
79 | "baseColorFactor": [
80 | 0.8,
81 | 0.431283,
82 | 0.253046,
83 | 1
84 | ],
85 | "metallicFactor": 0,
86 | "roughnessFactor": 1
87 | },
88 | "emissiveFactor": [
89 | 0,
90 | 0,
91 | 0
92 | ],
93 | "alphaMode": "OPAQUE",
94 | "doubleSided": false
95 | }
96 | ],
97 | "meshes": [
98 | {
99 | "primitives": [
100 | {
101 | "attributes": {
102 | "POSITION": 1,
103 | "NORMAL": 2
104 | },
105 | "indices": 0,
106 | "material": 0,
107 | "mode": 4
108 | }
109 | ]
110 | }
111 | ],
112 | "nodes": [
113 | {
114 | "mesh": 0
115 | }
116 | ],
117 | "scene": 0,
118 | "scenes": [
119 | {
120 | "nodes": [
121 | 0
122 | ]
123 | }
124 | ]
125 | }
126 |
--------------------------------------------------------------------------------
/data/red_box.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/3d-tiles-samples-generator/2f79f88e572caf80759a1262c67d039c7c0ac530/data/red_box.glb
--------------------------------------------------------------------------------
/data/textured_box.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/3d-tiles-samples-generator/2f79f88e572caf80759a1262c67d039c7c0ac530/data/textured_box.glb
--------------------------------------------------------------------------------
/data/textured_box_separate/bricks.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/3d-tiles-samples-generator/2f79f88e572caf80759a1262c67d039c7c0ac530/data/textured_box_separate/bricks.jpg
--------------------------------------------------------------------------------
/data/textured_box_separate/textured_box.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/3d-tiles-samples-generator/2f79f88e572caf80759a1262c67d039c7c0ac530/data/textured_box_separate/textured_box.glb
--------------------------------------------------------------------------------
/data/tree.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/3d-tiles-samples-generator/2f79f88e572caf80759a1262c67d039c7c0ac530/data/tree.glb
--------------------------------------------------------------------------------
/data/tree_billboard.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/3d-tiles-samples-generator/2f79f88e572caf80759a1262c67d039c7c0ac530/data/tree_billboard.glb
--------------------------------------------------------------------------------
/data/wood_red.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CesiumGS/3d-tiles-samples-generator/2f79f88e572caf80759a1262c67d039c7c0ac530/data/wood_red.jpg
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Promise = require('bluebird');
4 | const Cesium = require('cesium');
5 | const child_process = require('child_process');
6 | const gulp = require('gulp');
7 | const Jasmine = require('jasmine');
8 | const SpecReporter = require('jasmine-spec-reporter').SpecReporter;
9 | const path = require('path');
10 | const yargs = require('yargs');
11 | const fsExtra = require('fs-extra');
12 | const open = require('open');
13 |
14 | const defaultValue = Cesium.defaultValue;
15 | const defined = Cesium.defined;
16 | const argv = yargs.argv;
17 |
18 | // Add third-party node module binaries to the system path
19 | // since some tasks need to call them directly.
20 | const environmentSeparator = (process.platform === 'win32') ? ';' : ':';
21 | const nodeBinaries = path.join(__dirname, 'node_modules', '.bin');
22 | process.env.PATH += environmentSeparator + nodeBinaries;
23 |
24 | const specFiles = ['**/*.js', '!node_modules/**'];
25 |
26 | gulp.task('test', function (done) {
27 | const jasmine = new Jasmine();
28 | jasmine.loadConfigFile('specs/jasmine.json');
29 | jasmine.addReporter(new SpecReporter({
30 | displaySuccessfulSpec: !defined(argv.suppressPassed) || !argv.suppressPassed
31 | }));
32 | jasmine.execute();
33 | jasmine.onComplete(function (passed) {
34 | done(argv.failTaskOnError && !passed ? 1 : 0);
35 | });
36 | });
37 |
38 | gulp.task('test-watch', function () {
39 | gulp.watch(specFiles).on('change', function () {
40 | // We can't simply depend on the test task because Jasmine
41 | // does not like being run multiple times in the same process.
42 | try {
43 | child_process.execSync('jasmine JASMINE_CONFIG_PATH=specs/jasmine.json', {
44 | stdio: [process.stdin, process.stdout, process.stderr]
45 | });
46 | } catch (exception) {
47 | console.log('Tests failed to execute.');
48 | }
49 | });
50 | });
51 |
52 | gulp.task('coverage', function () {
53 | fsExtra.removeSync('coverage');
54 | child_process.execSync('nyc' +
55 | ' --all' +
56 | ' --reporter=lcov' +
57 | ' --dir coverage' +
58 | ' -x "specs/**"' +
59 | ' -x "coverage/**"' +
60 | ' -x "gulpfile.js"' +
61 | ' -x "index.js"' +
62 | ' node_modules/jasmine/bin/jasmine.js' +
63 | ' JASMINE_CONFIG_PATH=specs/jasmine.json', {
64 | stdio: [process.stdin, process.stdout, process.stderr]
65 | });
66 | open('coverage/lcov-report/index.html');
67 | });
68 |
69 | gulp.task('cloc', function() {
70 | let cmdLine;
71 | const clocPath = path.join('node_modules', 'cloc', 'lib', 'cloc');
72 |
73 | //Run cloc on primary Source files only
74 | const source = new Promise(function(resolve, reject) {
75 | cmdLine = `perl ${ clocPath } --quiet --progress-rate=0` +
76 | ` lib/ bin/`;
77 |
78 | child_process.exec(cmdLine, function(error, stdout, stderr) {
79 | if (error) {
80 | console.log(stderr);
81 | return reject(error);
82 | }
83 | console.log('Source:');
84 | console.log(stdout);
85 | resolve();
86 | });
87 | });
88 |
89 | //If running cloc on source succeeded, also run it on the tests.
90 | return source.then(function() {
91 | return new Promise(function(resolve, reject) {
92 | cmdLine = `perl ${ clocPath } --quiet --progress-rate=0` +
93 | ` test/lib/ test/bin/`;
94 | child_process.exec(cmdLine, function(error, stdout, stderr) {
95 | if (error) {
96 | console.log(stderr);
97 | return reject(error);
98 | }
99 | console.log('Specs:');
100 | console.log(stdout);
101 | resolve();
102 | });
103 | });
104 | });
105 | });
106 |
107 | function getLicenseDataFromPackage(packageName, override) {
108 | override = defaultValue(override, defaultValue.EMPTY_OBJECT);
109 | const packagePath = path.join('node_modules', packageName, 'package.json');
110 |
111 | if (!fsExtra.existsSync(packagePath)) {
112 | throw new Error(`Unable to find ${packageName} license information`);
113 | }
114 |
115 | const contents = fsExtra.readFileSync(packagePath);
116 | const packageJson = JSON.parse(contents);
117 |
118 | let licenseField = override.license;
119 |
120 | if (!licenseField) {
121 | licenseField = [packageJson.license];
122 | }
123 |
124 | if (!licenseField && packageJson.licenses) {
125 | licenseField = packageJson.licenses;
126 | }
127 |
128 | if (!licenseField) {
129 | console.log(`No license found for ${packageName}`);
130 | licenseField = ['NONE'];
131 | }
132 |
133 | let version = packageJson.version;
134 | if (!packageJson.version) {
135 | console.log(`No version information found for ${packageName}`);
136 | version = 'NONE';
137 | }
138 |
139 | return {
140 | name: packageName,
141 | license: licenseField,
142 | version: version,
143 | url: `https://www.npmjs.com/package/${packageName}`,
144 | notes: override.notes
145 | };
146 | }
147 |
148 | function readThirdPartyExtraJson() {
149 | const path = 'ThirdParty.extra.json';
150 | if (fsExtra.existsSync(path)) {
151 | const contents = fsExtra.readFileSync(path);
152 | return JSON.parse(contents);
153 | }
154 | return [];
155 | }
156 |
157 | gulp.task('generate-third-party', async function() {
158 | const packageJson = JSON.parse(fsExtra.readFileSync('package.json'));
159 | const thirdPartyExtraJson = readThirdPartyExtraJson();
160 |
161 | const thirdPartyJson = [];
162 |
163 | const dependencies = packageJson.dependencies;
164 | for (const packageName in dependencies) {
165 | if (dependencies.hasOwnProperty(packageName)) {
166 | const override = thirdPartyExtraJson.find(
167 | (entry) => entry.name === packageName
168 | );
169 | thirdPartyJson.push(
170 | getLicenseDataFromPackage(packageName, override)
171 | );
172 | }
173 | }
174 |
175 | thirdPartyJson.sort(function (a, b) {
176 | const nameA = a.name.toLowerCase();
177 | const nameB = b.name.toLowerCase();
178 | if (nameA < nameB) {
179 | return -1;
180 | }
181 | if (nameA > nameB) {
182 | return 1;
183 | }
184 | return 0;
185 | });
186 |
187 | fsExtra.writeFileSync(
188 | 'ThirdParty.json',
189 | JSON.stringify(thirdPartyJson, null, 2)
190 | );
191 | });
192 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | };
4 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | preset: 'ts-jest',
4 | clearMocks: true,
5 | coverageDirectory: 'coverage',
6 | testEnvironment: 'node',
7 | collectCoverageFrom: ['bin/**/*.ts', 'lib/**/*.ts', 'lib/**/*.js'],
8 | rootDir: '.',
9 | testMatch: [
10 | '/specs/**/__tests__/**/*.[jt]s?(x)',
11 | '/test/**/*(*.)@(spec|test).[tj]s?(x)'
12 | ],
13 | globals: {
14 | 'ts-jest': {
15 | diagnostics: false,
16 | isolatedModules: true
17 | }
18 | }
19 | };
20 |
--------------------------------------------------------------------------------
/lib/Extensions.ts:
--------------------------------------------------------------------------------
1 | import { TilesetJson } from './tilesetJson';
2 |
3 | export namespace Extensions {
4 |
5 | export type ExtensionObject = Pick
6 | /**
7 | * Add an extension to the list of extensions used for a tileset JSON.
8 | * @param {Object} tilesetJson The root tileset JSON object to which to add
9 | * the extension.
10 | * @param {String} extensionName The name of the extension to add.
11 | */
12 | export function addExtensionsUsed(
13 | tilesetJson: TilesetJson,
14 | extensionName: string
15 | ) {
16 | if (tilesetJson.extensionsUsed == null) {
17 | tilesetJson.extensionsUsed = [];
18 | }
19 |
20 | tilesetJson.extensionsUsed.push(extensionName);
21 | }
22 |
23 | /**
24 | * Add an extension to the list of extensions required for a tileset JSON.
25 | * @param {Object} tilesetJson The root tileset JSON object to which to
26 | * add the extension.
27 | * @param {String} extensionName The name of the extension to add.
28 | */
29 | export function addExtensionsRequired(
30 | tilesetJson: TilesetJson,
31 | extensionName: string
32 | ) {
33 | if (tilesetJson.extensionsRequired == null) {
34 | tilesetJson.extensionsRequired = [];
35 | }
36 |
37 | tilesetJson.extensionsRequired.push(extensionName);
38 | }
39 |
40 | /**
41 | * Add an extension to the extensions dictionary object for a JSON object.
42 | * @param {Object} tilesetJson The JSON object to which to add the extension.
43 | * @param {String} extensionName The name of the extension to add.
44 | * @param {*} extension The contents of the extension.
45 | */
46 | export function addExtension(
47 | tilesetJson: TilesetJson,
48 | extensionName: string,
49 | extension: object
50 | ) {
51 | if (tilesetJson.extensions == null) {
52 | tilesetJson.extensions = {};
53 | }
54 |
55 | tilesetJson.extensions[extensionName] = extension;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/lib/Material.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * A material that is applied to a mesh.
3 | *
4 | * @param {Object} [options] An object with the following properties:
5 | * @param {Array|String} [options.baseColor] The base color or base color texture path.
6 | *
7 | * @constructor
8 | */
9 |
10 | export class Material {
11 | baseColor: number[];
12 |
13 | // TODO: Original code combined rgbas with jpg uris, should refactor
14 | // this too.
15 | constructor(baseColor: number[] = [0.5, 0.5, 0.5, 1.0]) {
16 | this.baseColor = baseColor;
17 | }
18 |
19 | /**
20 | * Creates a Material from a glTF material. This utility is designed only for simple glTFs like those in the data folder.
21 | *
22 | * @param {Object} material The glTF material.
23 | * @returns {Material} The material.
24 | */
25 |
26 | static fromGltf(material: any): Material {
27 | return new Material(material.pbrMetallicRoughness.baseColorFactor);
28 | }
29 | }
30 |
31 | export class TexturedMaterial {
32 | // TODO: This MUST be named baseColor for now. Original version of this
33 | // code didn't discriminate between RGBA / TexturePath coordinates
34 | // createGltf.js will inspect the type of `baseColor` to determine
35 | // what to do with this object. Needs to be refactored later.
36 | baseColor: string;
37 |
38 | constructor(baseColor: string) {
39 | this.baseColor = baseColor;
40 | }
41 | }
--------------------------------------------------------------------------------
/lib/arguments.ts:
--------------------------------------------------------------------------------
1 | export interface GeneratorArgs {
2 | use3dTilesNext: boolean;
3 | useGlb: boolean;
4 | gltfConversionOptions: { resourceDirectory: string };
5 | prettyJson: boolean;
6 | gzip: boolean;
7 | geometricError: number;
8 | versionNumber: string;
9 | }
10 |
--------------------------------------------------------------------------------
/lib/atLeastN.ts:
--------------------------------------------------------------------------------
1 | // https://stackoverflow.com/a/59003541
2 | export type AtLeastOne = { [K in keyof T]: { [K2 in K]: T[K] } }[keyof T] &
3 | Partial;
4 |
--------------------------------------------------------------------------------
/lib/attribute.ts:
--------------------------------------------------------------------------------
1 | import { GltfType } from './gltfType';
2 |
3 | export interface Attribute {
4 | buffer: Buffer;
5 | propertyName: string;
6 | byteOffset?: number;
7 | byteAlignment?: number;
8 | componentType: GLenum;
9 | count: number;
10 | max: number[];
11 | min: number[];
12 | type: GltfType;
13 | }
14 |
--------------------------------------------------------------------------------
/lib/bufferUtil.ts:
--------------------------------------------------------------------------------
1 | import { FLOAT32_SIZE_BYTES, UINT16_SIZE_BYTES } from './typeSize';
2 |
3 | export function bufferToUint16Array(
4 | buffer: Buffer,
5 | byteOffset: number,
6 | length: number
7 | ) {
8 | const uint16Array = new Uint16Array(length);
9 | for (let i = 0; i < length; ++i) {
10 | uint16Array[i] = buffer.readUInt16LE(
11 | byteOffset + i * UINT16_SIZE_BYTES
12 | );
13 | }
14 | return uint16Array;
15 | }
16 |
17 | export function bufferToFloat32Array(
18 | buffer: Buffer,
19 | byteOffset: number,
20 | length: number
21 | ) {
22 | const float32Array = new Float32Array(length);
23 | for (let i = 0; i < length; ++i) {
24 | float32Array[i] = buffer.readFloatLE(
25 | byteOffset + i * FLOAT32_SIZE_BYTES
26 | );
27 | }
28 | return float32Array;
29 | }
30 |
--------------------------------------------------------------------------------
/lib/calculateBufferPadding.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Typed version of `getBufferPadded.js
3 | * @param {Buffer} buffer The buffer.
4 | * @param {Number} [byteOffset=0] The byte offset on which the buffer starts.
5 | * @returns {Buffer} The padded buffer.
6 | * @todo Delete `getBufferPadded.js` once all its callers are converted to
7 | * .ts
8 | */
9 |
10 | export function calculateBufferPadding(buffer: Buffer, byteOffset = 0): Buffer {
11 | var boundary = 8;
12 | var byteLength = buffer.length;
13 | var remainder = (byteOffset + byteLength) % boundary;
14 | var padding = (remainder === 0) ? 0 : boundary - remainder;
15 | var emptyBuffer = Buffer.alloc(padding);
16 | return Buffer.concat([buffer, emptyBuffer]);
17 | }
18 |
--------------------------------------------------------------------------------
/lib/calculateFilenameExt.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Calculates the filename extension suffix based off flags
3 | * @param {Boolean} use3dTilesNext If the extension is for use3dTilesNext
4 | * @param {Boolean} useGlb If the extension is for 3d-tiles-next, and we want
5 | * to use glb.
6 | * @param {String} defaultExt If use3dTilesNext and useGlb are both false,
7 | * then use defaultExt
8 | */
9 | export function calculateFilenameExt(
10 | use3dTilesNext: boolean,
11 | useGlb: boolean,
12 | defaultExt: string
13 | ) {
14 | if (use3dTilesNext && !useGlb) {
15 | return '.gltf';
16 | } else if (useGlb) {
17 | return '.glb';
18 | }
19 | return defaultExt;
20 | }
21 |
--------------------------------------------------------------------------------
/lib/calculateMinMax.ts:
--------------------------------------------------------------------------------
1 | export type MinMax = { min: number[]; max: number[] };
2 |
3 | /**
4 | * Type safe version of `getMinMax.js`
5 | * @param array Elements to check
6 | * @param components Size of each element in the array (1,2,3,4)
7 | * @param start where to begin iteration in the array (inclusive)
8 | * @param length Total number of elements to check
9 | * @todo Delete getMinMax.js once its callers are converted to .ts
10 | */
11 |
12 | export function calculateMinMax(
13 | array: number[],
14 | components: number,
15 | start: number = 0,
16 | length = array.length
17 | ): MinMax {
18 | const min = new Array(components).fill(+Infinity);
19 | const max = new Array(components).fill(-Infinity);
20 | const count = length / components;
21 | for (let i = 0; i < count; ++i) {
22 | for (let j = 0; j < components; ++j) {
23 | const index = start + i * components + j;
24 | const value = array[index];
25 | min[j] = Math.min(min[j], value);
26 | max[j] = Math.max(max[j], value);
27 | }
28 | }
29 |
30 | return { min: min, max: max };
31 | }
32 |
--------------------------------------------------------------------------------
/lib/colorTypes.ts:
--------------------------------------------------------------------------------
1 | export enum BaseColorType {
2 | White,
3 | Color,
4 | Texture
5 | }
6 |
7 | export enum TranslucencyType {
8 | Opaque,
9 | Translucent,
10 | Mix
11 | }
--------------------------------------------------------------------------------
/lib/compositeSamplesNext.ts:
--------------------------------------------------------------------------------
1 | import { GeneratorArgs } from './arguments';
2 | import { toCamelCase } from './utility';
3 | import { getTilesetOpts } from './tilesetJson';
4 | import { InstanceTileUtils } from './instanceUtilsNext';
5 | import { addBinaryBuffers } from './gltfUtil';
6 | import { getGltfFromGlbUri } from './gltfFromUri';
7 | import { instancesRegion } from './constants';
8 | import { FeatureTableUtils } from './featureMetatableUtilsNext';
9 | import { createBuildings } from './createBuilding';
10 | import { Mesh } from './Mesh';
11 | import { addBatchedMeshToGltf } from './addMeshToGltf';
12 | import { generateBuildingBatchTable } from './createBuildingsTile';
13 | import { createEXTMeshInstancingExtension } from './createEXTMeshInstancing';
14 | import { FeatureMetadata } from './featureMetadata';
15 | import { createTilesetJsonSingle } from './createTilesetJsonSingle';
16 | import { writeTilesetAndTile } from './ioUtil';
17 | import path = require('path');
18 | import getProperties = require('./getProperties');
19 |
20 | export namespace CompositeSamplesNext {
21 | export async function createComposite(args: GeneratorArgs) {
22 | // i3dm
23 | const opts = InstanceTileUtils.getDefaultTileOptions(
24 | 'output/Composite'
25 | );
26 |
27 | const i3dmHeights = new Array(opts.instancesLength).fill(
28 | opts.modelSize
29 | );
30 |
31 | const gltf = await getGltfFromGlbUri(
32 | opts.instancesUri,
33 | args.gltfConversionOptions
34 | );
35 |
36 | const positions = InstanceTileUtils.getPositions(
37 | opts.instancesLength,
38 | opts.tileWidth,
39 | opts.modelSize,
40 | opts.transform
41 | );
42 |
43 | // add EXT_mesh_gpu_instancing extension to i3dm mesh
44 | const positionAccessorIndex = gltf.accessors.length;
45 | addBinaryBuffers(gltf, positions);
46 | createEXTMeshInstancingExtension(gltf, gltf.nodes[0], {
47 | attributes: {
48 | TRANSLATION: positionAccessorIndex
49 | }
50 | });
51 |
52 | // add CESIUM_3dtiles_metadata_feature extension to i3dm mesh
53 | const prim = gltf.meshes[0].primitives[0];
54 | FeatureMetadata.updateExtensionUsed(gltf);
55 | FeatureMetadata.addFeatureLayer(prim, {
56 | featureTable: 0,
57 | instanceStride: 1,
58 | vertexAttribute: {
59 | implicit: {
60 | increment: 0,
61 | start: 0
62 | }
63 | }
64 | });
65 |
66 | FeatureMetadata.addFeatureTable(gltf, {
67 | featureCount: opts.instancesLength,
68 | properties: {
69 | Height: {
70 | values: i3dmHeights
71 | }
72 | }
73 | }
74 | );
75 |
76 | // b3dm
77 | const transform = FeatureTableUtils.getDefaultTransform();
78 | const buildingOptions = FeatureTableUtils.getDefaultBuildingGenerationOptions();
79 | const buildings = createBuildings(buildingOptions);
80 | const batchedMesh = Mesh.batch(
81 | FeatureTableUtils.createMeshes(transform, buildings, false)
82 | );
83 |
84 | addBatchedMeshToGltf(gltf, batchedMesh);
85 |
86 | const rtc = batchedMesh.center;
87 | const buildingTable = generateBuildingBatchTable(buildings);
88 | // explicit ids are unnecessary for the feature_metadata extension
89 | // but required for legacy b3dm
90 | delete buildingTable.id;
91 |
92 | // add CESIUM_3dtiles_feature_metadata extension
93 | gltf.extensions.CESIUM_3dtiles_feature_metadata.featureTables.push({
94 | featureCount: buildingTable.Height.length,
95 | properties: {
96 | Height: { values: buildingTable.Height },
97 | Longitude: { values: buildingTable.Longitude },
98 | Latitude: { values: buildingTable.Latitude }
99 | }
100 | });
101 |
102 | // setup the primitives.extension
103 | gltf.meshes[1].primitives[0].extensions = {
104 | CESIUM_3dtiles_feature_metadata: {
105 | featureLayers: [
106 | {
107 | featureTable: 1,
108 | instanceStride: 1,
109 | vertexAttribute: {
110 | implicit: {
111 | increment: 1,
112 | start: 0
113 | }
114 | }
115 | }
116 | ]
117 | }
118 | };
119 |
120 | // override nodes to use rtcCenter for the b3dm mesh
121 | gltf.nodes[1] = {
122 | name: 'RTC_CENTER',
123 | mesh: gltf.nodes[1].mesh!,
124 | translation: [rtc.x, rtc.y, rtc.z]
125 | };
126 |
127 | //
128 | // common
129 | //
130 |
131 | gltf.scenes[0].nodes.push(1);
132 |
133 | const outputFolder = 'Composite';
134 | const ext = args.useGlb ? '.glb' : '.gltf';
135 | const tileFilename = toCamelCase(outputFolder) + ext;
136 | const rootDir = 'output/Composite';
137 | const fullPath = path.join(rootDir, outputFolder);
138 | const tilesetOpts = getTilesetOpts(
139 | tileFilename,
140 | args.geometricError,
141 | args.versionNumber,
142 | instancesRegion
143 | );
144 |
145 | const compositeFeatureTable = {
146 | Longitude: buildingTable.Longitude,
147 | Latitude: buildingTable.Latitude,
148 | Height: [...i3dmHeights, ...buildingTable.Height]
149 | };
150 |
151 | tilesetOpts.properties = getProperties(compositeFeatureTable);
152 | let tilesetJson = createTilesetJsonSingle(tilesetOpts);
153 |
154 | await writeTilesetAndTile(
155 | fullPath,
156 | tileFilename,
157 | tilesetJson,
158 | gltf,
159 | args
160 | );
161 | }
162 |
163 | export async function createCompositeOfInstanced(args: GeneratorArgs) {
164 | const opts = InstanceTileUtils.getDefaultTileOptions(
165 | 'output/Composite'
166 | );
167 |
168 | const i3dmHeights = new Array(opts.instancesLength).fill(
169 | opts.modelSize
170 | );
171 |
172 | const gltf = await getGltfFromGlbUri(
173 | opts.instancesUri,
174 | args.gltfConversionOptions
175 | );
176 |
177 | const positions1 = InstanceTileUtils.getPositions(
178 | opts.instancesLength,
179 | opts.tileWidth,
180 | opts.modelSize,
181 | opts.transform
182 | );
183 |
184 | const positions2 = InstanceTileUtils.getPositions(
185 | opts.instancesLength,
186 | opts.tileWidth,
187 | opts.modelSize,
188 | opts.transform
189 | );
190 |
191 | const positionAccessorIndex = gltf.accessors.length;
192 | addBinaryBuffers(gltf, positions1, positions2);
193 | createEXTMeshInstancingExtension(gltf, gltf.nodes[0], {
194 | attributes: {
195 | TRANSLATION: positionAccessorIndex
196 | }
197 | });
198 |
199 | // add CESIUM_3dtiles_metadata_feature extension to i3dm mesh
200 | const prim = gltf.meshes[0].primitives[0];
201 | FeatureMetadata.updateExtensionUsed(gltf);
202 | FeatureMetadata.addFeatureLayer(prim, {
203 | featureTable: 0,
204 | instanceStride: 1,
205 | vertexAttribute: {
206 | implicit: {
207 | increment: 0,
208 | start: 0
209 | }
210 | }
211 | });
212 |
213 | FeatureMetadata.addFeatureTable(gltf, {
214 | featureCount: opts.instancesLength,
215 | properties: {
216 | Height: {
217 | values: i3dmHeights
218 | }
219 | }
220 | });
221 |
222 | const ext = args.useGlb ? '.glb' : '.gltf';
223 | const outputFolder = 'CompositeOfInstanced';
224 | const tileFilename = toCamelCase(outputFolder) + ext;
225 | const fullPath = path.join(opts.rootDir, outputFolder);
226 |
227 | const tilesetOpts = getTilesetOpts(
228 | tileFilename,
229 | args.geometricError,
230 | args.versionNumber,
231 | instancesRegion
232 | );
233 |
234 | tilesetOpts.properties = getProperties({
235 | Height: i3dmHeights
236 | });
237 | let tilesetJson = createTilesetJsonSingle(tilesetOpts);
238 |
239 | await writeTilesetAndTile(
240 | fullPath,
241 | tileFilename,
242 | tilesetJson,
243 | gltf,
244 | args
245 | );
246 | }
247 | }
248 |
--------------------------------------------------------------------------------
/lib/constants.ts:
--------------------------------------------------------------------------------
1 | const Cesium = require('cesium');
2 | const clone = Cesium.clone;
3 | const path = require('path');
4 | import { BaseColorType, TranslucencyType } from './colorTypes';
5 | import { Building } from './createBuilding';
6 |
7 | export const gltfConversionOptions = { resourceDirectory: path.join(__dirname, '../') };
8 | export const util = require('../lib/utility');
9 | export const wgs84Transform = util.wgs84Transform;
10 | export const metersToLongitude = util.metersToLongitude;
11 | export const metersToLatitude = util.metersToLatitude;
12 |
13 | export const prettyJson = true;
14 | export const gzip = false;
15 |
16 | export const outputDirectory = 'output';
17 |
18 | export const longitude = -1.31968;
19 | export const latitude = 0.698874;
20 | export const tileWidth = 200.0;
21 |
22 | export const longitudeExtent = metersToLongitude(tileWidth, latitude);
23 | export const latitudeExtent = metersToLatitude(tileWidth);
24 |
25 | export const west = longitude - longitudeExtent / 2.0;
26 | export const south = latitude - latitudeExtent / 2.0;
27 | export const east = longitude + longitudeExtent / 2.0;
28 | export const north = latitude + latitudeExtent / 2.0;
29 |
30 | export type BuildingTemplate = {
31 | uniform: boolean;
32 | numberOfBuildings: number;
33 | tileWidth: number;
34 | averageWidth: number;
35 | averageHeight: number;
36 | baseColorType: BaseColorType;
37 | translucencyType: TranslucencyType;
38 | longitude: number;
39 | latitude: number;
40 | };
41 |
42 | export const buildingTemplate = {
43 | numberOfBuildings: 10,
44 | tileWidth: tileWidth,
45 | averageWidth: 8.0,
46 | averageHeight: 10.0,
47 | baseColorType: BaseColorType.White,
48 | translucencyType: TranslucencyType.Opaque,
49 | longitude: longitude,
50 | latitude: latitude
51 | };
52 |
53 | // height is 0.0 because the base of building models is at the origin
54 | export const buildingsTransform = wgs84Transform(longitude, latitude, 0.0);
55 | export const buildingsCenter = [
56 | buildingsTransform[12],
57 | buildingsTransform[13],
58 | buildingsTransform[14]
59 | ];
60 |
61 | // Small buildings
62 | export const smallGeometricError = 70.0; // Estimated
63 | export const smallHeight = 20.0; // Estimated
64 | export const smallRegion = [west, south, east, north, 0.0, smallHeight];
65 | export const smallRadius = tileWidth * 0.707107;
66 | export const smallSphere = [
67 | buildingsCenter[0],
68 | buildingsCenter[1],
69 | buildingsCenter[2] + smallHeight / 2.0,
70 | smallRadius
71 | ];
72 | export const smallSphereLocal = [0.0, 0.0, smallHeight / 2.0, smallRadius];
73 | export const smallBoxLocal = [
74 | 0.0,
75 | 0.0,
76 | smallHeight / 2.0, // center
77 | tileWidth / 2.0,
78 | 0.0,
79 | 0.0, // width
80 | 0.0,
81 | tileWidth / 2.0,
82 | 0.0, // depth
83 | 0.0,
84 | 0.0,
85 | smallHeight / 2.0 // height
86 | ];
87 |
88 | // Large buildings
89 | export const largeGeometricError = 240.0; // Estimated
90 | export const largeHeight = 88.0; // Estimated
91 |
92 | // Point cloud
93 | export const pointsLength = 1000;
94 | export const pointCloudTileWidth = 10.0;
95 | export const pointCloudRadius = pointCloudTileWidth / 2.0;
96 | export const pointCloudTransform = wgs84Transform(
97 | longitude,
98 | latitude,
99 | pointCloudRadius
100 | );
101 | export const pointCloudGeometricError = 1.732 * pointCloudTileWidth; // Diagonal of the point cloud box
102 | export const pointCloudCenter = [
103 | pointCloudTransform[12],
104 | pointCloudTransform[13],
105 | pointCloudTransform[14]
106 | ];
107 | export const pointCloudSphere = [
108 | pointCloudCenter[0],
109 | pointCloudCenter[1],
110 | pointCloudCenter[2],
111 | pointCloudRadius
112 | ];
113 | export const pointCloudSphereLocal = [0.0, 0.0, 0.0, pointCloudRadius];
114 |
115 | // Instances
116 | export const instancesLength = 25;
117 | export const instancesGeometricError = 70.0; // Estimated
118 | export const instancesTileWidth = tileWidth;
119 | export const instancesUri = 'data/box.glb'; // Model's center is at the origin (and for below)
120 | export const instancesRedUri = 'data/red_box.glb';
121 | export const instancesTexturedUri = 'data/textured_box.glb';
122 | export const instancesAnimatedUri = 'data/animated_box.glb';
123 | export const instancesModelSize = 20.0;
124 | export const instancesHeight = instancesModelSize + 10.0; // Just a little extra padding at the top for aiding Cesium tests
125 | export const instancesTransform = wgs84Transform(
126 | longitude,
127 | latitude,
128 | instancesModelSize / 2.0
129 | );
130 | export const instancesRegion = [west, south, east, north, 0.0, instancesHeight];
131 | export const instancesBoxLocal = [
132 | 0.0,
133 | 0.0,
134 | 0.0, // center
135 | instancesTileWidth / 2.0,
136 | 0.0,
137 | 0.0, // width
138 | 0.0,
139 | instancesTileWidth / 2.0,
140 | 0.0, // depth
141 | 0.0,
142 | 0.0,
143 | instancesHeight / 2.0 // height
144 | ];
145 |
146 | // Composite
147 | export const compositeRegion = instancesRegion;
148 | export const compositeGeometricError = instancesGeometricError;
149 |
150 | // City Tileset
151 | export const parentRegion = [
152 | longitude - longitudeExtent,
153 | latitude - latitudeExtent,
154 | longitude + longitudeExtent,
155 | latitude + latitudeExtent,
156 | 0.0,
157 | largeHeight
158 | ];
159 | export const parentContentRegion = [
160 | longitude - longitudeExtent / 2.0,
161 | latitude - latitudeExtent / 2.0,
162 | longitude + longitudeExtent / 2.0,
163 | latitude + latitudeExtent / 2.0,
164 | 0.0,
165 | largeHeight
166 | ];
167 | export const parentOptions = clone(buildingTemplate);
168 | parentOptions.averageWidth = 20.0;
169 | parentOptions.averageHeight = 82.0;
170 | parentOptions.longitude = longitude;
171 | parentOptions.latitude = latitude;
172 |
173 | export type TileOptions = {
174 | buildingOptions: BuildingTemplate,
175 | createBatchTable: boolean,
176 | transform: object;
177 | relativeToCenter: boolean;
178 | }
179 |
180 | export const parentTileOptions: TileOptions = {
181 | buildingOptions: parentOptions,
182 | createBatchTable: true,
183 | transform: buildingsTransform,
184 | relativeToCenter: true
185 | };
186 |
187 | export const childrenRegion = [
188 | longitude - longitudeExtent,
189 | latitude - latitudeExtent,
190 | longitude + longitudeExtent,
191 | latitude + latitudeExtent,
192 | 0.0,
193 | smallHeight
194 | ];
195 |
196 | export const llRegion = [
197 | longitude - longitudeExtent,
198 | latitude - latitudeExtent,
199 | longitude,
200 | latitude,
201 | 0.0,
202 | smallHeight
203 | ];
204 | export const llLongitude = longitude - longitudeExtent / 2.0;
205 | export const llLatitude = latitude - latitudeExtent / 2.0;
206 | export const llTransform = wgs84Transform(llLongitude, llLatitude);
207 | export const llOptions = clone(buildingTemplate);
208 | llOptions.longitude = llLongitude;
209 | llOptions.latitude = llLatitude;
210 | llOptions.seed = 0;
211 | export const llTileOptions = {
212 | buildingOptions: llOptions,
213 | createBatchTable: true,
214 | transform: llTransform,
215 | relativeToCenter: true
216 | };
217 |
218 | export const lrRegion = [
219 | longitude,
220 | latitude - latitudeExtent,
221 | longitude + longitudeExtent,
222 | latitude,
223 | 0.0,
224 | smallHeight
225 | ];
226 | export const lrLongitude = longitude + longitudeExtent / 2.0;
227 | export const lrLatitude = latitude - latitudeExtent / 2.0;
228 | export const lrTransform = wgs84Transform(lrLongitude, lrLatitude);
229 | export const lrOptions = clone(buildingTemplate);
230 | lrOptions.longitude = lrLongitude;
231 | lrOptions.latitude = lrLatitude;
232 | lrOptions.seed = 1;
233 | export const lrTileOptions = {
234 | buildingOptions: lrOptions,
235 | createBatchTable: true,
236 | transform: lrTransform,
237 | relativeToCenter: true
238 | };
239 |
240 | export const urRegion = [
241 | longitude,
242 | latitude,
243 | longitude + longitudeExtent,
244 | latitude + latitudeExtent,
245 | 0.0,
246 | smallHeight
247 | ];
248 | export const urLongitude = longitude + longitudeExtent / 2.0;
249 | export const urLatitude = latitude + latitudeExtent / 2.0;
250 | export const urTransform = wgs84Transform(urLongitude, urLatitude);
251 | export const urOptions = clone(buildingTemplate);
252 | urOptions.longitude = urLongitude;
253 | urOptions.latitude = urLatitude;
254 | urOptions.seed = 2;
255 | export const urTileOptions = {
256 | buildingOptions: urOptions,
257 | createBatchTable: true,
258 | transform: urTransform,
259 | relativeToCenter: true
260 | };
261 |
262 | export const ulRegion = [
263 | longitude - longitudeExtent,
264 | latitude,
265 | longitude,
266 | latitude + latitudeExtent,
267 | 0.0,
268 | smallHeight
269 | ];
270 | export const ulLongitude = longitude - longitudeExtent / 2.0;
271 | export const ulLatitude = latitude + latitudeExtent / 2.0;
272 | export const ulTransform = wgs84Transform(ulLongitude, ulLatitude);
273 | export const ulOptions = clone(buildingTemplate);
274 | ulOptions.longitude = ulLongitude;
275 | ulOptions.latitude = ulLatitude;
276 | ulOptions.seed = 3;
277 | export const ulTileOptions = {
278 | buildingOptions: ulOptions,
279 | createBatchTable: true,
280 | transform: ulTransform,
281 | relativeToCenter: true
282 | };
283 |
284 |
285 |
286 | // Models are z-up, so add a z-up to y-up transform.
287 | // The glTF spec defines the y-axis as up, so this is the default behavior.
288 | // In CesiumJS a y-up to z-up transform is applied later so that the glTF and
289 | // 3D Tiles coordinate systems are consistent
290 | export const rootMatrix = [1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1];
291 |
292 |
293 | // 3d-tiles-next
294 | export const tilesNextTilesetJsonVersion = '1.1';
--------------------------------------------------------------------------------
/lib/createB3dm.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const Cesium = require('cesium');
3 | const getBufferPadded = require('./getBufferPadded');
4 | const getJsonBufferPadded = require('./getJsonBufferPadded');
5 |
6 | const defaultValue = Cesium.defaultValue;
7 |
8 | module.exports = createB3dm;
9 |
10 | /**
11 | * Create a Batched 3D Model (b3dm) tile from a binary glTF and per-feature metadata.
12 | *
13 | * @param {Object} options An object with the following properties:
14 | * @param {Buffer} options.glb The binary glTF buffer.
15 | * @param {Object} [options.featureTableJson] Feature table JSON.
16 | * @param {Buffer} [options.featureTableBinary] Feature table binary.
17 | * @param {Object} [options.batchTableJson] Batch table describing the per-feature metadata.
18 | * @param {Buffer} [options.batchTableBinary] The batch table binary.
19 | * @param {Boolean} [options.deprecated1=false] Save the b3dm with the deprecated 20-byte header.
20 | * @param {Boolean} [options.deprecated2=false] Save the b3dm with the deprecated 24-byte header.
21 | * @returns {Buffer} The generated b3dm tile buffer.
22 | */
23 | function createB3dm(options) {
24 | const glb = options.glb;
25 | const defaultFeatureTable = {
26 | BATCH_LENGTH : 0
27 | };
28 | const featureTableJson = defaultValue(options.featureTableJson, defaultFeatureTable);
29 | const batchLength = featureTableJson.BATCH_LENGTH;
30 |
31 | const headerByteLength = 28;
32 | const featureTableJsonBuffer = getJsonBufferPadded(featureTableJson, headerByteLength);
33 | const featureTableBinary = getBufferPadded(options.featureTableBinary);
34 | const batchTableJsonBuffer = getJsonBufferPadded(options.batchTableJson);
35 | const batchTableBinary = getBufferPadded(options.batchTableBinary);
36 |
37 | const deprecated1 = defaultValue(options.deprecated1, false);
38 | const deprecated2 = defaultValue(options.deprecated2, false);
39 |
40 | if (deprecated1) {
41 | return createB3dmDeprecated1(glb, batchLength, batchTableJsonBuffer);
42 | } else if (deprecated2) {
43 | return createB3dmDeprecated2(glb, batchLength, batchTableJsonBuffer, batchTableBinary);
44 | }
45 |
46 | return createB3dmCurrent(glb, featureTableJsonBuffer, featureTableBinary, batchTableJsonBuffer, batchTableBinary);
47 | }
48 |
49 | function createB3dmCurrent(glb, featureTableJson, featureTableBinary, batchTableJson, batchTableBinary) {
50 |
51 | const glbPadded = getBufferPadded(glb);
52 |
53 | const version = 1;
54 | const headerByteLength = 28;
55 | const featureTableJsonByteLength = featureTableJson.length;
56 | const featureTableBinaryByteLength = featureTableBinary.length;
57 | const batchTableJsonByteLength = batchTableJson.length;
58 | const batchTableBinaryByteLength = batchTableBinary.length;
59 | const gltfByteLength = glbPadded.length;
60 | const byteLength = headerByteLength + featureTableJsonByteLength + featureTableBinaryByteLength + batchTableJsonByteLength + batchTableBinaryByteLength + gltfByteLength;
61 |
62 | const header = Buffer.alloc(headerByteLength);
63 | header.write('b3dm', 0);
64 | header.writeUInt32LE(version, 4);
65 | header.writeUInt32LE(byteLength, 8);
66 | header.writeUInt32LE(featureTableJsonByteLength, 12);
67 | header.writeUInt32LE(featureTableBinaryByteLength, 16);
68 | header.writeUInt32LE(batchTableJsonByteLength, 20);
69 | header.writeUInt32LE(batchTableBinaryByteLength, 24);
70 |
71 | return Buffer.concat([header, featureTableJson, featureTableBinary, batchTableJson, batchTableBinary, glbPadded]);
72 | }
73 |
74 | function createB3dmDeprecated1(glb, batchLength, batchTableJson) {
75 | const version = 1;
76 | const headerByteLength = 20;
77 | const batchTableJsonByteLength = batchTableJson.length;
78 | const gltfByteLength = glb.length;
79 | const byteLength = headerByteLength + batchTableJsonByteLength + gltfByteLength;
80 |
81 | const header = Buffer.alloc(headerByteLength);
82 | header.write('b3dm', 0);
83 | header.writeUInt32LE(version, 4);
84 | header.writeUInt32LE(byteLength, 8);
85 | header.writeUInt32LE(batchLength, 12);
86 | header.writeUInt32LE(batchTableJsonByteLength, 16);
87 |
88 | return Buffer.concat([header, batchTableJson, glb]);
89 | }
90 |
91 | function createB3dmDeprecated2(glb, batchLength, batchTableJson, batchTableBinary) {
92 | const version = 1;
93 | const headerByteLength = 24;
94 | const batchTableJsonByteLength = batchTableJson.length;
95 | const batchTableBinaryByteLength = batchTableBinary.length;
96 | const gltfByteLength = glb.length;
97 | const byteLength = headerByteLength + batchTableJsonByteLength + batchTableBinaryByteLength + gltfByteLength;
98 |
99 | const header = Buffer.alloc(headerByteLength);
100 | header.write('b3dm', 0);
101 | header.writeUInt32LE(version, 4);
102 | header.writeUInt32LE(byteLength, 8);
103 | header.writeUInt32LE(batchTableJsonByteLength, 12);
104 | header.writeUInt32LE(batchTableBinaryByteLength, 16);
105 | header.writeUInt32LE(batchLength, 20);
106 |
107 | return Buffer.concat([header, batchTableJson, batchTableBinary, glb]);
108 | }
109 |
--------------------------------------------------------------------------------
/lib/createByteBuffer.ts:
--------------------------------------------------------------------------------
1 | import { FLOAT32_SIZE_BYTES, UINT8_SIZE_BYTES, UINT16_SIZE_BYTES } from './typeSize';
2 |
3 | export function createUInt16Buffer(items: number[]) {
4 | const out = Buffer.alloc(items.length * UINT16_SIZE_BYTES);
5 | for (let i = 0; i < items.length; ++i) {
6 | out.writeUInt16LE(items[i], i * UINT16_SIZE_BYTES);
7 | }
8 | return out;
9 | }
10 |
11 | export function createUInt8Buffer(items: number[]) {
12 | const out = Buffer.alloc(items.length * UINT8_SIZE_BYTES);
13 | for (let i = 0; i < items.length; ++i) {
14 | out.writeUInt8(items[i], i * UINT8_SIZE_BYTES);
15 | }
16 | return out;
17 | }
18 |
19 | export function createFloat32Buffer(items: number[]) {
20 | const out = Buffer.alloc(items.length * FLOAT32_SIZE_BYTES);
21 | for (let i = 0; i < items.length; ++i) {
22 | out.writeFloatLE(items[i], i * FLOAT32_SIZE_BYTES);
23 | }
24 | return out;
25 | }
26 |
--------------------------------------------------------------------------------
/lib/createCmpt.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const getBufferPadded = require('./getBufferPadded');
3 |
4 | module.exports = createCmpt;
5 |
6 | /**
7 | * Create a Composite (cmpt) tile from a set of tiles.
8 | *
9 | * @param {Buffer[]} tiles An array of buffers holding tile data.
10 | * @returns {Buffer} The generated cmpt tile buffer.
11 | */
12 | function createCmpt(tiles) {
13 | let byteLength = 0;
14 | const buffers = [];
15 | const tilesLength = tiles.length;
16 | for (let i = 0; i < tilesLength; i++) {
17 | const tile = getBufferPadded(tiles[i]);
18 | const tileByteLength = tile.length;
19 | tile.writeUInt32LE(tileByteLength, 8); // Edit the tile's byte length
20 | buffers.push(tile);
21 | byteLength += tileByteLength;
22 | }
23 |
24 | const version = 1;
25 | const headerByteLength = 16;
26 | byteLength += headerByteLength;
27 |
28 | const header = Buffer.alloc(headerByteLength);
29 | header.write('cmpt', 0);
30 | header.writeUInt32LE(version, 4);
31 | header.writeUInt32LE(byteLength, 8);
32 | header.writeUInt32LE(tilesLength, 12);
33 |
34 | buffers.unshift(header); // Add header first
35 |
36 | return Buffer.concat(buffers);
37 | }
38 |
--------------------------------------------------------------------------------
/lib/createConstantAttribute.ts:
--------------------------------------------------------------------------------
1 | import { Attribute } from './attribute';
2 | import { UINT32_SIZE_BYTES } from './typeSize';
3 | import { GltfComponentType, GltfType } from './gltfType';
4 |
5 | export function createConstantAttributeLEU32(
6 | name: string,
7 | constant: number,
8 | len: number
9 | ): Attribute {
10 | const buffer = Buffer.alloc(len * UINT32_SIZE_BYTES);
11 | for (let i = 0; i < len; ++i) {
12 | buffer.writeUInt32LE(constant, i * UINT32_SIZE_BYTES);
13 | }
14 |
15 | return {
16 | buffer: buffer,
17 | byteOffset: 0,
18 | componentType: GltfComponentType.UNSIGNED_INT,
19 | type: GltfType.SCALAR,
20 | count: len,
21 | min: [constant],
22 | max: [constant],
23 | propertyName: name,
24 | byteAlignment: 1
25 | };
26 | }
27 |
--------------------------------------------------------------------------------
/lib/createEXTMeshInstancing.ts:
--------------------------------------------------------------------------------
1 | import { Gltf, GltfNode } from './gltfType';
2 | import { AtLeastOne } from './atLeastN';
3 |
4 | const extensionName = 'EXT_mesh_gpu_instancing';
5 |
6 | export interface ExtMeshGpuInstancing {
7 | attributes: AtLeastOne<{
8 | TRANSLATION?: number;
9 | ROTATION?: number;
10 | SCALE?: number;
11 | }>;
12 | }
13 |
14 | export function createEXTMeshInstancingExtension(
15 | gltf: Gltf,
16 | node: GltfNode,
17 | extMeshGpuInstancing: ExtMeshGpuInstancing
18 | ) {
19 | if (gltf.extensionsUsed == null) {
20 | gltf.extensionsUsed = [];
21 | }
22 | gltf.extensionsUsed.push(extensionName);
23 |
24 | if (node.extensions == null) {
25 | node.extensions = {};
26 | }
27 |
28 | node.extensions.EXT_mesh_gpu_instancing = extMeshGpuInstancing;
29 | }
30 |
--------------------------------------------------------------------------------
/lib/createFeatureHierarchySubExtension.ts:
--------------------------------------------------------------------------------
1 | import { Gltf, GltfAccessor, GLenumName } from './gltfType';
2 | import {
3 | FeatureHierarchyClass,
4 | CompositeAccessorBufferView
5 | } from './featureHierarchyClass';
6 |
7 | export type FeatureHierarchyExtIds = { values: number[] };
8 | export type FeatureHierarchyExtValues = {
9 | values: (number | string | boolean)[];
10 | };
11 | export type FeatureHierarchyExtAccessor = { accessor: number };
12 | export type FeatureHierarchyExtValuesOrAccessor =
13 | | FeatureHierarchyExtValues
14 | | FeatureHierarchyExtAccessor;
15 | export type FeatureHierarchyExtIdsOrAccessor =
16 | | FeatureHierarchyExtIds
17 | | FeatureHierarchyExtAccessor;
18 |
19 | export type FeatureHiearchyExtProperties = {
20 | [propertyName: string]: FeatureHierarchyExtValuesOrAccessor;
21 | };
22 |
23 | /**
24 | * Differs from [featureHierarchyClass] in that the properties can only
25 | * be of the form {values: …} or {accessor: …}.
26 | */
27 | export interface FeatureHierarchyClassExt {
28 | name: string;
29 | instanceCount: number;
30 | properties: FeatureHiearchyExtProperties;
31 | }
32 |
33 | export interface FeatureHierarchyExtension {
34 | classes: FeatureHierarchyClassExt[];
35 | instanceCount: number;
36 | classIds: FeatureHierarchyExtIdsOrAccessor;
37 | parentIds?: FeatureHierarchyExtIdsOrAccessor;
38 | parentCounts?: FeatureHierarchyExtIdsOrAccessor;
39 | }
40 |
41 | const extensionName = 'CESIUM_3dtiles_feature_hierarchy';
42 | /**
43 | * Add CESIUM_3dtiles_feature_hierarchy as a sub extension to an existing
44 | * glTF object.
45 | *
46 | * @param gltf glTF asset to edit. This glTF asset should already have a
47 | * `CESIUM_3dtiles_feature_metadata` extension in the extensions member of
48 | * the glTF asset.
49 | * @param classes An array of FeatureHierarchyClasses to insert into the glTF
50 | * asset
51 | * @param classIds classIds for the feature hierarchy
52 | * @param instanceCount Total number of instances. Should be equal to the sum
53 | * of each classes[i].instancesCount
54 | * @param [parentIds] Optional list of parentIds associated with each classIds
55 | * @param [parentCounts] Optional numerical array containing the number of
56 | * parents each instance has.
57 | * @param [binaryData] Optional binaryData parameter. Should be provided if
58 | * using binary accessors. This function makes the assumption that all of the
59 | * binary class accessors / classIds / parentIds / parentCounts are all refering
60 | * to this buffer.
61 | * @throws TypeError If the provided glTF asset does not have a
62 | * `CESIUM_3dtiles_feature_metadata` extension already.
63 | * @throws RangeError If classes, parentIds, or parentCounts is empty. Or if
64 | * instancesLength is <= 0.
65 | */
66 |
67 | export function createFeatureHierarchySubExtension(
68 | gltf: Gltf,
69 | classes: FeatureHierarchyClass[],
70 | classIds: number[] | CompositeAccessorBufferView,
71 | instanceCount: number,
72 | parentIds?: number[] | CompositeAccessorBufferView,
73 | parentCounts?: number[] | CompositeAccessorBufferView,
74 | binaryData?: Buffer
75 | ) {
76 | assertParametersAreValid(
77 | classes,
78 | classIds,
79 | instanceCount,
80 | parentIds,
81 | parentCounts
82 | );
83 |
84 | if (gltf.extensions?.CESIUM_3dtiles_feature_metadata == null) {
85 | throw new TypeError(
86 | 'glTF asset is missing CESIUM_3dtiles_feature_metadata!'
87 | );
88 | }
89 |
90 | if (binaryData) {
91 | gltf.buffers.push({
92 | uri:
93 | 'data:application/octet-stream;base64,' +
94 | binaryData.toString('base64'),
95 | byteLength: binaryData.length
96 | });
97 | }
98 |
99 | var extension: FeatureHierarchyExtension = {
100 | classes: featureHierarchyClassToExt(gltf, classes),
101 | instanceCount: instanceCount,
102 | classIds: normalizeIdsOrGltfAccessor(gltf, classIds)
103 | };
104 |
105 | if (parentIds != null) {
106 | extension.parentIds = normalizeIdsOrGltfAccessor(gltf, parentIds);
107 | }
108 |
109 | if (parentCounts != null) {
110 | extension.parentCounts = normalizeIdsOrGltfAccessor(gltf, parentCounts);
111 | }
112 |
113 | gltf.extensions.CESIUM_3dtiles_feature_metadata.featureTables[0];
114 |
115 | // TODO: Right now we assume that the first featureTable is where the
116 | // featureHierarchy extension should go. This should be refactored.
117 | // eventually when we support exporting multiple feature tables
118 | // in the samples.
119 |
120 | const firstFeatureTable =
121 | gltf.extensions.CESIUM_3dtiles_feature_metadata.featureTables[0];
122 | if (firstFeatureTable.extensions == null) {
123 | firstFeatureTable.extensions = {};
124 | }
125 |
126 | firstFeatureTable.extensions.CESIUM_3dtiles_feature_hierarchy = extension;
127 |
128 | if (gltf.extensionsUsed == null) {
129 | gltf.extensionsUsed = [extensionName];
130 | } else {
131 | gltf.extensionsUsed.push(extensionName);
132 | }
133 | }
134 |
135 | /**
136 | * For each instance in each class, if the instance is a primitive array,
137 | * leave it as-is, otherwise add a new BufferView / Accessor to the glTF asset
138 | * and wrap it with the binary data with {accessor: index}
139 | */
140 |
141 | function featureHierarchyClassToExt(
142 | gltf: Gltf,
143 | classes: FeatureHierarchyClass[]
144 | ): FeatureHierarchyClassExt[] {
145 | const result: FeatureHierarchyClassExt[] = [];
146 |
147 | for (const cls of classes) {
148 | let normalizedProperties: FeatureHiearchyExtProperties = {};
149 | for (const propName in cls.properties) {
150 | if (cls.properties.hasOwnProperty(propName)) {
151 | const property = cls.properties[propName];
152 | if (Array.isArray(property)) {
153 | normalizedProperties[propName] = { values: property };
154 | } else {
155 | createAccessorAndBufferView(gltf, property);
156 | normalizedProperties[propName] = {
157 | accessor: gltf.accessors.length - 1
158 | };
159 | }
160 | }
161 | }
162 |
163 | result.push({
164 | name: cls.name,
165 | properties: normalizedProperties,
166 | instanceCount: cls.instanceCount
167 | });
168 | }
169 |
170 | return result;
171 | }
172 |
173 | function normalizeIdsOrGltfAccessor(
174 | gltf: Gltf,
175 | data: number[] | CompositeAccessorBufferView
176 | ): FeatureHierarchyExtIdsOrAccessor {
177 | if (Array.isArray(data)) {
178 | return { values: data };
179 | }
180 |
181 | createAccessorAndBufferView(gltf, data);
182 | return { accessor: gltf.accessors.length - 1 };
183 | }
184 |
185 | function createAccessorAndBufferView(
186 | gltf: Gltf,
187 | composite: CompositeAccessorBufferView
188 | ) {
189 | gltf.bufferViews.push({
190 | buffer: gltf.buffers.length - 1,
191 | byteLength: composite.byteLength,
192 | byteOffset: composite.byteOffset,
193 | target: GLenumName.ARRAY_BUFFER
194 | });
195 |
196 | const bufferViewIndex = gltf.bufferViews.length - 1;
197 | gltf.accessors.push({
198 | bufferView: bufferViewIndex,
199 | byteOffset: 0,
200 | componentType: composite.componentType,
201 | count: composite.count,
202 | min: composite.min,
203 | max: composite.max,
204 | type: composite.type
205 | });
206 | }
207 |
208 | function assertParametersAreValid(
209 | classes: FeatureHierarchyClass[],
210 | classIds: number[] | GltfAccessor,
211 | instanceCount: number,
212 | parentIds?: number[] | GltfAccessor,
213 | parentCounts?: number[] | GltfAccessor
214 | ) {
215 | if (classes.length === 0) {
216 | throw new RangeError(
217 | `Classes array is empty, should contain at least one element`
218 | );
219 | }
220 |
221 | if (Array.isArray(classIds) && classIds.length === 0) {
222 | throw new RangeError(
223 | `Classes array is empty, should contain at least one element`
224 | );
225 | }
226 |
227 | if (instanceCount <= 0) {
228 | throw new RangeError('instanceCount must be positive');
229 | }
230 |
231 | if (Array.isArray(parentIds) && parentIds?.length === 0) {
232 | throw new RangeError(
233 | 'Empty parentIds array provided, should contain at least one' +
234 | 'parentId'
235 | );
236 | }
237 |
238 | if (Array.isArray(parentCounts) && parentCounts?.length === 0) {
239 | throw new RangeError(
240 | 'Empty parentCounts provided, should contain at least one parentId'
241 | );
242 | }
243 | }
244 |
--------------------------------------------------------------------------------
/lib/createFeatureMetadataExtension.ts:
--------------------------------------------------------------------------------
1 | import { GltfAccessor, GLenumName } from './gltfType';
2 | import { Extensions } from './Extensions';
3 |
4 | const Cesium = require('cesium');
5 | const defined = Cesium.defined;
6 | const typeConversion = require('./typeConversion');
7 | const featureTableExtensionName = 'CESIUM_3dtiles_feature_metadata';
8 |
9 | function sortByByteOffset(a, b) {
10 | if (a.byteOffset < b.byteOffset) {
11 | return -1;
12 | }
13 |
14 | if (a.byteOffset > b.byteOffset) {
15 | return 1;
16 | }
17 |
18 | return 0;
19 | }
20 |
21 | function assertHumanReadableFeatureTableValueArraysHaveIdenticalLength(
22 | humanfeatureTables
23 | ) {
24 | if (humanfeatureTables.length === 0) {
25 | return 0;
26 | }
27 |
28 | const firstLength = humanfeatureTables[0].value.length;
29 | for (let i = 1; i < humanfeatureTables.length; ++i) {
30 | if (humanfeatureTables[i].value.length !== firstLength) {
31 | const badFeatureTable = humanfeatureTables[i].name;
32 | const badLength = humanfeatureTables[i].value.length;
33 | throw new Error(
34 | `${badFeatureTable} has incorrect length of ${badLength}, should match first length: ${firstLength}`
35 | );
36 | }
37 | }
38 |
39 | return firstLength;
40 | }
41 |
42 | function assertBinaryHasIdenticalCount(binaryfeatureTables) {
43 | if (binaryfeatureTables.length === 0) {
44 | return 0;
45 | }
46 |
47 | const firstLength = binaryfeatureTables[0].count;
48 | for (let i = 1; i < binaryfeatureTables.length; ++i) {
49 | if (binaryfeatureTables[i].count !== firstLength) {
50 | const badFeatureTable = binaryfeatureTables[i].name;
51 | const badLength = binaryfeatureTables[i].count;
52 | throw new Error(
53 | badFeatureTable +
54 | ' has incorrect length of: ' +
55 | badLength +
56 | ', should match the first length: ' +
57 | firstLength
58 | );
59 | }
60 | }
61 |
62 | return firstLength;
63 | }
64 |
65 | function isCandidateForImplicitBufferView(batchAttribute) {
66 | const min = batchAttribute.min;
67 | const max = batchAttribute.max;
68 | const count = batchAttribute.count;
69 | const minExists = defined(batchAttribute.min);
70 | const maxExists = defined(batchAttribute.max);
71 | const minPasses = minExists && min.length === 1 && min[0] === 0;
72 | const maxPasses = maxExists && max.length === 1 && max[0] === count - 1;
73 | return minPasses && maxPasses;
74 | }
75 |
76 | /**
77 | * @typedef humanReadableFeatureTableValue
78 | * @type {Object}
79 | * @property {Array} values An array of arbitrary values in human readable form
80 | * (e.g ["A", "B", "C"] or [1.3, 4.3, 1.5])
81 | */
82 |
83 | /**
84 | * @typedef binaryReadableFeatureTableValue
85 | * @type {Object}
86 | * @property {String} name Name of the featureTableAttribute (e.g to be placed
87 | * into the accessor)
88 | * @property {Number} [byteoffset] ByteOffset of the featureTableAttribute
89 | * @property {Number} [byteLength] Length of the attribute in bytes
90 | * @property {Number} count Count of logical number of elements in the
91 | * attribute (eg 9 floating point numbers with a vec3
92 | * property means count = 3)
93 | * @property {Number} componentType WebGL enum of the component property
94 | * (e.g GL_UNSIGNED_BYTE / 0x1401)
95 | */
96 |
97 | /**
98 | * @typedef featureTableAttribute
99 | * @type {Object.}
100 | */
101 |
102 | /**
103 | * @typedef attributeBufferType
104 | * @type {Object}
105 | * @property {Buffer} buffer BufferAttribute data
106 | * @property {String} componentType BufferAttribute componentType (FLOAT, UNSIGNED_BYTE, DOUBLE)
107 | * @property {String} propertyName BufferAttribute property name (POSITION, NORMAL, COLOR, WEIGHT)
108 | * @property {String} type (SCALAR, VEC2, VEC3, VEC4)
109 | * @property {String} target WebGL rendering target, like ARRAY_BUFFER, or ELEMENT_ARRAY_BUFFE (e.g 0x8892, 0x8893)
110 | * @property {Array.} min Minimum value for each component in the bufferAttribute
111 | * @property {Array.} max Maximum value for each component in the bufferAttribute
112 | */
113 |
114 | /**
115 | * Iterates over featureTableAttributes and adorns the provided gltf object with them via
116 | * the CESIUM_3dtiles_feature_metadata extension
117 | *
118 | * @param {Object} gltf The gltf object to edit
119 | * @param {featureTableAttribute} featureTableAttributes List of batch table attributes. The function will intelligently try to detect
120 | * if the attribute is a human readable value (a key value where the value is
121 | * just an array of non-binary data) or if the values are more keys describing the
122 | * layout of binary data in a binary buffer.
123 | * @param {Buffer} sharedBinaryBuffer Binary buffer. Must be defined if using at
124 | * least 1 binary batch attribute. This function currently assumes that all of
125 | * the binary batch attributes in featureTableAttributes are directly referring
126 | * to this buffer.
127 | */
128 | export function createFeatureMetadataExtension(
129 | gltf,
130 | featureTableAttributes,
131 | sharedBinaryBuffer
132 | ) {
133 | Extensions.addExtensionsUsed(gltf, featureTableExtensionName);
134 | Extensions.addExtension(gltf, featureTableExtensionName, {
135 | featureTables: [
136 | {
137 | properties: {}
138 | }
139 | ]
140 | });
141 |
142 | const humanReadable = [];
143 | const binaryReadable = [];
144 |
145 | const featureTableIndex = 0; // TODO: This will change when we add multiple batch table support
146 | const activeFeatureTable =
147 | gltf.extensions[featureTableExtensionName].featureTables[
148 | featureTableIndex
149 | ];
150 |
151 | const attributeNames = Object.keys(featureTableAttributes);
152 | for (let i = 0; i < attributeNames.length; ++i) {
153 | if (featureTableAttributes[attributeNames[i]] instanceof Array) {
154 | humanReadable.push({
155 | name: attributeNames[i],
156 | value: featureTableAttributes[attributeNames[i]]
157 | });
158 | } else {
159 | binaryReadable.push(featureTableAttributes[attributeNames[i]]);
160 | }
161 | }
162 |
163 | const humanFeatureCount = assertHumanReadableFeatureTableValueArraysHaveIdenticalLength(
164 | humanReadable
165 | );
166 | const binaryFeatureCount = assertBinaryHasIdenticalCount(binaryReadable);
167 |
168 | if (humanFeatureCount === 0 && binaryFeatureCount === 0) {
169 | throw new Error(
170 | 'Must have at least one binary or human readable batch table attribute'
171 | );
172 | }
173 |
174 | if (
175 | humanFeatureCount > 0 &&
176 | binaryFeatureCount > 0 &&
177 | binaryFeatureCount !== humanFeatureCount
178 | ) {
179 | throw new Error(
180 | 'Count must be identical across all batch table properties (binary or human readable)'
181 | );
182 | }
183 |
184 | activeFeatureTable.featureCount = Math.max(
185 | humanFeatureCount,
186 | binaryFeatureCount
187 | );
188 |
189 | for (let i = 0; i < humanReadable.length; ++i) {
190 | const attribute = humanReadable[i];
191 | activeFeatureTable.properties[attribute.name] = {
192 | values: attribute.value
193 | };
194 | }
195 |
196 | if (binaryReadable.length > 0 && !defined(sharedBinaryBuffer)) {
197 | throw new Error(
198 | 'Detected a binary attribute, but function called without binary buffer'
199 | );
200 | }
201 |
202 | binaryReadable.sort(sortByByteOffset);
203 | if (binaryReadable.length > 0) {
204 | gltf.buffers.push({
205 | byteLength: sharedBinaryBuffer.length,
206 | uri:
207 | 'data:application/octet-stream;base64,' +
208 | sharedBinaryBuffer.toString('base64')
209 | });
210 |
211 | const newBufferIndex = gltf.buffers.length - 1;
212 | let bufferViewIndex = gltf.bufferViews.length;
213 | let accessorIndex = gltf.accessors.length;
214 |
215 | for (let i = 0; i < binaryReadable.length; ++i, ++accessorIndex) {
216 | const batchAttribute = binaryReadable[i];
217 |
218 | const componentType = batchAttribute.componentType;
219 | const validComponentType = typeConversion.isValidWebGLDataTypeEnum(
220 | componentType
221 | );
222 | const normalizedComponentType = validComponentType
223 | ? componentType
224 | : typeConversion.componentTypeStringToInteger(componentType);
225 |
226 | const implicitBufferView = isCandidateForImplicitBufferView(
227 | batchAttribute
228 | );
229 | if (!implicitBufferView) {
230 | gltf.bufferViews.push({
231 | buffer: newBufferIndex,
232 | byteLength: batchAttribute.byteLength,
233 | byteOffset: batchAttribute.byteOffset,
234 | target: GLenumName.ARRAY_BUFFER
235 | });
236 | }
237 |
238 | const accessor: GltfAccessor = {
239 | componentType: normalizedComponentType,
240 | type: batchAttribute.type,
241 | count: batchAttribute.count,
242 | min: batchAttribute.min,
243 | max: batchAttribute.max
244 | };
245 |
246 | if (!implicitBufferView) {
247 | accessor.bufferView = bufferViewIndex++;
248 | accessor.byteOffset = 0;
249 | }
250 |
251 | gltf.accessors.push(accessor);
252 | activeFeatureTable.properties[batchAttribute.name] = {
253 | accessor: accessorIndex
254 | };
255 | }
256 | }
257 |
258 | // also add the extension to attributes
259 | const primitives = gltf.meshes[0].primitives;
260 | for (let i = 0; i < primitives.length; ++i) {
261 | primitives[i].extensions = {
262 | CESIUM_3dtiles_feature_metadata: {
263 | attributes: {
264 | _FEATURE_ID_0: 0
265 | }
266 | }
267 | };
268 | }
269 |
270 | return gltf;
271 | }
272 |
--------------------------------------------------------------------------------
/lib/createGlb.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const path = require('path');
3 | const createGltf = require('./createGltf');
4 | const gltfPipeline = require('gltf-pipeline');
5 | const gltfToGlb = gltfPipeline.gltfToGlb;
6 | const gltfConversionOptions = { resourceDirectory: path.join(__dirname, '../') };
7 |
8 | module.exports = createGlb;
9 |
10 | /**
11 | * Create a glb from a Mesh.
12 | *
13 | * @param {Object} options An object with the following properties:
14 | * @param {Mesh} options.mesh The mesh.
15 | * @param {Boolean} [options.useBatchIds=true] Modify the glTF to include the batchId vertex attribute.
16 | * @param {Boolean} [options.relativeToCenter=false] Set mesh positions relative to center.
17 | * @param {Boolean} [options.deprecated=false] Save the glTF with the old BATCHID semantic.
18 | *
19 | * @returns {Promise} A promise that resolves with the binary glb buffer.
20 | */
21 |
22 | function createGlb(options) {
23 | const gltf = createGltf(options);
24 | return gltfToGlb(gltf, gltfConversionOptions).then(function (results) {
25 | return results.glb;
26 | });
27 | }
28 |
--------------------------------------------------------------------------------
/lib/createGltfFromPnts.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const Cesium = require('cesium');
3 | const defined = Cesium.defined;
4 | const typeConversion = require('./typeConversion');
5 | module.exports = createGltfFromPnts;
6 |
7 | function isImplicitBufferView(attributeBuffer) {
8 | return (
9 | !defined(attributeBuffer.buffer) || attributeBuffer.buffer.length === 0
10 | );
11 | }
12 |
13 | /**
14 | @typedef attributeBufferType
15 | @type {Object}
16 | @property {Buffer} buffer BufferAttribute data
17 | @property {String} componentType BufferAttribute componentType
18 | (FLOAT, UNSIGNED_BYTE, DOUBLE)
19 | @property {String} propertyName BufferAttribute property name
20 | (POSITION, NORMAL, COLOR, WEIGHT)
21 | @property {String} type (SCALAR, VEC2, VEC3, VEC4)
22 | @property {String} target WebGL rendering target, like ARRAY_BUFFER, or
23 | ELEMENT_ARRAY_BUFFER (e.g 0x8892, 0x8893)
24 | @property {Array.} min Minimum value for each component in the
25 | bufferAttribute
26 | @property {Array.} max Maximum value for each component in the
27 | bufferAttribute
28 | */
29 |
30 | function createAmalgamatedGltfBuffer(attributeBuffers, indexBuffer) {
31 | let megaBuffer = Buffer.concat(
32 | attributeBuffers.map(function (ab) {
33 | return ab.buffer;
34 | })
35 | );
36 | if (defined(indexBuffer)) {
37 | megaBuffer = Buffer.concat([megaBuffer, indexBuffer.buffer]);
38 | }
39 |
40 | return [
41 | {
42 | uri:
43 | `data:application/octet-stream;base64,${
44 | Buffer.from(megaBuffer).toString('base64')}`,
45 | byteLength: megaBuffer.length
46 | }
47 | ];
48 | }
49 |
50 | /**
51 | * Generates a list of bufferViews. Buffer is currently hardcoded to 0, as
52 | * this module will only ever generate 1 buffer when converting pointcloud data
53 | * into a GLTF
54 | *
55 | * @param {Array} attributeBuffers A list of buffer
56 | * attributes to convert to GLTF
57 | * @param {attributeBufferType} [indexBuffer] An optional indexBuffer.
58 | * @returns {Object} a buffer views array
59 | */
60 | function createBufferViewsFromAttributeBuffers(attributeBuffers, indexBuffer) {
61 | const result = [];
62 | let byteOffset = 0;
63 |
64 | for (let i = 0; i < attributeBuffers.length; ++i) {
65 | if (isImplicitBufferView(attributeBuffers[i])) {
66 | continue;
67 | }
68 |
69 | const bufferView = {
70 | buffer: 0,
71 | byteLength: attributeBuffers[i].buffer.byteLength,
72 | byteOffset: byteOffset,
73 | target: attributeBuffers[i].target
74 | };
75 |
76 | if (defined(attributeBuffers[i].byteStride)) {
77 | bufferView.byteStride = attributeBuffers[i].byteStride;
78 | }
79 |
80 | result.push(bufferView);
81 |
82 | // All attribute data is tightly packed
83 | byteOffset += attributeBuffers[i].buffer.byteLength;
84 | }
85 |
86 | if (defined(indexBuffer)) {
87 | result.push({
88 | buffer: 0,
89 | byteLength: indexBuffer.buffer.byteLength,
90 | byteOffset: byteOffset,
91 | target: indexBuffer.target
92 | });
93 | }
94 |
95 | return result;
96 | }
97 |
98 | /**
99 | * Create a meshes singleton using bufferAttributes
100 | * @param {Array.} attributeBuffers A list of buffer
101 | * attributes to convert to GLTF
102 | * @param {attributeBufferType} [indexBuffer] An optional indexBuffer.
103 | * @returns {Array.