├── LICENSE.md ├── README.md ├── figures ├── Cesium3DTiles.png └── cesium.png ├── samples-generator ├── .eslintrc.json ├── .gitattributes ├── .gitignore ├── .idea │ ├── 3d-tiles-samples-generator.iml │ ├── encodings.xml │ ├── jsLibraryMappings.xml │ ├── jsLinters │ │ └── jshint.xml │ ├── libraries │ │ └── TypeScriptDefinitions.xml │ ├── misc.xml │ ├── modules.xml │ └── vcs.xml ├── .npmignore ├── .npmrc ├── CHANGES.md ├── LICENSE.md ├── README.md ├── bin │ └── 3d-tiles-samples-generator.js ├── data │ ├── 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 │ │ ├── Cesium_Logo_Flat.png │ │ └── textured_box.glb │ ├── tree.glb │ ├── tree_billboard.glb │ └── wood_red.jpg ├── gulpfile.js ├── index.js ├── lib │ ├── Extensions.js │ ├── Material.js │ ├── Mesh.js │ ├── createB3dm.js │ ├── createBatchTableHierarchy.js │ ├── createBuildings.js │ ├── createBuildingsTile.js │ ├── createCmpt.js │ ├── createGltf.js │ ├── createI3dm.js │ ├── createInstancesTile.js │ ├── createPnts.js │ ├── createPointCloudTile.js │ ├── createTilesetJsonSingle.js │ ├── getBufferPadded.js │ ├── getJsonBufferPadded.js │ ├── getProperties.js │ ├── saveTile.js │ ├── saveTilesetJson.js │ └── utility.js ├── package.json └── specs │ ├── .eslintrc.json │ ├── bin │ └── generatorSpec.js │ ├── jasmine.json │ └── matchers │ ├── addDefaultMatchers.js │ ├── customizeJasmine.js │ ├── equals.js │ ├── equalsMethodEqualityTester.js │ ├── expectPromise.js │ └── nodeHelper.js ├── tools ├── .eslintignore ├── .eslintrc.json ├── .gitattributes ├── .gitignore ├── .idea │ ├── encodings.xml │ ├── jsLibraryMappings.xml │ ├── jsLinters │ │ └── jshint.xml │ ├── modules.xml │ ├── tools.iml │ └── vcs.xml ├── .npmignore ├── .npmrc ├── .travis.yml ├── CHANGES.md ├── LICENSE.md ├── README.md ├── bin │ └── 3d-tiles-tools.js ├── gulpfile.js ├── index.js ├── lib │ ├── bufferToJson.js │ ├── combineTileset.js │ ├── databaseToTileset.js │ ├── directoryExists.js │ ├── extractB3dm.js │ ├── extractCmpt.js │ ├── extractI3dm.js │ ├── fileExists.js │ ├── getBufferPadded.js │ ├── getDefaultWriteCallback.js │ ├── getFilesInDirectory.js │ ├── getJsonBufferPadded.js │ ├── getMagic.js │ ├── getWorkingDirectory.js │ ├── glbToB3dm.js │ ├── glbToI3dm.js │ ├── gzipTileset.js │ ├── isGzipped.js │ ├── isGzippedFile.js │ ├── isJson.js │ ├── isTile.js │ ├── makeCompositeTile.js │ ├── optimizeGlb.js │ ├── readFile.js │ ├── runPipeline.js │ ├── tilesetToDatabase.js │ ├── upgradeTileset.js │ └── walkDirectory.js ├── package.json ├── specs │ ├── .eslintrc.json │ ├── data │ │ ├── BatchedDeprecated1 │ │ │ ├── batchedDeprecated1.b3dm │ │ │ └── tileset.json │ │ ├── BatchedDeprecated2 │ │ │ ├── batchedDeprecated2.b3dm │ │ │ └── tileset.json │ │ ├── CesiumTexturedBox │ │ │ └── CesiumTexturedBox.glb │ │ ├── Textured │ │ │ ├── batchedTextured.b3dm │ │ │ └── instancedTextured.i3dm │ │ ├── TilesetOfTilesets │ │ │ ├── lr.b3dm │ │ │ ├── parent.b3dm │ │ │ ├── tileset.json │ │ │ ├── tileset2.json │ │ │ ├── tileset3 │ │ │ │ ├── ll.b3dm │ │ │ │ └── tileset3.json │ │ │ ├── ul.b3dm │ │ │ └── ur.b3dm │ │ ├── batchedWithBatchTableBinary.b3dm │ │ ├── composite.cmpt │ │ ├── compositeOfComposite.cmpt │ │ ├── instancedWithBatchTableBinary.i3dm │ │ ├── pipeline.json │ │ └── tileset.3dtiles │ ├── jasmine.json │ ├── lib │ │ ├── combineTilesetSpec.js │ │ ├── databaseToTilesetSpec.js │ │ ├── extractB3dmSpec.js │ │ ├── extractCmptSpec.js │ │ ├── extractI3dmSpec.js │ │ ├── glbToB3dmSpec.js │ │ ├── glbToI3dmSpec.js │ │ ├── gzipTilesetSpec.js │ │ ├── makeCompositeTileSpec.js │ │ ├── optimizeGlbSpec.js │ │ ├── runPipelineSpec.js │ │ ├── tilesetToDatabseSpec.js │ │ └── upgradeTilesetSpec.js │ └── matchers │ │ ├── addDefaultMatchers.js │ │ ├── customizeJasmine.js │ │ ├── equals.js │ │ ├── equalsMethodEqualityTester.js │ │ ├── expectPromise.js │ │ └── nodeHelper.js └── tools │ └── jsdoc │ └── conf.json └── validator ├── .eslintignore ├── .eslintrc.json ├── .gitattributes ├── .gitignore ├── .idea ├── 3d-tiles-validator.iml ├── encodings.xml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── jsLibraryMappings.xml ├── jsLinters │ └── jshint.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── .npmignore ├── .npmrc ├── .travis.yml ├── CHANGES.md ├── LICENSE.md ├── README.md ├── bin └── 3d-tiles-validator.js ├── gulpfile.js ├── index.js ├── lib ├── isGzipped.js └── readTile.js ├── package.json ├── specs ├── .eslintrc.json ├── data │ └── Tileset │ │ ├── ll.b3dm │ │ ├── lr.b3dm │ │ ├── parent.b3dm │ │ ├── tileset.json │ │ ├── ul.b3dm │ │ └── ur.b3dm ├── jasmine.json ├── lib │ ├── isGzippedSpec.js │ └── readTileSpec.js └── matchers │ ├── addDefaultMatchers.js │ ├── customizeJasmine.js │ ├── equals.js │ ├── equalsMethodEqualityTester.js │ ├── expectPromise.js │ └── nodeHelper.js └── tools └── jsdoc └── conf.json /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | # 3D Tiles Tools 4 | 5 | Upcoming tools for [3D Tiles](https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/README.md) tilesets: 6 | * Manipulating tilesets, e.g., extracting glTF, gzipping, etc. See [tools/README.md](https://github.com/AnalyticalGraphicsInc/3d-tiles-tools/blob/master/tools/README.md). 7 | * Validating that a tileset conforms to the specification. See [validator/README.md](https://github.com/AnalyticalGraphicsInc/3d-tiles-tools/blob/master/validator/README.md). 8 | * Generate the sample tilesets in [3d-tiles-samples](https://github.com/AnalyticalGraphicsInc/3d-tiles-samples) and [Cesium](https://github.com/AnalyticalGraphicsInc/cesium). See [samples-generator/README.md](https://github.com/AnalyticalGraphicsInc/3d-tiles-tools/blob/master/samples-generator/README.md). 9 | 10 | See the [roadmap](https://github.com/AnalyticalGraphicsInc/3d-tiles-tools/issues/1). 11 | 12 | Created by the Cesium team and University of Pennsylvania students. 13 | 14 | ## Contributions 15 | 16 | Pull requests are appreciated! Please use the same [Contributor License Agreement (CLA)](https://github.com/AnalyticalGraphicsInc/cesium/blob/master/CONTRIBUTING.md) and [Coding Guide](https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Documentation/Contributors/CodingGuide/README.md) used for [Cesium](http://cesiumjs.org/). 17 | 18 | --- 19 | 20 |

21 | 22 |

23 | -------------------------------------------------------------------------------- /figures/Cesium3DTiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/figures/Cesium3DTiles.png -------------------------------------------------------------------------------- /figures/cesium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/figures/cesium.png -------------------------------------------------------------------------------- /samples-generator/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "cesium/node" 3 | } 4 | -------------------------------------------------------------------------------- /samples-generator/.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | package.json text eol=lf -------------------------------------------------------------------------------- /samples-generator/.gitignore: -------------------------------------------------------------------------------- 1 | # NPM 2 | node_modules 3 | npm-debug.log 4 | package-lock.json 5 | 6 | # WebStorm user-specific 7 | .idea/workspace.xml 8 | .idea/tasks.xml 9 | 10 | # Generated files 11 | .nyc_output 12 | .eslintcache 13 | coverage 14 | output 15 | *.tgz 16 | *.zip 17 | -------------------------------------------------------------------------------- /samples-generator/.idea/3d-tiles-samples-generator.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /samples-generator/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /samples-generator/.idea/jsLibraryMappings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /samples-generator/.idea/jsLinters/jshint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 85 | -------------------------------------------------------------------------------- /samples-generator/.idea/libraries/TypeScriptDefinitions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /samples-generator/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /samples-generator/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /samples-generator/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /samples-generator/.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 | -------------------------------------------------------------------------------- /samples-generator/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /samples-generator/CHANGES.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | ### 0.1.0 - 2017-02-07 5 | 6 | * Initial release. 7 | -------------------------------------------------------------------------------- /samples-generator/README.md: -------------------------------------------------------------------------------- 1 | # 3D Tiles Samples Generator 2 | 3 | The tilesets generated here are included in [3d-tiles-samples](https://github.com/AnalyticalGraphicsInc/3d-tiles-samples) and [Cesium](https://github.com/AnalyticalGraphicsInc/cesium). 4 | 5 | ## Instructions 6 | 7 | Clone this repo and install [Node.js](http://nodejs.org/). From the root directory of this repo, run: 8 | 9 | ``` 10 | npm install 11 | 12 | node bin/3d-tiles-samples-generator.js 13 | ``` 14 | 15 | 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 Cesium's `Specs/Data/Cesium3DTiles/` folder for testing with Cesium. The tilesets in the `Samples` folder may be copied to the `tilesets` folder in `3d-tiles-samples`. 16 | 17 | Run the tests: 18 | ``` 19 | npm run test 20 | ``` 21 | To run ESLint on the entire codebase, run: 22 | ``` 23 | npm run eslint 24 | ``` 25 | To run ESLint automatically when a file is saved, run the following and leave it open in a console window: 26 | ``` 27 | npm run eslint-watch 28 | ``` 29 | 30 | ## Contributions 31 | 32 | Pull requests are appreciated! Please use the same [Contributor License Agreement (CLA)](https://github.com/AnalyticalGraphicsInc/cesium/blob/master/CONTRIBUTING.md) and [Coding Guide](https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Documentation/Contributors/CodingGuide/README.md) used for [Cesium](http://cesiumjs.org/). 33 | 34 | --- 35 | 36 |

37 | 38 |

39 | -------------------------------------------------------------------------------- /samples-generator/data/box.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/samples-generator/data/box.glb -------------------------------------------------------------------------------- /samples-generator/data/building.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/samples-generator/data/building.glb -------------------------------------------------------------------------------- /samples-generator/data/dragon_high.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/samples-generator/data/dragon_high.glb -------------------------------------------------------------------------------- /samples-generator/data/dragon_low.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/samples-generator/data/dragon_low.glb -------------------------------------------------------------------------------- /samples-generator/data/dragon_medium.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/samples-generator/data/dragon_medium.glb -------------------------------------------------------------------------------- /samples-generator/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 | -------------------------------------------------------------------------------- /samples-generator/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 | -------------------------------------------------------------------------------- /samples-generator/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 | -------------------------------------------------------------------------------- /samples-generator/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 | -------------------------------------------------------------------------------- /samples-generator/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 | -------------------------------------------------------------------------------- /samples-generator/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 | -------------------------------------------------------------------------------- /samples-generator/data/red_box.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/samples-generator/data/red_box.glb -------------------------------------------------------------------------------- /samples-generator/data/textured_box.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/samples-generator/data/textured_box.glb -------------------------------------------------------------------------------- /samples-generator/data/textured_box_separate/Cesium_Logo_Flat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/samples-generator/data/textured_box_separate/Cesium_Logo_Flat.png -------------------------------------------------------------------------------- /samples-generator/data/textured_box_separate/textured_box.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/samples-generator/data/textured_box_separate/textured_box.glb -------------------------------------------------------------------------------- /samples-generator/data/tree.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/samples-generator/data/tree.glb -------------------------------------------------------------------------------- /samples-generator/data/tree_billboard.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/samples-generator/data/tree_billboard.glb -------------------------------------------------------------------------------- /samples-generator/data/wood_red.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/samples-generator/data/wood_red.jpg -------------------------------------------------------------------------------- /samples-generator/gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Promise = require('bluebird'); 4 | var Cesium = require('cesium'); 5 | var child_process = require('child_process'); 6 | var gulp = require('gulp'); 7 | var Jasmine = require('jasmine'); 8 | var SpecReporter = require('jasmine-spec-reporter').SpecReporter; 9 | var path = require('path'); 10 | var yargs = require('yargs'); 11 | var fsExtra = require('fs-extra'); 12 | var open = require('open'); 13 | 14 | var defined = Cesium.defined; 15 | var argv = yargs.argv; 16 | 17 | // Add third-party node module binaries to the system path 18 | // since some tasks need to call them directly. 19 | var environmentSeparator = process.platform === 'win32' ? ';' : ':'; 20 | var nodeBinaries = path.join(__dirname, 'node_modules', '.bin'); 21 | process.env.PATH += environmentSeparator + nodeBinaries; 22 | 23 | var specFiles = ['**/*.js', '!node_modules/**']; 24 | 25 | gulp.task('test', function (done) { 26 | var jasmine = new Jasmine(); 27 | jasmine.loadConfigFile('specs/jasmine.json'); 28 | jasmine.addReporter(new SpecReporter({ 29 | displaySuccessfulSpec: !defined(argv.suppressPassed) || !argv.suppressPassed 30 | })); 31 | jasmine.execute(); 32 | jasmine.onComplete(function (passed) { 33 | done(argv.failTaskOnError && !passed ? 1 : 0); 34 | }); 35 | }); 36 | 37 | gulp.task('test-watch', function () { 38 | gulp.watch(specFiles).on('change', function () { 39 | // We can't simply depend on the test task because Jasmine 40 | // does not like being run multiple times in the same process. 41 | try { 42 | child_process.execSync('jasmine JASMINE_CONFIG_PATH=specs/jasmine.json', { 43 | stdio: [process.stdin, process.stdout, process.stderr] 44 | }); 45 | } catch (exception) { 46 | console.log('Tests failed to execute.'); 47 | } 48 | }); 49 | }); 50 | 51 | gulp.task('coverage', function () { 52 | fsExtra.removeSync('coverage'); 53 | child_process.execSync('nyc' + 54 | ' --all' + 55 | ' --reporter=lcov' + 56 | ' --dir coverage' + 57 | ' -x "specs/**"' + 58 | ' -x "coverage/**"' + 59 | ' -x "gulpfile.js"' + 60 | ' -x "index.js"' + 61 | ' node_modules/jasmine/bin/jasmine.js' + 62 | ' JASMINE_CONFIG_PATH=specs/jasmine.json', { 63 | stdio: [process.stdin, process.stdout, process.stderr] 64 | }); 65 | open('coverage/lcov-report/index.html'); 66 | }); 67 | 68 | gulp.task('cloc', function() { 69 | var cmdLine; 70 | var clocPath = path.join('node_modules', 'cloc', 'lib', 'cloc'); 71 | 72 | //Run cloc on primary Source files only 73 | var source = new Promise(function(resolve, reject) { 74 | cmdLine = 'perl ' + clocPath + ' --quiet --progress-rate=0' + 75 | ' lib/ bin/'; 76 | 77 | child_process.exec(cmdLine, function(error, stdout, stderr) { 78 | if (error) { 79 | console.log(stderr); 80 | return reject(error); 81 | } 82 | console.log('Source:'); 83 | console.log(stdout); 84 | resolve(); 85 | }); 86 | }); 87 | 88 | //If running cloc on source succeeded, also run it on the tests. 89 | return source.then(function() { 90 | return new Promise(function(resolve, reject) { 91 | cmdLine = 'perl ' + clocPath + ' --quiet --progress-rate=0' + 92 | ' specs/lib/ specs/bin/'; 93 | child_process.exec(cmdLine, function(error, stdout, stderr) { 94 | if (error) { 95 | console.log(stderr); 96 | return reject(error); 97 | } 98 | console.log('Specs:'); 99 | console.log(stdout); 100 | resolve(); 101 | }); 102 | }); 103 | }); 104 | }); 105 | -------------------------------------------------------------------------------- /samples-generator/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | }; 4 | -------------------------------------------------------------------------------- /samples-generator/lib/Extensions.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | 4 | var defined = Cesium.defined; 5 | 6 | module.exports = Extensions; 7 | 8 | function Extensions() { } 9 | 10 | /** 11 | * Add an extension to the list of extensions used for a tileset JSON. 12 | * @param {Object} tilesetJson The root tileset JSON object to which to add the extension. 13 | * @param {String} extensionName The name of the extension to add. 14 | */ 15 | Extensions.addExtensionsUsed = function(tilesetJson, extensionName) { 16 | var extensionsUsed = tilesetJson.extensionsUsed; 17 | 18 | if (!defined(extensionsUsed)) { 19 | extensionsUsed = tilesetJson.extensionsUsed = []; 20 | } 21 | 22 | extensionsUsed.push(extensionName); 23 | }; 24 | 25 | /** 26 | * Add an extension to the list of extensions required for a tileset JSON. 27 | * @param {Object} tilesetJson The root tileset JSON object to which to add the extension. 28 | * @param {String} extensionName The name of the extension to add. 29 | */ 30 | Extensions.addExtensionsRequired = function(tilesetJson, extensionName) { 31 | var extensionsRequired = tilesetJson.extensionsRequired; 32 | 33 | if (!defined(extensionsRequired)) { 34 | extensionsRequired = tilesetJson.extensionsRequired = []; 35 | } 36 | 37 | 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 | Extensions.addExtension = function(tilesetJson, extensionName, extension) { 47 | var extensions = tilesetJson.extensions; 48 | 49 | if (!defined(extensions)) { 50 | extensions = tilesetJson.extensions = {}; 51 | } 52 | 53 | extensions[extensionName] = extension; 54 | }; 55 | -------------------------------------------------------------------------------- /samples-generator/lib/Material.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | 4 | var defaultValue = Cesium.defaultValue; 5 | 6 | module.exports = Material; 7 | 8 | /** 9 | * A material that is applied to a mesh. 10 | * 11 | * @param {Object} [options] An object with the following properties: 12 | * @param {Array|String} [options.baseColor] The base color or base color texture path. 13 | * 14 | * @constructor 15 | */ 16 | function Material(options) { 17 | options = defaultValue(options, defaultValue.EMPTY_OBJECT); 18 | this.baseColor = defaultValue(options.baseColor, [0.5, 0.5, 0.5, 1.0]); 19 | } 20 | 21 | /** 22 | * Creates a Material from a glTF material. This utility is designed only for simple glTFs like those in the data folder. 23 | * 24 | * @param {Object} material The glTF material. 25 | * @returns {Material} The material. 26 | */ 27 | Material.fromGltf = function(material) { 28 | return new Material({ 29 | baseColor : material.pbrMetallicRoughness.baseColorFactor 30 | }); 31 | }; 32 | -------------------------------------------------------------------------------- /samples-generator/lib/createB3dm.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | var getBufferPadded = require('./getBufferPadded'); 4 | var getJsonBufferPadded = require('./getJsonBufferPadded'); 5 | 6 | var 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 | var glb = options.glb; 25 | var defaultFeatureTable = { 26 | BATCH_LENGTH : 0 27 | }; 28 | var featureTableJson = defaultValue(options.featureTableJson, defaultFeatureTable); 29 | var batchLength = featureTableJson.BATCH_LENGTH; 30 | 31 | var headerByteLength = 28; 32 | var featureTableJsonBuffer = getJsonBufferPadded(featureTableJson, headerByteLength); 33 | var featureTableBinary = getBufferPadded(options.featureTableBinary); 34 | var batchTableJsonBuffer = getJsonBufferPadded(options.batchTableJson); 35 | var batchTableBinary = getBufferPadded(options.batchTableBinary); 36 | 37 | var deprecated1 = defaultValue(options.deprecated1, false); 38 | var 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 | var version = 1; 51 | var headerByteLength = 28; 52 | var featureTableJsonByteLength = featureTableJson.length; 53 | var featureTableBinaryByteLength = featureTableBinary.length; 54 | var batchTableJsonByteLength = batchTableJson.length; 55 | var batchTableBinaryByteLength = batchTableBinary.length; 56 | var gltfByteLength = glb.length; 57 | var byteLength = headerByteLength + featureTableJsonByteLength + featureTableBinaryByteLength + batchTableJsonByteLength + batchTableBinaryByteLength + gltfByteLength; 58 | 59 | var header = Buffer.alloc(headerByteLength); 60 | header.write('b3dm', 0); 61 | header.writeUInt32LE(version, 4); 62 | header.writeUInt32LE(byteLength, 8); 63 | header.writeUInt32LE(featureTableJsonByteLength, 12); 64 | header.writeUInt32LE(featureTableBinaryByteLength, 16); 65 | header.writeUInt32LE(batchTableJsonByteLength, 20); 66 | header.writeUInt32LE(batchTableBinaryByteLength, 24); 67 | 68 | return Buffer.concat([header, featureTableJson, featureTableBinary, batchTableJson, batchTableBinary, glb]); 69 | } 70 | 71 | function createB3dmDeprecated1(glb, batchLength, batchTableJson) { 72 | var version = 1; 73 | var headerByteLength = 20; 74 | var batchTableJsonByteLength = batchTableJson.length; 75 | var gltfByteLength = glb.length; 76 | var byteLength = headerByteLength + batchTableJsonByteLength + gltfByteLength; 77 | 78 | var header = Buffer.alloc(headerByteLength); 79 | header.write('b3dm', 0); 80 | header.writeUInt32LE(version, 4); 81 | header.writeUInt32LE(byteLength, 8); 82 | header.writeUInt32LE(batchLength, 12); 83 | header.writeUInt32LE(batchTableJsonByteLength, 16); 84 | 85 | return Buffer.concat([header, batchTableJson, glb]); 86 | } 87 | 88 | function createB3dmDeprecated2(glb, batchLength, batchTableJson, batchTableBinary) { 89 | var version = 1; 90 | var headerByteLength = 24; 91 | var batchTableJsonByteLength = batchTableJson.length; 92 | var batchTableBinaryByteLength = batchTableBinary.length; 93 | var gltfByteLength = glb.length; 94 | var byteLength = headerByteLength + batchTableJsonByteLength + batchTableBinaryByteLength + gltfByteLength; 95 | 96 | var header = Buffer.alloc(headerByteLength); 97 | header.write('b3dm', 0); 98 | header.writeUInt32LE(version, 4); 99 | header.writeUInt32LE(byteLength, 8); 100 | header.writeUInt32LE(batchTableJsonByteLength, 12); 101 | header.writeUInt32LE(batchTableBinaryByteLength, 16); 102 | header.writeUInt32LE(batchLength, 20); 103 | 104 | return Buffer.concat([header, batchTableJson, batchTableBinary, glb]); 105 | } 106 | -------------------------------------------------------------------------------- /samples-generator/lib/createCmpt.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var 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 | var byteLength = 0; 14 | var buffers = []; 15 | var tilesLength = tiles.length; 16 | for (var i = 0; i < tilesLength; i++) { 17 | var tile = getBufferPadded(tiles[i]); 18 | var tileByteLength = tile.length; 19 | tile.writeUInt32LE(tileByteLength, 8); // Edit the tile's byte length 20 | buffers.push(tile); 21 | byteLength += tileByteLength; 22 | } 23 | 24 | var version = 1; 25 | var headerByteLength = 16; 26 | byteLength += headerByteLength; 27 | 28 | var 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 | -------------------------------------------------------------------------------- /samples-generator/lib/createI3dm.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | var getJsonBufferPadded = require('./getJsonBufferPadded'); 4 | var getBufferPadded = require('./getBufferPadded'); 5 | 6 | var defined = Cesium.defined; 7 | 8 | module.exports = createI3dm; 9 | 10 | /** 11 | * Create an Instanced 3D Model (i3dm) tile from a feature table, batch table, and gltf buffer or uri. 12 | * 13 | * @param {Object} options An object with the following properties: 14 | * @param {Object} options.featureTableJson The feature table JSON. 15 | * @param {Buffer} options.featureTableBinary The feature table binary. 16 | * @param {Object} [options.batchTableJson] Batch table describing the per-feature metadata. 17 | * @param {Buffer} [options.batchTableBinary] The batch table binary. 18 | * @param {Buffer} [options.glb] The binary glTF buffer. 19 | * @param {String} [options.uri] Uri to an external glTF model when options.glb is not specified. 20 | * @returns {Buffer} The generated i3dm tile buffer. 21 | */ 22 | function createI3dm(options) { 23 | var featureTableJson = getJsonBufferPadded(options.featureTableJson); 24 | var featureTableBinary = getBufferPadded(options.featureTableBinary); 25 | var batchTableJson = getJsonBufferPadded(options.batchTableJson); 26 | var batchTableBinary = getBufferPadded(options.batchTableBinary); 27 | 28 | var gltfFormat = defined(options.glb) ? 1 : 0; 29 | var gltfBuffer = defined(options.glb) ? options.glb : getGltfUriBuffer(options.uri); 30 | 31 | var version = 1; 32 | var headerByteLength = 32; 33 | var featureTableJsonByteLength = featureTableJson.length; 34 | var featureTableBinaryByteLength = featureTableBinary.length; 35 | var batchTableJsonByteLength = batchTableJson.length; 36 | var batchTableBinaryByteLength = batchTableBinary.length; 37 | var gltfByteLength = gltfBuffer.length; 38 | var byteLength = headerByteLength + featureTableJsonByteLength + featureTableBinaryByteLength + batchTableJsonByteLength + batchTableBinaryByteLength + gltfByteLength; 39 | 40 | var header = Buffer.alloc(headerByteLength); 41 | header.write('i3dm', 0); 42 | header.writeUInt32LE(version, 4); 43 | header.writeUInt32LE(byteLength, 8); 44 | header.writeUInt32LE(featureTableJsonByteLength, 12); 45 | header.writeUInt32LE(featureTableBinaryByteLength, 16); 46 | header.writeUInt32LE(batchTableJsonByteLength, 20); 47 | header.writeUInt32LE(batchTableBinaryByteLength, 24); 48 | header.writeUInt32LE(gltfFormat, 28); 49 | 50 | return Buffer.concat([header, featureTableJson, featureTableBinary, batchTableJson, batchTableBinary, gltfBuffer]); 51 | } 52 | 53 | function getGltfUriBuffer(uri) { 54 | uri = uri.replace(/\\/g, '/'); 55 | return Buffer.from(uri); 56 | } 57 | -------------------------------------------------------------------------------- /samples-generator/lib/createPnts.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var getJsonBufferPadded = require('./getJsonBufferPadded'); 3 | var getBufferPadded = require('./getBufferPadded'); 4 | 5 | module.exports = createPnts; 6 | 7 | /** 8 | * Create a Point Cloud (pnts) tile from a feature table and batch table. 9 | * 10 | * @param {Object} options An object with the following properties: 11 | * @param {Object} options.featureTableJson The feature table JSON. 12 | * @param {Buffer} options.featureTableBinary The feature table binary. 13 | * @param {Object} [options.batchTableJson] Batch table describing the per-point metadata. 14 | * @param {Buffer} [options.batchTableBinary] The batch table binary. 15 | * @returns {Buffer} The generated pnts tile buffer. 16 | */ 17 | function createPnts(options) { 18 | var featureTableJson = getJsonBufferPadded(options.featureTableJson); 19 | var featureTableBinary = getBufferPadded(options.featureTableBinary); 20 | var batchTableJson = getJsonBufferPadded(options.batchTableJson); 21 | var batchTableBinary = getBufferPadded(options.batchTableBinary); 22 | 23 | var version = 1; 24 | var headerByteLength = 28; 25 | var featureTableJsonByteLength = featureTableJson.length; 26 | var featureTableBinaryByteLength = featureTableBinary.length; 27 | var batchTableJsonByteLength = batchTableJson.length; 28 | var batchTableBinaryByteLength = batchTableBinary.length; 29 | var byteLength = headerByteLength + featureTableJsonByteLength + featureTableBinaryByteLength + batchTableJsonByteLength + batchTableBinaryByteLength; 30 | 31 | var header = Buffer.alloc(headerByteLength); 32 | header.write('pnts', 0); 33 | header.writeUInt32LE(version, 4); 34 | header.writeUInt32LE(byteLength, 8); 35 | header.writeUInt32LE(featureTableJsonByteLength, 12); 36 | header.writeUInt32LE(featureTableBinaryByteLength, 16); 37 | header.writeUInt32LE(batchTableJsonByteLength, 20); 38 | header.writeUInt32LE(batchTableBinaryByteLength, 24); 39 | 40 | return Buffer.concat([header, featureTableJson, featureTableBinary, batchTableJson, batchTableBinary]); 41 | } 42 | -------------------------------------------------------------------------------- /samples-generator/lib/createTilesetJsonSingle.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | 4 | var defaultValue = Cesium.defaultValue; 5 | var defined = Cesium.defined; 6 | var Matrix4 = Cesium.Matrix4; 7 | 8 | module.exports = createTilesetJsonSingle; 9 | 10 | var defaultTilesetVersion = '1.0'; 11 | 12 | /** 13 | * Create a tileset JSON for a single tile. 14 | * 15 | * @param {Object} options Object with the following properties: 16 | * @param {String} options.contentUri The content URI of the root tile. This may be a relative filepath or a data URI. 17 | * @param {Number} options.geometricError Geometric error of the tile. 18 | * @param {String} options.versionNumber The 3D Tiles version number string. 19 | * @param {Object} [options.region] Bounding region of the tile. 20 | * @param {Object} [options.box] Bounding box of the tile. 21 | * @param {Object} [options.sphere] Bounding sphere of the tile. 22 | * @param {Matrix4} [options.transform=Matrix4.IDENTITY] The tile transform. 23 | * @param {Object} [options.properties] An object containing the min and max values for each property in the batch table. 24 | * @param {Object} [options.extensions] An object containing extensionsUsed, extensionsRequired, and extensions properties. 25 | * @param {Object} [options.expire] Tile expiration options. 26 | * 27 | * @returns {Object} The tileset JSON. 28 | */ 29 | function createTilesetJsonSingle(options) { 30 | var transform = defaultValue(options.transform, Matrix4.IDENTITY); 31 | var transformArray = (defined(transform) && !Matrix4.equals(transform, Matrix4.IDENTITY)) ? Matrix4.pack(transform, new Array(16)) : undefined; 32 | var boundingVolume = getBoundingVolume(options.region, options.box, options.sphere); 33 | var extensions = defaultValue(options.extensions, defaultValue.EMPTY_OBJECT); 34 | 35 | var tilesetJson = { 36 | asset : { 37 | version : defaultValue(options.versionNumber, defaultTilesetVersion) 38 | }, 39 | properties : options.properties, 40 | extensionsUsed : extensions.extensionsUsed, 41 | extensionsRequired : extensions.extensionsRequired, 42 | extensions : extensions.extensions, 43 | geometricError : options.geometricError, 44 | root : { 45 | transform : transformArray, 46 | expire : options.expire, 47 | refine : 'ADD', 48 | boundingVolume : boundingVolume, 49 | geometricError : 0.0, 50 | content : { 51 | uri : options.contentUri 52 | } 53 | } 54 | }; 55 | 56 | return tilesetJson; 57 | } 58 | 59 | function getBoundingVolume(region, box, sphere) { 60 | if (defined(region)) { 61 | return { 62 | region : region 63 | }; 64 | } else if (defined(box)) { 65 | return { 66 | box : box 67 | }; 68 | } else if (defined(sphere)) { 69 | return { 70 | sphere : sphere 71 | }; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /samples-generator/lib/getBufferPadded.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | 4 | var defaultValue = Cesium.defaultValue; 5 | var defined = Cesium.defined; 6 | 7 | module.exports = getBufferPadded; 8 | 9 | /** 10 | * Pad the buffer to the next 8-byte boundary to ensure proper alignment for the section that follows. 11 | * 12 | * @param {Buffer} buffer The buffer. 13 | * @param {Number} [byteOffset=0] The byte offset on which the buffer starts. 14 | * @returns {Buffer} The padded buffer. 15 | */ 16 | function getBufferPadded(buffer, byteOffset) { 17 | if (!defined(buffer)) { 18 | return Buffer.alloc(0); 19 | } 20 | 21 | byteOffset = defaultValue(byteOffset, 0); 22 | 23 | var boundary = 8; 24 | var byteLength = buffer.length; 25 | var remainder = (byteOffset + byteLength) % boundary; 26 | var padding = (remainder === 0) ? 0 : boundary - remainder; 27 | var emptyBuffer = Buffer.alloc(padding); 28 | return Buffer.concat([buffer, emptyBuffer]); 29 | } 30 | -------------------------------------------------------------------------------- /samples-generator/lib/getJsonBufferPadded.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | 4 | var defaultValue = Cesium.defaultValue; 5 | var defined = Cesium.defined; 6 | 7 | module.exports = getJsonBufferPadded; 8 | 9 | /** 10 | * Convert the JSON object to a padded buffer. 11 | * 12 | * Pad the JSON with extra whitespace to fit the next 8-byte boundary. This ensures proper alignment 13 | * for the section that follows (for example, batch table binary or feature table binary). 14 | * 15 | * @param {Object} [json] The JSON object. 16 | * @param {Number} [byteOffset=0] The byte offset on which the buffer starts. 17 | * @returns {Buffer} The padded JSON buffer. 18 | */ 19 | function getJsonBufferPadded(json, byteOffset) { 20 | if (!defined(json)) { 21 | return Buffer.alloc(0); 22 | } 23 | 24 | byteOffset = defaultValue(byteOffset, 0); 25 | var string = JSON.stringify(json); 26 | 27 | var boundary = 8; 28 | var byteLength = Buffer.byteLength(string); 29 | var remainder = (byteOffset + byteLength) % boundary; 30 | var padding = (remainder === 0) ? 0 : boundary - remainder; 31 | var whitespace = ''; 32 | for (var i = 0; i < padding; ++i) { 33 | whitespace += ' '; 34 | } 35 | string += whitespace; 36 | 37 | return Buffer.from(string); 38 | } 39 | -------------------------------------------------------------------------------- /samples-generator/lib/getProperties.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | 4 | var defined = Cesium.defined; 5 | 6 | module.exports = getProperties; 7 | 8 | /** 9 | * Get the minimum and maximum values for each property in the batch table. 10 | * Ignore properties in the batch table binary for now. Also ignore non-number values. 11 | * 12 | * @param {Object|Object[]} batchTable The batch table(s). 13 | * @returns {Object} An object with the minimum and maximum values for each property in the batch table. 14 | */ 15 | function getProperties(batchTable) { 16 | if (!defined(batchTable)) { 17 | return undefined; 18 | } 19 | var properties = {}; 20 | var batchTables = Array.isArray(batchTable) ? batchTable : [batchTable]; 21 | var batchTablesLength = batchTables.length; 22 | for (var i = 0; i < batchTablesLength; ++i) { 23 | batchTable = batchTables[i]; 24 | for (var name in batchTable) { 25 | if (batchTable.hasOwnProperty(name)) { 26 | var values = batchTable[name]; 27 | if (Array.isArray(values)) { 28 | if (typeof values[0] === 'number') { 29 | if (!defined(properties[name])) { 30 | properties[name] = { 31 | minimum : Number.POSITIVE_INFINITY, 32 | maximum : Number.NEGATIVE_INFINITY 33 | }; 34 | } 35 | var min = properties[name].minimum; 36 | var max = properties[name].maximum; 37 | var length = values.length; 38 | for (var j = 0; j < length; ++j) { 39 | var value = values[j]; 40 | min = Math.min(value, min); 41 | max = Math.max(value, max); 42 | } 43 | properties[name].minimum = min; 44 | properties[name].maximum = max; 45 | } 46 | } 47 | } 48 | } 49 | } 50 | if (Object.keys(properties).length === 0) { 51 | return undefined; 52 | } 53 | return properties; 54 | } 55 | -------------------------------------------------------------------------------- /samples-generator/lib/saveTile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | var fsExtra = require('fs-extra'); 4 | var zlib = require('zlib'); 5 | 6 | var defaultValue = Cesium.defaultValue; 7 | 8 | module.exports = saveTile; 9 | 10 | /** 11 | * Save a tile to disk. 12 | * 13 | * @param {String} path The tile path. 14 | * @param {Buffer} contents The contents of the tile. 15 | * @param {Boolean} [gzip=false] Whether to gzip the tile. 16 | * 17 | * @returns {Promise} A promise that resolves when the tile is saved. 18 | */ 19 | function saveTile(path, contents, gzip) { 20 | gzip = defaultValue(gzip, false); 21 | if (gzip) { 22 | contents = zlib.gzipSync(contents); 23 | } 24 | return fsExtra.outputFile(path, contents); 25 | } 26 | -------------------------------------------------------------------------------- /samples-generator/lib/saveTilesetJson.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | var fsExtra = require('fs-extra'); 4 | 5 | var defaultValue = Cesium.defaultValue; 6 | 7 | module.exports = saveTilesetJson; 8 | 9 | /** 10 | * Save a tileset.json file to disk. 11 | * 12 | * @param {String} path The path to save the tileset.json. 13 | * @param {Object} json The JSON. 14 | * @param {Boolean} [prettyJson=true] Whether to prettify the JSON. 15 | * 16 | * @returns {Promise} A promise that resolves when the tileset.json is saved. 17 | */ 18 | function saveTilesetJson(path, json, prettyJson) { 19 | prettyJson = defaultValue(prettyJson, true); 20 | var options = {}; 21 | if (prettyJson) { 22 | options.spaces = 2; 23 | } 24 | return fsExtra.outputJson(path, json, options); 25 | } 26 | -------------------------------------------------------------------------------- /samples-generator/lib/utility.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | 4 | var Cartesian3 = Cesium.Cartesian3; 5 | var HeadingPitchRoll = Cesium.HeadingPitchRoll; 6 | var Transforms = Cesium.Transforms; 7 | 8 | module.exports = { 9 | metersToLongitude : metersToLongitude, 10 | metersToLatitude : metersToLatitude, 11 | wgs84Transform : wgs84Transform, 12 | lowercase : lowercase, 13 | typeToNumberOfComponents : typeToNumberOfComponents 14 | }; 15 | 16 | function metersToLongitude(meters, latitude) { 17 | return meters * 0.000000156785 / Math.cos(latitude); 18 | } 19 | 20 | function metersToLatitude(meters) { 21 | return meters * 0.000000157891; 22 | } 23 | 24 | function wgs84Transform(longitude, latitude, height) { 25 | return Transforms.headingPitchRollToFixedFrame(Cartesian3.fromRadians(longitude, latitude, height), new HeadingPitchRoll()); 26 | } 27 | 28 | function lowercase(s) { 29 | return s[0].toLowerCase() + s.slice(1); 30 | } 31 | 32 | function typeToNumberOfComponents(type) { 33 | switch (type) { 34 | case 'SCALAR': 35 | return 1; 36 | case 'VEC2': 37 | return 2; 38 | case 'VEC3': 39 | return 3; 40 | case 'VEC4': 41 | return 4; 42 | case 'MAT2': 43 | return 4; 44 | case 'MAT3': 45 | return 9; 46 | case 'MAT4': 47 | return 16; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /samples-generator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "3d-tiles-samples-generator", 3 | "version": "0.1.0", 4 | "license": "Apache-2.0", 5 | "description": "Tools for generating sample 3D Tiles tilesets.", 6 | "author": { 7 | "name": "Analytical Graphics, Inc. and Contributors", 8 | "url": "https://github.com/AnalyticalGraphicsInc/3d-tiles-tools/graphs/contributors" 9 | }, 10 | "keywords": [ 11 | "3D Tiles" 12 | ], 13 | "homepage": "https://github.com/AnalyticalGraphicsInc/3d-tiles-tools", 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/AnalyticalGraphicsInc/3d-tiles-tools.git" 17 | }, 18 | "bugs": { 19 | "url": "https://github.com/AnalyticalGraphicsInc/3d-tiles-tools/issues" 20 | }, 21 | "main": "index.js", 22 | "engines": { 23 | "node": ">=4.0.0" 24 | }, 25 | "dependencies": { 26 | "bluebird": "^3.5.1", 27 | "cesium": "^1.39", 28 | "draco3d": "^1.3.3", 29 | "fs-extra": "^4.0.2", 30 | "gltf-pipeline": "^2.0.0", 31 | "mime": "^2.0.3", 32 | "simplex-noise": "^2.3.0" 33 | }, 34 | "devDependencies": { 35 | "cloc": "^2.3.3", 36 | "datauri": "^1.1.0", 37 | "eslint": "^4.10.0", 38 | "eslint-config-cesium": "^2.0.1", 39 | "gulp": "^3.9.1", 40 | "jasmine": "^2.8.0", 41 | "jasmine-spec-reporter": "^4.2.1", 42 | "nyc": "^11.3.0", 43 | "open": "^0.0.5", 44 | "requirejs": "^2.3.5", 45 | "yargs": "^10.0.3" 46 | }, 47 | "scripts": { 48 | "eslint": "eslint \"./**/*.js\" --cache --quiet", 49 | "test": "gulp test", 50 | "test-watch": "gulp test-watch", 51 | "coverage": "gulp coverage", 52 | "cloc": "gulp cloc" 53 | }, 54 | "bin": { 55 | "3d-tiles-samples-generator": "./bin/3d-tiles-samples-generator" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /samples-generator/specs/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.eslintrc.json", 3 | "env": { 4 | "jasmine": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /samples-generator/specs/bin/generatorSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var child_process = require('child_process'); 3 | var path = require('path'); 4 | 5 | var scriptPath = path.join(__dirname, '../../bin/3d-tiles-samples-generator.js'); 6 | 7 | describe('3d-tiles-samples-generator', function () { 8 | it('runs', function (done) { 9 | var command = 'node'; 10 | var args = [scriptPath]; 11 | var child = child_process.spawn(command, args); 12 | child.once('error', function (e) { 13 | fail(e); 14 | }); 15 | child.once('exit', function (code) { 16 | if (code !== 0) { 17 | fail('3d-tiles-samples-generator.js exited with an error code of ' + code); 18 | } else { 19 | done(); 20 | } 21 | }); 22 | }, 20000); 23 | }); 24 | -------------------------------------------------------------------------------- /samples-generator/specs/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "specs", 3 | "spec_files": [ 4 | "**/*Spec.js" 5 | ], 6 | "helpers": [ 7 | "matchers/nodeHelper.js" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /samples-generator/specs/matchers/customizeJasmine.js: -------------------------------------------------------------------------------- 1 | /*eslint strict: ["error", "function"]*/ 2 | /*eslint-env amd*/ 3 | define([ 4 | './addDefaultMatchers', 5 | './equalsMethodEqualityTester' 6 | ], function (addDefaultMatchers, 7 | equalsMethodEqualityTester) { 8 | 'use strict'; 9 | 10 | return function (env) { 11 | env.beforeEach(function () { 12 | addDefaultMatchers(true).call(env); 13 | env.addCustomEqualityTester(equalsMethodEqualityTester); 14 | }); 15 | }; 16 | }); 17 | -------------------------------------------------------------------------------- /samples-generator/specs/matchers/equals.js: -------------------------------------------------------------------------------- 1 | //This file is a copy of https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Specs/equals.js 2 | /*eslint strict: ["error", "function"]*/ 3 | /*eslint-env amd*/ 4 | define([ 5 | 'Cesium/Core/FeatureDetection' 6 | ], function( 7 | FeatureDetection) { 8 | 'use strict'; 9 | /*global CanvasPixelArray*/ 10 | 11 | var typedArrayTypes = []; 12 | 13 | // Earlier versions of IE do not support typed arrays 14 | if (FeatureDetection.supportsTypedArrays()) { 15 | typedArrayTypes.push(Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array); 16 | 17 | if (typeof Uint8ClampedArray !== 'undefined') { 18 | typedArrayTypes.push(Uint8ClampedArray); 19 | } 20 | 21 | if (typeof CanvasPixelArray !== 'undefined') { 22 | typedArrayTypes.push(CanvasPixelArray); 23 | } 24 | } 25 | 26 | function isTypedArray(o) { 27 | return typedArrayTypes.some(function(type) { 28 | return o instanceof type; 29 | }); 30 | } 31 | 32 | function typedArrayToArray(array) { 33 | if (array !== null && typeof array === 'object' && isTypedArray(array)) { 34 | return Array.prototype.slice.call(array, 0); 35 | } 36 | return array; 37 | } 38 | 39 | function equals(util, customEqualiyTesters, a, b) { 40 | a = typedArrayToArray(a); 41 | b = typedArrayToArray(b); 42 | return util.equals(a, b, customEqualiyTesters); 43 | } 44 | 45 | return equals; 46 | }); 47 | -------------------------------------------------------------------------------- /samples-generator/specs/matchers/equalsMethodEqualityTester.js: -------------------------------------------------------------------------------- 1 | //This file is a copy of https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Specs/equalsMethodEqualityTester.js 2 | /*eslint strict: ["error", "function"]*/ 3 | /*eslint-env amd*/ 4 | define([ 5 | 'Cesium/Core/defined' 6 | ], function( 7 | defined) { 8 | 'use strict'; 9 | 10 | return function(a, b) { 11 | var to_run; 12 | // if either a or b have an equals method, call it. 13 | if (a !== null && defined(a)) { 14 | if (typeof a.equals === 'function') { 15 | return a.equals(b); 16 | } else if(a instanceof Object) { 17 | // Check if the current object has a static function named 'equals' 18 | to_run = Object.getPrototypeOf(a).constructor.equals; 19 | if( typeof to_run === 'function') { 20 | return to_run(a, b); 21 | } 22 | } 23 | } 24 | 25 | if (b !== null && defined(b)) { 26 | if (typeof b.equals === 'function') { 27 | return b.equals(a); 28 | } else if(b instanceof Object) { 29 | // Check if the current object has a static function named 'equals' 30 | to_run = Object.getPrototypeOf(b).constructor.equals; 31 | if( typeof to_run === 'function') { 32 | return to_run(b, a); 33 | } 34 | } 35 | } 36 | 37 | // fall back to default equality checks. 38 | return undefined; 39 | }; 40 | }); 41 | -------------------------------------------------------------------------------- /samples-generator/specs/matchers/expectPromise.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Cesium = require('cesium'); 4 | 5 | var defined = Cesium.defined; 6 | var defaultValue = Cesium.defaultValue; 7 | 8 | module.exports = function expectPromise(promise, done) { 9 | return { 10 | toResolve: function toResolve() { 11 | return promise 12 | .then(done) 13 | .catch(function(err){ 14 | done.fail('Expected promise to resolve' + err); 15 | }); 16 | }, 17 | toResolveWith: function toResolveWith(expectedValue) { 18 | return promise 19 | .then(function (result) { 20 | expect(result).toEqual(expectedValue); 21 | done(); 22 | }) 23 | .catch(function(err){ 24 | done.fail('Expected promise to resolve' + err); 25 | }); 26 | }, 27 | toRejectWith: function toRejectWith(ErrorType, errorMessage) { 28 | var typeName = defaultValue(ErrorType.displayName, ErrorType.name); 29 | 30 | promise 31 | .then(function () { 32 | done.fail('expected promise to reject with ' + typeName); 33 | }) 34 | .catch(function (error) { 35 | if (!(error instanceof ErrorType)) { 36 | done.fail(defaultValue(defaultValue(error.displayName, error.name), ErrorType) + ' to be instance of ' + typeName); 37 | console.log(error); 38 | } 39 | 40 | if (defined(errorMessage)) { 41 | expect(error.message).toEqual(errorMessage); 42 | } 43 | done(); 44 | }); 45 | } 46 | }; 47 | }; 48 | -------------------------------------------------------------------------------- /samples-generator/specs/matchers/nodeHelper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var requirejs = require('requirejs'); 5 | 6 | var expectPromise = require('./expectPromise'); 7 | 8 | //Since Jasmine matchers are shared between client and server code 9 | //We need to use requirejs to bring them into node. 10 | requirejs.config({ 11 | baseUrl: path.join(__dirname, '../..'), 12 | paths: { 13 | 'Cesium': 'node_modules/cesium/source' 14 | }, 15 | nodeRequire: require 16 | }); 17 | 18 | var customizeJasmine = requirejs('./specs/matchers/customizeJasmine'); 19 | 20 | var env = jasmine.getEnv(); 21 | customizeJasmine(env); 22 | 23 | var oldExpect = global.expect; 24 | global.expect = function (promise, done) { 25 | //We can't use instanceof Promise here because promise 26 | //may not be a bluebird-defined Promise 27 | if (promise && promise.then && done) { 28 | return expectPromise(promise, done); 29 | } 30 | 31 | //If it wasn't a promise, call original implementation 32 | return oldExpect.apply(global, arguments); 33 | }; 34 | -------------------------------------------------------------------------------- /tools/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/** 2 | coverage/** 3 | doc/** 4 | -------------------------------------------------------------------------------- /tools/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "cesium/node" 3 | } 4 | -------------------------------------------------------------------------------- /tools/.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | package.json text eol=lf -------------------------------------------------------------------------------- /tools/.gitignore: -------------------------------------------------------------------------------- 1 | # NPM 2 | node_modules 3 | npm-debug.log 4 | package-lock.json 5 | 6 | # WebStorm user-specific 7 | .idea/workspace.xml 8 | .idea/tasks.xml 9 | 10 | # Generated files 11 | .nyc_output 12 | .eslintcache 13 | doc 14 | coverage 15 | output 16 | *.tgz 17 | *.zip 18 | 19 | # User files 20 | test -------------------------------------------------------------------------------- /tools/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /tools/.idea/jsLibraryMappings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /tools/.idea/jsLinters/jshint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 85 | -------------------------------------------------------------------------------- /tools/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /tools/.idea/tools.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /tools/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /tools/.npmignore: -------------------------------------------------------------------------------- 1 | /coverage 2 | /doc 3 | /output 4 | /test 5 | /specs 6 | /tools 7 | /.idea 8 | .eslintrc.json 9 | .gitattributes 10 | .npmignore 11 | .travis.yml 12 | gulpfile.js 13 | *.tgz 14 | .eslintcache 15 | .nyc_output 16 | -------------------------------------------------------------------------------- /tools/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /tools/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 8 4 | script: 5 | - npm run eslint 6 | - npm run test -- --failTaskOnError --suppressPassed 7 | after_success: 'npm run coverage' -------------------------------------------------------------------------------- /tools/CHANGES.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | ### 0.1.3 - 2017-04-14 5 | 6 | * Cleaned up project files and upgraded dependencies. [#70](https://github.com/AnalyticalGraphicsInc/3d-tiles-tools/pull/70) 7 | 8 | ### 0.1.2 - 2017-04-07 9 | 10 | * Breaking changes 11 | * `extractB3dm` and `extractI3dm` now return the feature table JSON and batch table JSON instead of buffers. 12 | * `glbToB3dm` and `glbToI3dm` now take feature table JSON and batch table JSON instead of buffers. 13 | * Handle b3dm tiles with the legacy 24-byte header. [#69](https://github.com/AnalyticalGraphicsInc/3d-tiles-tools/pull/69) 14 | 15 | ### 0.1.1 - 2017-03-15 16 | 17 | * Breaking changes 18 | * Renamed `tileset2sqlite3` to `tilesetToDatabase`. 19 | * Added `databaseToTileset` for unpacking a .3dtiles file to a tileset directory. [#62](https://github.com/AnalyticalGraphicsInc/3d-tiles-tools/pull/62) 20 | * Added `glbToI3dm` and `optimizeI3dm` command line tools. [#46](https://github.com/AnalyticalGraphicsInc/3d-tiles-tools/pull/46) 21 | * Handle b3dm tiles with the legacy 20-byte header. [#45](https://github.com/AnalyticalGraphicsInc/3d-tiles-tools/pull/45) 22 | * Added `extractCmpt` to extract inner tiles from a cmpt tile and the `cmptToGlb` command line tool. [#42](https://github.com/AnalyticalGraphicsInc/3d-tiles-tools/pull/42) 23 | 24 | ### 0.1.0 - 2016-12-16 25 | 26 | * Initial release. -------------------------------------------------------------------------------- /tools/gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Cesium = require('cesium'); 4 | var child_process = require('child_process'); 5 | var fsExtra = require('fs-extra'); 6 | var gulp = require('gulp'); 7 | var Jasmine = require('jasmine'); 8 | var jasmineSpecReporter = require('jasmine-spec-reporter'); 9 | var open = require('open'); 10 | var path = require('path'); 11 | var Promise = require('bluebird'); 12 | var yargs = require('yargs'); 13 | 14 | var defined = Cesium.defined; 15 | var argv = yargs.argv; 16 | 17 | // Add third-party node module binaries to the system path 18 | // since some tasks need to call them directly. 19 | var environmentSeparator = process.platform === 'win32' ? ';' : ':'; 20 | var nodeBinaries = path.join(__dirname, 'node_modules', '.bin'); 21 | process.env.PATH += environmentSeparator + nodeBinaries; 22 | 23 | var specFiles = ['**/*.js', '!node_modules/**', '!coverage/**']; 24 | 25 | gulp.task('test', function (done) { 26 | var jasmine = new Jasmine(); 27 | jasmine.loadConfigFile('specs/jasmine.json'); 28 | jasmine.addReporter(new jasmineSpecReporter.SpecReporter({ 29 | displaySuccessfulSpec: !defined(argv.suppressPassed) || !argv.suppressPassed 30 | })); 31 | jasmine.execute(); 32 | jasmine.onComplete(function (passed) { 33 | done(argv.failTaskOnError && !passed ? 1 : 0); 34 | }); 35 | }); 36 | 37 | gulp.task('test-watch', function () { 38 | gulp.watch(specFiles).on('change', function () { 39 | // We can't simply depend on the test task because Jasmine 40 | // does not like being run multiple times in the same process. 41 | try { 42 | child_process.execSync('jasmine JASMINE_CONFIG_PATH=specs/jasmine.json', { 43 | stdio: [process.stdin, process.stdout, process.stderr] 44 | }); 45 | } catch (exception) { 46 | console.log('Tests failed to execute.'); 47 | } 48 | }); 49 | }); 50 | 51 | gulp.task('coverage', function () { 52 | fsExtra.removeSync('coverage/server'); 53 | child_process.execSync('nyc' + 54 | ' --all' + 55 | ' --reporter=lcov' + 56 | ' --dir coverage' + 57 | ' -x "doc/**"' + 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('jsDoc', function() { 70 | return new Promise(function(resolve, reject) { 71 | child_process.exec('jsdoc --configure tools/jsdoc/conf.json', function(error, stdout, stderr) { 72 | if (error) { 73 | console.log(stderr); 74 | return reject(error); 75 | } 76 | console.log(stdout); 77 | open('doc/index.html'); 78 | resolve(); 79 | }); 80 | }); 81 | }); 82 | 83 | gulp.task('cloc', function() { 84 | var cmdLine; 85 | var clocPath = path.join('node_modules', 'cloc', 'lib', 'cloc'); 86 | 87 | //Run cloc on primary Source files only 88 | var source = new Promise(function(resolve, reject) { 89 | cmdLine = 'perl ' + clocPath + ' --quiet --progress-rate=0' + 90 | ' lib/ bin/'; 91 | 92 | child_process.exec(cmdLine, function(error, stdout, stderr) { 93 | if (error) { 94 | console.log(stderr); 95 | return reject(error); 96 | } 97 | console.log('Source:'); 98 | console.log(stdout); 99 | resolve(); 100 | }); 101 | }); 102 | 103 | //If running cloc on source succeeded, also run it on the tests. 104 | return source.then(function() { 105 | return new Promise(function(resolve, reject) { 106 | cmdLine = 'perl ' + clocPath + ' --quiet --progress-rate=0' + 107 | ' specs/lib/'; 108 | child_process.exec(cmdLine, function(error, stdout, stderr) { 109 | if (error) { 110 | console.log(stderr); 111 | return reject(error); 112 | } 113 | console.log('Specs:'); 114 | console.log(stdout); 115 | resolve(); 116 | }); 117 | }); 118 | }); 119 | }); 120 | 121 | -------------------------------------------------------------------------------- /tools/index.js: -------------------------------------------------------------------------------- 1 | /*eslint-disable global-require*/ 2 | 'use strict'; 3 | module.exports = { 4 | databaseToTileset : require('./lib/databaseToTileset'), 5 | extractB3dm : require('./lib/extractB3dm'), 6 | extractCmpt : require('./lib/extractCmpt'), 7 | extractI3dm : require('./lib/extractI3dm'), 8 | glbToB3dm : require('./lib/glbToB3dm'), 9 | glbToI3dm : require('./lib/glbToI3dm'), 10 | gzipTileset : require('./lib/gzipTileset'), 11 | runPipeline : require('./lib/runPipeline'), 12 | tilesetToDatabase : require('./lib/tilesetToDatabase') 13 | }; 14 | -------------------------------------------------------------------------------- /tools/lib/bufferToJson.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = bufferToJson; 3 | 4 | /** 5 | * @private 6 | */ 7 | function bufferToJson(buffer) { 8 | if (buffer.length === 0) { 9 | return {}; 10 | } 11 | return JSON.parse(buffer.toString()); 12 | } 13 | -------------------------------------------------------------------------------- /tools/lib/databaseToTileset.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | var fsExtra = require('fs-extra'); 4 | var path = require('path'); 5 | var Promise = require('bluebird'); 6 | var sqlite3 = require('sqlite3'); 7 | var zlib = require('zlib'); 8 | var isGzipped = require('../lib/isGzipped'); 9 | 10 | var defaultValue = Cesium.defaultValue; 11 | var defined = Cesium.defined; 12 | var DeveloperError = Cesium.DeveloperError; 13 | 14 | module.exports = databaseToTileset; 15 | 16 | /** 17 | * Unpacks a .3dtiles database to a tileset folder. 18 | * 19 | * @param {String} inputFile The input .3dtiles database file. 20 | * @param {String} [outputDirectory] The output directory of the tileset. 21 | * @returns {Promise} A promise that resolves when the tileset is written. 22 | */ 23 | function databaseToTileset(inputFile, outputDirectory) { 24 | if (!defined(inputFile)) { 25 | throw new DeveloperError('inputFile is required.'); 26 | } 27 | 28 | outputDirectory = defaultValue(outputDirectory, path.join(path.dirname(inputFile), path.basename(inputFile, path.extname(inputFile)))); 29 | 30 | // Open the database. 31 | var db = new sqlite3.Database(inputFile, sqlite3.OPEN_READWRITE); 32 | var dbAll = Promise.promisify(db.all, {context : db}); 33 | 34 | // Read a chunk of rows from the database at a time. Since the row contents contain tile blobs the limit should not be too high. 35 | var offset = 0; 36 | var limit = 100; 37 | var processChunk = function() { 38 | return dbAll('SELECT * FROM media LIMIT ? OFFSET ?', limit, offset) 39 | .then(function(rows) { 40 | if (rows.length === 0) { 41 | // No more rows left 42 | return Promise.resolve(); 43 | } 44 | return Promise.map(rows, function(row) { 45 | ++offset; 46 | return writeFile(outputDirectory, row.key, row.content); 47 | }) 48 | .then(function () { 49 | return processChunk(); 50 | }); 51 | }); 52 | }; 53 | 54 | return processChunk().finally(function() { 55 | db.close(); 56 | }); 57 | } 58 | 59 | function writeFile(outputDirectory, file, data) { 60 | var filePath = path.normalize(path.join(outputDirectory, file)); 61 | if (isGzipped(data)) { 62 | data = zlib.gunzipSync(data); 63 | } 64 | return fsExtra.outputFile(filePath, data); 65 | } 66 | -------------------------------------------------------------------------------- /tools/lib/directoryExists.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var fsExtra = require('fs-extra'); 3 | 4 | module.exports = directoryExists; 5 | 6 | /** 7 | * @private 8 | */ 9 | function directoryExists(directory) { 10 | return fsExtra.stat(directory) 11 | .then(function(stats) { 12 | return stats.isDirectory(); 13 | }) 14 | .catch(function(err) { 15 | // If the directory doesn't exist the error code is ENOENT. 16 | // Otherwise something else went wrong - permission issues, etc. 17 | if (err.code !== 'ENOENT') { 18 | throw err; 19 | } 20 | return false; 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /tools/lib/extractB3dm.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | var bufferToJson = require('./bufferToJson'); 4 | var getMagic = require('./getMagic'); 5 | 6 | var defined = Cesium.defined; 7 | var DeveloperError = Cesium.DeveloperError; 8 | 9 | module.exports = extractB3dm; 10 | 11 | /** 12 | * Extracts information and sections from a b3dm buffer. 13 | * 14 | * @param {Buffer} b3dmBuffer A buffer containing a b3dm asset. 15 | * @returns {Object} An object containing the header and sections of the b3dm asset. 16 | */ 17 | function extractB3dm(b3dmBuffer) { 18 | if (!defined(b3dmBuffer)) { 19 | throw new DeveloperError('b3dmBuffer is not defined.'); 20 | } 21 | var magic = getMagic(b3dmBuffer); 22 | if (magic !== 'b3dm') { 23 | throw new DeveloperError('Invalid magic, expected "b3dm", got: "' + magic + '".'); 24 | } 25 | var version = b3dmBuffer.readUInt32LE(4); 26 | if (version !== 1) { 27 | throw new DeveloperError('Invalid version, only "1" is valid, got: "' + version + '".'); 28 | } 29 | var headerByteLength = 28; 30 | var byteLength = b3dmBuffer.readUInt32LE(8); 31 | var featureTableJsonByteLength = b3dmBuffer.readUInt32LE(12); 32 | var featureTableBinaryByteLength = b3dmBuffer.readUInt32LE(16); 33 | var batchTableJsonByteLength = b3dmBuffer.readUInt32LE(20); 34 | var batchTableBinaryByteLength = b3dmBuffer.readUInt32LE(24); 35 | var batchLength = 0; 36 | 37 | // Keep this legacy check in for now since a lot of tilesets are still using the old header. 38 | // Legacy header #1: [batchLength] [batchTableByteLength] 39 | // Legacy header #2: [batchTableJsonByteLength] [batchTableBinaryByteLength] [batchLength] 40 | // Current header: [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] 41 | // If the header is in the first legacy format 'batchTableJsonByteLength' will be the start of the JSON string (a quotation mark) or the glTF magic. 42 | // Accordingly its first byte will be either 0x22 or 0x67, and so the minimum uint32 expected is 0x22000000 = 570425344 = 570MB. It is unlikely that the feature table Json will exceed this length. 43 | // The check for the second legacy format is similar, except it checks 'batchTableBinaryByteLength' instead 44 | if (batchTableJsonByteLength >= 570425344) { 45 | // First legacy check 46 | headerByteLength = 20; 47 | batchLength = featureTableJsonByteLength; 48 | batchTableJsonByteLength = featureTableBinaryByteLength; 49 | batchTableBinaryByteLength = 0; 50 | featureTableJsonByteLength = 0; 51 | featureTableBinaryByteLength = 0; 52 | } else if (batchTableBinaryByteLength >= 570425344) { 53 | // Second legacy check 54 | headerByteLength = 24; 55 | batchLength = batchTableJsonByteLength; 56 | batchTableJsonByteLength = featureTableJsonByteLength; 57 | batchTableBinaryByteLength = featureTableBinaryByteLength; 58 | featureTableJsonByteLength = 0; 59 | featureTableBinaryByteLength = 0; 60 | } 61 | 62 | var featureTableJsonByteOffset = headerByteLength; 63 | var featureTableBinaryByteOffset = featureTableJsonByteOffset + featureTableJsonByteLength; 64 | var batchTableJsonByteOffset = featureTableBinaryByteOffset + featureTableBinaryByteLength; 65 | var batchTableBinaryByteOffset = batchTableJsonByteOffset + batchTableJsonByteLength; 66 | var glbByteOffset = batchTableBinaryByteOffset + batchTableBinaryByteLength; 67 | 68 | var featureTableJsonBuffer = b3dmBuffer.slice(featureTableJsonByteOffset, featureTableBinaryByteOffset); 69 | var featureTableBinary = b3dmBuffer.slice(featureTableBinaryByteOffset, batchTableJsonByteOffset); 70 | var batchTableJsonBuffer = b3dmBuffer.slice(batchTableJsonByteOffset, batchTableBinaryByteOffset); 71 | var batchTableBinary = b3dmBuffer.slice(batchTableBinaryByteOffset, glbByteOffset); 72 | var glbBuffer = b3dmBuffer.slice(glbByteOffset, byteLength); 73 | glbBuffer = alignGlb(glbBuffer, glbByteOffset); 74 | 75 | var featureTableJson = bufferToJson(featureTableJsonBuffer); 76 | var batchTableJson = bufferToJson(batchTableJsonBuffer); 77 | 78 | if (Object.keys(featureTableJson).length === 0) { 79 | featureTableJson = { 80 | BATCH_LENGTH : batchLength 81 | }; 82 | } 83 | 84 | return { 85 | header : { 86 | magic : magic, 87 | version : version 88 | }, 89 | featureTable : { 90 | json : featureTableJson, 91 | binary : featureTableBinary 92 | }, 93 | batchTable : { 94 | json : batchTableJson, 95 | binary : batchTableBinary 96 | }, 97 | glb : glbBuffer 98 | }; 99 | } 100 | 101 | function alignGlb(buffer, byteOffset) { 102 | // The glb may not be aligned to an 8-byte boundary within the tile, causing gltf-pipeline operations to fail. 103 | // If unaligned, copy the glb to a new buffer. 104 | if (byteOffset % 8 === 0) { 105 | return buffer; 106 | } 107 | return Buffer.from(buffer); 108 | } 109 | -------------------------------------------------------------------------------- /tools/lib/extractCmpt.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | 4 | var getMagic = require('./getMagic'); 5 | 6 | var defined = Cesium.defined; 7 | var DeveloperError = Cesium.DeveloperError; 8 | 9 | module.exports = extractCmpt; 10 | 11 | /** 12 | * Extracts interior tiles from a cmpt buffer. This operates recursively on interior cmpt tiles. 13 | * 14 | * @param {Buffer} buffer A buffer containing a cmpt asset. 15 | * @returns {Buffer[]} An array containing interior tiles. 16 | */ 17 | function extractCmpt(buffer) { 18 | var results = []; 19 | extractCmptInner(buffer, results); 20 | return results; 21 | } 22 | 23 | function extractCmptInner(buffer, results) { 24 | if (!defined(buffer)) { 25 | throw new DeveloperError('buffer is not defined.'); 26 | } 27 | 28 | var magic = getMagic(buffer); 29 | if (magic !== 'cmpt') { 30 | throw new DeveloperError('Invalid magic, expected "cmpt", got: "' + magic + '".'); 31 | } 32 | 33 | var version = buffer.readUInt32LE(4); 34 | if (version !== 1) { 35 | throw new DeveloperError('Invalid version, only "1" is valid, got: "' + version + '".'); 36 | } 37 | 38 | var tilesLength = buffer.readUInt32LE(12); 39 | var byteOffset = 16; 40 | 41 | for (var i = 0; i < tilesLength; ++i) { 42 | var innerMagic = getMagic(buffer, byteOffset); 43 | var innerByteLength = buffer.readUInt32LE(byteOffset + 8); 44 | var innerBuffer = buffer.slice(byteOffset, byteOffset + innerByteLength); 45 | byteOffset += innerByteLength; 46 | 47 | if (innerMagic === 'cmpt') { 48 | extractCmptInner(innerBuffer, results); 49 | } else { 50 | results.push(innerBuffer); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tools/lib/extractI3dm.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Cesium = require('cesium'); 4 | var bufferToJson = require('./bufferToJson'); 5 | var getMagic = require('./getMagic'); 6 | 7 | var defined = Cesium.defined; 8 | var DeveloperError = Cesium.DeveloperError; 9 | 10 | module.exports = extractI3dm; 11 | 12 | /** 13 | * Extracts information and sections from an i3dm buffer. 14 | * 15 | * @param {Buffer} buffer A buffer containing an i3dm asset. 16 | * @returns {Object} An object containing the header and sections of the i3dm asset. 17 | */ 18 | function extractI3dm(buffer) { 19 | if (!defined(buffer)) { 20 | throw new DeveloperError('buffer is not defined.'); 21 | } 22 | var magic = getMagic(buffer); 23 | if (magic !== 'i3dm') { 24 | throw new DeveloperError('Invalid magic, expected "i3dm", got: "' + magic + '".'); 25 | } 26 | var version = buffer.readUInt32LE(4); 27 | if (version !== 1) { 28 | throw new DeveloperError('Invalid version, only "1" is valid, got: "' + version + '".'); 29 | } 30 | 31 | var byteLength = buffer.readUInt32LE(8); 32 | var featureTableJsonByteLength = buffer.readUInt32LE(12); 33 | var featureTableBinaryByteLength = buffer.readUInt32LE(16); 34 | var batchTableJsonByteLength = buffer.readUInt32LE(20); 35 | var batchTableBinaryByteLength = buffer.readUInt32LE(24); 36 | var gltfFormat = buffer.readUInt32LE(28); 37 | 38 | if (gltfFormat !== 1) { 39 | throw new DeveloperError('Only embedded binary glTF is supported.'); 40 | } 41 | 42 | var headerByteLength = 32; 43 | var featureTableJsonByteOffset = headerByteLength; 44 | var featureTableBinaryByteOffset = featureTableJsonByteOffset + featureTableJsonByteLength; 45 | var batchTableJsonByteOffset = featureTableBinaryByteOffset + featureTableBinaryByteLength; 46 | var batchTableBinaryByteOffset = batchTableJsonByteOffset + batchTableJsonByteLength; 47 | var gltfByteOffset = batchTableBinaryByteOffset + batchTableBinaryByteLength; 48 | 49 | var gltfByteLength = byteLength - gltfByteOffset; 50 | if (gltfByteLength === 0) { 51 | throw new DeveloperError('glTF byte length is zero, i3dm must have a glTF to instance.'); 52 | } 53 | 54 | var featureTableJsonBuffer = buffer.slice(featureTableJsonByteOffset, featureTableBinaryByteOffset); 55 | var featureTableBinaryBuffer = buffer.slice(featureTableBinaryByteOffset, batchTableJsonByteOffset); 56 | var batchTableJsonBuffer = buffer.slice(batchTableJsonByteOffset, batchTableBinaryByteOffset); 57 | var batchTableBinaryBuffer = buffer.slice(batchTableBinaryByteOffset, gltfByteOffset); 58 | var glbBuffer = buffer.slice(gltfByteOffset, byteLength); 59 | glbBuffer = alignGlb(glbBuffer, gltfByteOffset); 60 | 61 | var featureTableJson = bufferToJson(featureTableJsonBuffer); 62 | var batchTableJson = bufferToJson(batchTableJsonBuffer); 63 | 64 | return { 65 | header : { 66 | magic : magic, 67 | version : version, 68 | gltfFormat : gltfFormat 69 | }, 70 | featureTable : { 71 | json : featureTableJson, 72 | binary : featureTableBinaryBuffer 73 | }, 74 | batchTable : { 75 | json : batchTableJson, 76 | binary : batchTableBinaryBuffer 77 | }, 78 | glb : glbBuffer 79 | }; 80 | } 81 | 82 | function alignGlb(buffer, byteOffset) { 83 | if (byteOffset % 4 === 0) { 84 | return buffer; 85 | } 86 | return Buffer.from(buffer); 87 | } 88 | -------------------------------------------------------------------------------- /tools/lib/fileExists.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var fsExtra = require('fs-extra'); 3 | 4 | module.exports = fileExists; 5 | 6 | /** 7 | * @private 8 | */ 9 | function fileExists(filePath) { 10 | return fsExtra.stat(filePath) 11 | .then(function(stats) { 12 | return stats.isFile(); 13 | }) 14 | .catch(function(err) { 15 | // If the file doesn't exist the error code is ENOENT. 16 | // Otherwise something else went wrong - permission issues, etc. 17 | if (err.code !== 'ENOENT') { 18 | throw err; 19 | } 20 | return false; 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /tools/lib/getBufferPadded.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | 4 | var defaultValue = Cesium.defaultValue; 5 | var defined = Cesium.defined; 6 | 7 | module.exports = getBufferPadded; 8 | 9 | /** 10 | * Pad the buffer to the next 8-byte boundary to ensure proper alignment for the section that follows. 11 | * Padding is not required by the 3D Tiles spec but is important when using Typed Arrays in JavaScript. 12 | * 13 | * @param {Buffer} buffer The buffer. 14 | * @param {Number} [byteOffset=0] The byte offset on which the buffer starts. 15 | * @returns {Buffer} The padded buffer. 16 | * 17 | * @private 18 | */ 19 | function getBufferPadded(buffer, byteOffset) { 20 | if (!defined(buffer)) { 21 | return Buffer.alloc(0); 22 | } 23 | 24 | byteOffset = defaultValue(byteOffset, 0); 25 | 26 | var boundary = 8; 27 | var byteLength = buffer.length; 28 | var remainder = (byteOffset + byteLength) % boundary; 29 | var padding = (remainder === 0) ? 0 : boundary - remainder; 30 | var emptyBuffer = Buffer.alloc(padding); 31 | return Buffer.concat([buffer, emptyBuffer]); 32 | } 33 | -------------------------------------------------------------------------------- /tools/lib/getDefaultWriteCallback.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var fsExtra = require('fs-extra'); 3 | var path = require('path'); 4 | 5 | module.exports = getDefaultWriteCallback; 6 | 7 | /** 8 | * @private 9 | */ 10 | function getDefaultWriteCallback(outputDirectory) { 11 | return function(file, data) { 12 | var outputFile = path.join(outputDirectory, file); 13 | return fsExtra.outputFile(outputFile, data); 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /tools/lib/getFilesInDirectory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var klaw = require('klaw'); 3 | var Promise = require('bluebird'); 4 | 5 | module.exports = getFilesInDirectory; 6 | 7 | /** 8 | * @private 9 | */ 10 | function getFilesInDirectory(directory) { 11 | return new Promise(function (resolve, reject) { 12 | var files = []; 13 | klaw(directory) 14 | .on('data', function (item) { 15 | if (!item.stats.isDirectory()) { 16 | files.push(item.path); 17 | } 18 | }) 19 | .on('end', function () { 20 | resolve(files); 21 | }) 22 | .on('error', reject); 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /tools/lib/getJsonBufferPadded.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | 4 | var defaultValue = Cesium.defaultValue; 5 | var defined = Cesium.defined; 6 | 7 | module.exports = getJsonBufferPadded; 8 | 9 | /** 10 | * Convert the JSON object to a padded buffer. 11 | * 12 | * Pad the JSON with extra whitespace to fit the next 8-byte boundary. This ensures proper alignment 13 | * for the section that follows (for example, batch table binary or feature table binary). 14 | * Padding is not required by the 3D Tiles spec but is important when using Typed Arrays in JavaScript. 15 | * 16 | * @param {Object} [json] The JSON object. 17 | * @param {Number} [byteOffset=0] The byte offset on which the buffer starts. 18 | * @returns {Buffer} The padded JSON buffer. 19 | * 20 | * @private 21 | */ 22 | function getJsonBufferPadded(json, byteOffset) { 23 | // Check for undefined or empty 24 | if (!defined(json) || Object.keys(json).length === 0) { 25 | return Buffer.alloc(0); 26 | } 27 | 28 | byteOffset = defaultValue(byteOffset, 0); 29 | var string = JSON.stringify(json); 30 | 31 | var boundary = 8; 32 | var byteLength = Buffer.byteLength(string); 33 | var remainder = (byteOffset + byteLength) % boundary; 34 | var padding = (remainder === 0) ? 0 : boundary - remainder; 35 | var whitespace = ''; 36 | for (var i = 0; i < padding; ++i) { 37 | whitespace += ' '; 38 | } 39 | string += whitespace; 40 | 41 | return Buffer.from(string); 42 | } 43 | -------------------------------------------------------------------------------- /tools/lib/getMagic.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | 4 | var defaultValue = Cesium.defaultValue; 5 | 6 | module.exports = getMagic; 7 | 8 | /** 9 | * @private 10 | */ 11 | function getMagic(tileBuffer, byteOffset) { 12 | byteOffset = defaultValue(byteOffset, 0); 13 | return tileBuffer.toString('utf8', byteOffset, byteOffset + 4); 14 | } 15 | -------------------------------------------------------------------------------- /tools/lib/getWorkingDirectory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | var os = require('os'); 4 | var uuid = require('uuid'); 5 | 6 | module.exports = getWorkingDirectory; 7 | 8 | /** 9 | * @private 10 | */ 11 | function getWorkingDirectory() { 12 | var tempDirectory = os.tmpdir(); 13 | var randomId = uuid.v4(); 14 | return path.join(tempDirectory, randomId); 15 | } 16 | -------------------------------------------------------------------------------- /tools/lib/glbToB3dm.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | var getBufferPadded = require('./getBufferPadded'); 4 | var getJsonBufferPadded = require('./getJsonBufferPadded'); 5 | 6 | var defined = Cesium.defined; 7 | var DeveloperError = Cesium.DeveloperError; 8 | 9 | module.exports = glbToB3dm; 10 | 11 | /** 12 | * Generates a new Buffer representing a b3dm asset. 13 | * 14 | * @param {Buffer} glbBuffer A buffer containing a binary glTF asset. 15 | * @param {Object} [featureTableJson] The feature table JSON. 16 | * @param {Buffer} [featureTableBinary] The feature table binary. 17 | * @param {Object} [batchTableJson] The batch table JSON. 18 | * @param {Buffer} [batchTableBinary] The batch table binary. 19 | * @returns {Buffer} Buffer representing the b3dm asset. 20 | */ 21 | function glbToB3dm(glbBuffer, featureTableJson, featureTableBinary, batchTableJson, batchTableBinary) { 22 | if (!defined(glbBuffer)) { 23 | throw new DeveloperError('glbBuffer is not defined.'); 24 | } 25 | 26 | var headerByteLength = 28; 27 | var featureTableJsonBuffer = getJsonBufferPadded(featureTableJson, headerByteLength); 28 | var featureTableBinaryBuffer = getBufferPadded(featureTableBinary); 29 | var batchTableJsonBuffer = getJsonBufferPadded(batchTableJson); 30 | var batchTableBinaryBuffer = getBufferPadded(batchTableBinary); 31 | 32 | var byteLength = headerByteLength + featureTableJsonBuffer.length + featureTableBinaryBuffer.length + batchTableJsonBuffer.length + batchTableBinaryBuffer.length + glbBuffer.length; 33 | var header = Buffer.alloc(headerByteLength); 34 | header.write('b3dm', 0); // magic 35 | header.writeUInt32LE(1, 4); // version 36 | header.writeUInt32LE(byteLength, 8); // byteLength - length of entire tile, including header, in bytes 37 | header.writeUInt32LE(featureTableJsonBuffer.length, 12); // featureTableJSONByteLength - length of feature table JSON section in bytes. 38 | header.writeUInt32LE(featureTableBinaryBuffer.length, 16); // featureTableBinaryByteLength - length of feature table binary section in bytes. 39 | header.writeUInt32LE(batchTableJsonBuffer.length, 20); // batchTableJSONByteLength - length of batch table JSON section in bytes. (0 for basic, no batches) 40 | header.writeUInt32LE(batchTableBinaryBuffer.length, 24); // batchTableBinaryByteLength - length of batch table binary section in bytes. (0 for basic, no batches) 41 | 42 | return Buffer.concat([header, featureTableJsonBuffer, featureTableBinaryBuffer, batchTableJsonBuffer, batchTableBinaryBuffer, glbBuffer]); 43 | } 44 | -------------------------------------------------------------------------------- /tools/lib/glbToI3dm.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | var getBufferPadded = require('./getBufferPadded'); 4 | var getJsonBufferPadded = require('./getJsonBufferPadded'); 5 | 6 | var defined = Cesium.defined; 7 | var DeveloperError = Cesium.DeveloperError; 8 | 9 | module.exports = glbToI3dm; 10 | 11 | /** 12 | * Generates a new Buffer representing a i3dm asset. 13 | * 14 | * @param {Buffer} glbBuffer A buffer containing a binary glTF asset. 15 | * @param {Object} [featureTableJson] The feature table JSON. 16 | * @param {Buffer} [featureTableBinary] The feature table binary. 17 | * @param {Object} [batchTableJson] The batch table JSON. 18 | * @param {Buffer} [batchTableBinary] The batch table binary. 19 | * @returns {Buffer} Buffer representing the i3dm asset. 20 | */ 21 | function glbToI3dm(glbBuffer, featureTableJson, featureTableBinary, batchTableJson, batchTableBinary) { 22 | if (!defined(glbBuffer)) { 23 | throw new DeveloperError('glbBuffer is not defined.'); 24 | } 25 | 26 | var featureTableJsonBuffer = getJsonBufferPadded(featureTableJson); 27 | var featureTableBinaryBuffer = getBufferPadded(featureTableBinary); 28 | var batchTableJsonBuffer = getJsonBufferPadded(batchTableJson); 29 | var batchTableBinaryBuffer = getBufferPadded(batchTableBinary); 30 | 31 | var headerByteLength = 32; 32 | var byteLength = headerByteLength + featureTableJsonBuffer.length + featureTableBinaryBuffer.length + batchTableJsonBuffer.length + batchTableBinaryBuffer.length + glbBuffer.length; 33 | var gltfFormat = 1; 34 | 35 | var header = Buffer.alloc(32); 36 | header.write('i3dm', 0); // magic 37 | header.writeUInt32LE(1, 4); // version 38 | header.writeUInt32LE(byteLength, 8); // byteLength - length of entire tile, including header, in bytes 39 | header.writeUInt32LE(featureTableJsonBuffer.length, 12); // featureTableJsonByteLength - length of feature table JSON section in bytes. 40 | header.writeUInt32LE(featureTableBinaryBuffer.length, 16); // featureTableBinaryByteLength - length of feature table binary section in bytes. 41 | header.writeUInt32LE(batchTableJsonBuffer.length, 20); // batchTableJsonByteLength - length of batch table JSON section in bytes. 42 | header.writeUInt32LE(batchTableBinaryBuffer.length, 24); // batchTableBinaryByteLength - length of batch table binary section in bytes. 43 | header.writeUInt32LE(gltfFormat, 28); // gltfFormat - format of the glTF body field (0 for URL, 1 for embedded binary) 44 | 45 | return Buffer.concat([header, featureTableJsonBuffer, featureTableBinaryBuffer, batchTableJsonBuffer, batchTableBinaryBuffer, glbBuffer]); 46 | } 47 | -------------------------------------------------------------------------------- /tools/lib/gzipTileset.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | var fsExtra = require('fs-extra'); 4 | var path = require('path'); 5 | var zlib = require('zlib'); 6 | var getDefaultWriteCallback = require('./getDefaultWriteCallback'); 7 | var isGzipped = require('./isGzipped'); 8 | var isTile = require('./isTile'); 9 | var walkDirectory = require('./walkDirectory'); 10 | 11 | var Check = Cesium.Check; 12 | var defaultValue = Cesium.defaultValue; 13 | var defined = Cesium.defined; 14 | 15 | module.exports = gzipTileset; 16 | 17 | /** 18 | * gzips or ungzips the input tileset. 19 | * 20 | * @param {Object} options Object with the following properties: 21 | * @param {String} options.inputDirectory Path to the input directory. 22 | * @param {Object} [options.outputDirectory] Path to the output directory. 23 | * @param {Boolean} [options.gzip=true] Whether to gzip or ungzip the tileset. 24 | * @param {Boolean} [options.tilesOnly=false] Only gzip tiles, does not gzip tileset.json or other files. 25 | * @param {WriteCallback} [options.writeCallback] A callback function that writes files after they have been processed. 26 | * @param {LogCallback} [options.logCallback] A callback function that logs messages. 27 | * 28 | * @returns {Promise} A promise that resolves when the operation completes. 29 | */ 30 | function gzipTileset(options) { 31 | options = defaultValue(options, defaultValue.EMPTY_OBJECT); 32 | var inputDirectory = options.inputDirectory; 33 | var outputDirectory = options.outputDirectory; 34 | var gzip = defaultValue(options.gzip, true); 35 | var tilesOnly = defaultValue(options.tilesOnly, false); 36 | 37 | Check.typeOf.string('options.inputDirectory', inputDirectory); 38 | 39 | inputDirectory = path.normalize(inputDirectory); 40 | outputDirectory = path.normalize(defaultValue(outputDirectory, 41 | path.join(path.dirname(inputDirectory), path.basename(inputDirectory) + '-' + (gzip ? 'gzipped' : 'ungzipped')))); 42 | 43 | var writeCallback = defaultValue(options.writeCallback, getDefaultWriteCallback(outputDirectory)); 44 | var logCallback = options.logCallback; 45 | 46 | if (defined(logCallback)) { 47 | logCallback((gzip ? 'Compressing' : 'Uncompressing') + ' files...'); 48 | } 49 | 50 | var operation = gzip ? zlib.gzipSync : zlib.gunzipSync; 51 | return walkDirectory(inputDirectory, function(file) { 52 | return fsExtra.readFile(file) 53 | .then(function(data) { 54 | if (!(gzip && tilesOnly && !isTile(file)) && (isGzipped(data) !== gzip)) { 55 | data = operation(data); 56 | } 57 | var relativePath = path.relative(inputDirectory, file); 58 | return writeCallback(relativePath, data); 59 | }); 60 | }); 61 | } 62 | -------------------------------------------------------------------------------- /tools/lib/isGzipped.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = isGzipped; 3 | 4 | /** 5 | * @private 6 | */ 7 | function isGzipped(buffer) { 8 | return (buffer[0] === 0x1f) && (buffer[1] === 0x8b); 9 | } 10 | -------------------------------------------------------------------------------- /tools/lib/isGzippedFile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var fsExtra = require('fs-extra'); 3 | var Promise = require('bluebird'); 4 | var isGzipped = require('./isGzipped'); 5 | 6 | module.exports = isGzippedFile; 7 | 8 | var readStreamOptions = { 9 | start : 0, 10 | end : 2 11 | }; 12 | 13 | /** 14 | * @private 15 | */ 16 | function isGzippedFile(file) { 17 | return new Promise(function (resolve, reject) { 18 | var readStream = fsExtra.createReadStream(file, readStreamOptions); 19 | readStream.on('error', reject); 20 | readStream.on('data', function(chunk) { 21 | resolve(isGzipped(chunk)); 22 | readStream.destroy(); 23 | }); 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /tools/lib/isJson.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | 4 | module.exports = isJson; 5 | 6 | /** 7 | * @private 8 | */ 9 | function isJson(file) { 10 | return path.extname(file) === '.json'; 11 | } 12 | -------------------------------------------------------------------------------- /tools/lib/isTile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | 4 | module.exports = isTile; 5 | 6 | /** 7 | * @private 8 | */ 9 | function isTile(file) { 10 | var extension = path.extname(file); 11 | return extension === '.b3dm' || 12 | extension === '.i3dm' || 13 | extension === '.pnts' || 14 | extension === '.cmpt' || 15 | extension === '.vctr'; 16 | } 17 | -------------------------------------------------------------------------------- /tools/lib/makeCompositeTile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var getBufferPadded = require('./getBufferPadded'); 3 | 4 | module.exports = makeCompositeTile; 5 | 6 | /** 7 | * Combines an array of tile buffers into a single composite tile. 8 | * 9 | * @param {Buffer[]} tileBuffers An array of buffers holding tile data. 10 | * @returns {Buffer} A single buffer holding the composite tile. 11 | */ 12 | function makeCompositeTile(tileBuffers) { 13 | var headerByteLength = 16; 14 | var buffers = []; 15 | var byteLength = headerByteLength; 16 | var tilesLength = tileBuffers.length; 17 | for (var i = 0; i < tilesLength; i++) { 18 | var tile = tileBuffers[i]; 19 | tile = getBufferPadded(tile, byteLength); 20 | tile.writeUInt32LE(tile.length, 8); // Rewrite byte length 21 | buffers.push(tile); 22 | byteLength += tile.length; 23 | } 24 | var header = Buffer.alloc(16); 25 | header.write('cmpt', 0); // magic 26 | header.writeUInt32LE(1, 4); // version 27 | header.writeUInt32LE(byteLength, 8); // byteLength 28 | header.writeUInt32LE(tilesLength, 12); // tilesLength 29 | 30 | buffers.unshift(header); 31 | return Buffer.concat(buffers); 32 | } 33 | -------------------------------------------------------------------------------- /tools/lib/optimizeGlb.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | var GltfPipeline = require('gltf-pipeline'); 4 | 5 | var Cartesian3 = Cesium.Cartesian3; 6 | var DeveloperError = Cesium.DeveloperError; 7 | var defaultValue = Cesium.defaultValue; 8 | var defined = Cesium.defined; 9 | 10 | var addCesiumRTC = GltfPipeline.addCesiumRTC; 11 | var getBinaryGltf = GltfPipeline.getBinaryGltf; 12 | var loadGltfUris = GltfPipeline.loadGltfUris; 13 | var parseBinaryGltf = GltfPipeline.parseBinaryGltf; 14 | var Pipeline = GltfPipeline.Pipeline; 15 | 16 | module.exports = optimizeGlb; 17 | 18 | /** 19 | * Given an input buffer containing a binary glTF asset, optimize it using gltf-pipeline with the provided options 20 | * 21 | * @param {Buffer} glbBuffer The buffer containing the binary glTF. 22 | * @param {Object} [options] Options specifying custom gltf-pipeline behavior. 23 | * @returns {Promise} A promise that resolves to the optimized binary glTF. 24 | * @private 25 | */ 26 | function optimizeGlb(glbBuffer, options) { 27 | options = defaultValue(options, defaultValue.EMPTY_OBJECT); 28 | if (!defined(glbBuffer)) { 29 | throw new DeveloperError('glbBuffer is not defined.'); 30 | } 31 | var rtcPosition; 32 | var gltf = parseBinaryGltf(glbBuffer); 33 | var extensions = gltf.extensions; 34 | if (defined(extensions)) { 35 | // If it is used, extract the CesiumRTC extension and add it back after processing 36 | var cesiumRTC = extensions.CESIUM_RTC; 37 | if (defined(cesiumRTC)) { 38 | rtcPosition = Cartesian3.unpack(cesiumRTC.center); 39 | } 40 | } 41 | fixBatchIdSemantic(gltf); 42 | return loadGltfUris(gltf, options) 43 | .then(function() { 44 | return Pipeline.processJSONWithExtras(gltf, options) 45 | .then(function(gltf) { 46 | if (defined(rtcPosition)) { 47 | addCesiumRTC(gltf, { 48 | position: rtcPosition 49 | }); 50 | } 51 | var embed = defaultValue(options.embed, true); 52 | var embedImage = defaultValue(options.embedImage, true); 53 | return getBinaryGltf(gltf, embed, embedImage).glb; 54 | }); 55 | }); 56 | } 57 | 58 | function fixBatchIdSemantic(gltf) { 59 | var meshes = gltf.meshes; 60 | for (var meshId in meshes) { 61 | if (meshes.hasOwnProperty(meshId)) { 62 | var primitives = meshes[meshId].primitives; 63 | var primitivesLength = primitives.length; 64 | for (var i = 0; i < primitivesLength; ++i) { 65 | var attributes = primitives[i].attributes; 66 | if (defined(attributes.BATCHID)) { 67 | attributes._BATCHID = attributes.BATCHID; 68 | delete attributes.BATCHID; 69 | } 70 | } 71 | } 72 | } 73 | 74 | var techniques = gltf.techniques; 75 | for (var techniqueId in techniques) { 76 | if (techniques.hasOwnProperty(techniqueId)) { 77 | var parameters = techniques[techniqueId].parameters; 78 | for (var parameterId in parameters) { 79 | if (parameters.hasOwnProperty(parameterId)) { 80 | var parameter = parameters[parameterId]; 81 | if (parameter.semantic === 'BATCHID') { 82 | parameter.semantic = '_BATCHID'; 83 | } 84 | } 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /tools/lib/readFile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | var fsExtra = require('fs-extra'); 4 | var zlib = require('zlib'); 5 | var isGzipped = require('./isGzipped'); 6 | 7 | var defaultValue = Cesium.defaultValue; 8 | 9 | module.exports = readFile; 10 | 11 | /** 12 | * Reads the contents of a file. 13 | * 14 | * @param {String} filePath The file path to read from. 15 | * @param {String} [type=binary] Whether to read the file as 'binary', 'text', or 'json'. 16 | * @returns {Promise} A promise that resolves with the file contents as either a Buffer, String, or JSON object. 17 | */ 18 | function readFile(filePath, type) { 19 | type = defaultValue(type, 'binary'); 20 | return fsExtra.readFile(filePath) 21 | .then(function (data) { 22 | if (isGzipped(data)) { 23 | data = zlib.gunzipSync(data); 24 | } 25 | if (type === 'text') { 26 | return data.toString(); 27 | } else if (type === 'json') { 28 | return JSON.parse(data.toString()); 29 | } 30 | return data; 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /tools/lib/runPipeline.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | var fsExtra = require('fs-extra'); 4 | var path = require('path'); 5 | var Promise = require('bluebird'); 6 | var combineTileset = require('./combineTileset'); 7 | var getWorkingDirectory = require('./getWorkingDirectory'); 8 | var gzipTileset = require('./gzipTileset'); 9 | var upgradeTileset = require('./upgradeTileset'); 10 | 11 | var defaultValue = Cesium.defaultValue; 12 | var defined = Cesium.defined; 13 | var DeveloperError = Cesium.DeveloperError; 14 | 15 | module.exports = runPipeline; 16 | 17 | /** 18 | * Run an input pipeline. 19 | * 20 | * @param {Object} pipeline A JSON object containing an input, output, and list of stages to run. 21 | * @param {String} pipeline.input Input tileset path. 22 | * @param {String} [pipeline.output] Output tileset path. 23 | * @param {Array} [pipeline.stages] The stages to run on the tileset. 24 | * @param {Object} [options] An object with the following properties: 25 | * @param {WriteCallback} [options.writeCallback] A callback function that writes files after they have been processed. 26 | * @param {LogCallback} [options.logCallback] A callback function that logs messages. 27 | */ 28 | function runPipeline(pipeline, options) { 29 | pipeline = defaultValue(pipeline, defaultValue.EMPTY_OBJECT); 30 | var inputDirectory = pipeline.input; 31 | var outputDirectory = pipeline.output; 32 | var stages = pipeline.stages; 33 | if (!defined(inputDirectory)) { 34 | throw new DeveloperError('pipeline.input is required'); 35 | } 36 | 37 | inputDirectory = path.normalize(inputDirectory); 38 | outputDirectory = path.normalize(defaultValue(outputDirectory, 39 | path.join(path.dirname(inputDirectory), path.basename(inputDirectory) + '-processed'))); 40 | 41 | if (!defined(stages)) { 42 | return fsExtra.copy(inputDirectory, outputDirectory); 43 | } 44 | 45 | options = defaultValue(options, defaultValue.EMPTY_OBJECT); 46 | 47 | var writeCallback = options.writeCallback; 48 | var logCallback = options.logCallback; 49 | 50 | var workingDirectory1 = getWorkingDirectory(); 51 | var workingDirectory2 = getWorkingDirectory(); 52 | 53 | var stageObjects = []; 54 | 55 | var stagesLength = stages.length; 56 | for (var i = 0; i < stagesLength; ++i) { 57 | var stageName; 58 | var stageOptions; 59 | var stage = stages[i]; 60 | if (typeof stage === 'string') { 61 | stageName = stage; 62 | stageOptions = {}; 63 | } else { 64 | stageName = stage.name; 65 | if (!defined(stageName)) { 66 | throw new DeveloperError('Stage must have a "name" property'); 67 | } 68 | stageOptions = stage; 69 | } 70 | 71 | // Ping-pong between the two working directories when multiple stages are run 72 | var stageInputDirectory = (i === 0) ? inputDirectory : ((i % 2 === 0) ? workingDirectory2 : workingDirectory1); 73 | var stageOutputDirectory = (i === stagesLength - 1) ? outputDirectory : ((i % 2 === 0) ? workingDirectory1 : workingDirectory2); 74 | 75 | stageOptions.inputDirectory = stageInputDirectory; 76 | stageOptions.outputDirectory = stageOutputDirectory; 77 | stageOptions.logCallback = logCallback; 78 | 79 | if (i === stagesLength - 1) { 80 | // TODO : Not sure if this is the right approach. Should the writeCallback also have control over the temp directories? How would that work? 81 | // Only allow the write callback to act on the last stage. The intermediary stages always write to temp directories. 82 | stageOptions.writeCallback = writeCallback; 83 | } 84 | 85 | var stageFunction = getStageFunction(stageName, stageOptions); 86 | if (!defined(stageFunction)) { 87 | throw new DeveloperError('Stage "' + stageName + '" does not exist'); 88 | } 89 | 90 | stageObjects.push({ 91 | options : stageOptions, 92 | stageFunction : stageFunction, 93 | name : stageName 94 | }); 95 | } 96 | 97 | // Run the stages in sequence 98 | return Promise.each(stageObjects, function(stage) { 99 | return fsExtra.emptyDir(stage.options.outputDirectory) 100 | .then(function() { 101 | if (defined(logCallback)) { 102 | logCallback('Running ' + stage.name); 103 | } 104 | return stage.stageFunction(stage.options); 105 | }); 106 | }).finally(function() { 107 | return Promise.all([ 108 | fsExtra.remove(workingDirectory1), 109 | fsExtra.remove(workingDirectory2) 110 | ]); 111 | }); 112 | } 113 | 114 | function getStageFunction(stageName, stageOptions) { 115 | switch (stageName) { 116 | case 'gzip': 117 | stageOptions.gzip = true; 118 | return gzipTileset; 119 | case 'ungzip': 120 | stageOptions.gzip = false; 121 | return gzipTileset; 122 | case 'combine': 123 | return combineTileset; 124 | case 'upgrade': 125 | return upgradeTileset; 126 | default: 127 | return undefined; 128 | } 129 | } 130 | 131 | /** 132 | * A callback function that writes files after they have been processed. 133 | * @callback WriteCallback 134 | * 135 | * @param {String} file Relative path of the file. 136 | * @param {Buffer} buffer A buffer storing the processed file's data. 137 | * @returns {Promise} A promise that resolves when the callback is complete. 138 | */ 139 | 140 | /** 141 | * A callback function that logs messages. 142 | * @callback LogCallback 143 | * 144 | * @param {String} message A log message. 145 | */ 146 | -------------------------------------------------------------------------------- /tools/lib/tilesetToDatabase.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | var fsExtra = require('fs-extra'); 4 | var klaw = require('klaw'); 5 | var path = require('path'); 6 | var Promise = require('bluebird'); 7 | var sqlite3 = require('sqlite3'); 8 | var zlib = require('zlib'); 9 | var isGzipped = require('../lib/isGzipped'); 10 | var isTile = require('../lib/isTile'); 11 | 12 | var defaultValue = Cesium.defaultValue; 13 | var defined = Cesium.defined; 14 | var DeveloperError = Cesium.DeveloperError; 15 | 16 | module.exports = tilesetToDatabase; 17 | 18 | /** 19 | * Generates a sqlite database for a tileset, saved as a .3dtiles file. 20 | * 21 | * @param {String} inputDirectory The input directory of the tileset. 22 | * @param {String} [outputFile] The output .3dtiles database file. 23 | * @returns {Promise} A promise that resolves when the database is written. 24 | */ 25 | function tilesetToDatabase(inputDirectory, outputFile) { 26 | if (!defined(inputDirectory)) { 27 | throw new DeveloperError('inputDirectory is required.'); 28 | } 29 | 30 | outputFile = defaultValue(outputFile, 31 | path.join(path.dirname(inputDirectory), path.basename(inputDirectory) + '.3dtiles')); 32 | 33 | var db; 34 | var dbRun; 35 | // Delete the .3dtiles file if it already exists 36 | return Promise.resolve(fsExtra.remove(outputFile)) 37 | .then(function () { 38 | // Create the database. 39 | db = new sqlite3.Database(outputFile, sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE); 40 | dbRun = Promise.promisify(db.run, {context: db}); 41 | 42 | // Disable journaling and create the table. 43 | return dbRun('PRAGMA journal_mode=off;'); 44 | }) 45 | .then(function () { 46 | return dbRun('BEGIN'); 47 | }) 48 | .then(function () { 49 | return dbRun('CREATE TABLE media (key TEXT PRIMARY KEY, content BLOB)'); 50 | }) 51 | .then(function () { 52 | //Build the collection of file paths to be inserted. 53 | var filePaths = []; 54 | var stream = klaw(inputDirectory); 55 | stream.on('readable', function () { 56 | var filePath = stream.read(); 57 | while (defined(filePath)) { 58 | if (filePath.stats.isFile()) { 59 | filePaths.push(filePath.path); 60 | } 61 | filePath = stream.read(); 62 | } 63 | }); 64 | 65 | return new Promise(function (resolve, reject) { 66 | stream.on('error', reject); 67 | stream.on('end', function () { 68 | resolve(filePaths); 69 | }); 70 | }); 71 | }) 72 | .then(function (filePaths) { 73 | return Promise.map(filePaths, function (filePath) { 74 | return fsExtra.readFile(filePath) 75 | .then(function (data) { 76 | filePath = path.normalize(path.relative(inputDirectory, filePath)).replace(/\\/g, '/'); 77 | // Only gzip tiles and json files. Other files like external textures should not be gzipped. 78 | var shouldGzip = isTile(filePath) || path.extname(filePath) === '.json'; 79 | if (shouldGzip && !isGzipped(data)) { 80 | data = zlib.gzipSync(data); 81 | } 82 | return dbRun('INSERT INTO media VALUES (?, ?)', [filePath, data]); 83 | }); 84 | }, {concurrency: 100}); 85 | }) 86 | .then(function () { 87 | return dbRun('COMMIT'); 88 | }) 89 | .finally(function () { 90 | if (defined(db)) { 91 | db.close(); 92 | } 93 | }); 94 | } 95 | -------------------------------------------------------------------------------- /tools/lib/upgradeTileset.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | var path = require('path'); 4 | var Promise = require('bluebird'); 5 | var zlib = require('zlib'); 6 | var extractB3dm = require('./extractB3dm'); 7 | var extractCmpt = require('./extractCmpt'); 8 | var getDefaultWriteCallback = require('./getDefaultWriteCallback'); 9 | var getMagic = require('./getMagic'); 10 | var glbToB3dm = require('./glbToB3dm'); 11 | var isGzippedFile = require('./isGzippedFile'); 12 | var isJson = require('./isJson'); 13 | var isTile = require('./isTile'); 14 | var makeCompositeTile = require('./makeCompositeTile'); 15 | var optimizeGlb = require('./optimizeGlb'); 16 | var readFile = require('./readFile'); 17 | var walkDirectory = require('./walkDirectory'); 18 | 19 | var Check = Cesium.Check; 20 | var defaultValue = Cesium.defaultValue; 21 | var defined = Cesium.defined; 22 | 23 | module.exports = upgradeTileset; 24 | 25 | /** 26 | * Upgrades the input tileset to the latest version of the 3D Tiles spec. Embedded glTF models will be upgraded to glTF 2.0. 27 | * 28 | * @param {Object} options Object with the following properties: 29 | * @param {String} options.inputDirectory Path to the input directory. 30 | * @param {Object} [options.outputDirectory] Path to the output directory. 31 | * @param {WriteCallback} [options.writeCallback] A callback function that writes files after they have been processed. 32 | * @param {LogCallback} [options.logCallback] A callback function that logs messages. 33 | * 34 | * @returns {Promise} A promise that resolves when the operation completes. 35 | */ 36 | function upgradeTileset(options) { 37 | options = defaultValue(options, defaultValue.EMPTY_OBJECT); 38 | var inputDirectory = options.inputDirectory; 39 | var outputDirectory = options.outputDirectory; 40 | 41 | Check.typeOf.string('options.inputDirectory', inputDirectory); 42 | 43 | inputDirectory = path.normalize(inputDirectory); 44 | outputDirectory = path.normalize(defaultValue(outputDirectory, 45 | path.join(path.dirname(inputDirectory), path.basename(inputDirectory) + '-upgrades'))); 46 | 47 | var writeCallback = defaultValue(options.writeCallback, getDefaultWriteCallback(outputDirectory)); 48 | var logCallback = options.logCallback; 49 | 50 | if (defined(logCallback)) { 51 | logCallback('Upgrading to 3D Tiles version 1.0'); 52 | } 53 | 54 | return walkDirectory(inputDirectory, function(file) { 55 | return isGzippedFile(file) 56 | .then(function(gzipped) { 57 | return upgradeFile(file) 58 | .then(function(data) { 59 | if (gzipped) { 60 | data = zlib.gzipSync(data); 61 | } 62 | var relativePath = path.relative(inputDirectory, file); 63 | return writeCallback(relativePath, data); 64 | }); 65 | }); 66 | }); 67 | } 68 | 69 | function upgradeFile(file) { 70 | if (isJson(file)) { 71 | return upgradeTilesetJson(file); 72 | } else if (isTile(file)) { 73 | return upgradeTile(file); 74 | } 75 | return readFile(file); 76 | } 77 | 78 | function upgradeTilesetJson(file) { 79 | return readFile(file, 'text') 80 | .then(function(contents) { 81 | contents = contents.replace(/"version": ".*"/g, '"version": "1.0"'); 82 | contents = contents.replace(/"add"/g, '"ADD"'); 83 | contents = contents.replace(/"replace"/g, '"REPLACE"'); 84 | return Buffer.from(contents); 85 | }); 86 | } 87 | 88 | var optimizeOptions = { 89 | preserve : true 90 | }; 91 | 92 | function upgradeTile(file) { 93 | return readFile(file) 94 | .then(function(buffer) { 95 | return upgradeTileContent(buffer, path.dirname(file)); 96 | }); 97 | } 98 | 99 | function upgradeTileContent(buffer, basePath) { 100 | var magic = getMagic(buffer); 101 | if (magic === 'b3dm') { 102 | var b3dm = extractB3dm(buffer); 103 | return optimizeGlb(b3dm.glb, Object.assign({}, optimizeOptions, {basePath: basePath})) 104 | .then(function(glb) { 105 | return glbToB3dm(glb, b3dm.featureTable.json, b3dm.featureTable.binary, b3dm.batchTable.json, b3dm.batchTable.binary); 106 | }); 107 | } else if (magic === 'cmpt') { 108 | var tiles = extractCmpt(buffer); 109 | return Promise.map(tiles, function(tile) { 110 | return upgradeTileContent(tile, basePath); 111 | }).then(function(upgradedTiles) { 112 | return makeCompositeTile(upgradedTiles); 113 | }); 114 | } 115 | return buffer; 116 | } 117 | -------------------------------------------------------------------------------- /tools/lib/walkDirectory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | var klaw = require('klaw'); 4 | var Promise = require('bluebird'); 5 | 6 | var defined = Cesium.defined; 7 | 8 | module.exports = walkDirectory; 9 | 10 | /** 11 | * @private 12 | */ 13 | function walkDirectory(directory, processFileCallback) { 14 | return new Promise(function(resolve, reject) { 15 | getNumberOfFilesInDirectory(directory) 16 | .then(function(numberOfFiles) { 17 | var numberComplete = 0; 18 | function complete() { 19 | ++numberComplete; 20 | if (numberComplete === numberOfFiles) { 21 | resolve(); 22 | } 23 | } 24 | walk(directory, processFileCallback, complete, reject); 25 | }) 26 | .catch(reject); 27 | }); 28 | } 29 | 30 | function getNumberOfFilesInDirectory(directory) { 31 | return new Promise(function(resolve, reject) { 32 | var numberOfFiles = 0; 33 | klaw(directory) 34 | .on('data', function (item) { 35 | if (!item.stats.isDirectory()) { 36 | ++numberOfFiles; 37 | } 38 | }) 39 | .on('end', function () { 40 | resolve(numberOfFiles); 41 | }) 42 | .on('error', reject); 43 | }); 44 | } 45 | 46 | function walk(directory, processFileCallback, resolve, reject) { 47 | klaw(directory) 48 | .on('data', function (item) { 49 | if (!item.stats.isDirectory()) { 50 | var promise = processFileCallback(item.path); 51 | if (defined(promise) && defined(promise.then)) { 52 | promise 53 | .then(resolve) 54 | .catch(reject); 55 | } else { 56 | resolve(); 57 | } 58 | } 59 | }) 60 | .on('error', reject); 61 | } 62 | -------------------------------------------------------------------------------- /tools/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "3d-tiles-tools", 3 | "version": "0.1.3", 4 | "license": "Apache-2.0", 5 | "description": "Tools for debugging, analyzing, and validating 3D Tiles tilesets.", 6 | "author": { 7 | "name": "Analytical Graphics, Inc. and Contributors" 8 | }, 9 | "keywords": [ 10 | "3D Tiles" 11 | ], 12 | "homepage": "https://github.com/AnalyticalGraphicsInc/3d-tiles-tools", 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/AnalyticalGraphicsInc/3d-tiles-tools.git" 16 | }, 17 | "bugs": { 18 | "url": "https://github.com/AnalyticalGraphicsInc/3d-tiles-tools/issues" 19 | }, 20 | "main": "index.js", 21 | "engines": { 22 | "node": ">=4.0.0" 23 | }, 24 | "dependencies": { 25 | "bluebird": "^3.5.1", 26 | "cesium": "^1.39", 27 | "fs-extra": "^4.0.2", 28 | "gltf-bounding-box": "^0.2.2", 29 | "gltf-pipeline": "^1.0.2", 30 | "klaw": "^2.1.0", 31 | "recursive-readdir": "^2.2.2", 32 | "sqlite3": "file:mapbox-node-sqlite3-v4.0.6-2-ga9dc5a3.tar.gz", 33 | "uuid": "^3.1.0", 34 | "yargs": "^10.0.3" 35 | }, 36 | "devDependencies": { 37 | "cloc": "^2.3.3", 38 | "eslint": "^4.10.0", 39 | "eslint-config-cesium": "^2.0.1", 40 | "gulp": "^3.9.1", 41 | "jasmine": "^2.8.0", 42 | "jasmine-spec-reporter": "^4.2.1", 43 | "jsdoc": "^3.5.5", 44 | "nyc": "^11.3.0", 45 | "open": "^0.0.5", 46 | "request": "^2.83.0", 47 | "requirejs": "^2.3.5" 48 | }, 49 | "scripts": { 50 | "eslint": "eslint \"./**/*.js\" --cache --quiet", 51 | "test": "gulp test", 52 | "test-watch": "gulp test-watch", 53 | "coverage": "gulp coverage", 54 | "jsDoc": "gulp jsDoc", 55 | "cloc": "gulp cloc" 56 | }, 57 | "bin": { 58 | "3d-tiles-tools": "./bin/3d-tiles-tools.js" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tools/specs/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.eslintrc.json", 3 | "env": { 4 | "jasmine": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tools/specs/data/BatchedDeprecated1/batchedDeprecated1.b3dm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/tools/specs/data/BatchedDeprecated1/batchedDeprecated1.b3dm -------------------------------------------------------------------------------- /tools/specs/data/BatchedDeprecated1/tileset.json: -------------------------------------------------------------------------------- 1 | { 2 | "asset": { 3 | "version": "1.0" 4 | }, 5 | "properties": { 6 | "id": { 7 | "minimum": 0, 8 | "maximum": 9 9 | }, 10 | "Longitude": { 11 | "minimum": -1.3196972173766555, 12 | "maximum": -1.3196718547473905 13 | }, 14 | "Latitude": { 15 | "minimum": 0.6988624606923348, 16 | "maximum": 0.6988888301460953 17 | }, 18 | "Height": { 19 | "minimum": 6.2074098233133554, 20 | "maximum": 12.83180232718587 21 | } 22 | }, 23 | "geometricError": 70, 24 | "root": { 25 | "refine": "ADD", 26 | "boundingVolume": { 27 | "region": [ 28 | -1.3197004795898053, 29 | 0.6988582109, 30 | -1.3196595204101946, 31 | 0.6988897891, 32 | 0, 33 | 20 34 | ] 35 | }, 36 | "geometricError": 0, 37 | "content": { 38 | "url": "batchedDeprecated1.b3dm" 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tools/specs/data/BatchedDeprecated2/batchedDeprecated2.b3dm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/tools/specs/data/BatchedDeprecated2/batchedDeprecated2.b3dm -------------------------------------------------------------------------------- /tools/specs/data/BatchedDeprecated2/tileset.json: -------------------------------------------------------------------------------- 1 | { 2 | "asset": { 3 | "version": "1.0" 4 | }, 5 | "properties": { 6 | "id": { 7 | "minimum": 0, 8 | "maximum": 9 9 | }, 10 | "Longitude": { 11 | "minimum": -1.3196972173766555, 12 | "maximum": -1.3196718547473905 13 | }, 14 | "Latitude": { 15 | "minimum": 0.6988624606923348, 16 | "maximum": 0.6988888301460953 17 | }, 18 | "Height": { 19 | "minimum": 6.2074098233133554, 20 | "maximum": 12.83180232718587 21 | } 22 | }, 23 | "geometricError": 70, 24 | "root": { 25 | "refine": "ADD", 26 | "boundingVolume": { 27 | "region": [ 28 | -1.3197004795898053, 29 | 0.6988582109, 30 | -1.3196595204101946, 31 | 0.6988897891, 32 | 0, 33 | 20 34 | ] 35 | }, 36 | "geometricError": 0, 37 | "content": { 38 | "url": "batchedDeprecated2.b3dm" 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tools/specs/data/CesiumTexturedBox/CesiumTexturedBox.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/tools/specs/data/CesiumTexturedBox/CesiumTexturedBox.glb -------------------------------------------------------------------------------- /tools/specs/data/Textured/batchedTextured.b3dm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/tools/specs/data/Textured/batchedTextured.b3dm -------------------------------------------------------------------------------- /tools/specs/data/Textured/instancedTextured.i3dm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/tools/specs/data/Textured/instancedTextured.i3dm -------------------------------------------------------------------------------- /tools/specs/data/TilesetOfTilesets/lr.b3dm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/tools/specs/data/TilesetOfTilesets/lr.b3dm -------------------------------------------------------------------------------- /tools/specs/data/TilesetOfTilesets/parent.b3dm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/tools/specs/data/TilesetOfTilesets/parent.b3dm -------------------------------------------------------------------------------- /tools/specs/data/TilesetOfTilesets/tileset.json: -------------------------------------------------------------------------------- 1 | { 2 | "asset": { 3 | "version": "1.0" 4 | }, 5 | "properties": { 6 | "id": { 7 | "minimum": 0, 8 | "maximum": 9 9 | }, 10 | "Longitude": { 11 | "minimum": -1.3197192952275933, 12 | "maximum": -1.319644104024109 13 | }, 14 | "Latitude": { 15 | "minimum": 0.698848878034009, 16 | "maximum": 0.6989046192460953 17 | }, 18 | "Height": { 19 | "minimum": 6.161747192963958, 20 | "maximum": 84.83180232718587 21 | } 22 | }, 23 | "geometricError": 240, 24 | "root": { 25 | "boundingVolume": { 26 | "region": [ 27 | -1.3197209591796106, 28 | 0.6988424218, 29 | -1.3196390408203893, 30 | 0.6989055782, 31 | 0, 32 | 88 33 | ] 34 | }, 35 | "geometricError": 70, 36 | "refine": "ADD", 37 | "content": { 38 | "url": "tileset2.json" 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tools/specs/data/TilesetOfTilesets/tileset2.json: -------------------------------------------------------------------------------- 1 | { 2 | "asset": { 3 | "version": "1.0" 4 | }, 5 | "geometricError": 70, 6 | "root": { 7 | "boundingVolume": { 8 | "region": [ 9 | -1.3197209591796106, 10 | 0.6988424218, 11 | -1.3196390408203893, 12 | 0.6989055782, 13 | 0, 14 | 88 15 | ] 16 | }, 17 | "geometricError": 70, 18 | "refine": "ADD", 19 | "content": { 20 | "url": "parent.b3dm" 21 | }, 22 | "children": [ 23 | { 24 | "boundingVolume": { 25 | "region": [ 26 | -1.3197209591796106, 27 | 0.6988424218, 28 | -1.31968, 29 | 0.698874, 30 | 0, 31 | 20 32 | ] 33 | }, 34 | "geometricError": 0, 35 | "content": { 36 | "url": "tileset3/tileset3.json" 37 | } 38 | }, 39 | { 40 | "boundingVolume": { 41 | "region": [ 42 | -1.31968, 43 | 0.6988424218, 44 | -1.3196390408203893, 45 | 0.698874, 46 | 0, 47 | 20 48 | ] 49 | }, 50 | "geometricError": 0, 51 | "content": { 52 | "url": "lr.b3dm" 53 | } 54 | }, 55 | { 56 | "boundingVolume": { 57 | "region": [ 58 | -1.31968, 59 | 0.698874, 60 | -1.3196390408203893, 61 | 0.6989055782, 62 | 0, 63 | 20 64 | ] 65 | }, 66 | "geometricError": 0, 67 | "content": { 68 | "url": "ur.b3dm" 69 | } 70 | }, 71 | { 72 | "boundingVolume": { 73 | "region": [ 74 | -1.3197209591796106, 75 | 0.698874, 76 | -1.31968, 77 | 0.6989055782, 78 | 0, 79 | 20 80 | ] 81 | }, 82 | "geometricError": 0, 83 | "content": { 84 | "url": "ul.b3dm" 85 | } 86 | } 87 | ] 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /tools/specs/data/TilesetOfTilesets/tileset3/ll.b3dm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/tools/specs/data/TilesetOfTilesets/tileset3/ll.b3dm -------------------------------------------------------------------------------- /tools/specs/data/TilesetOfTilesets/tileset3/tileset3.json: -------------------------------------------------------------------------------- 1 | { 2 | "asset": { 3 | "version": "1.0" 4 | }, 5 | "geometricError": 0, 6 | "root": { 7 | "boundingVolume": { 8 | "region": [ 9 | -1.3197209591796106, 10 | 0.6988424218, 11 | -1.31968, 12 | 0.698874, 13 | 0, 14 | 20 15 | ] 16 | }, 17 | "geometricError": 0, 18 | "refine": "ADD", 19 | "content": { 20 | "url": "ll.b3dm" 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /tools/specs/data/TilesetOfTilesets/ul.b3dm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/tools/specs/data/TilesetOfTilesets/ul.b3dm -------------------------------------------------------------------------------- /tools/specs/data/TilesetOfTilesets/ur.b3dm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/tools/specs/data/TilesetOfTilesets/ur.b3dm -------------------------------------------------------------------------------- /tools/specs/data/batchedWithBatchTableBinary.b3dm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/tools/specs/data/batchedWithBatchTableBinary.b3dm -------------------------------------------------------------------------------- /tools/specs/data/composite.cmpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/tools/specs/data/composite.cmpt -------------------------------------------------------------------------------- /tools/specs/data/compositeOfComposite.cmpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/tools/specs/data/compositeOfComposite.cmpt -------------------------------------------------------------------------------- /tools/specs/data/instancedWithBatchTableBinary.i3dm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/tools/specs/data/instancedWithBatchTableBinary.i3dm -------------------------------------------------------------------------------- /tools/specs/data/pipeline.json: -------------------------------------------------------------------------------- 1 | { 2 | "input": "TilesetOfTilesets/", 3 | "output": "TilesetOfTilesets-output/", 4 | "stages": [ 5 | "combine", 6 | { 7 | "name": "gzip", 8 | "tilesOnly": true 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /tools/specs/data/tileset.3dtiles: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/tools/specs/data/tileset.3dtiles -------------------------------------------------------------------------------- /tools/specs/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "specs", 3 | "spec_files": [ 4 | "**/*Spec.js" 5 | ], 6 | "helpers": [ 7 | "matchers/nodeHelper.js" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /tools/specs/lib/databaseToTilesetSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var fsExtra = require('fs-extra'); 3 | var databaseToTileset = require('../../lib/databaseToTileset'); 4 | var fileExists = require('../../lib/fileExists'); 5 | var isGzipped = require('../../lib/isGzipped'); 6 | 7 | var inputFile = './specs/data/tileset.3dtiles'; 8 | var outputDirectory = './specs/data/Tileset/'; 9 | var tilesetJsonFile = './specs/data/TilesetOfTilesets/tileset.json'; 10 | 11 | describe('databaseToTileset', function() { 12 | afterEach(function (done) { 13 | fsExtra.remove(outputDirectory) 14 | .then(function() { 15 | done(); 16 | }); 17 | }); 18 | 19 | it('creates a tileset from an sqlite database', function(done) { 20 | expect(databaseToTileset(inputFile, outputDirectory) 21 | .then(function() { 22 | return fileExists(tilesetJsonFile) 23 | .then(function(exists) { 24 | expect(exists).toEqual(true); 25 | return fsExtra.readJson(tilesetJsonFile); 26 | }).then(function(data) { 27 | expect(isGzipped(data)).toBe(false); 28 | }); 29 | }), done).toResolve(); 30 | }); 31 | 32 | it('throws an error if no input file is provided', function() { 33 | expect(function() { 34 | databaseToTileset(undefined, outputDirectory); 35 | }).toThrowError('inputFile is required.'); 36 | }); 37 | 38 | it('works when no output directory is provided', function(done) { 39 | expect(databaseToTileset(inputFile) 40 | .then(function() { 41 | return fileExists(tilesetJsonFile) 42 | .then(function(exists) { 43 | expect(exists).toBe(true); 44 | }); 45 | }), done).toResolve(); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /tools/specs/lib/extractB3dmSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var fs = require('fs'); 3 | var extractB3dm = require('../../lib/extractB3dm'); 4 | 5 | var b3dmPath = './specs/data/batchedWithBatchTableBinary.b3dm'; 6 | 7 | describe('extractB3dm', function() { 8 | var b3dmBuffer; 9 | beforeAll(function() { 10 | b3dmBuffer = fs.readFileSync(b3dmPath); 11 | }); 12 | 13 | it('extracts a b3dm from buffer', function() { 14 | var b3dm = extractB3dm(b3dmBuffer); 15 | expect(b3dm.header.magic).toBe('b3dm'); 16 | expect(b3dm.header.version).toBe(1); 17 | expect(b3dm.featureTable.json).toBeDefined(); 18 | expect(b3dm.featureTable.json.BATCH_LENGTH).toBe(10); 19 | expect(b3dm.featureTable.binary.length).toBe(0); 20 | expect(b3dm.batchTable.json).toBeDefined(); 21 | expect(b3dm.batchTable.json.Height).toBeDefined(); 22 | expect(b3dm.batchTable.binary.length).toBe(256); 23 | expect(b3dm.glb.length).toBe(14141); 24 | }); 25 | 26 | it('throws an error if no buffer is provided', function() { 27 | expect(function() { 28 | extractB3dm(); 29 | }).toThrowError(); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /tools/specs/lib/extractCmptSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var fsExtra = require('fs-extra'); 3 | var Promise = require('bluebird'); 4 | var extractCmpt = require('../../lib/extractCmpt'); 5 | 6 | var compositePath = './specs/data/composite.cmpt'; 7 | var compositeOfCompositePath = './specs/data/compositeOfComposite.cmpt'; 8 | 9 | describe('extractCmpt', function() { 10 | var compositeBuffer; 11 | var compositeOfCompositeBuffer; //eslint-disable-line no-unused-vars 12 | beforeAll(function(done) { 13 | Promise.all([ 14 | fsExtra.readFile(compositePath) 15 | .then(function(data) { 16 | compositeBuffer = data; 17 | }), 18 | fsExtra.readFile(compositeOfCompositePath) 19 | .then(function(data) { 20 | compositeOfCompositeBuffer = data; 21 | }) 22 | ]).then(done); 23 | }); 24 | 25 | it('extracts a b3dm and i3dm from composite buffer', function() { 26 | var innerTiles = extractCmpt(compositeBuffer); 27 | var b3dmMagic = innerTiles[0].toString('utf8', 0, 4); 28 | var i3dmMagic = innerTiles[1].toString('utf8', 0, 4); 29 | expect(innerTiles.length).toBe(2); 30 | expect(b3dmMagic).toBe('b3dm'); 31 | expect(i3dmMagic).toBe('i3dm'); 32 | }); 33 | 34 | it('extracts a b3dm and i3dm from composite-of-composite buffer', function() { 35 | var innerTiles = extractCmpt(compositeBuffer); 36 | var b3dmMagic = innerTiles[0].toString('utf8', 0, 4); 37 | var i3dmMagic = innerTiles[1].toString('utf8', 0, 4); 38 | expect(innerTiles.length).toBe(2); 39 | expect(b3dmMagic).toBe('b3dm'); 40 | expect(i3dmMagic).toBe('i3dm'); 41 | }); 42 | 43 | it('throws an error if no buffer is provided', function() { 44 | expect(function() { 45 | extractCmpt(); 46 | }).toThrowError(); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /tools/specs/lib/extractI3dmSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var fs = require('fs'); 3 | var extractI3dm = require('../../lib/extractI3dm'); 4 | 5 | var i3dmPath = './specs/data/instancedWithBatchTableBinary.i3dm'; 6 | 7 | describe('extractI3dm', function() { 8 | var i3dmBuffer; 9 | 10 | beforeAll(function() { 11 | i3dmBuffer = fs.readFileSync(i3dmPath); 12 | }); 13 | 14 | it('extracts a i3dm from buffer', function() { 15 | var i3dm = extractI3dm(i3dmBuffer); 16 | expect(i3dm.header.magic).toBe('i3dm'); 17 | expect(i3dm.header.version).toBe(1); 18 | expect(i3dm.featureTable.json).toBeDefined(); 19 | expect(i3dm.featureTable.json.INSTANCES_LENGTH).toBe(25); 20 | expect(i3dm.featureTable.binary.length).toBe(304); 21 | expect(i3dm.batchTable.json).toBeDefined(); 22 | expect(i3dm.batchTable.json.id).toBeDefined(); 23 | expect(i3dm.batchTable.binary.length).toBe(104); 24 | expect(i3dm.glb.length).toBe(5352); 25 | }); 26 | 27 | it('throws an error if no buffer is provided', function() { 28 | expect(function() { 29 | extractI3dm(); 30 | }).toThrowError(); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /tools/specs/lib/glbToB3dmSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var fs = require('fs'); 3 | var extractB3dm = require('../../lib/extractB3dm'); 4 | var getBufferPadded = require('../../lib/getBufferPadded'); 5 | var getJsonBufferPadded = require('../../lib/getJsonBufferPadded'); 6 | var glbToB3dm = require('../../lib/glbToB3dm'); 7 | 8 | var glbPath = './specs/data/CesiumTexturedBox/CesiumTexturedBox.glb'; 9 | var b3dmPath = './specs/data/batchedWithBatchTableBinary.b3dm'; 10 | 11 | describe('glbToB3dm', function() { 12 | var glbBuffer; 13 | 14 | beforeAll(function() { 15 | glbBuffer = fs.readFileSync(glbPath); 16 | }); 17 | 18 | it('generates a basic b3dm header for a glb', function() { 19 | var headerByteLength = 28; 20 | var byteLength = headerByteLength + glbBuffer.length; 21 | var b3dmBuffer = glbToB3dm(glbBuffer); 22 | var header = b3dmBuffer.slice(0, headerByteLength); 23 | expect(header.toString('utf8', 0, 4)).toEqual('b3dm'); // magic 24 | expect(header.readUInt32LE(4)).toEqual(1); // version 25 | expect(header.readUInt32LE(8)).toEqual(byteLength); // byteLength 26 | expect(header.readUInt32LE(12)).toEqual(0); // featureTableJSONByteLength 27 | expect(header.readUInt32LE(16)).toEqual(0); // featureTableBinaryByteLength 28 | expect(header.readUInt32LE(20)).toEqual(0); // batchTableJSONByteLength 29 | expect(header.readUInt32LE(24)).toEqual(0); // batchTableBinaryByteLength 30 | expect(b3dmBuffer.length).toEqual(byteLength); 31 | }); 32 | 33 | it('generates a b3dm with feature table and batch table', function() { 34 | var featureTableJson = { 35 | BATCH_LENGTH : 10 36 | }; 37 | var batchTableJson = { 38 | height : { 39 | componentType : 'FLOAT', 40 | type : 'SCALAR', 41 | byteOffset : 0 42 | } 43 | }; 44 | 45 | var headerByteLength = 28; 46 | var featureTableJsonBuffer = getJsonBufferPadded(featureTableJson, headerByteLength); 47 | var featureTableBinaryBuffer = getBufferPadded(Buffer.alloc(16)); // Contents don't matter 48 | var batchTableJsonBuffer = getJsonBufferPadded(batchTableJson); 49 | var batchTableBinaryBuffer = getBufferPadded(Buffer.alloc(32)); // Contents don't matter 50 | 51 | var byteLength = headerByteLength + featureTableJsonBuffer.length + featureTableBinaryBuffer.length + batchTableJsonBuffer.length + batchTableBinaryBuffer.length + glbBuffer.length; 52 | 53 | var b3dmBuffer = glbToB3dm(glbBuffer, featureTableJson, featureTableBinaryBuffer, batchTableJson, batchTableBinaryBuffer); 54 | var header = b3dmBuffer.slice(0, headerByteLength); 55 | expect(header.toString('utf8', 0, 4)).toEqual('b3dm'); // magic 56 | expect(header.readUInt32LE(4)).toEqual(1); // version 57 | expect(header.readUInt32LE(8)).toEqual(byteLength); // byteLength 58 | expect(header.readUInt32LE(12)).toEqual(featureTableJsonBuffer.length); // featureTableJSONByteLength 59 | expect(header.readUInt32LE(16)).toEqual(featureTableBinaryBuffer.length); // featureTableBinaryByteLength 60 | expect(header.readUInt32LE(20)).toEqual(batchTableJsonBuffer.length); // batchTableJSONByteLength 61 | expect(header.readUInt32LE(24)).toEqual(batchTableBinaryBuffer.length); // batchTableBinaryByteLength 62 | expect(b3dmBuffer.length).toEqual(byteLength); 63 | }); 64 | 65 | it('convert b3dm to glb and back to b3dm', function() { 66 | var b3dmBuffer = fs.readFileSync(b3dmPath); 67 | var b3dm = extractB3dm(b3dmBuffer); 68 | var b3dmOut = glbToB3dm(b3dm.glb, b3dm.featureTable.json, b3dm.featureTable.binary, b3dm.batchTable.json, b3dm.batchTable.binary); 69 | expect(b3dm).toEqual(extractB3dm(b3dmOut)); 70 | expect(b3dmOut).toEqual(b3dmBuffer); 71 | }); 72 | 73 | it('throws an error if no glbBuffer is provided', function() { 74 | expect(function() { 75 | glbToB3dm(); 76 | }).toThrowError('glbBuffer is not defined.'); 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /tools/specs/lib/glbToI3dmSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var fs = require('fs'); 3 | var extractI3dm = require('../../lib/extractI3dm'); 4 | var getBufferPadded = require('../../lib/getBufferPadded'); 5 | var getJsonBufferPadded = require('../../lib/getJsonBufferPadded'); 6 | var glbToI3dm = require('../../lib/glbToI3dm'); 7 | 8 | var glbPath = './specs/data/CesiumTexturedBox/CesiumTexturedBox.glb'; 9 | var i3dmPath = './specs/data/instancedWithBatchTableBinary.i3dm'; 10 | 11 | describe('glbToI3dm', function() { 12 | var glbBuffer; 13 | 14 | beforeAll(function() { 15 | glbBuffer = fs.readFileSync(glbPath); 16 | }); 17 | 18 | it('generates a basic i3dm header for a glb', function() { 19 | var headerByteLength = 32; 20 | var byteLength = headerByteLength + glbBuffer.length; 21 | var i3dmBuffer = glbToI3dm(glbBuffer); 22 | var header = i3dmBuffer.slice(0, headerByteLength); 23 | expect(header.toString('utf8', 0, 4)).toEqual('i3dm'); // magic 24 | expect(header.readUInt32LE(4)).toEqual(1); // version 25 | expect(header.readUInt32LE(8)).toEqual(byteLength); // byteLength 26 | expect(header.readUInt32LE(12)).toEqual(0); // featureTableJSONByteLength 27 | expect(header.readUInt32LE(16)).toEqual(0); // featureTableBinaryByteLength 28 | expect(header.readUInt32LE(20)).toEqual(0); // batchTableJSONByteLength 29 | expect(header.readUInt32LE(24)).toEqual(0); // batchTableBinaryByteLength 30 | expect(header.readUInt32LE(28)).toEqual(1); // gltfFormat 31 | expect(i3dmBuffer.length).toEqual(byteLength); 32 | }); 33 | 34 | it('generates an i3dm with feature table and batch table', function() { 35 | var featureTableJson = { 36 | INSTANCES_LENGTH : 1, 37 | POSITION : { 38 | byteOffset : 0 39 | } 40 | }; 41 | var batchTableJson = { 42 | height : { 43 | componentType : 'FLOAT', 44 | type : 'SCALAR', 45 | byteOffset : 0 46 | } 47 | }; 48 | 49 | var featureTableJsonBuffer = getJsonBufferPadded(featureTableJson); 50 | var featureTableBinaryBuffer = getBufferPadded(Buffer.alloc(16)); // Contents don't matter 51 | var batchTableJsonBuffer = getJsonBufferPadded(batchTableJson); 52 | var batchTableBinaryBuffer = getBufferPadded(Buffer.alloc(32)); // Contents don't matter 53 | 54 | var headerByteLength = 32; 55 | var byteLength = headerByteLength + featureTableJsonBuffer.length + featureTableBinaryBuffer.length + batchTableJsonBuffer.length + batchTableBinaryBuffer.length + glbBuffer.length; 56 | 57 | var i3dmBuffer = glbToI3dm(glbBuffer, featureTableJson, featureTableBinaryBuffer, batchTableJson, batchTableBinaryBuffer); 58 | var header = i3dmBuffer.slice(0, headerByteLength); 59 | expect(header.toString('utf8', 0, 4)).toEqual('i3dm'); // magic 60 | expect(header.readUInt32LE(4)).toEqual(1); // version 61 | expect(header.readUInt32LE(8)).toEqual(byteLength); // byteLength 62 | expect(header.readUInt32LE(12)).toEqual(featureTableJsonBuffer.length); // featureTableJSONByteLength 63 | expect(header.readUInt32LE(16)).toEqual(featureTableBinaryBuffer.length); // featureTableBinaryByteLength 64 | expect(header.readUInt32LE(20)).toEqual(batchTableJsonBuffer.length); // batchTableJSONByteLength 65 | expect(header.readUInt32LE(24)).toEqual(batchTableBinaryBuffer.length); // batchTableBinaryByteLength 66 | expect(header.readUInt32LE(28)).toEqual(1); // gltfFormat 67 | expect(i3dmBuffer.length).toEqual(byteLength); 68 | }); 69 | 70 | it('convert i3dm to glb and back to i3dm', function() { 71 | var i3dmBuffer = fs.readFileSync(i3dmPath); 72 | var i3dm = extractI3dm(i3dmBuffer); 73 | var i3dmOut = glbToI3dm(i3dm.glb, i3dm.featureTable.json, i3dm.featureTable.binary, i3dm.batchTable.json, i3dm.batchTable.binary); 74 | expect(i3dm).toEqual(extractI3dm(i3dmOut)); 75 | expect(i3dmOut).toEqual(i3dmBuffer); 76 | }); 77 | 78 | it('throws an error if no glbBuffer is provided', function() { 79 | expect(function() { 80 | glbToI3dm(); 81 | }).toThrowError('glbBuffer is not defined.'); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /tools/specs/lib/makeCompositeTileSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var fsExtra = require('fs-extra'); 3 | var getMagic = require('../../lib/getMagic'); 4 | var makeCompositeTile = require('../../lib/makeCompositeTile'); 5 | 6 | var b3dmPath = './specs/data/batchedWithBatchTableBinary.b3dm'; 7 | var i3dmPath = './specs/data/instancedWithBatchTableBinary.i3dm'; 8 | 9 | function getPaddedByteLength(byteLength) { 10 | var boundary = 8; 11 | var remainder = byteLength % boundary; 12 | var padding = (remainder === 0) ? 0 : boundary - remainder; 13 | return byteLength + padding; 14 | } 15 | 16 | describe('makeCompositeTile', function() { 17 | it('makes a composite tile', function() { 18 | var b3dm = fsExtra.readFileSync(b3dmPath); 19 | var i3dm = fsExtra.readFileSync(i3dmPath); 20 | 21 | var b3dmOriginalLength = b3dm.length; 22 | var i3dmOriginalLength = i3dm.length; 23 | expect(b3dmOriginalLength % 8 > 0).toBe(true); // initially not aligned 24 | 25 | var cmpt = makeCompositeTile([b3dm, i3dm]); 26 | var magic = getMagic(cmpt); 27 | var version = cmpt.readUInt32LE(4); 28 | var byteLength = cmpt.readUInt32LE(8); 29 | var tilesLength = cmpt.readUInt32LE(12); 30 | 31 | var headerByteLength = 16; 32 | var expectedByteLength = headerByteLength + getPaddedByteLength(b3dmOriginalLength) + getPaddedByteLength(i3dmOriginalLength); 33 | 34 | expect(magic).toBe('cmpt'); 35 | expect(version).toBe(1); 36 | expect(byteLength).toBe(cmpt.length); 37 | expect(byteLength).toBe(expectedByteLength); 38 | expect(tilesLength).toBe(2); 39 | 40 | var b3dmMagic = getMagic(cmpt, headerByteLength); 41 | var b3dmByteLength = cmpt.readUInt32LE(headerByteLength + 8); 42 | expect(b3dmMagic).toBe('b3dm'); 43 | expect(b3dmByteLength % 8 === 0).toBe(true); // b3dm is aligned 44 | 45 | var i3dmMagic = getMagic(cmpt, headerByteLength + b3dmByteLength); 46 | var i3dmByteLength = cmpt.readUInt32LE(headerByteLength + b3dmByteLength + 8); 47 | expect(i3dmMagic).toBe('i3dm'); 48 | expect(i3dmByteLength % 8 === 0).toBe(true); // i3dm is aligned 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /tools/specs/lib/optimizeGlbSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var fsExtra = require('fs-extra'); 3 | var Promise = require('bluebird'); 4 | var optimizeGlb = require('../../lib/optimizeGlb'); 5 | 6 | var glbPath = './specs/data/CesiumTexturedBox/CesiumTexturedBox.glb'; 7 | 8 | describe('optimizeGlb', function() { 9 | var buffer; 10 | beforeAll(function(done) { 11 | fsExtra.readFile(glbPath) 12 | .then(function(data) { 13 | buffer = data; 14 | done(); 15 | }); 16 | }); 17 | 18 | it('optimizes a glb using the gltf-pipeline', function(done) { 19 | expect(optimizeGlb(buffer) 20 | .then(function(optimizedGlb) { 21 | expect(optimizedGlb).not.toEqual(buffer); 22 | }), done).toResolve(); 23 | }); 24 | 25 | it('compresses textures in a glb using the gltf-pipeline', function(done) { 26 | var compressionOptions = { 27 | textureCompressionOptions : { 28 | format: 'dxt1', 29 | quality: 10 30 | } 31 | }; 32 | 33 | var promises = []; 34 | promises.push(optimizeGlb(buffer)); 35 | promises.push(optimizeGlb(buffer, compressionOptions)); 36 | 37 | expect(Promise.all(promises) 38 | .then(function(optimizedGlbs) { 39 | expect(optimizedGlbs.length).toEqual(2); 40 | expect(optimizedGlbs[0]).not.toEqual(optimizedGlbs[1]); 41 | }), done).toResolve(); 42 | }); 43 | 44 | it('throws an error if no buffer is provided', function() { 45 | expect(function() { 46 | optimizeGlb(); 47 | }).toThrowError(); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /tools/specs/lib/tilesetToDatabseSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Cesium = require('cesium'); 3 | var fsExtra = require('fs-extra'); 4 | var Promise = require('bluebird'); 5 | var sqlite3 = require('sqlite3'); 6 | var zlib = require('zlib'); 7 | var fileExists = require('../../lib/fileExists'); 8 | var isGzipped = require('../../lib/isGzipped'); 9 | var tilesetToDatabase = require('../../lib/tilesetToDatabase'); 10 | 11 | var zlibGunzip = Promise.promisify(zlib.gunzip); 12 | var getStringFromTypedArray = Cesium.getStringFromTypedArray; 13 | 14 | var inputDirectory = './specs/data/TilesetOfTilesets/'; 15 | var tilesetJsonFile = './specs/data/TilesetOfTilesets/tileset.json'; 16 | var outputFile = './specs/data/TilesetOfTilesets.3dtiles'; 17 | 18 | describe('tilesetToDatabase', function() { 19 | afterEach(function (done) { 20 | fsExtra.remove(outputFile) 21 | .then(done) 22 | .catch(done.fail); 23 | }); 24 | 25 | it('creates a sqlite database from a tileset', function(done) { 26 | expect(tilesetToDatabase(inputDirectory, outputFile) 27 | .then(function() { 28 | var db; 29 | return Promise.resolve(fileExists(outputFile)) 30 | .then(function(exists) { 31 | expect(exists).toEqual(true); 32 | }).then(function() { 33 | db = new sqlite3.Database(outputFile); 34 | var dbAll = Promise.promisify(db.all, {context : db}); 35 | return dbAll("SELECT * FROM media WHERE key='tileset.json'"); 36 | }).then(function(rows) { 37 | expect(rows.length).toEqual(1); 38 | 39 | var content = rows[0].content; 40 | expect(isGzipped(content)).toEqual(true); 41 | 42 | return Promise.all([ 43 | zlibGunzip(content), 44 | fsExtra.readJson(tilesetJsonFile) 45 | ]).then(function(data) { 46 | var jsonStr = getStringFromTypedArray(data[0]); 47 | var dbTilesetJson = JSON.parse(jsonStr); 48 | var tilesetJson = data[1]; 49 | expect(dbTilesetJson).toEqual(tilesetJson); 50 | }); 51 | }).finally(function() { 52 | db.close(); 53 | }); 54 | }), done).toResolve(); 55 | }); 56 | 57 | it('throws an error if no input directory is provided', function() { 58 | expect(function() { 59 | tilesetToDatabase(undefined, outputFile); 60 | }).toThrowError('inputDirectory is required.'); 61 | }); 62 | 63 | it('works when no output file is provided', function(done) { 64 | expect(tilesetToDatabase(inputDirectory) 65 | .then(function() { 66 | return fileExists(outputFile) 67 | .then(function(exists) { 68 | expect(exists).toBe(true); 69 | }); 70 | }), done).toResolve(); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /tools/specs/matchers/customizeJasmine.js: -------------------------------------------------------------------------------- 1 | /*eslint strict: ["error", "function"]*/ 2 | /*eslint-env amd*/ 3 | define([ 4 | './addDefaultMatchers', 5 | './equalsMethodEqualityTester' 6 | ], function (addDefaultMatchers, 7 | equalsMethodEqualityTester) { 8 | 'use strict'; 9 | 10 | return function (env) { 11 | env.beforeEach(function () { 12 | addDefaultMatchers(true).call(env); 13 | env.addCustomEqualityTester(equalsMethodEqualityTester); 14 | }); 15 | }; 16 | }); 17 | -------------------------------------------------------------------------------- /tools/specs/matchers/equals.js: -------------------------------------------------------------------------------- 1 | //This file is a copy of https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Specs/equals.js 2 | /*eslint strict: ["error", "function"]*/ 3 | /*eslint-env amd*/ 4 | define([ 5 | 'Cesium/Core/FeatureDetection' 6 | ], function( 7 | FeatureDetection) { 8 | 'use strict'; 9 | /*global CanvasPixelArray*/ 10 | 11 | var typedArrayTypes = []; 12 | 13 | // Earlier versions of IE do not support typed arrays 14 | if (FeatureDetection.supportsTypedArrays()) { 15 | typedArrayTypes.push(Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array); 16 | 17 | if (typeof Uint8ClampedArray !== 'undefined') { 18 | typedArrayTypes.push(Uint8ClampedArray); 19 | } 20 | 21 | if (typeof CanvasPixelArray !== 'undefined') { 22 | typedArrayTypes.push(CanvasPixelArray); 23 | } 24 | } 25 | 26 | function isTypedArray(o) { 27 | return typedArrayTypes.some(function(type) { 28 | return o instanceof type; 29 | }); 30 | } 31 | 32 | function typedArrayToArray(array) { 33 | if (array !== null && typeof array === 'object' && isTypedArray(array)) { 34 | return Array.prototype.slice.call(array, 0); 35 | } 36 | return array; 37 | } 38 | 39 | function equals(util, customEqualiyTesters, a, b) { 40 | a = typedArrayToArray(a); 41 | b = typedArrayToArray(b); 42 | return util.equals(a, b, customEqualiyTesters); 43 | } 44 | 45 | return equals; 46 | }); 47 | -------------------------------------------------------------------------------- /tools/specs/matchers/equalsMethodEqualityTester.js: -------------------------------------------------------------------------------- 1 | //This file is a copy of https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Specs/equalsMethodEqualityTester.js 2 | /*eslint strict: ["error", "function"]*/ 3 | /*eslint-env amd*/ 4 | define([ 5 | 'Cesium/Core/defined' 6 | ], function( 7 | defined) { 8 | 'use strict'; 9 | 10 | return function(a, b) { 11 | var to_run; 12 | // if either a or b have an equals method, call it. 13 | if (a !== null && defined(a)) { 14 | if (typeof a.equals === 'function') { 15 | return a.equals(b); 16 | } else if(a instanceof Object) { 17 | // Check if the current object has a static function named 'equals' 18 | to_run = Object.getPrototypeOf(a).constructor.equals; 19 | if( typeof to_run === 'function') { 20 | return to_run(a, b); 21 | } 22 | } 23 | } 24 | 25 | if (b !== null && defined(b)) { 26 | if (typeof b.equals === 'function') { 27 | return b.equals(a); 28 | } else if(b instanceof Object) { 29 | // Check if the current object has a static function named 'equals' 30 | to_run = Object.getPrototypeOf(b).constructor.equals; 31 | if( typeof to_run === 'function') { 32 | return to_run(b, a); 33 | } 34 | } 35 | } 36 | 37 | // fall back to default equality checks. 38 | return undefined; 39 | }; 40 | }); 41 | -------------------------------------------------------------------------------- /tools/specs/matchers/expectPromise.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Cesium = require('cesium'); 4 | 5 | var defined = Cesium.defined; 6 | var defaultValue = Cesium.defaultValue; 7 | 8 | module.exports = function expectPromise(promise, done) { 9 | return { 10 | toResolve: function toResolve() { 11 | return promise 12 | .then(done) 13 | .catch(function(err){ 14 | done.fail('Expected promise to resolve' + err); 15 | }); 16 | }, 17 | toResolveWith: function toResolveWith(expectedValue) { 18 | return promise 19 | .then(function (result) { 20 | expect(result).toEqual(expectedValue); 21 | done(); 22 | }) 23 | .catch(function(err){ 24 | done.fail('Expected promise to resolve' + err); 25 | }); 26 | }, 27 | toRejectWith: function toRejectWith(ErrorType, errorMessage) { 28 | var typeName = defaultValue(ErrorType.displayName, ErrorType.name); 29 | 30 | promise 31 | .then(function () { 32 | done.fail('expected promise to reject with ' + typeName); 33 | }) 34 | .catch(function (error) { 35 | if (!(error instanceof ErrorType)) { 36 | done.fail(defaultValue(defaultValue(error.displayName, error.name), ErrorType) + ' to be instance of ' + typeName); 37 | console.log(error); 38 | } 39 | 40 | if (defined(errorMessage)) { 41 | expect(error.message).toEqual(errorMessage); 42 | } 43 | done(); 44 | }); 45 | } 46 | }; 47 | }; 48 | -------------------------------------------------------------------------------- /tools/specs/matchers/nodeHelper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var requirejs = require('requirejs'); 5 | 6 | var expectPromise = require('./expectPromise'); 7 | 8 | //Since Jasmine matchers are shared between client and server code 9 | //We need to use requirejs to bring them into node. 10 | requirejs.config({ 11 | baseUrl: path.join(__dirname, '../..'), 12 | paths: { 13 | 'Cesium': 'node_modules/cesium/source' 14 | }, 15 | nodeRequire: require 16 | }); 17 | 18 | var customizeJasmine = requirejs('./specs/matchers/customizeJasmine'); 19 | 20 | var env = jasmine.getEnv(); 21 | customizeJasmine(env); 22 | 23 | var oldExpect = global.expect; 24 | global.expect = function (promise, done) { 25 | //We can't use instanceof Promise here because promise 26 | //may not be a bluebird-defined Promise 27 | if (promise && promise.then && done) { 28 | return expectPromise(promise, done); 29 | } 30 | 31 | //If it wasn't a promise, call original implementation 32 | return oldExpect.apply(global, arguments); 33 | }; 34 | -------------------------------------------------------------------------------- /tools/tools/jsdoc/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/AnalyticalGraphicsInc/3d-tiles-tools/blob/{version}/lib/{filename}" 16 | }, 17 | "opts": { 18 | "destination": "doc", 19 | "recurse": true 20 | } 21 | } -------------------------------------------------------------------------------- /validator/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/** 2 | coverage/** 3 | doc/** 4 | -------------------------------------------------------------------------------- /validator/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "cesium/node" 3 | } 4 | -------------------------------------------------------------------------------- /validator/.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | package.json text eol=lf -------------------------------------------------------------------------------- /validator/.gitignore: -------------------------------------------------------------------------------- 1 | # NPM 2 | node_modules 3 | npm-debug.log 4 | package-lock.json 5 | 6 | # WebStorm user-specific 7 | .idea/workspace.xml 8 | .idea/tasks.xml 9 | 10 | # Generated files 11 | .nyc_output 12 | .eslintcache 13 | doc 14 | coverage 15 | *.tgz 16 | *.zip 17 | -------------------------------------------------------------------------------- /validator/.idea/3d-tiles-validator.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /validator/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /validator/.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /validator/.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /validator/.idea/jsLibraryMappings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /validator/.idea/jsLinters/jshint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 85 | -------------------------------------------------------------------------------- /validator/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /validator/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /validator/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /validator/.npmignore: -------------------------------------------------------------------------------- 1 | /coverage 2 | /doc 3 | /specs 4 | /tools 5 | /.idea 6 | .eslintrc.json 7 | .gitattributes 8 | .npmignore 9 | .travis.yml 10 | gulpfile.js 11 | *.tgz 12 | .eslintcache 13 | .nyc_output 14 | -------------------------------------------------------------------------------- /validator/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /validator/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 8 4 | script: 5 | - npm run eslint 6 | - npm run test -- --failTaskOnError --suppressPassed 7 | -------------------------------------------------------------------------------- /validator/CHANGES.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | -------------------------------------------------------------------------------- /validator/README.md: -------------------------------------------------------------------------------- 1 | # 3D Tiles Validator 2 | 3 | Node.js library and command-line tools for validating 3D Tiles tilesets. 4 | 5 | ## Instructions 6 | 7 | Clone this repo and install [Node.js](http://nodejs.org/). From the root directory of this repo, run: 8 | ``` 9 | npm install 10 | ``` 11 | 12 | ## Command line tools 13 | 14 | ### validate 15 | 16 | Validates the input tileset. 17 | 18 | ``` 19 | node ./bin/3d-tiles-validator.js ./specs/data/Tileset/ 20 | ``` 21 | ``` 22 | node ./bin/3d-tiles-validator.js -i ./specs/data/Tileset/ 23 | ``` 24 | 25 | |Flag|Description|Required| 26 | |----|-----------|--------| 27 | |`-i`, `--input`|Input directory of the tileset.| No. An input tileset is required but this flag may be omitted| 28 | 29 | ## Build Instructions 30 | 31 | Run the tests: 32 | ``` 33 | npm run test 34 | ``` 35 | To run ESLint on the entire codebase, run: 36 | ``` 37 | npm run eslint 38 | ``` 39 | To run ESLint automatically when a file is saved, run the following and leave it open in a console window: 40 | ``` 41 | npm run eslint-watch 42 | ``` 43 | 44 | ### Running Test Coverage 45 | 46 | Coverage uses [istanbul](https://github.com/gotwarlost/istanbul). Run: 47 | ``` 48 | npm run coverage 49 | ``` 50 | For complete coverage details, open `coverage/lcov-report/index.html`. 51 | 52 | The tests and coverage covers the Node.js module; it does not cover the command-line interface. 53 | 54 | ## Generating Documentation 55 | 56 | To generate the documentation: 57 | ``` 58 | npm run jsDoc 59 | ``` 60 | 61 | The documentation will be placed in the `doc` folder. 62 | 63 | ### Debugging 64 | 65 | * To debug the tests in Webstorm, open the Gulp tab, right click the `test` task, and click `Debug 'test'`. 66 | * To run a single test, change the test function from `it` to `fit`. -------------------------------------------------------------------------------- /validator/bin/3d-tiles-validator.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | -------------------------------------------------------------------------------- /validator/gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Cesium = require('cesium'); 4 | var child_process = require('child_process'); 5 | var fsExtra = require('fs-extra'); 6 | var gulp = require('gulp'); 7 | var Jasmine = require('jasmine'); 8 | var jasmineSpecReporter = require('jasmine-spec-reporter'); 9 | var open = require('open'); 10 | var path = require('path'); 11 | var Promise = require('bluebird'); 12 | var yargs = require('yargs'); 13 | 14 | var defined = Cesium.defined; 15 | var argv = yargs.argv; 16 | 17 | // Add third-party node module binaries to the system path 18 | // since some tasks need to call them directly. 19 | var environmentSeparator = process.platform === 'win32' ? ';' : ':'; 20 | var nodeBinaries = path.join(__dirname, 'node_modules', '.bin'); 21 | process.env.PATH += environmentSeparator + nodeBinaries; 22 | 23 | var specFiles = ['**/*.js', '!node_modules/**', '!coverage/**']; 24 | 25 | gulp.task('test', function (done) { 26 | var jasmine = new Jasmine(); 27 | jasmine.loadConfigFile('specs/jasmine.json'); 28 | jasmine.addReporter(new jasmineSpecReporter.SpecReporter({ 29 | displaySuccessfulSpec: !defined(argv.suppressPassed) || !argv.suppressPassed 30 | })); 31 | jasmine.execute(); 32 | jasmine.onComplete(function (passed) { 33 | done(argv.failTaskOnError && !passed ? 1 : 0); 34 | }); 35 | }); 36 | 37 | gulp.task('test-watch', function () { 38 | gulp.watch(specFiles).on('change', function () { 39 | // We can't simply depend on the test task because Jasmine 40 | // does not like being run multiple times in the same process. 41 | try { 42 | child_process.execSync('jasmine JASMINE_CONFIG_PATH=specs/jasmine.json', { 43 | stdio: [process.stdin, process.stdout, process.stderr] 44 | }); 45 | } catch (exception) { 46 | console.log('Tests failed to execute.'); 47 | } 48 | }); 49 | }); 50 | 51 | gulp.task('coverage', function () { 52 | fsExtra.removeSync('coverage/server'); 53 | child_process.execSync('nyc' + 54 | ' --all' + 55 | ' --reporter=lcov' + 56 | ' --dir coverage' + 57 | ' -x "bin/**"' + 58 | ' -x "doc/**"' + 59 | ' -x "specs/**"' + 60 | ' -x "coverage/**"' + 61 | ' -x index.js' + 62 | ' -x gulpfile.js"' + 63 | ' node_modules/jasmine/bin/jasmine.js' + 64 | ' JASMINE_CONFIG_PATH=specs/jasmine.json', { 65 | stdio: [process.stdin, process.stdout, process.stderr] 66 | }); 67 | open('coverage/lcov-report/index.html'); 68 | }); 69 | 70 | gulp.task('jsDoc', function() { 71 | return new Promise(function(resolve, reject) { 72 | child_process.exec('jsdoc --configure tools/jsdoc/conf.json', function(error, stdout, stderr) { 73 | if (error) { 74 | console.log(stderr); 75 | return reject(error); 76 | } 77 | console.log(stdout); 78 | open('doc/index.html'); 79 | resolve(); 80 | }); 81 | }); 82 | }); 83 | 84 | gulp.task('cloc', function() { 85 | var cmdLine; 86 | var clocPath = path.join('node_modules', 'cloc', 'lib', 'cloc'); 87 | 88 | //Run cloc on primary Source files only 89 | var source = new Promise(function(resolve, reject) { 90 | cmdLine = 'perl ' + clocPath + ' --quiet --progress-rate=0' + 91 | ' lib/ bin/'; 92 | 93 | child_process.exec(cmdLine, function(error, stdout, stderr) { 94 | if (error) { 95 | console.log(stderr); 96 | return reject(error); 97 | } 98 | console.log('Source:'); 99 | console.log(stdout); 100 | resolve(); 101 | }); 102 | }); 103 | 104 | //If running cloc on source succeeded, also run it on the tests. 105 | return source.then(function() { 106 | return new Promise(function(resolve, reject) { 107 | cmdLine = 'perl ' + clocPath + ' --quiet --progress-rate=0' + 108 | ' specs/lib/'; 109 | child_process.exec(cmdLine, function(error, stdout, stderr) { 110 | if (error) { 111 | console.log(stderr); 112 | return reject(error); 113 | } 114 | console.log('Specs:'); 115 | console.log(stdout); 116 | resolve(); 117 | }); 118 | }); 119 | }); 120 | }); 121 | -------------------------------------------------------------------------------- /validator/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | }; 4 | -------------------------------------------------------------------------------- /validator/lib/isGzipped.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = isGzipped; 3 | 4 | /** 5 | * Test if the provided data is gzipped. 6 | * 7 | * @param {Buffer} data A buffer containing the data to test. 8 | * @returns {Boolean} True if the data is gzipped, False if not. 9 | */ 10 | function isGzipped(data) { 11 | return data[0] === 0x1f && data[1] === 0x8b; 12 | } 13 | -------------------------------------------------------------------------------- /validator/lib/readTile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var fs = require('fs-extra'); 3 | var Promise = require('bluebird'); 4 | var zlib = require('zlib'); 5 | var isGzipped = require('./isGzipped'); 6 | 7 | var fsReadFile = Promise.promisify(fs.readFile); 8 | var zlibGunzip = Promise.promisify(zlib.gunzip); 9 | 10 | module.exports = readTile; 11 | 12 | /** 13 | * Reads tile data from a file. 14 | * 15 | * @param {String} filePath The file path to read from. 16 | * @returns {Promise} A promise that resolves with the data when the read operation completes. 17 | */ 18 | function readTile(filePath) { 19 | return fsReadFile(filePath) 20 | .then(function(buffer) { 21 | if (isGzipped(buffer)) { 22 | return zlibGunzip(buffer); 23 | } 24 | return buffer; 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /validator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "3d-tiles-validator", 3 | "version": "0.1.0", 4 | "license": "Apache-2.0", 5 | "description": "Tools for validating 3D Tiles tilesets.", 6 | "author": { 7 | "name": "Analytical Graphics, Inc. and Contributors" 8 | }, 9 | "keywords": [ 10 | "3D Tiles" 11 | ], 12 | "homepage": "https://github.com/AnalyticalGraphicsInc/3d-tiles-tools", 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/AnalyticalGraphicsInc/3d-tiles-tools.git" 16 | }, 17 | "bugs": { 18 | "url": "https://github.com/AnalyticalGraphicsInc/3d-tiles-tools/issues" 19 | }, 20 | "main": "index.js", 21 | "engines": { 22 | "node": ">=4.0.0" 23 | }, 24 | "dependencies": { 25 | "bluebird": "^3.5.1", 26 | "cesium": "^1.39", 27 | "fs-extra": "^4.0.2", 28 | "yargs": "^10.0.3" 29 | }, 30 | "devDependencies": { 31 | "cloc": "^2.3.3", 32 | "eslint": "^4.10.0", 33 | "eslint-config-cesium": "^2.0.1", 34 | "gulp": "^3.9.1", 35 | "jasmine": "^2.8.0", 36 | "jasmine-spec-reporter": "^4.2.1", 37 | "jsdoc": "^3.5.5", 38 | "nyc": "^11.3.0", 39 | "open": "^0.0.5", 40 | "requirejs": "^2.3.5" 41 | }, 42 | "scripts": { 43 | "eslint": "eslint \"./**/*.js\" --cache --quiet", 44 | "test": "gulp test", 45 | "test-watch": "gulp test-watch", 46 | "coverage": "gulp coverage", 47 | "jsDoc": "gulp jsDoc", 48 | "cloc": "gulp cloc" 49 | }, 50 | "bin": { 51 | "3d-tiles-validator": "./bin/3d-tiles-validator.js" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /validator/specs/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.eslintrc.json", 3 | "env": { 4 | "jasmine": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /validator/specs/data/Tileset/ll.b3dm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/validator/specs/data/Tileset/ll.b3dm -------------------------------------------------------------------------------- /validator/specs/data/Tileset/lr.b3dm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/validator/specs/data/Tileset/lr.b3dm -------------------------------------------------------------------------------- /validator/specs/data/Tileset/parent.b3dm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/validator/specs/data/Tileset/parent.b3dm -------------------------------------------------------------------------------- /validator/specs/data/Tileset/tileset.json: -------------------------------------------------------------------------------- 1 | { 2 | "asset": { 3 | "version": "1.0", 4 | "tilesetVersion": "1.2.3" 5 | }, 6 | "properties": { 7 | "id": { 8 | "minimum": 0, 9 | "maximum": 9 10 | }, 11 | "Longitude": { 12 | "minimum": -1.3197190069941716, 13 | "maximum": -1.3196399825465384 14 | }, 15 | "Latitude": { 16 | "minimum": 0.6988468038519597, 17 | "maximum": 0.6989046685398855 18 | }, 19 | "Height": { 20 | "minimum": 6, 21 | "maximum": 84 22 | } 23 | }, 24 | "geometricError": 240, 25 | "root": { 26 | "boundingVolume": { 27 | "region": [ 28 | -1.3197209591796106, 29 | 0.6988424218, 30 | -1.3196390408203893, 31 | 0.6989055782, 32 | 0, 33 | 88 34 | ] 35 | }, 36 | "geometricError": 70, 37 | "refine": "ADD", 38 | "content": { 39 | "url": "parent.b3dm", 40 | "boundingVolume": { 41 | "region": [ 42 | -1.3197004795898053, 43 | 0.6988582109, 44 | -1.3196595204101946, 45 | 0.6988897891, 46 | 0, 47 | 88 48 | ] 49 | } 50 | }, 51 | "children": [ 52 | { 53 | "boundingVolume": { 54 | "region": [ 55 | -1.3197209591796106, 56 | 0.6988424218, 57 | -1.31968, 58 | 0.698874, 59 | 0, 60 | 20 61 | ] 62 | }, 63 | "geometricError": 0, 64 | "content": { 65 | "url": "ll.b3dm" 66 | } 67 | }, 68 | { 69 | "boundingVolume": { 70 | "region": [ 71 | -1.31968, 72 | 0.6988424218, 73 | -1.3196390408203893, 74 | 0.698874, 75 | 0, 76 | 20 77 | ] 78 | }, 79 | "geometricError": 0, 80 | "content": { 81 | "url": "lr.b3dm" 82 | } 83 | }, 84 | { 85 | "boundingVolume": { 86 | "region": [ 87 | -1.31968, 88 | 0.698874, 89 | -1.3196390408203893, 90 | 0.6989055782, 91 | 0, 92 | 20 93 | ] 94 | }, 95 | "geometricError": 0, 96 | "content": { 97 | "url": "ur.b3dm" 98 | } 99 | }, 100 | { 101 | "boundingVolume": { 102 | "region": [ 103 | -1.3197209591796106, 104 | 0.698874, 105 | -1.31968, 106 | 0.6989055782, 107 | 0, 108 | 20 109 | ] 110 | }, 111 | "geometricError": 0, 112 | "content": { 113 | "url": "ul.b3dm" 114 | } 115 | } 116 | ] 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /validator/specs/data/Tileset/ul.b3dm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/validator/specs/data/Tileset/ul.b3dm -------------------------------------------------------------------------------- /validator/specs/data/Tileset/ur.b3dm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nxddsnc/3d-tiles-tools/591b35885803a42b3d6436f33031437b282c0e0e/validator/specs/data/Tileset/ur.b3dm -------------------------------------------------------------------------------- /validator/specs/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "specs", 3 | "spec_files": [ 4 | "**/*Spec.js" 5 | ], 6 | "helpers": [ 7 | "matchers/nodeHelper.js" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /validator/specs/lib/isGzippedSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Promise = require('bluebird'); 3 | var zlib = require('zlib'); 4 | var isGzipped = require('../../lib/isGzipped'); 5 | 6 | var zlibGzip = Promise.promisify(zlib.gzip); 7 | 8 | describe('isGzipped', function() { 9 | it('detects when data is gzipped', function(done) { 10 | var data = Buffer.alloc(40); 11 | expect(isGzipped(data)).toBe(false); 12 | expect(zlibGzip(data) 13 | .then(function(zippedData) { 14 | expect(isGzipped(zippedData)).toBe(true); 15 | }), done).toResolve(); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /validator/specs/lib/readTileSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var readTile = require('../../lib/readTile'); 3 | 4 | describe('readTile', function() { 5 | it('reads a tile', function(done) { 6 | expect(readTile('./specs/data/Tileset/parent.b3dm') 7 | .then(function(tileData) { 8 | var magic = tileData.toString('utf8', 0, 4); 9 | expect(magic).toEqual('b3dm'); 10 | }), done).toResolve(); 11 | }); 12 | 13 | it('reads a gzipped tile', function(done) { 14 | expect(readTile('./specs/data/Tileset/parent.b3dm') 15 | .then(function(tileData) { 16 | var magic = tileData.toString('utf8', 0, 4); 17 | expect(magic).toEqual('b3dm'); 18 | }), done).toResolve(); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /validator/specs/matchers/customizeJasmine.js: -------------------------------------------------------------------------------- 1 | /*eslint strict: ["error", "function"]*/ 2 | /*eslint-env amd*/ 3 | define([ 4 | './addDefaultMatchers', 5 | './equalsMethodEqualityTester' 6 | ], function (addDefaultMatchers, 7 | equalsMethodEqualityTester) { 8 | 'use strict'; 9 | 10 | return function (env) { 11 | env.beforeEach(function () { 12 | addDefaultMatchers(true).call(env); 13 | env.addCustomEqualityTester(equalsMethodEqualityTester); 14 | }); 15 | }; 16 | }); 17 | -------------------------------------------------------------------------------- /validator/specs/matchers/equals.js: -------------------------------------------------------------------------------- 1 | //This file is a copy of https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Specs/equals.js 2 | /*eslint strict: ["error", "function"]*/ 3 | /*eslint-env amd*/ 4 | define([ 5 | 'Cesium/Core/FeatureDetection' 6 | ], function( 7 | FeatureDetection) { 8 | 'use strict'; 9 | /*global CanvasPixelArray*/ 10 | 11 | var typedArrayTypes = []; 12 | 13 | // Earlier versions of IE do not support typed arrays 14 | if (FeatureDetection.supportsTypedArrays()) { 15 | typedArrayTypes.push(Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array); 16 | 17 | if (typeof Uint8ClampedArray !== 'undefined') { 18 | typedArrayTypes.push(Uint8ClampedArray); 19 | } 20 | 21 | if (typeof CanvasPixelArray !== 'undefined') { 22 | typedArrayTypes.push(CanvasPixelArray); 23 | } 24 | } 25 | 26 | function isTypedArray(o) { 27 | return typedArrayTypes.some(function(type) { 28 | return o instanceof type; 29 | }); 30 | } 31 | 32 | function typedArrayToArray(array) { 33 | if (array !== null && typeof array === 'object' && isTypedArray(array)) { 34 | return Array.prototype.slice.call(array, 0); 35 | } 36 | return array; 37 | } 38 | 39 | function equals(util, customEqualiyTesters, a, b) { 40 | a = typedArrayToArray(a); 41 | b = typedArrayToArray(b); 42 | return util.equals(a, b, customEqualiyTesters); 43 | } 44 | 45 | return equals; 46 | }); 47 | -------------------------------------------------------------------------------- /validator/specs/matchers/equalsMethodEqualityTester.js: -------------------------------------------------------------------------------- 1 | //This file is a copy of https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Specs/equalsMethodEqualityTester.js 2 | /*eslint strict: ["error", "function"]*/ 3 | /*eslint-env amd*/ 4 | define([ 5 | 'Cesium/Core/defined' 6 | ], function( 7 | defined) { 8 | 'use strict'; 9 | 10 | return function(a, b) { 11 | var to_run; 12 | // if either a or b have an equals method, call it. 13 | if (a !== null && defined(a)) { 14 | if (typeof a.equals === 'function') { 15 | return a.equals(b); 16 | } else if(a instanceof Object) { 17 | // Check if the current object has a static function named 'equals' 18 | to_run = Object.getPrototypeOf(a).constructor.equals; 19 | if( typeof to_run === 'function') { 20 | return to_run(a, b); 21 | } 22 | } 23 | } 24 | 25 | if (b !== null && defined(b)) { 26 | if (typeof b.equals === 'function') { 27 | return b.equals(a); 28 | } else if(b instanceof Object) { 29 | // Check if the current object has a static function named 'equals' 30 | to_run = Object.getPrototypeOf(b).constructor.equals; 31 | if( typeof to_run === 'function') { 32 | return to_run(b, a); 33 | } 34 | } 35 | } 36 | 37 | // fall back to default equality checks. 38 | return undefined; 39 | }; 40 | }); 41 | -------------------------------------------------------------------------------- /validator/specs/matchers/expectPromise.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Cesium = require('cesium'); 4 | 5 | var defined = Cesium.defined; 6 | var defaultValue = Cesium.defaultValue; 7 | 8 | module.exports = function expectPromise(promise, done) { 9 | return { 10 | toResolve: function toResolve() { 11 | return promise 12 | .then(done) 13 | .catch(function(err){ 14 | done.fail('Expected promise to resolve' + err); 15 | }); 16 | }, 17 | toResolveWith: function toResolveWith(expectedValue) { 18 | return promise 19 | .then(function (result) { 20 | expect(result).toEqual(expectedValue); 21 | done(); 22 | }) 23 | .catch(function(err){ 24 | done.fail('Expected promise to resolve' + err); 25 | }); 26 | }, 27 | toRejectWith: function toRejectWith(ErrorType, errorMessage) { 28 | var typeName = defaultValue(ErrorType.displayName, ErrorType.name); 29 | 30 | promise 31 | .then(function () { 32 | done.fail('expected promise to reject with ' + typeName); 33 | }) 34 | .catch(function (error) { 35 | if (!(error instanceof ErrorType)) { 36 | done.fail(defaultValue(defaultValue(error.displayName, error.name), ErrorType) + ' to be instance of ' + typeName); 37 | console.log(error); 38 | } 39 | 40 | if (defined(errorMessage)) { 41 | expect(error.message).toEqual(errorMessage); 42 | } 43 | done(); 44 | }); 45 | } 46 | }; 47 | }; 48 | -------------------------------------------------------------------------------- /validator/specs/matchers/nodeHelper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var requirejs = require('requirejs'); 5 | 6 | var expectPromise = require('./expectPromise'); 7 | 8 | //Since Jasmine matchers are shared between client and server code 9 | //We need to use requirejs to bring them into node. 10 | requirejs.config({ 11 | baseUrl: path.join(__dirname, '../..'), 12 | paths: { 13 | 'Cesium': 'node_modules/cesium/source' 14 | }, 15 | nodeRequire: require 16 | }); 17 | 18 | var customizeJasmine = requirejs('./specs/matchers/customizeJasmine'); 19 | 20 | var env = jasmine.getEnv(); 21 | customizeJasmine(env); 22 | 23 | var oldExpect = global.expect; 24 | global.expect = function (promise, done) { 25 | //We can't use instanceof Promise here because promise 26 | //may not be a bluebird-defined Promise 27 | if (promise && promise.then && done) { 28 | return expectPromise(promise, done); 29 | } 30 | 31 | //If it wasn't a promise, call original implementation 32 | return oldExpect.apply(global, arguments); 33 | }; 34 | -------------------------------------------------------------------------------- /validator/tools/jsdoc/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/AnalyticalGraphicsInc/3d-tiles-tools/blob/{version}/lib/{filename}" 16 | }, 17 | "opts": { 18 | "destination": "doc", 19 | "recurse": true 20 | } 21 | } --------------------------------------------------------------------------------