diff --git a/CHANGES.md b/CHANGES.md index aee77dea..645b4e91 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,9 @@ Change Log ========== +### Next Release +* Fixed `combinePrimitives` stage and re-added it to the pipeline. [#108](https://github.com/AnalyticalGraphicsInc/gltf-pipeline/issues/108) + ### 0.1.0-alpha5 - 2016-11-02 * Added `MergeDuplicateProperties` for stages merging duplicate glTF properties, like materials and shaders. [#152](https://github.com/AnalyticalGraphicsInc/gltf-pipeline/pull/152) diff --git a/LICENSE.md b/LICENSE.md index f2f91c6e..17518c33 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -204,32 +204,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -### object-values - -https://www.npmjs.com/package/object-values - -> The MIT License (MIT) -> -> Copyright (c) Sindre Sorhus (sindresorhus.com) -> -> Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: -> -> The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. -> -> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ### promise https://www.npmjs.com/package/promise diff --git a/lib/AccessorReader.js b/lib/AccessorReader.js index 52da7a46..1cbb9b42 100644 --- a/lib/AccessorReader.js +++ b/lib/AccessorReader.js @@ -98,12 +98,12 @@ AccessorReader.prototype.write = function(data, componentType, dataOffset) { }; /** - * Get if the AccessorReader is at the end of the accessor data. + * Get if the AccessorReader is past the end of the accessor data. * - * @returns {Boolean} True if there is more data to read, false if not. + * @returns {Boolean} True if the current index does not correspond to valid data, False if there is data to read. */ -AccessorReader.prototype.hasNext = function() { - return this.index < this.count; +AccessorReader.prototype.pastEnd = function() { + return this.index >= this.count; }; /** diff --git a/lib/Pipeline.js b/lib/Pipeline.js index f1c1b6d4..8a3fe5ea 100644 --- a/lib/Pipeline.js +++ b/lib/Pipeline.js @@ -12,7 +12,7 @@ var combineMeshes = require('./combineMeshes'); var combineNodes = require('./combineNodes'); var compressIntegerAccessors = require('./compressIntegerAccessors'); var compressTextureCoordinates = require('./compressTextureCoordinates'); -// var combinePrimitives = require('./combinePrimitives'); +var combinePrimitives = require('./combinePrimitives'); var convertDagToTree = require('./convertDagToTree'); var encodeImages = require('./encodeImages'); var generateNormals = require('./generateNormals'); @@ -85,21 +85,19 @@ Pipeline.processJSONWithExtras = function(gltfWithExtras, options) { } addDefaults(gltfWithExtras, options); RemoveUnusedProperties.removeAll(gltfWithExtras); - // It is generally better to merge the duplicate vertices before merging accessors. - // Once accessors merge, there is more likely to be overlap in accessor usage between primitives - // which limits the effectiveness of merging duplicate vertices. generateNormals(gltfWithExtras, options); mergeDuplicateVertices(gltfWithExtras); MergeDuplicateProperties.mergeAll(gltfWithExtras); RemoveUnusedProperties.removeAll(gltfWithExtras); removeDuplicatePrimitives(gltfWithExtras); + combinePrimitives(gltfWithExtras); convertDagToTree(gltfWithExtras); combineNodes(gltfWithExtras); combineMeshes(gltfWithExtras); - // TODO: Combine primitives can be uncommented and added back into the pipeline once it is fixed, but right now there are too many issues with it to allow in the main pipeline. - // combinePrimitives(gltfWithExtras); - // Merging duplicate vertices again to prevent repeat data in newly combined primitives - // mergeDuplicateVertices(gltfWithExtras); + combinePrimitives(gltfWithExtras); + MergeDuplicateProperties.mergeAll(gltfWithExtras); + removeDuplicatePrimitives(gltfWithExtras); + RemoveUnusedProperties.removeAll(gltfWithExtras); optimizeForVertexCache(gltfWithExtras); // run AO after optimizeForVertexCache since AO adds new attributes. @@ -108,8 +106,6 @@ Pipeline.processJSONWithExtras = function(gltfWithExtras, options) { bakeAmbientOcclusion(gltfWithExtras, aoOptions); } - // Run removeAll stage again after all pipeline stages have been run to remove objects that become unused - RemoveUnusedProperties.removeAll(gltfWithExtras); var waitForStages = [new Promise(function(resolve) {resolve();})]; if (options.encodeNormals) { waitForStages.push(octEncodeNormals(gltfWithExtras)); @@ -135,8 +131,6 @@ Pipeline.processJSONWithExtras = function(gltfWithExtras, options) { } quantizeAttributes(gltfWithExtras, quantizedOptions); } - // Remove duplicates again after all stages to minimize the buffer size - mergeDuplicateVertices(gltfWithExtras); return encodeImages(gltfWithExtras); }); }; diff --git a/lib/PrimitiveHelpers.js b/lib/PrimitiveHelpers.js new file mode 100644 index 00000000..680b8649 --- /dev/null +++ b/lib/PrimitiveHelpers.js @@ -0,0 +1,194 @@ +'use strict'; +var Cesium = require('cesium'); +var deepEqual = require('deep-equal'); +var AccessorReader = require('./AccessorReader'); +var getPrimitiveAttributeSemantics = require('./getPrimitiveAttributeSemantics'); +var readAccessor = require('./readAccessor'); + +var Cartesian3 = Cesium.Cartesian3; +var Matrix4 = Cesium.Matrix4; +var defined = Cesium.defined; + +module.exports = { + getAllPrimitives : getAllPrimitives, + getPrimitivesByMaterialMode : getPrimitivesByMaterialMode, + getPrimitiveConflicts : getPrimitiveConflicts, + primitiveEquals : primitiveEquals, + primitivesShareAttributeAccessor : primitivesShareAttributeAccessor, + primitivesHaveOverlappingIndexAccessors : primitivesHaveOverlappingIndexAccessors, + transformPrimitives : transformPrimitives +}; + +function primitivesShareAttributeAccessor(primitive, comparePrimitive) { + var attributes = primitive.attributes; + var compareAttributes = comparePrimitive.attributes; + for (var attribute in attributes) { + if (attributes.hasOwnProperty(attribute)) { + if (compareAttributes.hasOwnProperty(attribute)) { + if (attributes[attribute] === compareAttributes[attribute]) { + return true; + } + } + } + } + return false; +} + +function primitivesHaveOverlappingIndexAccessors(gltf, primitive, comparePrimitive) { + var accessors = gltf.accessors; + var indexAccessorId = primitive.indices; + var compareIndexAccessorId = comparePrimitive.indices; + if (!defined(indexAccessorId) || !defined(compareIndexAccessorId)) { + return false; + } + if (indexAccessorId === compareIndexAccessorId) { + return true; + } + var indexAccessor = accessors[indexAccessorId]; + var compareIndexAccessor = accessors[compareIndexAccessorId]; + var indices = []; + readAccessor(gltf, indexAccessor, indices); + var accessorReader = new AccessorReader(gltf, compareIndexAccessor); + var value = []; + + while (!accessorReader.pastEnd()) { + var index = accessorReader.read(value)[0]; + if (indices.indexOf(index) >= 0) { + return true; + } + accessorReader.next(); + } + return false; +} + +function transformPrimitives(gltf, primitives, transform) { + var inverseTranspose = new Matrix4(); + if (Matrix4.equals(transform, Matrix4.IDENTITY)) { + return; + } + var accessors = gltf.accessors; + Matrix4.inverseTransformation(transform, inverseTranspose); + Matrix4.transpose(inverseTranspose, inverseTranspose); + + var scratchIndexArray = []; + var scratchCartesianArray = []; + var scratchCartesian = new Cartesian3(); + var doneIndicesByAccessor = {}; + + var primitivesLength = primitives.length; + for (var i = 0; i < primitivesLength; i++) { + var primitive = primitives[i]; + var attributes = primitive.attributes; + var indexAccessorReader; + var index = 0; + if (defined(primitive.indices)) { + indexAccessorReader = new AccessorReader(gltf, accessors[primitive.indices]); + indexAccessorReader.read(scratchIndexArray); + index = scratchIndexArray[0]; + } + var positionAccessorReader; + var positionSemantics = getPrimitiveAttributeSemantics(primitive, 'POSITION'); + var positionAccessorId = attributes[positionSemantics[0]]; + if (positionSemantics.length > 0) { + doneIndicesByAccessor[positionAccessorId] = {}; + positionAccessorReader = new AccessorReader(gltf, accessors[positionAccessorId]); + } + var normalAccessorReader; + var normalSemantics = getPrimitiveAttributeSemantics(primitive, 'NORMAL'); + var normalAccessorId = attributes[normalSemantics[0]]; + if (normalSemantics.length > 0) { + doneIndicesByAccessor[normalAccessorId] = {}; + normalAccessorReader = new AccessorReader(gltf, accessors[normalAccessorId]); + } + var keepReading = true; + while (keepReading) { + if (defined(positionAccessorReader) && !doneIndicesByAccessor[positionAccessorId][index]) { + positionAccessorReader.index = index; + positionAccessorReader.read(scratchCartesianArray); + Cartesian3.unpack(scratchCartesianArray, 0, scratchCartesian); + Matrix4.multiplyByPoint(transform, scratchCartesian, scratchCartesian); + Cartesian3.pack(scratchCartesian, scratchCartesianArray); + positionAccessorReader.write(scratchCartesianArray); + doneIndicesByAccessor[positionAccessorId][index] = true; + } + if (defined(normalAccessorReader) && !doneIndicesByAccessor[normalAccessorId][index]) { + normalAccessorReader.index = index; + normalAccessorReader.read(scratchCartesianArray); + Cartesian3.unpack(scratchCartesianArray, 0, scratchCartesian); + Matrix4.multiplyByPointAsVector(inverseTranspose, scratchCartesian, scratchCartesian); + Cartesian3.normalize(scratchCartesian, scratchCartesian); + Cartesian3.pack(scratchCartesian, scratchCartesianArray); + normalAccessorReader.write(scratchCartesianArray); + doneIndicesByAccessor[normalAccessorId][index] = true; + } + if (defined(indexAccessorReader)) { + if (!indexAccessorReader.pastEnd()) { + indexAccessorReader.next(); + indexAccessorReader.read(scratchIndexArray); + index = scratchIndexArray[0]; + } else { + keepReading = false; + } + } else { + if (!positionAccessorReader.pastEnd() && !normalAccessorReader.pastEnd()) { + index++; + } else { + keepReading = false; + } + } + } + } +} + +function getPrimitivesByMaterialMode(primitives) { + var primitivesLength = primitives.length; + var primitivesByMaterialMode = {}; + for (var i = 0; i < primitivesLength; i++) { + var primitive = primitives[i]; + var materialId = primitive.material; + var primitivesByMode = primitivesByMaterialMode[materialId]; + if (!defined(primitivesByMode)) { + primitivesByMode = {}; + primitivesByMaterialMode[materialId] = primitivesByMode; + } + var mode = primitive.mode; + var primitivesArray = primitivesByMode[mode]; + if (!defined(primitivesArray)) { + primitivesArray = []; + primitivesByMode[mode] = primitivesArray; + } + primitivesArray.push(primitive); + } + return primitivesByMaterialMode; +} + +function getPrimitiveConflicts(primitives, primitive) { + var primitivesLength = primitives.length; + var conflicts = []; + for (var i = 0; i < primitivesLength; i++) { + var otherPrimitive = primitives[i]; + if (primitive !== otherPrimitive && primitivesShareAttributeAccessor(primitive, otherPrimitive)) { + conflicts.push(i); + } + } + return conflicts; +} + +function getAllPrimitives(gltf) { + var primitives = []; + var meshes = gltf.meshes; + for (var meshId in meshes) { + if (meshes.hasOwnProperty(meshId)) { + var mesh = meshes[meshId]; + primitives = primitives.concat(mesh.primitives); + } + } + return primitives; +} + +function primitiveEquals(primitiveOne, primitiveTwo) { + return primitiveOne.mode === primitiveTwo.mode && + primitiveOne.material === primitiveTwo.material && + primitiveOne.indices === primitiveTwo.indices && + deepEqual(primitiveOne.attributes, primitiveTwo.attributes); +} \ No newline at end of file diff --git a/lib/RemoveUnusedProperties.js b/lib/RemoveUnusedProperties.js index fbe9a8ec..8748a7e6 100644 --- a/lib/RemoveUnusedProperties.js +++ b/lib/RemoveUnusedProperties.js @@ -113,6 +113,7 @@ RemoveUnusedProperties.removeCameras = function(gltf) { */ RemoveUnusedProperties.removeMeshes = function(gltf) { var usedMeshIds = {}; + var meshes = gltf.meshes; var nodes = gltf.nodes; // Build hash of used meshes by iterating through nodes @@ -124,7 +125,15 @@ RemoveUnusedProperties.removeMeshes = function(gltf) { var length = nodeMeshes.length; for (var i = 0; i < length; i++) { var id = nodeMeshes[i]; - usedMeshIds[id] = true; + var mesh = meshes[id]; + if (!defined(mesh.primitives) || mesh.primitives.length === 0) { + // This is an empty mesh, remove it + nodeMeshes.splice(i, 1); + i--; + length--; + } else { + usedMeshIds[id] = true; + } } } } diff --git a/lib/cesiumGeometryToGltfPrimitive.js b/lib/cesiumGeometryToGltfPrimitive.js index a3000ac4..3b43ae2f 100644 --- a/lib/cesiumGeometryToGltfPrimitive.js +++ b/lib/cesiumGeometryToGltfPrimitive.js @@ -1,83 +1,33 @@ 'use strict'; var Cesium = require('cesium'); -var WebGLConstants = Cesium.WebGLConstants; - +var createAccessor = require('./createAccessor'); var findAccessorMinMax = require('./findAccessorMinMax'); -var getUniqueId = require('./getUniqueId'); +var getPrimitiveAttributeSemantics = require('./getPrimitiveAttributeSemantics'); var mergeBuffers = require('./mergeBuffers'); -var numberOfComponentsForType = require('./numberOfComponentsForType'); var uninterleaveAndPackBuffers = require('./uninterleaveAndPackBuffers'); var writeAccessor = require('./writeAccessor'); +var DeveloperError = Cesium.DeveloperError; +var WebGLConstants = Cesium.WebGLConstants; + module.exports = cesiumGeometryToGltfPrimitive; -// Creates a buffer location to write the new values to. mapGeometryAttributeToPrimitive does the actual writing -function createAttributeSemantic(gltf, primitive, semantic, packedLength) { +function getFirstAttributeSemantic(gltf, primitive, semantic, packedLength) { + var semantics = getPrimitiveAttributeSemantics(primitive, semantic); var type; - if (semantic.indexOf('POSITION') === 0 || semantic.indexOf('NORMAL') === 0) { - type = 'VEC3'; - } else if (semantic.indexOf('TEXCOORD') === 0) { + if (semantic === 'TEXCOORD') { type = 'VEC2'; + } else if (semantic === 'POSITION' || semantic === 'NORMAL') { + type = 'VEC3'; + } else { + throw new DeveloperError('Unsupported attribute semantic: ' + semantic); } - - var bytesPerComponent = 4; - var componentsPerType = numberOfComponentsForType(type); - var attributeLength = packedLength / componentsPerType; - - var bufferLength = attributeLength * componentsPerType * bytesPerComponent; - var buffer = new Buffer(bufferLength); - - var lowercaseSemantic = semantic.toLowerCase(); - - var bufferId = getUniqueId(gltf, 'buffer_' + lowercaseSemantic + '_generated'); - var bufferViewId = getUniqueId(gltf, 'bufferView_' + lowercaseSemantic + '_generated'); - var accessorId = getUniqueId(gltf, 'accessor_' + lowercaseSemantic + '_generated'); - - gltf.buffers[bufferId] = { - byteLength : bufferLength, - type : 'arraybuffer', - extras : { - _pipeline : { - source : buffer, - extension : '.bin' - } - } - }; - - gltf.bufferViews[bufferViewId] = { - buffer : bufferId, - byteLength : bufferLength, - byteOffset : 0, - target : WebGLConstants.ARRAY_BUFFER - }; - - var accessor = { - bufferView : bufferViewId, - byteOffset : 0, - byteStride : 0, - componentType : WebGLConstants.FLOAT, - count : attributeLength, - type : type - }; - - gltf.accessors[accessorId] = accessor; - primitive.attributes[semantic] = accessorId; -} - -// Get the first gltf attribute semantic of that type -function getFirstAttributeSemantic(gltf, primitive, semantic, packedLength) { - var attributes = primitive.attributes; - for (var attributeSemantic in attributes) { - if (attributes.hasOwnProperty(attributeSemantic)) { - if (attributeSemantic.indexOf(semantic) === 0) { - return attributeSemantic; - } - } + if (semantics.length <= 0) { + primitive.attributes[semantic] = createAccessor(gltf, packedLength, type, WebGLConstants.FLOAT, WebGLConstants.ARRAY_BUFFER); + return semantic; } - // If the primitive does not have the corresponding attribute, make one - createAttributeSemantic(gltf, primitive, semantic, packedLength); - return semantic; + return semantics[0]; } // Helper function to write attributes to gltf primitive from cesium geometry diff --git a/lib/changeAccessorComponentType.js b/lib/changeAccessorComponentType.js index e6a1fe71..0e1997d5 100644 --- a/lib/changeAccessorComponentType.js +++ b/lib/changeAccessorComponentType.js @@ -46,7 +46,7 @@ function changeAccessorComponentType(gltf, accessor, newComponentType) { } var accessorReader = new AccessorReader(gltf, accessor); var components = []; - while (accessorReader.hasNext()) { + while (!accessorReader.pastEnd()) { accessorReader.read(components); if (defined(writeBuffer)) { for (var i = 0; i < numberOfComponents; i++) { diff --git a/lib/combineNodes.js b/lib/combineNodes.js index 97a5072d..c6b128f0 100644 --- a/lib/combineNodes.js +++ b/lib/combineNodes.js @@ -1,18 +1,14 @@ 'use strict'; var Cesium = require('cesium'); var jp = require('jsonpath'); -var _ = require('underscore'); +var NodeHelpers = require('./NodeHelpers'); +var PrimitiveHelpers = require('./PrimitiveHelpers'); +var RemoveUnusedProperties = require('./RemoveUnusedProperties'); +var getUniqueId = require('./getUniqueId'); -var Cartesian3 = Cesium.Cartesian3; var Matrix4 = Cesium.Matrix4; var defined = Cesium.defined; - -var AccessorReader = require('./AccessorReader'); -var getUniqueId = require('./getUniqueId'); -var NodeHelpers = require('./NodeHelpers'); -var readAccessor = require('./readAccessor'); -var removeNodes = require('./RemoveUnusedProperties').removeNodes; -var writeAccessor = require('./writeAccessor'); +var removeNodes = RemoveUnusedProperties.removeNodes; module.exports = combineNodes; @@ -110,15 +106,17 @@ function mergeChildrenIntoNode(gltf, nodeId, meshesToNodes, meshesToMeshes, excl childNode.meshes = []; while(meshIds.length > 0) { var meshId = meshIds.pop(); - if (meshesToNodes[meshId].length === 1 && Object.keys(meshesToMeshes[meshId]).length === 0) { + if (meshesToNodes[meshId].length === 1 && meshesToMeshes[meshId].length === 0) { var mesh = meshes[meshId]; if (!Matrix4.equals(transform, Matrix4.IDENTITY)) { - transformPrimitives(gltf, mesh.primitives, transform); + PrimitiveHelpers.transformPrimitives(gltf, mesh.primitives, transform); } if (!defined(node.meshes)) { node.meshes = []; } - node.meshes.push(meshId); + if (node.meshes.indexOf(meshId) < 0) { + node.meshes.push(meshId); + } } else { childNode.meshes.push(meshId); preserve = true; @@ -265,123 +263,12 @@ function meshHasConflict(gltf, mesh, compareMesh) { var primitive = primitives[i]; for (var j = 0; j < comparePrimitivesLength; j++) { var comparePrimitive = comparePrimitives[j]; - if (primitivesShareAttributeAccessor(primitive, comparePrimitive)) { - if (primitivesHaveOverlappingIndexAccessors(gltf, primitive, comparePrimitive)) { - return true; - } - } - } - } - return false; -} - -function primitivesShareAttributeAccessor(primitive, comparePrimitive) { - var attributes = primitive.attributes; - var compareAttributes = comparePrimitive.attributes; - for (var attribute in attributes) { - if (attributes.hasOwnProperty(attribute)) { - if (compareAttributes.hasOwnProperty(attribute)) { - if (attributes[attribute] === compareAttributes[attribute]) { + if (PrimitiveHelpers.primitivesShareAttributeAccessor(primitive, comparePrimitive)) { + if (PrimitiveHelpers.primitivesHaveOverlappingIndexAccessors(gltf, primitive, comparePrimitive)) { return true; } } } } return false; -} - -function primitivesHaveOverlappingIndexAccessors(gltf, primitive, comparePrimitive) { - var accessors = gltf.accessors; - var indexAccessorId = primitive.indices; - var compareIndexAccessorId = comparePrimitive.indices; - var indexAccessor = accessors[indexAccessorId]; - var compareIndexAccessor = accessors[compareIndexAccessorId]; - var indices = []; - readAccessor(gltf, indexAccessor, indices); - var accessorReader = new AccessorReader(gltf, compareIndexAccessor); - var value = []; - - while(accessorReader.hasNext()) { - var index = accessorReader.read(value)[0]; - if(_.contains(indices, index)) { - return true; - } - accessorReader.next(); - } - return false; -} - -var inverseTranspose = new Matrix4(); -function transformPrimitives(gltf, primitives, transform) { - if (Matrix4.equals(transform, Matrix4.IDENTITY)) { - return; - } - var accessors = gltf.accessors; - Matrix4.inverseTransformation(transform, inverseTranspose); - Matrix4.transpose(inverseTranspose, inverseTranspose); - - var packedPositions = []; - var packedNormals = []; - - var primitivesLength = primitives.length; - var doneIndicesByAccessor = {}; - for (var i = 0; i < primitivesLength; i++) { - var indices = []; - var positions = []; - var normals = []; - - var primitive = primitives[i]; - var attributes = primitive.attributes; - var positionSemantic; - var normalSemantic; - for (var attribute in attributes) { - if (defined(positionSemantic) && defined(normalSemantic)) { - break; - } else if (attribute.indexOf('POSITION') === 0) { - positionSemantic = attribute; - } else if (attribute.indexOf('NORMAL') === 0) { - normalSemantic = attribute; - } - } - var indexAccessorId = primitive.indices; - var positionAccessorId = attributes[positionSemantic]; - var normalAccessorId = attributes[normalSemantic]; - if (defined(positionSemantic) && defined(normalSemantic)) { - readAccessor(gltf, accessors[positionAccessorId], positions); - readAccessor(gltf, accessors[normalAccessorId], normals); - if (defined(indexAccessorId)) { - readAccessor(gltf, accessors[indexAccessorId], indices); - } else if (positions.length === normals.length) { - indices = _.range(positions.length); - } - var indicesLength = indices.length; - var donePositionIndices = doneIndicesByAccessor[positionAccessorId]; - var doneNormalIndices = doneIndicesByAccessor[normalAccessorId]; - if (!defined(donePositionIndices)) { - donePositionIndices = {}; - doneIndicesByAccessor[positionAccessorId] = donePositionIndices; - } - if (!defined(doneNormalIndices)) { - doneNormalIndices = {}; - doneIndicesByAccessor[normalAccessorId] = doneNormalIndices; - } - for (var j = 0; j < indicesLength; j++) { - var index = indices[j]; - if (!defined(donePositionIndices[index])) { - donePositionIndices[index] = true; - var position = positions[index]; - Matrix4.multiplyByPoint(transform, position, position); - } - if (!defined(doneNormalIndices[index])) { - doneNormalIndices[index] = true; - var normal = normals[index]; - Matrix4.multiplyByPointAsVector(inverseTranspose, normal, normal); - } - } - Cartesian3.packArray(positions, packedPositions); - Cartesian3.packArray(normals, packedNormals); - writeAccessor(gltf, accessors[positionAccessorId], packedPositions); - writeAccessor(gltf, accessors[normalAccessorId], packedNormals); - } - } } \ No newline at end of file diff --git a/lib/combinePrimitives.js b/lib/combinePrimitives.js index f9ef053e..187eeab7 100755 --- a/lib/combinePrimitives.js +++ b/lib/combinePrimitives.js @@ -1,255 +1,259 @@ 'use strict'; var Cesium = require('cesium'); -var defined = Cesium.defined; -var defaultValue = Cesium.defaultValue; -var DeveloperError = Cesium.DeveloperError; +var deepEqual = require('deep-equal'); +var PrimitiveHelpers = require('./PrimitiveHelpers'); +var createAccessor = require('./createAccessor'); +var readAccessor = require('./readAccessor'); -var byteLengthForComponentType = require('./byteLengthForComponentType'); -var getAccessorByteStride = require('./getAccessorByteStride'); -var numberOfComponentsForType = require('./numberOfComponentsForType'); -var readBufferComponent = require('./readBufferComponent'); -var writeBufferComponent = require('./writeBufferComponent'); +var WebGLConstants = Cesium.WebGLConstants; +var defaultValue = Cesium.defaultValue; +var defined = Cesium.defined; module.exports = combinePrimitives; -//Combines primitives in a mesh which have the same material, mode, and attributes. +/** + * Combines all of the provided primitives if possible. + * If primitives have no shared data with the other primitives, their attributes and indices will be concatenated together and + * the indices will be incremented by the attribute offset. If the primitives have the same attribute accessors but different index + * accessors, the index accessors will be concatenated. In all other cases, the primitives will be left alone. + + * @param {Object} gltf A javascript object containing a glTF asset. + * @returns {Object} The glTF asset with combined primitives. + * + * @see addPipelineExtras + * @see loadGltfUris + * @see combineMeshes + */ function combinePrimitives(gltf) { - if (defined(gltf.accessors) && defined(gltf.bufferViews) && defined(gltf.buffers)) { - var meshes = gltf.meshes; - if (defined(meshes)) { - for (var meshId in meshes) { - if (meshes.hasOwnProperty(meshId)) { - var mesh = meshes[meshId]; - var primitives = mesh.primitives; - if (defined(primitives)) { - mesh.primitives = combineMeshPrimitives(gltf, meshId, primitives); + var allPrimitives = PrimitiveHelpers.getAllPrimitives(gltf); + var meshes = gltf.meshes; + for (var meshId in meshes) { + if (meshes.hasOwnProperty(meshId)) { + var mesh = meshes[meshId]; + var primitivesByMaterialMode = PrimitiveHelpers.getPrimitivesByMaterialMode(mesh.primitives); + mesh.primitives = []; + for (var materialId in primitivesByMaterialMode) { + if (primitivesByMaterialMode.hasOwnProperty(materialId)) { + var primitivesByMode = primitivesByMaterialMode[materialId]; + for (var mode in primitivesByMode) { + if (primitivesByMode.hasOwnProperty(mode)) { + var primitives = primitivesByMode[mode]; + primitives = combinePrimitivesInGroup(gltf, allPrimitives, primitives, materialId, parseInt(mode), false); + var primitivesLength = primitives.length; + for (var i = 0; i < primitivesLength; i++) { + mesh.primitives.push(primitives[i]); + } + } } } } } } - return gltf; } -//Combines the primitives for a given mesh. -function combineMeshPrimitives(gltf, meshId, primitives) { - //Group the primitives which share the same material, mode, and attibutes. - var primitiveGroups = []; - var combinedPrimitives = []; - while (primitives.length > 0) { - var currentPrimitive = primitives.shift(); - var currentGroup = [currentPrimitive]; - for (var i = 0; i < primitives.length; i++) { - var nextPrimitive = primitives[i]; - if (readyToCombine(currentPrimitive, nextPrimitive)) { - currentGroup.push(nextPrimitive); - primitives.splice(i, 1); - i--; - } - } - primitiveGroups.push(currentGroup); - } - - for (var j = 0; j < primitiveGroups.length; j++) { - combinedPrimitives.push(combinePrimitiveGroup(gltf, meshId, primitiveGroups[j])); - } - - return combinedPrimitives; -} - - -//Checks if two primitives can be combined based on their attributes, materials, and modes. -function readyToCombine(a, b) { - if (a.material !== b.material || a.mode !== b.mode || defined(a.indices) !== defined(b.indices)) { - return false; - } - - var aKeys = Object.keys(a.attributes); - var bKeys = Object.keys(b.attributes); - if (aKeys.length !== bKeys.length) { - return false; - } - - aKeys.sort(); - bKeys.sort(); - for (var i = 0; i < aKeys.length; i++) { - if (aKeys[i] !== bKeys[i]) { - return false; - } - } - - return true; -} +function combinePrimitivesInGroup(gltf, allPrimitives, primitives, materialId, mode, uint32Enabled) { + uint32Enabled = defaultValue(uint32Enabled, false); + var accessors = gltf.accessors; + var primitivesLength = primitives.length; + if (primitivesLength > 1) { + var accessor; + var i, j, k; + var mergeIndicesPrimitiveGroups = []; + var mergeIndicesPrimitiveGroupsLength; + var mergeIndicesGroup; + var mergeIndicesGroupLength; + var rootPrimitive; + var primitive; + var concatenatePrimitives = []; + var preservePrimitives = []; + var attributes = {}; + var attributeTypes = {}; + var indices = []; + var accessorCount; -//Creates a single primitive from a group of similar primitives. -function combinePrimitiveGroup(gltf, meshId, primitiveGroup) { - var referencePrimitive = primitiveGroup[0]; - if (primitiveGroup.length > 1) { - var newAttributes = {}; - var newPrimitive = { - "material": referencePrimitive.material, - "mode": referencePrimitive.mode - }; - //For each attribute, combine all referenced accessors into a single accessor. - if (defined(referencePrimitive.indices)) { - newPrimitive.indices = mergeAccessors(gltf, meshId, undefined, primitiveGroup, referencePrimitive); + var rootPrimitiveAttributes = primitives[0].attributes; + var semantics = Object.keys(rootPrimitiveAttributes); + var semantic; + var semanticsLength = semantics.length; + for (i = 0; i < semanticsLength; i++) { + semantic = semantics[i]; + accessor = accessors[rootPrimitiveAttributes[semantic]]; + attributes[semantic] = []; + attributeTypes[semantic] = { + type : accessor.type, + componentType : accessor.componentType + }; } - var attributeKeys = Object.keys(referencePrimitive.attributes); - for (var i = 0; i < attributeKeys.length; i++) { - var attributeKey = attributeKeys[i]; - newAttributes[attributeKey] = mergeAccessors(gltf, meshId, attributeKey, primitiveGroup, referencePrimitive); - } - newPrimitive.attributes = newAttributes; - return newPrimitive; - } - return referencePrimitive; -} -//Create a new accessor, bufferView, and buffer based on those referenced by the primitive group. -//If the attributeType is undefined, we are merging an index accessor. -function mergeAccessors(gltf, meshId, attributeType, primitiveGroup, referencePrimitive) { - var accessors = gltf.accessors; - var bufferViews = gltf.bufferViews; - var buffers = gltf.buffers; - var source = new Buffer(0); - var referenceAccessor = defined(attributeType) ? accessors[referencePrimitive.attributes[attributeType]] : accessors[referencePrimitive.indices]; - var componentType = referenceAccessor.componentType; - var type = referenceAccessor.type; - var count = 0; - var target; - var min, max; - var maxIndex = -1; - var attributeKey = defaultValue(attributeType, 'INDEX'); - var newBuffer = { - "type": "arraybuffer", - "uri": "data:,", - "extras": { - "_pipeline": { - "extension": ".bin", - "deleteExtras": true + // Sort primitives into the three combine cases + for (i = 0; i < primitivesLength; i++) { + primitive = primitives[i]; + // If the primitive's attribute accessors are different lengths, there is no reasonable strategy for combining + var attributeLength = -1; + var canResolve = true; + for (j = 0; j < semanticsLength; j++) { + var compareAttributeLength = accessors[primitive.attributes[semantics[j]]].count; + if (attributeLength < 0) { + attributeLength = compareAttributeLength; + } else if (attributeLength !== compareAttributeLength) { + preservePrimitives.push(primitive); + canResolve = false; + break; + } } - } - }; - var newBufferView = { - "buffer": createNewId(gltf, meshId, attributeKey, 'buffer'), - "byteOffset": 0, - "extras": { - "_pipeline": { - "deleteExtras": true + if (!canResolve) { + continue; } - } - }; - var newAccessor = { - "bufferView": createNewId(gltf, meshId, attributeKey, 'bufferView'), - "byteOffset": 0, - "byteStride": defaultValue(referenceAccessor.byteStride, 0), - "componentType": referenceAccessor.componentType, - "type": referenceAccessor.type, - "extras": { - "_pipeline": { - "deleteExtras": true + var conflicts = PrimitiveHelpers.getPrimitiveConflicts(allPrimitives, primitive); + var conflictsLength = conflicts.length; + canResolve = true; + for (j = 0; j < conflictsLength; j++) { + var comparePrimitive = allPrimitives[conflicts[j]]; + if (primitives.indexOf(comparePrimitive) < 0) { + canResolve = false; + break; + } + } + if (!canResolve) { + // The primitive has conflicts outside of this group, it cannot be combined. + preservePrimitives.push(primitive); + } else if (conflictsLength > 0) { + // The primitive has conflicts but they are all in this group, try to add it to an existing mergeIndicesGroup + mergeIndicesPrimitiveGroupsLength = mergeIndicesPrimitiveGroups.length; + var matched = false; + for (j = 0; j < mergeIndicesPrimitiveGroupsLength; j++) { + mergeIndicesGroup = mergeIndicesPrimitiveGroups[j]; + rootPrimitive = mergeIndicesGroup[0]; + if (deepEqual(primitive.attributes, rootPrimitive.attributes)) { + mergeIndicesGroup.push(primitive); + matched = true; + break; + } + } + // No existing matches, make a new group + if (!matched) { + mergeIndicesPrimitiveGroups.push([primitive]); + } + } else { + // No conflicts, just concatenate + concatenatePrimitives.push(primitive); } } - }; - - //Generate combined source for all grouped primitives. - for (var i = 0; i < primitiveGroup.length; i++) { - var primitive = primitiveGroup[i]; - var accessorId = defined(attributeType) ? primitive.attributes[attributeType] : primitive.indices; - var accessor = accessors[accessorId]; - var bufferViewId = accessor.bufferView; - var bufferView = bufferViews[bufferViewId]; - var numberOfComponents = numberOfComponentsForType(accessor.type); - if (!defined(min)) { - min = defaultValue(accessor.min, new Array(numberOfComponents).fill(Number.MAX_VALUE)); - } - if (!defined(max)) { - max = defaultValue(accessor.max, new Array(numberOfComponents).fill(-Number.MAX_VALUE)); - } + primitives = []; - //If the current accessor has a different type or componentType, throw an error. - if (accessor.componentType !== componentType || accessor.type !== type) { - throw new DeveloperError('Attributes cannot reference accessors with different types or componentTypes.'); + mergeIndicesPrimitiveGroupsLength = mergeIndicesPrimitiveGroups.length; + var indexOffset = 0; + var startIndices; + var indicesLength; + if (mergeIndicesPrimitiveGroupsLength > 0) { + for (i = 0; i < mergeIndicesPrimitiveGroupsLength; i++) { + startIndices = indices.length; + mergeIndicesGroup = mergeIndicesPrimitiveGroups[i]; + mergeIndicesGroupLength = mergeIndicesGroup.length; + rootPrimitive = mergeIndicesGroup[0]; + if (mergeIndicesGroupLength > 1) { + accessorCount = 0; + for (j = 0; j < semanticsLength; j++) { + semantic = semantics[j]; + accessor = accessors[rootPrimitive.attributes[semantic]]; + accessorCount = accessor.count; + readAccessor(gltf, accessor, attributes[semantic], false); + } + for (j = 0; j < mergeIndicesGroupLength; j++) { + primitive = mergeIndicesGroup[j]; + if (defined(primitive.indices)) { + readAccessor(gltf, accessors[primitive.indices], indices, false); + } else { + for (k = 0; k < accessorCount; k++) { + indices.push(k); + } + } + } + if (indexOffset > 0) { + indicesLength = indices.length; + for (j = startIndices; j < indicesLength; j++) { + indices[j] += indexOffset; + } + } + indexOffset += accessor.count; + if (indices.length > 0) { + concatenatePrimitives.push(createPrimitive(gltf, attributes, attributeTypes, indices, materialId, mode, uint32Enabled)); + } + // Reset + indexOffset = 0; + for (j = 0; j < semanticsLength; j++) { + semantic = semantics[j]; + attributes[semantic] = []; + } + indices = []; + } else { + preservePrimitives.push(rootPrimitive); + } + } } - checkCombineWarning(accessor, 'accessor', accessorId); - - var buffer = buffers[bufferView.buffer]; - checkCombineWarning(buffer, 'buffer', bufferView.buffer); - var bufferSource = buffer.extras._pipeline.source; - - //Copy the accessor data into a new buffer - var accessorCount = accessor.count; - var componentByteLength = byteLengthForComponentType(accessor.componentType); - var elementByteLength = componentByteLength * numberOfComponents; - var accessorLength = elementByteLength * accessorCount; - var accessorByteStride = getAccessorByteStride(accessor); - var byteOffset = accessor.byteOffset + bufferView.byteOffset; - var accessorSource = new Buffer(accessorLength); - - var indexOffset = maxIndex + 1; - var accessorSourceOffset = 0; - for (var j = 0; j < accessorCount; j++) { - for (var k = 0; k < numberOfComponents; k++) { - var value = readBufferComponent(bufferSource, accessor.componentType, byteOffset + k * componentByteLength); - //If we are creating an index accessor, offset the indices based on the current primitive. - if (attributeKey === 'INDEX') { - value += indexOffset; - if (value > maxIndex) { - maxIndex = value; + var concatenatePrimitivesLength = concatenatePrimitives.length; + if (concatenatePrimitivesLength > 1) { + for (i = 0; i < concatenatePrimitivesLength; i++) { + primitive = concatenatePrimitives[i]; + startIndices = indices.length; + accessorCount = 0; + for (j = 0; j < semanticsLength; j++) { + semantic = semantics[j]; + accessor = accessors[primitive.attributes[semantic]]; + accessorCount = accessor.count; + readAccessor(gltf, accessor, attributes[semantic], false); + } + if (defined(primitive.indices)) { + readAccessor(gltf, accessors[primitive.indices], indices, false); + } else { + for (k = 0; k < accessorCount; k++) { + indices.push(k); + } + } + if (indexOffset > 0) { + indicesLength = indices.length; + for (j = startIndices; j < indicesLength; j++) { + indices[j] += indexOffset; } } - writeBufferComponent(accessorSource, accessor.componentType, value, accessorSourceOffset + k * componentByteLength); - min[k] = Math.min(min[k], value); - max[k] = Math.max(max[k], value); + indexOffset += accessor.count; } - byteOffset += accessorByteStride; - accessorSourceOffset += elementByteLength; + if (indices.length > 0) { + primitives.push(createPrimitive(gltf, attributes, attributeTypes, indices, materialId, mode, uint32Enabled)); + } + } else if (concatenatePrimitivesLength > 0){ + primitives.push(concatenatePrimitives[0]); } - //Update the properties for the new accessor. - count += accessor.count; - source = Buffer.concat([source, accessorSource]); - - // If it exists, add the target to the new bufferView object. - if (!defined(newBufferView.target) && defined(bufferView.target)) { - newBufferView.target = bufferView.target; - target = bufferView.target; + var preservePrimitivesLength = preservePrimitives.length; + for (i = 0; i < preservePrimitivesLength; i++) { + primitives.push(preservePrimitives[i]); } } - //Assign the properties calculated among all primitives and add the new objects to the glTF object. - newAccessor.count = count; - if (defined(max)) { - newAccessor.max = max; - } - if (defined(min)) { - newAccessor.min = min; - } - newBufferView.byteLength = source.length; - newBuffer.byteLength = source.length; - newBuffer.extras._pipeline.source = source; - - var accessorName = createNewId(gltf, meshId, attributeKey, 'accessor'); - accessors[accessorName] = newAccessor; - bufferViews[newAccessor.bufferView] = newBufferView; - buffers[newBufferView.buffer] = newBuffer; - - return accessorName; + return primitives; } -//Creates a new object id based on the attribute type and existing objects. -function createNewId(gltf, meshId, attributeType, objectType) { - var idCount = 0; - var newId = meshId + '_' + attributeType + '_' + objectType + '_' + idCount; - var objectKeys = Object.keys(gltf[objectType + 's']); - while (objectKeys.indexOf(newId) !== -1) { - idCount++; - newId = newId.slice(0, -1) + idCount; +function createPrimitive(gltf, attributes, attributeTypes, indices, materialId, mode, uint32Enabled) { + var primitive = { + attributes : {}, + material : materialId, + mode : mode, + extras : { + _pipeline : { + deleteExtras : true + } + } + }; + if(defined(indices)) { + primitive.indices = createAccessor(gltf, indices, 'SCALAR', + uint32Enabled ? WebGLConstants.UNSIGNED_INT : WebGLConstants.UNSIGNED_SHORT, + WebGLConstants.ELEMENT_ARRAY_BUFFER); } - return newId; -} - -//Outputs a warning if there are extensions or non-pipeline extras in the object that will be lost. -function checkCombineWarning(object, objectType, objectId) { - if (defined(object.extensions) || (defined(object.extras) && Object.keys(object.extras).length > 1)) { - console.log('Warning: Extensions and extras for ' + objectType + ' "' + objectId + '" will be lost.'); + for (var semantic in attributes) { + if (attributes.hasOwnProperty(semantic)) { + var attributeTypeData = attributeTypes[semantic]; + primitive.attributes[semantic] = createAccessor(gltf, attributes[semantic], + attributeTypeData.type, attributeTypeData.componentType, WebGLConstants.ARRAY_BUFFER); + } } + return primitive; } \ No newline at end of file diff --git a/lib/createAccessor.js b/lib/createAccessor.js index 3528a4a3..c082ed4d 100644 --- a/lib/createAccessor.js +++ b/lib/createAccessor.js @@ -16,17 +16,25 @@ module.exports = createAccessor; * Creates an accessor from data and adds it to the glTF asset. * * @param {Object} gltf A javascript object containing a glTF asset. + * @param {Array.|Number} dataOrLength The data to store in the accessor buffer or the length of data to allocate. * @param {String} type glTF type (e.g. 'scalar', 'vec3') * @param {Number} componentType glTF component type (e.g. 5126 (float)) - * @param {Array.} data The data to store in the accessor buffer. * @param {Number} target glTF bufferView target (e.g. 34962 (ARRAY_BUFFER), 34963 (ELEMENT_ARRAY_BUFFER) * @param {String} [id] The id to use when assigning the accessor to the glTF asset. If undefined, a unique id is generated. * * @returns {String} The accessor's id. */ -function createAccessor(gltf, data, type, componentType, target, id) { +function createAccessor(gltf, dataOrLength, type, componentType, target, id) { var numberOfComponents = numberOfComponentsForType(type); - if (data.length % numberOfComponents !== 0) { + var data; + var dataLength; + if (Array.isArray(dataOrLength)) { + data = dataOrLength; + dataLength = data.length; + } else { + dataLength = dataOrLength; + } + if (dataLength % numberOfComponents !== 0) { throw new DeveloperError('Length of data written must be a multiple of the number of accessor components.'); } var componentByteLength = byteLengthForComponentType(componentType); @@ -35,16 +43,18 @@ function createAccessor(gltf, data, type, componentType, target, id) { } var bufferViewId = getUniqueId(gltf, 'bufferView'); var bufferId = getUniqueId(gltf, 'buffer'); - var bufferData = new Buffer(data.length * componentByteLength); - for (var i = 0; i < data.length; i++) { - writeBufferComponent(bufferData, componentType, data[i], i*componentByteLength); + var bufferData = new Buffer(dataLength * componentByteLength); + if (defined(data)) { + for (var i = 0; i < data.length; i++) { + writeBufferComponent(bufferData, componentType, data[i], i * componentByteLength); + } } var accessor = { bufferView : bufferViewId, byteOffset : 0, byteStride : 0, componentType : componentType, - count : data.length / numberOfComponents, + count : dataLength / numberOfComponents, extras : { _pipeline : { deleteExtras : true diff --git a/lib/mergeBuffers.js b/lib/mergeBuffers.js index 694f3082..b658f1d0 100755 --- a/lib/mergeBuffers.js +++ b/lib/mergeBuffers.js @@ -1,7 +1,5 @@ -/*jshint loopfunc: true */ 'use strict'; var Cesium = require('cesium'); - var getUniqueId = require('./getUniqueId'); var defined = Cesium.defined; diff --git a/lib/parseBinaryGltf.js b/lib/parseBinaryGltf.js index 45da694d..8f3ac681 100755 --- a/lib/parseBinaryGltf.js +++ b/lib/parseBinaryGltf.js @@ -2,7 +2,6 @@ var Cesium = require('cesium'); var StringDecoder = require('string_decoder').StringDecoder; var bufferEqual = require('buffer-equal'); -var objectValues = require('object-values'); var defined = Cesium.defined; var DeveloperError = Cesium.DeveloperError; @@ -21,6 +20,7 @@ module.exports = parseBinaryGltf; function parseBinaryGltf(data) { var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT; var byteOffset = 0; + var bufferViewId; //Check that the magic string is present if (data.slice(byteOffset, sizeOfUint32).toString() !== 'glTF') { @@ -67,7 +67,7 @@ function parseBinaryGltf(data) { if (defined(buffers) && defined(buffers.binary_glTF)) { if (defined(bufferViews)) { //Add id to each bufferView object - for (var bufferViewId in bufferViews) { + for (bufferViewId in bufferViews) { if (bufferViews.hasOwnProperty(bufferViewId)) { var bufferView = bufferViews[bufferViewId]; bufferView.extras._pipeline.id = bufferViewId; @@ -75,7 +75,12 @@ function parseBinaryGltf(data) { } //Create bufferView array and get bufferViews referencing binary_glTF - var sortedBufferViews = objectValues(bufferViews); + var sortedBufferViews = []; + for (bufferViewId in bufferViews) { + if (bufferViews.hasOwnProperty(bufferViewId)) { + sortedBufferViews.push(bufferViews[bufferViewId]); + } + } sortedBufferViews = sortedBufferViews.filter(function(bufferView) { return bufferView.buffer === 'binary_glTF'; }); diff --git a/lib/readAccessor.js b/lib/readAccessor.js index 6e6478f1..7e943718 100644 --- a/lib/readAccessor.js +++ b/lib/readAccessor.js @@ -8,7 +8,7 @@ var Matrix2 = Cesium.Matrix2; var Matrix3 = Cesium.Matrix3; var Matrix4 = Cesium.Matrix4; var WebGLConstants = Cesium.WebGLConstants; -var defined = Cesium.defined; +var defaultValue = Cesium.defaultValue; var AccessorReader = require('./AccessorReader'); @@ -27,7 +27,8 @@ module.exports = readAccessor; * @see addPipelineExtras * @see loadGltfUris */ -function readAccessor(gltf, accessor, results) { +function readAccessor(gltf, accessor, results, generateAttributes) { + generateAttributes = defaultValue(generateAttributes, true); var generateAttributeFunction; var type; var accessorReader = new AccessorReader(gltf, accessor); @@ -67,8 +68,16 @@ function readAccessor(gltf, accessor, results) { } var components = []; - while(defined(accessorReader.read(components))) { - results.push(generateAttributeFunction(components)); + while(!accessorReader.pastEnd()) { + accessorReader.read(components); + if (generateAttributes) { + results.push(generateAttributeFunction(components)); + } else { + var componentsLength = components.length; + for (var i = 0; i < componentsLength; i++) { + results.push(components[i]); + } + } accessorReader.next(); } return type; diff --git a/lib/removeDuplicatePrimitives.js b/lib/removeDuplicatePrimitives.js index 9a58ab0a..17b0ce8f 100644 --- a/lib/removeDuplicatePrimitives.js +++ b/lib/removeDuplicatePrimitives.js @@ -1,11 +1,12 @@ 'use strict'; var Cesium = require('cesium'); -var _ = require('underscore'); -var NodeHelpers = require('./NodeHelpers'); -var getUniqueId = require('./getUniqueId'); var defined = Cesium.defined; +var NodeHelpers = require('./NodeHelpers'); +var PrimitiveHelpers = require('./PrimitiveHelpers'); +var getUniqueId = require('./getUniqueId'); + module.exports = removeDuplicatePrimitives; /** @@ -28,7 +29,7 @@ function removeDuplicatePrimitives(gltf) { var primitivesLength = primitives.length; for (var i = 0; i < primitivesLength; i++) { var primitive = primitives[i]; - var index = findObject(seenPrimitives, primitive); + var index = findObject(seenPrimitives, primitive, PrimitiveHelpers.primitiveEquals); if (index < 0) { meshIndex = {}; meshIndex[meshId] = {}; @@ -102,12 +103,13 @@ function removePrimitivesFromMesh(gltf, meshId, indices, startIndex) { var primitives = mesh.primitives; var indicesLength = indices.length; for (var i = startIndex; i < indicesLength; i++) { - primitives[i] = undefined; + primitives[indices[i]] = undefined; } } function finalizeRemoval(gltf) { var meshes = gltf.meshes; + var removeMeshes = []; for (var meshId in meshes) { if (meshes.hasOwnProperty(meshId)) { var mesh = meshes[meshId]; @@ -118,20 +120,20 @@ function finalizeRemoval(gltf) { i--; } } + if (primitives.length === 0) { + removeMeshes.push(meshId); + } } } } -function findObject(array, object) { - var index; - _.find(array, function(item, i) { - if (_.isEqual(object, item)) { - index = i; - return; +function findObject(array, object, equalsTest) { + var arrayLength = array.length; + for (var i = 0; i < arrayLength; i++) { + if (equalsTest(array[i], object)) { + return i; } - }); - if (!defined(index)) { - return -1; } - return index; -} \ No newline at end of file + return -1; +} + diff --git a/lib/writeAccessor.js b/lib/writeAccessor.js index 952992f7..7f1eac7e 100644 --- a/lib/writeAccessor.js +++ b/lib/writeAccessor.js @@ -20,9 +20,8 @@ function writeAccessor(gltf, accessor, values) { var accessorReader = new AccessorReader(gltf, accessor); var componentType = accessor.componentType; var numberOfComponents = numberOfComponentsForType(accessor.type); - do { + while (!accessorReader.pastEnd()) { accessorReader.write(values, componentType, numberOfComponents * accessorReader.index); accessorReader.next(); } - while (accessorReader.hasNext()); } diff --git a/package.json b/package.json index 94f23bc6..d3a7ccfb 100644 --- a/package.json +++ b/package.json @@ -40,9 +40,7 @@ "jsonpath": "0.2.7", "mime": "1.3.4", "mkdirp": "0.5.1", - "object-values": "1.0.0", "promise": "7.1.1", - "underscore": "1.8.3", "yargs": "6.3.0" }, "devDependencies": { diff --git a/specs/data/combineObjects/box.gltf b/specs/data/combineObjects/box.gltf deleted file mode 100755 index 30e81fca..00000000 --- a/specs/data/combineObjects/box.gltf +++ /dev/null @@ -1,267 +0,0 @@ -{ - "accessors": { - "accessor_21": { - "bufferView": "bufferView_29", - "byteOffset": 0, - "byteStride": 0, - "componentType": 5123, - "count": 36, - "type": "SCALAR" - }, - "accessor_23": { - "bufferView": "bufferView_30", - "byteOffset": 0, - "byteStride": 12, - "componentType": 5126, - "count": 24, - "max": [ - 0.5, - 0.5, - 0.5 - ], - "min": [ - -0.5, - -0.5, - -0.5 - ], - "type": "VEC3" - }, - "accessor_25": { - "bufferView": "bufferView_30", - "byteOffset": 288, - "byteStride": 12, - "componentType": 5126, - "count": 24, - "max": [ - 1, - 1, - 1 - ], - "min": [ - -1, - -1, - -1 - ], - "type": "VEC3" - }, - "accessor_27": { - "bufferView": "bufferView_30", - "byteOffset": 576, - "byteStride": 8, - "componentType": 5126, - "count": 24, - "max": [ - 1, - 1 - ], - "min": [ - 0, - 0 - ], - "type": "VEC2" - } - }, - "animations": {}, - "asset": { - "generator": "collada2gltf@ceec062e3d5793f2f249f53cbd843aee382ad40b", - "premultipliedAlpha": true, - "profile": { - "api": "WebGL", - "version": "1.0.2" - }, - "version": 1 - }, - "bufferViews": { - "bufferView_29": { - "buffer": "box", - "byteLength": 72, - "byteOffset": 0, - "target": 34963 - }, - "bufferView_30": { - "buffer": "box", - "byteLength": 768, - "byteOffset": 72, - "target": 34962 - } - }, - "buffers": { - "box": { - "byteLength": 840, - "type": "arraybuffer", - "uri": "data:application/octet-stream;base64,AAABAAIAAwACAAEABAAFAAYABwAGAAUACAAJAAoACwAKAAkADAANAA4ADwAOAA0AEAARABIAEwASABEAFAAVABYAFwAWABUAAAAAvwAAAL8AAAA/AAAAPwAAAL8AAAA/AAAAvwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAL8AAAA/AAAAvwAAAL8AAAA/AAAAPwAAAL8AAAC/AAAAvwAAAL8AAAC/AAAAPwAAAD8AAAA/AAAAPwAAAL8AAAA/AAAAPwAAAD8AAAC/AAAAPwAAAL8AAAC/AAAAvwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAvwAAAD8AAAC/AAAAPwAAAD8AAAC/AAAAvwAAAL8AAAA/AAAAvwAAAD8AAAA/AAAAvwAAAL8AAAC/AAAAvwAAAD8AAAC/AAAAvwAAAL8AAAC/AAAAvwAAAD8AAAC/AAAAPwAAAL8AAAC/AAAAPwAAAD8AAAC/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACAPgAAAAAAAIA+oKqqPgAAAD8AAAAAAAAAP6Cqqj4AAIA+oKqqPgAAAACgqqo+AACAPrCqKj8AAAAAsKoqPwAAAD+gqqo+AACAPqCqqj4AAAA/sKoqPwAAgD6wqio/AABAP6Cqqj4AAAA/oKqqPgAAQD+wqio/AAAAP7CqKj8AAIA/oKqqPgAAQD+gqqo+AACAP7CqKj8AAEA/sKoqPwAAgD4AAIA/AAAAPwAAgD8AAIA+sKoqPwAAAD+wqio/" - } - }, - "materials": { - "Effect-Red": { - "name": "Red", - "technique": "technique0", - "values": { - "diffuse": [ - 0.8, - 0, - 0, - 1 - ], - "shininess": 256, - "specular": [ - 0.2, - 0.2, - 0.2, - 1 - ] - } - } - }, - "meshes": { - "Geometry-mesh002": { - "name": "Mesh", - "primitives": [ - { - "attributes": { - "NORMAL": "accessor_25", - "POSITION": "accessor_23", - "TEXCOORD_0": "accessor_27" - }, - "indices": "accessor_21", - "material": "Effect-Red", - "mode": 4 - } - ] - } - }, - "nodes": { - "Geometry-mesh002Node": { - "children": [], - "matrix": [ - 1, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 1 - ], - "meshes": [ - "Geometry-mesh002" - ], - "name": "Mesh" - }, - "node_1": { - "children": [ - "Geometry-mesh002Node" - ], - "matrix": [ - 1, - 0, - 0, - 0, - 0, - 0, - -1, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 0, - 1 - ], - "name": "Y_UP_Transform" - } - }, - "programs": { - "program_0": { - "attributes": [ - "a_normal", - "a_position" - ], - "fragmentShader": "box0FS", - "vertexShader": "box0VS" - } - }, - "scene": "defaultScene", - "scenes": { - "defaultScene": { - "nodes": [ - "node_1" - ] - } - }, - "shaders": { - "box0FS": { - "type": 35632, - "uri": "data:text/plain;base64,cHJlY2lzaW9uIGhpZ2hwIGZsb2F0Owp2YXJ5aW5nIHZlYzMgdl9ub3JtYWw7CnVuaWZvcm0gdmVjNCB1X2RpZmZ1c2U7CnVuaWZvcm0gdmVjNCB1X3NwZWN1bGFyOwp1bmlmb3JtIGZsb2F0IHVfc2hpbmluZXNzOwp2b2lkIG1haW4odm9pZCkgewp2ZWMzIG5vcm1hbCA9IG5vcm1hbGl6ZSh2X25vcm1hbCk7CnZlYzQgY29sb3IgPSB2ZWM0KDAuLCAwLiwgMC4sIDAuKTsKdmVjNCBkaWZmdXNlID0gdmVjNCgwLiwgMC4sIDAuLCAxLik7CnZlYzQgc3BlY3VsYXI7CmRpZmZ1c2UgPSB1X2RpZmZ1c2U7CnNwZWN1bGFyID0gdV9zcGVjdWxhcjsKZGlmZnVzZS54eXogKj0gbWF4KGRvdChub3JtYWwsdmVjMygwLiwwLiwxLikpLCAwLik7CmNvbG9yLnh5eiArPSBkaWZmdXNlLnh5ejsKY29sb3IgPSB2ZWM0KGNvbG9yLnJnYiAqIGRpZmZ1c2UuYSwgZGlmZnVzZS5hKTsKZ2xfRnJhZ0NvbG9yID0gY29sb3I7Cn0K" - }, - "box0VS": { - "type": 35633, - "uri": "data:text/plain;base64,cHJlY2lzaW9uIGhpZ2hwIGZsb2F0OwphdHRyaWJ1dGUgdmVjMyBhX3Bvc2l0aW9uOwphdHRyaWJ1dGUgdmVjMyBhX25vcm1hbDsKdmFyeWluZyB2ZWMzIHZfbm9ybWFsOwp1bmlmb3JtIG1hdDMgdV9ub3JtYWxNYXRyaXg7CnVuaWZvcm0gbWF0NCB1X21vZGVsVmlld01hdHJpeDsKdW5pZm9ybSBtYXQ0IHVfcHJvamVjdGlvbk1hdHJpeDsKdm9pZCBtYWluKHZvaWQpIHsKdmVjNCBwb3MgPSB1X21vZGVsVmlld01hdHJpeCAqIHZlYzQoYV9wb3NpdGlvbiwxLjApOwp2X25vcm1hbCA9IHVfbm9ybWFsTWF0cml4ICogYV9ub3JtYWw7CmdsX1Bvc2l0aW9uID0gdV9wcm9qZWN0aW9uTWF0cml4ICogcG9zOwp9Cg==" - } - }, - "skins": {}, - "techniques": { - "technique0": { - "attributes": { - "a_normal": "normal", - "a_position": "position" - }, - "parameters": { - "diffuse": { - "type": 35666 - }, - "modelViewMatrix": { - "semantic": "MODELVIEW", - "type": 35676 - }, - "normal": { - "semantic": "NORMAL", - "type": 35665 - }, - "normalMatrix": { - "semantic": "MODELVIEWINVERSETRANSPOSE", - "type": 35675 - }, - "position": { - "semantic": "POSITION", - "type": 35665 - }, - "projectionMatrix": { - "semantic": "PROJECTION", - "type": 35676 - }, - "shininess": { - "type": 5126 - }, - "specular": { - "type": 35666 - } - }, - "program": "program_0", - "states": { - "enable": [ - 2929, - 2884 - ] - }, - "uniforms": { - "u_diffuse": "diffuse", - "u_modelViewMatrix": "modelViewMatrix", - "u_normalMatrix": "normalMatrix", - "u_projectionMatrix": "projectionMatrix", - "u_shininess": "shininess", - "u_specular": "specular" - } - } - } -} \ No newline at end of file diff --git a/specs/data/combineObjects/doubleBoxNotCombined.gltf b/specs/data/combineObjects/doubleBoxNotCombined.gltf deleted file mode 100755 index 4970e123..00000000 --- a/specs/data/combineObjects/doubleBoxNotCombined.gltf +++ /dev/null @@ -1,430 +0,0 @@ -{ - "accessors": { - "accessor_29": { - "bufferView": "bufferView_64", - "byteOffset": 0, - "byteStride": 0, - "componentType": 5123, - "count": 384, - "type": "SCALAR" - }, - "accessor_31": { - "bufferView": "bufferView_65", - "byteOffset": 0, - "byteStride": 12, - "componentType": 5126, - "count": 224, - "max": [ - 0.5, - 0.5, - 0.5 - ], - "min": [ - -0.5, - -0.5, - -0.5 - ], - "type": "VEC3" - }, - "accessor_33": { - "bufferView": "bufferView_65", - "byteOffset": 2688, - "byteStride": 12, - "componentType": 5126, - "count": 224, - "max": [ - 1, - 1, - 1 - ], - "min": [ - -1, - -1, - -1 - ], - "type": "VEC3" - }, - "accessor_35": { - "bufferView": "bufferView_65", - "byteOffset": 5376, - "byteStride": 8, - "componentType": 5126, - "count": 224, - "max": [ - 1.0007699728012085, - 1.0013258457183838 - ], - "min": [ - -0.000750000006519258, - 0.0013260245323181152 - ], - "type": "VEC2" - }, - "accessor_56": { - "bufferView": "bufferView_64", - "byteOffset": 768, - "byteStride": 0, - "componentType": 5123, - "count": 132, - "type": "SCALAR" - }, - "accessor_58": { - "bufferView": "bufferView_65", - "byteOffset": 7168, - "byteStride": 12, - "componentType": 5126, - "count": 96, - "max": [ - 0.33504000306129456, - 0.5, - 0.33504000306129456 - ], - "min": [ - -0.33504000306129456, - -0.5, - -0.33504000306129456 - ], - "type": "VEC3" - }, - "accessor_60": { - "bufferView": "bufferView_65", - "byteOffset": 8320, - "byteStride": 12, - "componentType": 5126, - "count": 96, - "max": [ - 1, - 1, - 1 - ], - "min": [ - -1, - -1, - -1 - ], - "type": "VEC3" - }, - "accessor_62": { - "bufferView": "bufferView_65", - "byteOffset": 9472, - "byteStride": 8, - "componentType": 5126, - "count": 96, - "max": [ - 0.40562498569488525, - 0.9055818915367126 - ], - "min": [ - 0.34437498450279236, - 0.0944179892539978 - ], - "type": "VEC2" - } - }, - "asset": { - "generator": "collada2gltf@", - "premultipliedAlpha": true, - "profile": { - "api": "WebGL", - "version": "1.0.2" - }, - "version": 1 - }, - "bufferViews": { - "bufferView_63": { - "buffer": "input", - "byteLength": 104, - "byteOffset": 0 - }, - "bufferView_64": { - "buffer": "input", - "byteLength": 1032, - "byteOffset": 104, - "target": 34963 - }, - "bufferView_65": { - "buffer": "input", - "byteLength": 10240, - "byteOffset": 1136, - "target": 34962 - } - }, - "buffers": { - "input": { - "byteLength": 11376, - "type": "arraybuffer", - "uri": "data:application/octet-stream;base64,AACgPwAAIEAAAAAAAAAAAAAAAAAAAIA/AACAvwAAAIAAAACAEnVFrgAAAAAAAKA/AAAgQEdVbUAAAAAAAAAAAAAAAAAAAAAArkchQAAAAAAAAAAArkchQAAAAAAAAAAAAAAAAAAAAAAAAAEAAgACAAMAAAAEAAUABgAGAAcABAAIAAkACgAKAAsACAAMAA0ADgAOAA8ADAAQABEAEgASABMAEAAUABUAFgAWABcAFAAYABkAGgAaABsAGAAcAB0AHgAeAB8AHAAgACEAIgAiACMAIAAkACUAJgAmACcAJAAoACkAKgAqACsAKAAsAC0ALgAuAC8ALAAwADEAMgAyADMAMAA0ADUANgA2ADcANAA4ADkAOgA6ADsAOAA8AD0APgA+AD8APABAAEEAQgBCAEMAQABEAEUARgBGAEcARABIAEkASgBKAEsASABMAE0ATgBOAE8ATABQAFEAUgBSAFMAUABUAFUAVgBWAFcAVABYAFkAWgBaAFsAWABcAF0AXgBeAF8AXABgAGEAYgBiAGMAYABkAGUAZgBmAGcAZABoAGkAagBqAGsAaABsAG0AbgBuAG8AbABwAHEAcgByAHMAcAB0AHUAdgB2AHcAdAB4AHkAegB6AHsAeAB8AH0AfgB+AH8AfACAAIEAggCCAIMAgACEAIUAhgCGAIcAhACIAIkAigCKAIsAiACMAI0AjgCOAI8AjACQAJEAkgCSAJMAkACUAJUAlgCWAJcAlACYAJkAmgCaAJsAmACcAJ0AngCeAJ8AnAApACgAQwBDAEIAKQAFAAQAKwArACoABQBVAFQABwAHAAYAVQBBAEAAVwBXAFYAQQCgAKEAogCiAKMAoACkAKUApgCmAKcApACoAKkAqgCqAKsAqACsAK0ArgCuAK8ArACwALEAsgCyALMAsAC0ALUAtgC2ALcAtAC4ALAAswCzALkAuAC6ALQAtwC3ALsAugC8ALgAuQC5AL0AvACxALoAuwC7ALIAsQC+ALwAvQC9AL8AvgC1AL4AvwC/ALYAtQDAAMEAwgDCAMMAwADEAMUAxgDGAMcAxADIAMkAygDKAMsAyADMAM0AzgDOAM8AzADQANEA0gDSANMA0ADUANUA1gDWANcA1ADYANkA2gDaANsA2ADcAN0A3gDeAN8A3AAAAAEAAgACAAMAAAAEAAUABgAGAAcABAAIAAkA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AAAAAPcENb/3BDU/AAAAAPcENb/3BDU/AAAAAPcENb/3BDU/AAAAAPcENb8AAAAAaM0TP9EFUT8AAAAAaM0TP9EFUT8AAAAAaM0TP9EFUT8AAAAAaM0TP9EFUT8AAAAAAACAvwAAAIAAAAAAAACAvwAAAIAAAAAAAACAvwAAAIAAAAAAAACAvwAAAIAAAACAurgtP7cIPD8AAACAurgtP7cIPD8AAACAurgtP7cIPD8AAACAurgtP7cIPD/3BDW/AAAAAPcENb/3BDW/AAAAAPcENb/3BDW/AAAAAPcENb/3BDW/AAAAAPcENb/RBVE/aM0TPwAAAIDRBVE/aM0TPwAAAIDRBVE/aM0TPwAAAIDRBVE/aM0TPwAAAIDRBVG/aM0TPwAAAADRBVG/aM0TPwAAAADRBVG/aM0TPwAAAADRBVG/aM0TPwAAAAAAAAAAAACAvwAAAIAAAAAAAACAvwAAAIAAAAAAAACAvwAAAIAAAAAAAACAvwAAAID3BDW/AAAAAPcENT/3BDW/AAAAAPcENT/3BDW/AAAAAPcENT/3BDW/AAAAAPcENT8AAAAAaM0TP9EFUb8AAAAAaM0TP9EFUb8AAAAAaM0TP9EFUb8AAAAAaM0TP9EFUb+9//++GAU1v73//769//++GAU1v73//769//++GAU1v73//769//++GAU1v73//769//8+GAU1v73//769//8+GAU1v73//769//8+GAU1v73//769//8+GAU1v73//769//8+GAU1v73//z69//8+GAU1v73//z69//8+GAU1v73//z69//8+GAU1v73//z69//++GAU1v73//z69//++GAU1v73//z69//++GAU1v73//z69//++GAU1v73//z69//++GAU1P73//769//++GAU1P73//769//++GAU1P73//769//++GAU1P73//769//8+GAU1P73//769//8+GAU1P73//769//8+GAU1P73//769//8+GAU1P73//769//8+GAU1P73//z69//8+GAU1P73//z69//8+GAU1P73//z69//8+GAU1P73//z69//++GAU1P73//z69//++GAU1P73//z69//++GAU1P73//z69//++GAU1P73//z4/xvQ+lJ88vz/G9D4/xvQ+lJ88vz/G9D4/xvQ+lJ88vz/G9D4/xvQ+lJ88vz/G9D4/xvS+lJ88vz/G9D4/xvS+lJ88vz/G9D4/xvS+lJ88vz/G9D4/xvS+lJ88vz/G9D4/xvS+lJ88vz/G9L4/xvS+lJ88vz/G9L4/xvS+lJ88vz/G9L4/xvS+lJ88vz/G9L4/xvQ+lJ88vz/G9L4/xvQ+lJ88vz/G9L4/xvQ+lJ88vz/G9L4/xvQ+lJ88vz/G9L4/xvQ+lJ88Pz/G9D4/xvQ+lJ88Pz/G9D4/xvQ+lJ88Pz/G9D4/xvQ+lJ88Pz/G9D4/xvS+lJ88Pz/G9D4/xvS+lJ88Pz/G9D4/xvS+lJ88Pz/G9D4/xvS+lJ88Pz/G9D4/xvS+lJ88Pz/G9L4/xvS+lJ88Pz/G9L4/xvS+lJ88Pz/G9L4/xvS+lJ88Pz/G9L4/xvQ+lJ88Pz/G9L4/xvQ+lJ88Pz/G9L4/xvQ+lJ88Pz/G9L4/xvQ+lJ88Pz/G9L4AAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAACAAAAAgAAAgL8AAACAAAAAgAAAgL8AAACAAAAAgAAAgL8AAACAAAAAgAAAgL8AAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAACAAAAAgAAAgL8AAACAAAAAgAAAgL8AAACAAAAAgAAAgL8AAACAAAAAgAAAgL/3BDW/AAAAAPcENT/3BDW/AAAAAPcENT/3BDW/AAAAAPcENT/3BDW/AAAAAPcENT8AAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/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" - } - }, - "materials": { - "Effect-inner": { - "name": "inner", - "technique": "technique0", - "values": { - "diffuse": [ - 0.800000011920929, - 0.4159420132637024, - 0.7952920198440552, - 1 - ], - "shininess": 256, - "specular": [ - 0.20000000298023224, - 0.20000000298023224, - 0.20000000298023224, - 1 - ] - } - }, - "Effect_outer": { - "name": "outer", - "technique": "technique0", - "values": { - "diffuse": [ - 0.3016040027141571, - 0.5335419774055481, - 0.800000011920929, - 1 - ], - "shininess": 256, - "specular": [ - 0.20000000298023224, - 0.20000000298023224, - 0.20000000298023224, - 1 - ] - } - } - }, - "meshes": { - "meshTest": { - "name": "box", - "primitives": [ - { - "attributes": { - "NORMAL": "accessor_60", - "POSITION": "accessor_58", - "TEXCOORD_0": "accessor_62" - }, - "indices": "accessor_56", - "material": "Effect_inner", - "mode": 4 - }, - { - "attributes": { - "NORMAL": "accessor_33", - "POSITION": "accessor_31", - "TEXCOORD_0": "accessor_35" - }, - "indices": "accessor_29", - "material": "Effect_outer", - "mode": 4 - } - ] - } - }, - "nodes": { - "Camera-camera002Node": { - "camera": "", - "children": [], - "matrix": [ - 1, - 0, - 0, - 0, - 0, - 0.9961947202682495, - -0.08715574443340302, - 0, - 0, - 0.08715574443340302, - 0.9961947202682495, - 0, - 0, - 0.75, - 4, - 1 - ], - "name": "Camera" - }, - "Geometry-mesh019Node": { - "children": [], - "matrix": [ - 1, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 1 - ], - "meshes": [ - "meshTest" - ], - "name": "box" - }, - "Light-sunLight011Node": { - "children": [], - "matrix": [ - 0.7071067690849304, - 0, - 0.7071067690849304, - 0, - 0.3535533845424652, - 0.8660253882408142, - -0.3535533845424652, - 0, - -0.6123724579811096, - 0.5, - 0.6123724579811096, - 0, - -2, - 2, - 2, - 1 - ], - "name": "Directional_Light" - }, - "polyRender005": { - "children": [], - "matrix": [ - 1, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 1 - ], - "name": "Render" - } - }, - "programs": { - "program_0": { - "attributes": [ - "a_normal", - "a_position" - ], - "fragmentShader": "input0FS", - "vertexShader": "input0VS" - } - }, - "scene": "defaultScene", - "scenes": { - "defaultScene": { - "nodes": [ - "polyRender005", - "Geometry-mesh019Node", - "Camera-camera002Node", - "Light-sunLight011Node" - ] - } - }, - "shaders": { - "input0FS": { - "type": 35632, - "uri": "data:text/plain;base64,cHJlY2lzaW9uIGhpZ2hwIGZsb2F0Owp2YXJ5aW5nIHZlYzMgdl9ub3JtYWw7CnVuaWZvcm0gdmVjNCB1X2RpZmZ1c2U7CnVuaWZvcm0gdmVjNCB1X3NwZWN1bGFyOwp1bmlmb3JtIGZsb2F0IHVfc2hpbmluZXNzOwp1bmlmb3JtIHZlYzMgdV9saWdodDBDb2xvcjsKdmFyeWluZyB2ZWMzIHZfbGlnaHQxRGlyZWN0aW9uOwp2YXJ5aW5nIHZlYzMgdl9wb3NpdGlvbjsKdW5pZm9ybSB2ZWMzIHVfbGlnaHQxQ29sb3I7CnZvaWQgbWFpbih2b2lkKSB7CnZlYzMgbm9ybWFsID0gbm9ybWFsaXplKHZfbm9ybWFsKTsKdmVjNCBjb2xvciA9IHZlYzQoMC4sIDAuLCAwLiwgMC4pOwp2ZWM0IGRpZmZ1c2UgPSB2ZWM0KDAuLCAwLiwgMC4sIDEuKTsKdmVjMyBkaWZmdXNlTGlnaHQgPSB2ZWMzKDAuLCAwLiwgMC4pOwp2ZWM0IHNwZWN1bGFyOwpkaWZmdXNlID0gdV9kaWZmdXNlOwpzcGVjdWxhciA9IHVfc3BlY3VsYXI7CnZlYzMgc3BlY3VsYXJMaWdodCA9IHZlYzMoMC4sIDAuLCAwLik7CnZlYzMgYW1iaWVudExpZ2h0ID0gdmVjMygwLiwgMC4sIDAuKTsKewphbWJpZW50TGlnaHQgKz0gdV9saWdodDBDb2xvcjsKfQp7CmZsb2F0IHNwZWN1bGFySW50ZW5zaXR5ID0gMC47CmZsb2F0IGF0dGVudWF0aW9uID0gMS4wOwp2ZWMzIGwgPSBub3JtYWxpemUodl9saWdodDFEaXJlY3Rpb24pOwp2ZWMzIHZpZXdEaXIgPSAtbm9ybWFsaXplKHZfcG9zaXRpb24pOwpmbG9hdCBwaG9uZ1Rlcm0gPSBtYXgoMC4wLCBkb3QocmVmbGVjdCgtbCxub3JtYWwpLCB2aWV3RGlyKSk7CnNwZWN1bGFySW50ZW5zaXR5ID0gbWF4KDAuLCBwb3cocGhvbmdUZXJtICwgdV9zaGluaW5lc3MpKSAqIGF0dGVudWF0aW9uOwpzcGVjdWxhckxpZ2h0ICs9IHVfbGlnaHQxQ29sb3IgKiBzcGVjdWxhckludGVuc2l0eTsKZGlmZnVzZUxpZ2h0ICs9IHVfbGlnaHQxQ29sb3IgKiBtYXgoZG90KG5vcm1hbCxsKSwgMC4pICogYXR0ZW51YXRpb247Cn0Kc3BlY3VsYXIueHl6ICo9IHNwZWN1bGFyTGlnaHQ7CmNvbG9yLnh5eiArPSBzcGVjdWxhci54eXo7CmRpZmZ1c2UueHl6ICo9IGRpZmZ1c2VMaWdodDsKY29sb3IueHl6ICs9IGRpZmZ1c2UueHl6Owpjb2xvciA9IHZlYzQoY29sb3IucmdiICogZGlmZnVzZS5hLCBkaWZmdXNlLmEpOwpnbF9GcmFnQ29sb3IgPSBjb2xvcjsKfQo=" - }, - "input0VS": { - "type": 35633, - "uri": "data:text/plain;base64,cHJlY2lzaW9uIGhpZ2hwIGZsb2F0OwphdHRyaWJ1dGUgdmVjMyBhX3Bvc2l0aW9uOwphdHRyaWJ1dGUgdmVjMyBhX25vcm1hbDsKdmFyeWluZyB2ZWMzIHZfbm9ybWFsOwp1bmlmb3JtIG1hdDMgdV9ub3JtYWxNYXRyaXg7CnVuaWZvcm0gbWF0NCB1X21vZGVsVmlld01hdHJpeDsKdW5pZm9ybSBtYXQ0IHVfcHJvamVjdGlvbk1hdHJpeDsKdmFyeWluZyB2ZWMzIHZfbGlnaHQxRGlyZWN0aW9uOwp2YXJ5aW5nIHZlYzMgdl9wb3NpdGlvbjsKdW5pZm9ybSBtYXQ0IHVfbGlnaHQxVHJhbnNmb3JtOwp2b2lkIG1haW4odm9pZCkgewp2ZWM0IHBvcyA9IHVfbW9kZWxWaWV3TWF0cml4ICogdmVjNChhX3Bvc2l0aW9uLDEuMCk7CnZfbm9ybWFsID0gdV9ub3JtYWxNYXRyaXggKiBhX25vcm1hbDsKdl9wb3NpdGlvbiA9IHBvcy54eXo7CnZfbGlnaHQxRGlyZWN0aW9uID0gbWF0Myh1X2xpZ2h0MVRyYW5zZm9ybSkgKiB2ZWMzKDAuLDAuLDEuKTsKZ2xfUG9zaXRpb24gPSB1X3Byb2plY3Rpb25NYXRyaXggKiBwb3M7Cn0K" - } - }, - "skins": {}, - "techniques": { - "technique0": { - "attributes": { - "a_normal": "normal", - "a_position": "position" - }, - "parameters": { - "diffuse": { - "type": 35666 - }, - "light0Color": { - "type": 35665, - "value": [ - 0, - 0, - 0 - ] - }, - "light1Color": { - "type": 35665, - "value": [ - 1, - 1, - 1 - ] - }, - "light1Transform": { - "node": "Light-sunLight011Node", - "semantic": "MODELVIEW", - "type": 35676 - }, - "modelViewMatrix": { - "semantic": "MODELVIEW", - "type": 35676 - }, - "normal": { - "semantic": "NORMAL", - "type": 35665 - }, - "normalMatrix": { - "semantic": "MODELVIEWINVERSETRANSPOSE", - "type": 35675 - }, - "position": { - "semantic": "POSITION", - "type": 35665 - }, - "projectionMatrix": { - "semantic": "PROJECTION", - "type": 35676 - }, - "shininess": { - "type": 5126 - }, - "specular": { - "type": 35666 - } - }, - "program": "program_0", - "states": { - "enable": [ - 2929, - 2884 - ] - }, - "uniforms": { - "u_diffuse": "diffuse", - "u_light0Color": "light0Color", - "u_light1Color": "light1Color", - "u_light1Transform": "light1Transform", - "u_modelViewMatrix": "modelViewMatrix", - "u_normalMatrix": "normalMatrix", - "u_projectionMatrix": "projectionMatrix", - "u_shininess": "shininess", - "u_specular": "specular" - } - } - } -} \ No newline at end of file diff --git a/specs/data/combineObjects/doubleBoxToCombine.gltf b/specs/data/combineObjects/doubleBoxToCombine.gltf deleted file mode 100755 index f9049ef3..00000000 --- a/specs/data/combineObjects/doubleBoxToCombine.gltf +++ /dev/null @@ -1,411 +0,0 @@ -{ - "accessors": { - "accessor_29": { - "bufferView": "bufferView_64", - "byteOffset": 0, - "byteStride": 0, - "componentType": 5123, - "count": 384, - "type": "SCALAR" - }, - "accessor_31": { - "bufferView": "bufferView_65", - "byteOffset": 0, - "byteStride": 12, - "componentType": 5126, - "count": 224, - "max": [ - 0.5, - 0.5, - 0.5 - ], - "min": [ - -0.5, - -0.5, - -0.5 - ], - "type": "VEC3" - }, - "accessor_33": { - "bufferView": "bufferView_65", - "byteOffset": 2688, - "byteStride": 12, - "componentType": 5126, - "count": 224, - "max": [ - 1, - 1, - 1 - ], - "min": [ - -1, - -1, - -1 - ], - "type": "VEC3" - }, - "accessor_35": { - "bufferView": "bufferView_65", - "byteOffset": 5376, - "byteStride": 8, - "componentType": 5126, - "count": 224, - "max": [ - 1.0007699728012085, - 1.0013258457183838 - ], - "min": [ - -0.000750000006519258, - 0.0013260245323181152 - ], - "type": "VEC2" - }, - "accessor_56": { - "bufferView": "bufferView_64", - "byteOffset": 768, - "byteStride": 0, - "componentType": 5123, - "count": 132, - "type": "SCALAR" - }, - "accessor_58": { - "bufferView": "bufferView_65", - "byteOffset": 7168, - "byteStride": 12, - "componentType": 5126, - "count": 96, - "max": [ - 0.33504000306129456, - 0.5, - 0.33504000306129456 - ], - "min": [ - -0.33504000306129456, - -0.5, - -0.33504000306129456 - ], - "type": "VEC3" - }, - "accessor_60": { - "bufferView": "bufferView_65", - "byteOffset": 8320, - "byteStride": 12, - "componentType": 5126, - "count": 96, - "max": [ - 1, - 1, - 1 - ], - "min": [ - -1, - -1, - -1 - ], - "type": "VEC3" - }, - "accessor_62": { - "bufferView": "bufferView_65", - "byteOffset": 9472, - "byteStride": 8, - "componentType": 5126, - "count": 96, - "max": [ - 0.40562498569488525, - 0.9055818915367126 - ], - "min": [ - 0.34437498450279236, - 0.0944179892539978 - ], - "type": "VEC2" - } - }, - "asset": { - "generator": "collada2gltf@", - "premultipliedAlpha": true, - "profile": { - "api": "WebGL", - "version": "1.0.2" - }, - "version": 1 - }, - "bufferViews": { - "bufferView_63": { - "buffer": "input", - "byteLength": 104, - "byteOffset": 0 - }, - "bufferView_64": { - "buffer": "input", - "byteLength": 1032, - "byteOffset": 104, - "target": 34963 - }, - "bufferView_65": { - "buffer": "input", - "byteLength": 10240, - "byteOffset": 1136, - "target": 34962 - } - }, - "buffers": { - "input": { - "byteLength": 11376, - "type": "arraybuffer", - "uri": "data:application/octet-stream;base64,AACgPwAAIEAAAAAAAAAAAAAAAAAAAIA/AACAvwAAAIAAAACAEnVFrgAAAAAAAKA/AAAgQEdVbUAAAAAAAAAAAAAAAAAAAAAArkchQAAAAAAAAAAArkchQAAAAAAAAAAAAAAAAAAAAAAAAAEAAgACAAMAAAAEAAUABgAGAAcABAAIAAkACgAKAAsACAAMAA0ADgAOAA8ADAAQABEAEgASABMAEAAUABUAFgAWABcAFAAYABkAGgAaABsAGAAcAB0AHgAeAB8AHAAgACEAIgAiACMAIAAkACUAJgAmACcAJAAoACkAKgAqACsAKAAsAC0ALgAuAC8ALAAwADEAMgAyADMAMAA0ADUANgA2ADcANAA4ADkAOgA6ADsAOAA8AD0APgA+AD8APABAAEEAQgBCAEMAQABEAEUARgBGAEcARABIAEkASgBKAEsASABMAE0ATgBOAE8ATABQAFEAUgBSAFMAUABUAFUAVgBWAFcAVABYAFkAWgBaAFsAWABcAF0AXgBeAF8AXABgAGEAYgBiAGMAYABkAGUAZgBmAGcAZABoAGkAagBqAGsAaABsAG0AbgBuAG8AbABwAHEAcgByAHMAcAB0AHUAdgB2AHcAdAB4AHkAegB6AHsAeAB8AH0AfgB+AH8AfACAAIEAggCCAIMAgACEAIUAhgCGAIcAhACIAIkAigCKAIsAiACMAI0AjgCOAI8AjACQAJEAkgCSAJMAkACUAJUAlgCWAJcAlACYAJkAmgCaAJsAmACcAJ0AngCeAJ8AnAApACgAQwBDAEIAKQAFAAQAKwArACoABQBVAFQABwAHAAYAVQBBAEAAVwBXAFYAQQCgAKEAogCiAKMAoACkAKUApgCmAKcApACoAKkAqgCqAKsAqACsAK0ArgCuAK8ArACwALEAsgCyALMAsAC0ALUAtgC2ALcAtAC4ALAAswCzALkAuAC6ALQAtwC3ALsAugC8ALgAuQC5AL0AvACxALoAuwC7ALIAsQC+ALwAvQC9AL8AvgC1AL4AvwC/ALYAtQDAAMEAwgDCAMMAwADEAMUAxgDGAMcAxADIAMkAygDKAMsAyADMAM0AzgDOAM8AzADQANEA0gDSANMA0ADUANUA1gDWANcA1ADYANkA2gDaANsA2ADcAN0A3gDeAN8A3AAAAAEAAgACAAMAAAAEAAUABgAGAAcABAAIAAkA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AAAAAPcENb/3BDU/AAAAAPcENb/3BDU/AAAAAPcENb/3BDU/AAAAAPcENb8AAAAAaM0TP9EFUT8AAAAAaM0TP9EFUT8AAAAAaM0TP9EFUT8AAAAAaM0TP9EFUT8AAAAAAACAvwAAAIAAAAAAAACAvwAAAIAAAAAAAACAvwAAAIAAAAAAAACAvwAAAIAAAACAurgtP7cIPD8AAACAurgtP7cIPD8AAACAurgtP7cIPD8AAACAurgtP7cIPD/3BDW/AAAAAPcENb/3BDW/AAAAAPcENb/3BDW/AAAAAPcENb/3BDW/AAAAAPcENb/RBVE/aM0TPwAAAIDRBVE/aM0TPwAAAIDRBVE/aM0TPwAAAIDRBVE/aM0TPwAAAIDRBVG/aM0TPwAAAADRBVG/aM0TPwAAAADRBVG/aM0TPwAAAADRBVG/aM0TPwAAAAAAAAAAAACAvwAAAIAAAAAAAACAvwAAAIAAAAAAAACAvwAAAIAAAAAAAACAvwAAAID3BDW/AAAAAPcENT/3BDW/AAAAAPcENT/3BDW/AAAAAPcENT/3BDW/AAAAAPcENT8AAAAAaM0TP9EFUb8AAAAAaM0TP9EFUb8AAAAAaM0TP9EFUb8AAAAAaM0TP9EFUb+9//++GAU1v73//769//++GAU1v73//769//++GAU1v73//769//++GAU1v73//769//8+GAU1v73//769//8+GAU1v73//769//8+GAU1v73//769//8+GAU1v73//769//8+GAU1v73//z69//8+GAU1v73//z69//8+GAU1v73//z69//8+GAU1v73//z69//++GAU1v73//z69//++GAU1v73//z69//++GAU1v73//z69//++GAU1v73//z69//++GAU1P73//769//++GAU1P73//769//++GAU1P73//769//++GAU1P73//769//8+GAU1P73//769//8+GAU1P73//769//8+GAU1P73//769//8+GAU1P73//769//8+GAU1P73//z69//8+GAU1P73//z69//8+GAU1P73//z69//8+GAU1P73//z69//++GAU1P73//z69//++GAU1P73//z69//++GAU1P73//z69//++GAU1P73//z4/xvQ+lJ88vz/G9D4/xvQ+lJ88vz/G9D4/xvQ+lJ88vz/G9D4/xvQ+lJ88vz/G9D4/xvS+lJ88vz/G9D4/xvS+lJ88vz/G9D4/xvS+lJ88vz/G9D4/xvS+lJ88vz/G9D4/xvS+lJ88vz/G9L4/xvS+lJ88vz/G9L4/xvS+lJ88vz/G9L4/xvS+lJ88vz/G9L4/xvQ+lJ88vz/G9L4/xvQ+lJ88vz/G9L4/xvQ+lJ88vz/G9L4/xvQ+lJ88vz/G9L4/xvQ+lJ88Pz/G9D4/xvQ+lJ88Pz/G9D4/xvQ+lJ88Pz/G9D4/xvQ+lJ88Pz/G9D4/xvS+lJ88Pz/G9D4/xvS+lJ88Pz/G9D4/xvS+lJ88Pz/G9D4/xvS+lJ88Pz/G9D4/xvS+lJ88Pz/G9L4/xvS+lJ88Pz/G9L4/xvS+lJ88Pz/G9L4/xvS+lJ88Pz/G9L4/xvQ+lJ88Pz/G9L4/xvQ+lJ88Pz/G9L4/xvQ+lJ88Pz/G9L4/xvQ+lJ88Pz/G9L4AAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAACAAAAAgAAAgL8AAACAAAAAgAAAgL8AAACAAAAAgAAAgL8AAACAAAAAgAAAgL8AAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAACAAAAAgAAAgL8AAACAAAAAgAAAgL8AAACAAAAAgAAAgL8AAACAAAAAgAAAgL/3BDW/AAAAAPcENT/3BDW/AAAAAPcENT/3BDW/AAAAAPcENT/3BDW/AAAAAPcENT8AAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD/3BDW/AAAAAPcENb/3BDW/AAAAAPcENb/3BDW/AAAAAPcENb/3BDW/AAAAAPcENb/3BDU/AAAAAPcENT/3BDU/AAAAAPcENT/3BDU/AAAAAPcENT/3BDU/AAAAAPcENT/3BDU/AAAAAPcENb/3BDU/AAAAAPcENb/3BDU/AAAAAPcENb/3BDU/AAAAAPcENb9kdLA+9GxCP5yLzz70bEI/AmTQPhNiQj/+m68+E2JCP+durz5cE2g/za2APpNnfz8bEIE+HKl/P/6brz6RSGg/61GwPmiJxz3rUbA+0A5nP2R0sD6MN2c/ZHSwPtBDxj0Urs8+uJVCPxSuzz7QDmc/GZHQPlwTaD8ZkdA+SpdCP+durz5Kl0I/526vPlwTaD/rUbA+0A5nP+tRsD64lUI/sp3/PidpKj9OYoA+J2kqPxsQgT6XASs/5e/+PpcBKz+ci88+jDdnP2R0sD6MN2c//puvPpFIaD8CZNA+kUhoP5yLzz4wTHY+ZHSwPjBMdj7+m68+tHd2PgJk0D60d3Y+ZqR+PhBDKz9mpH4+k2d/P82tgD6TZ38/za2APhBDKz/nVgA/k2d/P+dWAD8QQys/M1L/PhBDKz8zUv8+k2d/P/6brz4TYkI/GxCBPpcBKz/NrYA+EEMrP+durz5Kl0I/FK7PPmiJxz0Urs8+IKl1PhmR0D7YonU+GZHQPlhlvz3rUbA+IKl1PutRsD5oicc9526vPlhlvz3nbq8+2KJ1PhsQgT5yK4A/5e/+PnIrgD/l7/4+HKl/PxsQgT4cqX8/ZHSwPjBMdj5kdLA+9GxCP+tRsD64lUI/61GwPiCpdT7l7/4+9PypPhsQgT70/Kk+TmKAPrItqz6ynf8+si2rPhmR0D5Kl0I/M1L/PhBDKz/l7/4+lwErPwJk0D4TYkI/ZHSwPtBDxj2ci88+0EPGPQJk0D6gu709/puvPqC7vT2ci88+9GxCP5yLzz4wTHY+FK7PPiCpdT4Urs8+uJVCP9nOPz+OJ6o+JzEAP44nqj4nMQA/si2rPtnOPz+yLas+ZDt/Po4nqj6mm0Q6jieqPqabRDqyLas+ZDt/PrItqz4CZNA+kUhoP+Xv/j4cqX8/M1L/PpNnfz8ZkdA+XBNoPxSuzz7QDmc/FK7PPmiJxz2ci88+0EPGPZyLzz6MN2c/2c5/P44nqj4nMUA/jieqPicxQD+yLas+2c5/P7Itqz4mNn8+WPd/P2bdfz6NzX8/7WSAPlQEgD9NEYA+ORmAP6cIAD+NzX8/djIAP1j3fz+z7v8+ORmAPxOb/z5UBIA/sp3/PidpKj/l7/4+lwErPzNS/z4QQys/JzEAPydpKj9kO38+J2kqP82tgD4QQys/GxCBPpcBKz9OYoA+J2kqP0IEgD+MD6s+OxmAP+y7qj5Z938/1EWqPonNfz9Smao+Wfc/P4wPqz52MkA/UpmqPqcIQD/URao+ic0/P+y7qj4zUv8+wHmpPuXv/j70/Kk+sp3/PrItqz4nMQA/si2rPhsQgT70/Kk+za2APsB5qT5kO38+si2rPk5igD6yLas+/puvPpFIaD9kdLA+jDdnP+tRsD7QDmc/526vPlwTaD8ZkdA+XBNoPxSuzz7QDmc/nIvPPow3Zz8CZNA+kUhoPwJk0D4TYkI/nIvPPvRsQj8Urs8+uJVCPxmR0D5Kl0I/526vPkqXQj/rUbA+uJVCP2R0sD70bEI//puvPhNiQj/nbq8+WGW/PetRsD5oicc9ZHSwPtBDxj3+m68+oLu9PQJk0D6gu709nIvPPtBDxj0Urs8+aInHPRmR0D5YZb89GZHQPtiidT4Urs8+IKl1PpyLzz4wTHY+AmTQPrR3dj7+m68+tHd2PmR0sD4wTHY+61GwPiCpdT7nbq8+2KJ1PutRsD4gqXU+61GwPriVQj/rUbA+0A5nP+tRsD5oicc9ZHSwPtBDxj1kdLA+jDdnP5yLzz6MN2c/nIvPPtBDxj0Urs8+aInHPRSuzz7QDmc/FK7PPriVQj8Urs8+IKl1PpyLzz4wTHY+nIvPPvRsQj9kdLA+9GxCP2R0sD4wTHY+M1L/PgBwGDvl7/4+AM6tOgJk0D6gu709GZHQPlhlvz3NrYA+AHAYO82tgD7Aeak+526vPtiidT7nbq8+WGW/PTNS/z7Aeak+GZHQPtiidT4bEIE+AM6tOv6brz6gu7095e/+PvT8qT4CZNA+tHd2PhsQgT70/Kk+/puvPrR3dj5kO38+si2rPqabRDqyLas+pptEOidpKj9kO38+J2kqP9nOfz+yLas+JzFAP7Itqz4nMUA/J2kqP9nOfz8naSo/TmKAPrItqz5kO38+si2rPmQ7fz4naSo/TmKAPidpKj/Zzj8/si2rPicxAD+yLas+JzEAPydpKj/Zzj8/J2kqP7Kd/z6yLas+TmKAPrItqz5OYoA+J2kqP7Kd/z4naSo/pptEOrItqz6mm0S6si2rPqabRLonaSo/pptEOidpKj8nMQA/si2rPrKd/z6yLas+sp3/PidpKj8nMQA/J2kqPycxQD+yLas+2c4/P7Itqz7Zzj8/J2kqPycxQD8naSo/JQGqvgAAAL8lAao+JQGqPgAAAL8lAao+JQGqPsl2/r5diqs+JQGqvsl2/r5diqs+JQGqvsl2/j5diqu+JQGqvsl2/r5diqu+XYqrvsl2/r4lAaq+XYqrvsl2/j4lAaq+JQGqPgAAAL8lAao+JQGqPgAAAL8lAaq+XYqrPsl2/r4lAaq+XYqrPsl2/r4lAao+XYqrvsl2/r4lAao+XYqrvsl2/r4lAaq+JQGqvgAAAL8lAaq+JQGqvgAAAL8lAao+JQGqPgAAAL8lAaq+JQGqvgAAAL8lAaq+JQGqvsl2/r5diqu+JQGqPsl2/r5diqu+JQGqvsl2/j5diqs+JQGqPsl2/j5diqs+JQGqPgAAAD8lAao+JQGqvgAAAD8lAao+XYqrPsl2/j4lAao+XYqrPsl2/j4lAaq+JQGqPgAAAD8lAaq+JQGqPgAAAD8lAao+JQGqvgAAAD8lAao+JQGqvgAAAD8lAaq+XYqrvsl2/j4lAaq+XYqrvsl2/j4lAao+XYqrvsl2/j4lAao+XYqrvsl2/r4lAao+JQGqvsl2/r5diqs+JQGqvsl2/j5diqs+JQGqPsl2/j5diqu+JQGqvsl2/j5diqu+JQGqvgAAAD8lAaq+JQGqPgAAAD8lAaq+JQGqPsl2/j5diqs+JQGqPsl2/r5diqs+XYqrPsl2/r4lAao+XYqrPsl2/j4lAao+XYqrPsl2/j4lAaq+XYqrPsl2/r4lAaq+JQGqPsl2/r5diqu+JQGqPsl2/j5diqu+JQGqvsl2/r5diqu+JQGqvgAAAL8lAaq+XYqrvsl2/r4lAaq+XYqrPsl2/r4lAaq+JQGqPgAAAL8lAaq+JQGqPsl2/r5diqu+JQGqPsl2/r5diqs+JQGqPgAAAL8lAao+XYqrPsl2/r4lAao+JQGqvgAAAL8lAao+JQGqvsl2/r5diqs+XYqrvsl2/r4lAao+JQGqvgAAAD8lAaq+JQGqvsl2/j5diqu+XYqrvsl2/j4lAaq+JQGqPgAAAD8lAaq+XYqrPsl2/j4lAaq+JQGqPsl2/j5diqu+JQGqPgAAAD8lAao+JQGqPsl2/j5diqs+XYqrPsl2/j4lAao+XYqrvsl2/j4lAao+JQGqvsl2/j5diqs+JQGqvgAAAD8lAao+XYqrvsl2/r4lAaq+XYqrvsl2/r4lAao+XYqrvsl2/j4lAao+XYqrvsl2/j4lAaq+JQGqPsl2/r5diqu+JQGqvsl2/r5diqu+JQGqvsl2/j5diqu+JQGqPsl2/j5diqu+XYqrPsl2/r4lAao+XYqrPsl2/r4lAaq+XYqrPsl2/j4lAaq+XYqrPsl2/j4lAao+JQGqvsl2/r5diqs+JQGqPsl2/r5diqs+JQGqPsl2/j5diqs+JQGqvsl2/j5diqs+JQGqvgAAAD8lAaq+JQGqvgAAAD8lAao+JQGqPgAAAD8lAao+JQGqPgAAAD8lAaq+JQGqPgAAAL8lAaq+JQGqPgAAAL8lAao+JQGqvgAAAL8lAao+JQGqvgAAAL8lAaq+AAAAAPcENb/3BDU/AAAAAPcENb/3BDU/AAAAAPcENb/3BDU/AAAAAPcENb/3BDU/9wQ1vwAAAID3BDW/9wQ1vwAAAID3BDW/9wQ1vwAAAID3BDW/9wQ1vwAAAID3BDW/9wQ1P/cENb8AAAAA9wQ1P/cENb8AAAAA9wQ1P/cENb8AAAAA9wQ1P/cENb8AAAAA9wQ1v/cENb8AAACA9wQ1v/cENb8AAACA9wQ1v/cENb8AAACA9wQ1v/cENb8AAACAAAAAgPcENb/3BDW/AAAAgPcENb/3BDW/AAAAgPcENb/3BDW/AAAAgPcENb/3BDW/AAAAgPcENT/3BDU/AAAAgPcENT/3BDU/AAAAgPcENT/3BDU/AAAAgPcENT/3BDU/9wQ1P/cENT8AAAAA9wQ1P/cENT8AAAAA9wQ1P/cENT8AAAAA9wQ1P/cENT8AAAAA9wQ1v/cENT8AAAAA9wQ1v/cENT8AAAAA9wQ1v/cENT8AAAAA9wQ1v/cENT8AAAAA9wQ1vwAAAAD3BDU/9wQ1vwAAAAD3BDU/9wQ1vwAAAAD3BDU/9wQ1vwAAAAD3BDU/AAAAAPcENT/3BDW/AAAAAPcENT/3BDW/AAAAAPcENT/3BDW/AAAAAPcENT/3BDW/9wQ1PwAAAAD3BDU/9wQ1PwAAAAD3BDU/9wQ1PwAAAAD3BDU/9wQ1PwAAAAD3BDU/9wQ1PwAAAAD3BDW/9wQ1PwAAAAD3BDW/9wQ1PwAAAAD3BDW/9wQ1PwAAAAD3BDW/Nc0TvzXNE781zRO/Nc0TvzXNE781zRO/Nc0TvzXNE781zRO/Nc0TPzXNE781zRO/Nc0TPzXNE781zRO/Nc0TPzXNE781zRO/Nc0TPzXNE781zRM/Nc0TPzXNE781zRM/Nc0TPzXNE781zRM/Nc0TvzXNE781zRM/Nc0TvzXNE781zRM/Nc0TvzXNE781zRM/Nc0TvzXNEz81zRO/Nc0TvzXNEz81zRO/Nc0TvzXNEz81zRO/Nc0TPzXNEz81zRO/Nc0TPzXNEz81zRO/Nc0TPzXNEz81zRO/Nc0TPzXNEz81zRM/Nc0TPzXNEz81zRM/Nc0TPzXNEz81zRM/Nc0TvzXNEz81zRM/Nc0TvzXNEz81zRM/Nc0TvzXNEz81zRM/AACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACAPwAAAIAAAAAAAACAPwAAAIAAAAAAAACAPwAAAIAAAAAAAACAPwAAAIAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAR1SwPnjWQj+4q88+eNZCP7irzz7EzEI/R1SwPsTMQj9HVLA+oJzBPUdUsD5pzGc/61GwPpTJZz/rUbA+KLPBPbirzz541kI/uKvPPjfUZz8Urs8+lMlnPxSuzz6Wz0I/61GwPpbPQj/rUbA+lMlnP0dUsD421Gc/R1SwPnjWQj+4q88+N9RnP0dUsD421Gc/R1SwPmnMZz+4q88+acxnP0dUsD7wzHQ+uKvPPvDMdD64q88+JKZ0PkdUsD4kpnQ+FK7PPqjBdD4Urs8+KLPBPbirzz44XsE9uKvPPiSmdD5HVLA+JKZ0PkdUsD44XsE961GwPiizwT3rUbA+qMF0PutRsD6owXQ+61GwPpbPQj9HVLA+xMxCP0dUsD7wzHQ+uKvPPqCcwT1HVLA+oJzBPUdUsD44XsE9uKvPPjhewT24q88+8Mx0Prirzz7EzEI/FK7PPpbPQj8Urs8+qMF0PhSuzz4os8E9FK7PPpTJZz+4q88+acxnP7irzz6gnME9R1SwPmnMZz9HVLA+NtRnP+tRsD6UyWc/FK7PPpTJZz+4q88+N9RnP7irzz5pzGc/uKvPPsTMQj+4q88+eNZCPxSuzz6Wz0I/R1SwPnjWQj9HVLA+xMxCP+tRsD6Wz0I/R1SwPjhewT1HVLA+oJzBPetRsD4os8E9uKvPPjhewT0Urs8+KLPBPbirzz6gnME9uKvPPiSmdD64q88+8Mx0PhSuzz6owXQ+61GwPqjBdD5HVLA+8Mx0PkdUsD4kpnQ+61GwPpTJZz/rUbA+ls9CP+tRsD6owXQ+61GwPiizwT24q88+acxnP0dUsD5pzGc/R1SwPqCcwT24q88+oJzBPRSuzz6Wz0I/FK7PPpTJZz8Urs8+KLPBPRSuzz6owXQ+R1SwPsTMQj+4q88+xMxCP7irzz7wzHQ+R1SwPvDMdD5HVLA+OF7BPUdUsD4kpnQ+uKvPPiSmdD64q88+OF7BPbirzz431Gc/uKvPPnjWQj9HVLA+eNZCP0dUsD421Gc/" - } - }, - "materials": { - "Effect_outer": { - "name": "outer", - "technique": "technique0", - "values": { - "diffuse": [ - 0.3016040027141571, - 0.5335419774055481, - 0.800000011920929, - 1 - ], - "shininess": 256, - "specular": [ - 0.20000000298023224, - 0.20000000298023224, - 0.20000000298023224, - 1 - ] - } - } - }, - "meshes": { - "meshTest": { - "name": "box", - "primitives": [ - { - "attributes": { - "NORMAL": "accessor_60", - "POSITION": "accessor_58", - "TEXCOORD_0": "accessor_62" - }, - "indices": "accessor_56", - "material": "Effect_outer", - "mode": 4 - }, - { - "attributes": { - "NORMAL": "accessor_33", - "POSITION": "accessor_31", - "TEXCOORD_0": "accessor_35" - }, - "indices": "accessor_29", - "material": "Effect_outer", - "mode": 4 - } - ] - } - }, - "nodes": { - "Camera-camera002Node": { - "camera": "", - "children": [], - "matrix": [ - 1, - 0, - 0, - 0, - 0, - 0.9961947202682495, - -0.08715574443340302, - 0, - 0, - 0.08715574443340302, - 0.9961947202682495, - 0, - 0, - 0.75, - 4, - 1 - ], - "name": "Camera" - }, - "Geometry-mesh019Node": { - "children": [], - "matrix": [ - 1, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 1 - ], - "meshes": [ - "meshTest" - ], - "name": "box" - }, - "Light-sunLight011Node": { - "children": [], - "matrix": [ - 0.7071067690849304, - 0, - 0.7071067690849304, - 0, - 0.3535533845424652, - 0.8660253882408142, - -0.3535533845424652, - 0, - -0.6123724579811096, - 0.5, - 0.6123724579811096, - 0, - -2, - 2, - 2, - 1 - ], - "name": "Directional_Light" - }, - "polyRender005": { - "children": [], - "matrix": [ - 1, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 1 - ], - "name": "Render" - } - }, - "programs": { - "program_0": { - "attributes": [ - "a_normal", - "a_position" - ], - "fragmentShader": "input0FS", - "vertexShader": "input0VS" - } - }, - "scene": "defaultScene", - "scenes": { - "defaultScene": { - "nodes": [ - "polyRender005", - "Geometry-mesh019Node", - "Camera-camera002Node", - "Light-sunLight011Node" - ] - } - }, - "shaders": { - "input0FS": { - "type": 35632, - "uri": "data:text/plain;base64,cHJlY2lzaW9uIGhpZ2hwIGZsb2F0Owp2YXJ5aW5nIHZlYzMgdl9ub3JtYWw7CnVuaWZvcm0gdmVjNCB1X2RpZmZ1c2U7CnVuaWZvcm0gdmVjNCB1X3NwZWN1bGFyOwp1bmlmb3JtIGZsb2F0IHVfc2hpbmluZXNzOwp1bmlmb3JtIHZlYzMgdV9saWdodDBDb2xvcjsKdmFyeWluZyB2ZWMzIHZfbGlnaHQxRGlyZWN0aW9uOwp2YXJ5aW5nIHZlYzMgdl9wb3NpdGlvbjsKdW5pZm9ybSB2ZWMzIHVfbGlnaHQxQ29sb3I7CnZvaWQgbWFpbih2b2lkKSB7CnZlYzMgbm9ybWFsID0gbm9ybWFsaXplKHZfbm9ybWFsKTsKdmVjNCBjb2xvciA9IHZlYzQoMC4sIDAuLCAwLiwgMC4pOwp2ZWM0IGRpZmZ1c2UgPSB2ZWM0KDAuLCAwLiwgMC4sIDEuKTsKdmVjMyBkaWZmdXNlTGlnaHQgPSB2ZWMzKDAuLCAwLiwgMC4pOwp2ZWM0IHNwZWN1bGFyOwpkaWZmdXNlID0gdV9kaWZmdXNlOwpzcGVjdWxhciA9IHVfc3BlY3VsYXI7CnZlYzMgc3BlY3VsYXJMaWdodCA9IHZlYzMoMC4sIDAuLCAwLik7CnZlYzMgYW1iaWVudExpZ2h0ID0gdmVjMygwLiwgMC4sIDAuKTsKewphbWJpZW50TGlnaHQgKz0gdV9saWdodDBDb2xvcjsKfQp7CmZsb2F0IHNwZWN1bGFySW50ZW5zaXR5ID0gMC47CmZsb2F0IGF0dGVudWF0aW9uID0gMS4wOwp2ZWMzIGwgPSBub3JtYWxpemUodl9saWdodDFEaXJlY3Rpb24pOwp2ZWMzIHZpZXdEaXIgPSAtbm9ybWFsaXplKHZfcG9zaXRpb24pOwpmbG9hdCBwaG9uZ1Rlcm0gPSBtYXgoMC4wLCBkb3QocmVmbGVjdCgtbCxub3JtYWwpLCB2aWV3RGlyKSk7CnNwZWN1bGFySW50ZW5zaXR5ID0gbWF4KDAuLCBwb3cocGhvbmdUZXJtICwgdV9zaGluaW5lc3MpKSAqIGF0dGVudWF0aW9uOwpzcGVjdWxhckxpZ2h0ICs9IHVfbGlnaHQxQ29sb3IgKiBzcGVjdWxhckludGVuc2l0eTsKZGlmZnVzZUxpZ2h0ICs9IHVfbGlnaHQxQ29sb3IgKiBtYXgoZG90KG5vcm1hbCxsKSwgMC4pICogYXR0ZW51YXRpb247Cn0Kc3BlY3VsYXIueHl6ICo9IHNwZWN1bGFyTGlnaHQ7CmNvbG9yLnh5eiArPSBzcGVjdWxhci54eXo7CmRpZmZ1c2UueHl6ICo9IGRpZmZ1c2VMaWdodDsKY29sb3IueHl6ICs9IGRpZmZ1c2UueHl6Owpjb2xvciA9IHZlYzQoY29sb3IucmdiICogZGlmZnVzZS5hLCBkaWZmdXNlLmEpOwpnbF9GcmFnQ29sb3IgPSBjb2xvcjsKfQo=" - }, - "input0VS": { - "type": 35633, - "uri": "data:text/plain;base64,cHJlY2lzaW9uIGhpZ2hwIGZsb2F0OwphdHRyaWJ1dGUgdmVjMyBhX3Bvc2l0aW9uOwphdHRyaWJ1dGUgdmVjMyBhX25vcm1hbDsKdmFyeWluZyB2ZWMzIHZfbm9ybWFsOwp1bmlmb3JtIG1hdDMgdV9ub3JtYWxNYXRyaXg7CnVuaWZvcm0gbWF0NCB1X21vZGVsVmlld01hdHJpeDsKdW5pZm9ybSBtYXQ0IHVfcHJvamVjdGlvbk1hdHJpeDsKdmFyeWluZyB2ZWMzIHZfbGlnaHQxRGlyZWN0aW9uOwp2YXJ5aW5nIHZlYzMgdl9wb3NpdGlvbjsKdW5pZm9ybSBtYXQ0IHVfbGlnaHQxVHJhbnNmb3JtOwp2b2lkIG1haW4odm9pZCkgewp2ZWM0IHBvcyA9IHVfbW9kZWxWaWV3TWF0cml4ICogdmVjNChhX3Bvc2l0aW9uLDEuMCk7CnZfbm9ybWFsID0gdV9ub3JtYWxNYXRyaXggKiBhX25vcm1hbDsKdl9wb3NpdGlvbiA9IHBvcy54eXo7CnZfbGlnaHQxRGlyZWN0aW9uID0gbWF0Myh1X2xpZ2h0MVRyYW5zZm9ybSkgKiB2ZWMzKDAuLDAuLDEuKTsKZ2xfUG9zaXRpb24gPSB1X3Byb2plY3Rpb25NYXRyaXggKiBwb3M7Cn0K" - } - }, - "skins": {}, - "techniques": { - "technique0": { - "attributes": { - "a_normal": "normal", - "a_position": "position" - }, - "parameters": { - "diffuse": { - "type": 35666 - }, - "light0Color": { - "type": 35665, - "value": [ - 0, - 0, - 0 - ] - }, - "light1Color": { - "type": 35665, - "value": [ - 1, - 1, - 1 - ] - }, - "light1Transform": { - "node": "Light-sunLight011Node", - "semantic": "MODELVIEW", - "type": 35676 - }, - "modelViewMatrix": { - "semantic": "MODELVIEW", - "type": 35676 - }, - "normal": { - "semantic": "NORMAL", - "type": 35665 - }, - "normalMatrix": { - "semantic": "MODELVIEWINVERSETRANSPOSE", - "type": 35675 - }, - "position": { - "semantic": "POSITION", - "type": 35665 - }, - "projectionMatrix": { - "semantic": "PROJECTION", - "type": 35676 - }, - "shininess": { - "type": 5126 - }, - "specular": { - "type": 35666 - } - }, - "program": "program_0", - "states": { - "enable": [ - 2929, - 2884 - ] - }, - "uniforms": { - "u_diffuse": "diffuse", - "u_light0Color": "light0Color", - "u_light1Color": "light1Color", - "u_light1Transform": "light1Transform", - "u_modelViewMatrix": "modelViewMatrix", - "u_normalMatrix": "normalMatrix", - "u_projectionMatrix": "projectionMatrix", - "u_shininess": "shininess", - "u_specular": "specular" - } - } - } -} \ No newline at end of file diff --git a/specs/lib/RemoveUnusedPropertiesSpec.js b/specs/lib/RemoveUnusedPropertiesSpec.js index 062b45bd..83880975 100644 --- a/specs/lib/RemoveUnusedPropertiesSpec.js +++ b/specs/lib/RemoveUnusedPropertiesSpec.js @@ -269,7 +269,9 @@ describe('RemoveUnusedProperties', function() { it('removes a mesh', function () { var gltf = { "meshes": { - "Geometry-mesh002": {}, + "Geometry-mesh002": { + primitives: [{}] + }, "unusedMeshId": {} }, "nodes": { @@ -288,7 +290,9 @@ describe('RemoveUnusedProperties', function() { it('does not remove any meshes', function () { var gltf = { "meshes": { - "Geometry-mesh002": {} + "Geometry-mesh002": { + primitives: [{}] + } }, "nodes": { "Geometry-mesh002Node": { diff --git a/specs/lib/changeAccessorComponentTypeSpec.js b/specs/lib/changeAccessorComponentTypeSpec.js index ce054a4c..dd2959b1 100644 --- a/specs/lib/changeAccessorComponentTypeSpec.js +++ b/specs/lib/changeAccessorComponentTypeSpec.js @@ -57,7 +57,7 @@ describe('changeAccessorComponentType', function() { expect(Object.keys(gltf.buffers).length).toBe(1); var accessorReader = new AccessorReader(gltf, accessor); var components = []; - while (accessorReader.hasNext()) { + while (!accessorReader.pastEnd()) { accessorReader.read(components); expect(components[0]).toBe(accessorReader.index); accessorReader.next(); @@ -102,7 +102,7 @@ describe('changeAccessorComponentType', function() { expect(Object.keys(gltf.buffers).length).toBe(1); var accessorReader = new AccessorReader(gltf, accessor); var components = []; - while (accessorReader.hasNext()) { + while (!accessorReader.pastEnd()) { accessorReader.read(components); expect(components[0]).toBe(accessorReader.index); accessorReader.next(); @@ -147,7 +147,7 @@ describe('changeAccessorComponentType', function() { expect(Object.keys(gltf.buffers).length).toBe(2); var accessorReader = new AccessorReader(gltf, accessor); var components = []; - while (accessorReader.hasNext()) { + while (!accessorReader.pastEnd()) { accessorReader.read(components); expect(components[0]).toBe(accessorReader.index); accessorReader.next(); diff --git a/specs/lib/combinePrimitivesSpec.js b/specs/lib/combinePrimitivesSpec.js index 1859bbe1..91b7bf8f 100755 --- a/specs/lib/combinePrimitivesSpec.js +++ b/specs/lib/combinePrimitivesSpec.js @@ -1,122 +1,530 @@ 'use strict'; +var Cesium = require('cesium'); +var clone = require('clone'); var combinePrimitives = require('../../lib/combinePrimitives'); -var readGltf = require('../../lib/readGltf'); +var readAccessor = require('../../lib/readAccessor'); var removePipelineExtras = require('../../lib/removePipelineExtras'); -var boxPath = './specs/data/combineObjects/box.gltf'; -var doubleBoxToCombinePath = './specs/data/combineObjects/doubleBoxToCombine.gltf'; -var doubleBoxNotCombinedPath = './specs/data/combineObjects/doubleBoxNotCombined.gltf'; -var fiveBoxPath = './specs/data/combineObjects/fiveBox.gltf'; +var WebGLConstants = Cesium.WebGLConstants; describe('combinePrimitives', function() { - it('does not affect single primitives', function(done){ - expect(readGltf(boxPath) - .then(function(gltf) { - var box = gltf; - var stringBox = JSON.stringify(box); - combinePrimitives(box); - expect(stringBox).toEqual(JSON.stringify(box)); - }), done).toResolve(); + var arrayOneA = new Float32Array([1, 2, 3]); + var bufferOneA = new Buffer(arrayOneA.buffer); + var arrayTwoA = new Float32Array([4, 5, 6]); + var bufferTwoA = new Buffer(arrayTwoA.buffer); + var arrayOneB = new Float32Array([1, 2, 3]); + var arrayTwoB = new Float32Array([5, 6, 7, 8]); + var bufferTwoB = new Buffer(arrayTwoB.buffer); + + it('combines two primitives without indices by concatenating them', function() { + var buffer = Buffer.concat([bufferOneA, bufferTwoA]); + var gltf = { + accessors : { + accessorOneA : { + bufferView : 'bufferView', + byteOffset : 0, + byteStride : 0, + componentType : WebGLConstants.FLOAT, + count : arrayOneA.length, + type : 'SCALAR' + }, + accessorTwoA : { + bufferView : 'bufferView', + byteOffset : bufferOneA.length, + byteStride : 0, + componentType : WebGLConstants.FLOAT, + count : arrayTwoA.length, + type : 'SCALAR' + } + }, + bufferViews : { + bufferView : { + buffer : 'buffer', + byteLength : buffer.length, + byteOffset : 0 + } + }, + buffers : { + buffer : { + byteLength : buffer.length, + extras : { + _pipeline : { + source : buffer + } + } + } + }, + meshes : { + mesh : { + primitives : [{ + attributes : { + A : 'accessorOneA' + }, + extras : { + _pipeline : {} + } + }, { + attributes : { + A : 'accessorTwoA' + }, + extras : { + _pipeline : {} + } + }] + } + } + }; + combinePrimitives(gltf); + var accessors = gltf.accessors; + var primitives = gltf.meshes.mesh.primitives; + expect(primitives.length).toEqual(1); + var primitive = primitives[0]; + expect(primitive.indices).toBeDefined(); + var indices = []; + readAccessor(gltf, accessors[primitive.indices], indices); + expect(indices).toEqual([0, 1, 2, 3, 4, 5]); + var attribute = []; + readAccessor(gltf, accessors[primitive.attributes.A], attribute); + expect(attribute).toEqual([1, 2, 3, 4, 5, 6]); + }); + + it('combines two primitives with indices by concatenating them', function() { + var indicesOne = new Uint16Array([2, 0, 1]); + var bufferIndicesOne = new Buffer(indicesOne.buffer); + var indicesTwo = new Uint16Array([1, 2, 0]); + var bufferIndicesTwo = new Buffer(indicesTwo.buffer); + var buffer = Buffer.concat([bufferOneA, bufferTwoA, bufferIndicesOne, bufferIndicesTwo]); + var gltf = { + accessors : { + accessorOneA : { + bufferView : 'bufferView', + byteOffset : 0, + byteStride : 0, + componentType : WebGLConstants.FLOAT, + count : arrayOneA.length, + type : 'SCALAR' + }, + accessorTwoA : { + bufferView : 'bufferView', + byteOffset : bufferOneA.length, + byteStride : 0, + componentType : WebGLConstants.FLOAT, + count : arrayTwoA.length, + type : 'SCALAR' + }, + indicesOne : { + bufferView : 'bufferView', + byteOffset : bufferOneA.length + bufferTwoA.length, + byteStride : 0, + componentType : WebGLConstants.UNSIGNED_SHORT, + count : indicesOne.length, + type : 'SCALAR' + }, + indicesTwo : { + bufferView : 'bufferView', + byteOffset : bufferOneA.length + bufferTwoA.length + bufferIndicesOne.length, + byteStride : 0, + componentType : WebGLConstants.UNSIGNED_SHORT, + count : indicesOne.length, + type : 'SCALAR' + } + }, + bufferViews : { + bufferView : { + buffer : 'buffer', + byteLength : buffer.length, + byteOffset : 0 + } + }, + buffers : { + buffer : { + byteLength : buffer.length, + extras : { + _pipeline : { + source : buffer + } + } + } + }, + meshes : { + mesh : { + primitives : [{ + attributes : { + A : 'accessorOneA' + }, + indices : 'indicesOne', + extras : { + _pipeline : {} + } + }, { + attributes : { + A : 'accessorTwoA' + }, + indices : 'indicesTwo', + extras : { + _pipeline : {} + } + }] + } + } + }; + combinePrimitives(gltf); + var accessors = gltf.accessors; + var primitives = gltf.meshes.mesh.primitives; + expect(primitives.length).toEqual(1); + var primitive = primitives[0]; + var indices = []; + readAccessor(gltf, accessors[primitive.indices], indices); + expect(indices).toEqual([2, 0, 1, 4, 5, 3]); + var attribute = []; + readAccessor(gltf, accessors[primitive.attributes.A], attribute); + expect(attribute).toEqual([1, 2, 3, 4, 5, 6]); + }); + + it('combines two primitives with shared attribute accessors by merging them', function() { + var indicesOne = new Uint16Array([0, 2]); + var bufferIndicesOne = new Buffer(indicesOne.buffer); + var indicesTwo = new Uint16Array([2, 1]); + var bufferIndicesTwo = new Buffer(indicesTwo.buffer); + var buffer = Buffer.concat([bufferOneA, bufferIndicesOne, bufferIndicesTwo]); + var gltf = { + accessors : { + accessorOneA : { + bufferView : 'bufferView', + byteOffset : 0, + byteStride : 0, + componentType : WebGLConstants.FLOAT, + count : arrayOneA.length, + type : 'SCALAR' + }, + indicesOne : { + bufferView : 'bufferView', + byteOffset : bufferOneA.length, + byteStride : 0, + componentType : WebGLConstants.UNSIGNED_SHORT, + count : indicesOne.length, + type : 'SCALAR' + }, + indicesTwo : { + bufferView : 'bufferView', + byteOffset : bufferOneA.length + bufferIndicesOne.length, + byteStride : 0, + componentType : WebGLConstants.UNSIGNED_SHORT, + count : indicesOne.length, + type : 'SCALAR' + } + }, + bufferViews : { + bufferView : { + buffer : 'buffer', + byteLength : buffer.length, + byteOffset : 0 + } + }, + buffers : { + buffer : { + byteLength : buffer.length, + extras : { + _pipeline : { + source : buffer + } + } + } + }, + meshes : { + mesh : { + primitives : [{ + attributes : { + A : 'accessorOneA' + }, + indices : 'indicesOne', + extras : { + _pipeline : {} + } + }, { + attributes : { + A : 'accessorOneA' + }, + indices : 'indicesTwo', + extras : { + _pipeline : {} + } + }] + } + } + }; + combinePrimitives(gltf); + var accessors = gltf.accessors; + var primitives = gltf.meshes.mesh.primitives; + expect(primitives.length).toEqual(1); + var primitive = primitives[0]; + var indices = []; + readAccessor(gltf, accessors[primitive.indices], indices); + expect(indices).toEqual([0, 2, 2, 1]); + var attribute = []; + readAccessor(gltf, accessors[primitive.attributes.A], attribute); + expect(attribute).toEqual([1, 2, 3]); }); - it('does not combine two primitives', function(done) { - expect(readGltf(doubleBoxNotCombinedPath) - .then(function(gltf) { - var doubleBoxNotCombined = gltf; - var stringDoubleBoxNotCombined = JSON.stringify(doubleBoxNotCombined); - combinePrimitives(doubleBoxNotCombined); - expect(stringDoubleBoxNotCombined).toEqual(JSON.stringify(doubleBoxNotCombined)); - }), done).toResolve(); + it('combines three primitives, merging two and then concatenating the result with the third', function() { + var indicesOne = new Uint16Array([0, 2]); + var bufferIndicesOne = new Buffer(indicesOne.buffer); + var indicesTwo = new Uint16Array([2, 1]); + var bufferIndicesTwo = new Buffer(indicesTwo.buffer); + var indicesThree = new Uint16Array([0, 1, 2, 3, 2, 1]); + var bufferIndicesThree = new Buffer(indicesThree.buffer); + var buffer = Buffer.concat([bufferOneA, bufferTwoB, bufferIndicesOne, bufferIndicesTwo, bufferIndicesThree]); + var gltf = { + accessors : { + accessorOneA : { + bufferView : 'bufferView', + byteOffset : 0, + byteStride : 0, + componentType : WebGLConstants.FLOAT, + count : arrayOneA.length, + type : 'SCALAR' + }, + accessorTwoB : { + bufferView : 'bufferView', + byteOffset : bufferOneA.length, + byteStride : 0, + componentType : WebGLConstants.FLOAT, + count : arrayTwoB.length, + type : 'SCALAR' + }, + indicesOne : { + bufferView : 'bufferView', + byteOffset : bufferOneA.length + bufferTwoB.length, + byteStride : 0, + componentType : WebGLConstants.UNSIGNED_SHORT, + count : indicesOne.length, + type : 'SCALAR' + }, + indicesTwo : { + bufferView : 'bufferView', + byteOffset : bufferOneA.length + bufferTwoB.length + bufferIndicesOne.length, + byteStride : 0, + componentType : WebGLConstants.UNSIGNED_SHORT, + count : indicesTwo.length, + type : 'SCALAR' + }, + indicesThree : { + bufferView : 'bufferView', + byteOffset : bufferOneA.length + bufferTwoB.length + bufferIndicesOne.length + bufferIndicesTwo.length, + byteStride : 0, + componentType : WebGLConstants.UNSIGNED_SHORT, + count : indicesThree.length, + type : 'SCALAR' + } + }, + bufferViews : { + bufferView : { + buffer : 'buffer', + byteLength : buffer.length, + byteOffset : 0 + } + }, + buffers : { + buffer : { + byteLength : buffer.length, + extras : { + _pipeline : { + source : buffer + } + } + } + }, + meshes : { + mesh : { + primitives : [{ + attributes : { + A : 'accessorOneA' + }, + indices : 'indicesOne', + extras : { + _pipeline : {} + } + }, { + attributes : { + A : 'accessorOneA' + }, + indices : 'indicesTwo', + extras : { + _pipeline : {} + } + }, { + attributes : { + A : 'accessorTwoB' + }, + indices : 'indicesThree', + extras : { + _pipeline : {} + } + }] + } + } + }; + combinePrimitives(gltf); + var accessors = gltf.accessors; + var primitives = gltf.meshes.mesh.primitives; + expect(primitives.length).toEqual(1); + var primitive = primitives[0]; + var indices = []; + readAccessor(gltf, accessors[primitive.indices], indices); + expect(indices).toEqual([0, 1, 2, 3, 2, 1, 4, 6, 6, 5]); + var attribute = []; + readAccessor(gltf, accessors[primitive.attributes.A], attribute); + expect(attribute).toEqual([5, 6, 7, 8, 1, 2, 3]); }); - it('combines two primitives', function(done) { - expect(readGltf(doubleBoxToCombinePath) - .then(function(gltf) { - var doubleBoxToCombine = gltf; - - combinePrimitives(doubleBoxToCombine); - removePipelineExtras(doubleBoxToCombine); - - expect(doubleBoxToCombine.meshes.meshTest.primitives.length).toEqual(1); - expect(doubleBoxToCombine.meshes.meshTest.primitives[0].material).toEqual('Effect_outer'); - expect(doubleBoxToCombine.meshes.meshTest.primitives[0].mode).toEqual(4); - expect(doubleBoxToCombine.meshes.meshTest.primitives[0].indices).toEqual('meshTest_INDEX_accessor_0'); - - expect(doubleBoxToCombine.meshes.meshTest.primitives[0].attributes).toEqual({ - "NORMAL": 'meshTest_NORMAL_accessor_0', - "POSITION": 'meshTest_POSITION_accessor_0', - "TEXCOORD_0": 'meshTest_TEXCOORD_0_accessor_0' - }); - - expect(doubleBoxToCombine.accessors.meshTest_INDEX_accessor_0).toEqual({ - "bufferView": "meshTest_INDEX_bufferView_0", - "byteOffset": 0, - "byteStride": 0, - "componentType": 5123, - "type": "SCALAR", - "count": 516, - "max": [319], - "min": [0] - }); - - expect(doubleBoxToCombine.accessors.meshTest_POSITION_accessor_0).toEqual({ - "bufferView": "meshTest_POSITION_bufferView_0", - "byteOffset": 0, - "byteStride": 12, - "componentType": 5126, - "type": "VEC3", - "count": 320, - "max": [0.5, 0.5, 0.5], - "min": [-0.5, -0.5, -0.5] - }); - expect(doubleBoxToCombine.bufferViews.meshTest_INDEX_bufferView_0.buffer).toEqual('meshTest_INDEX_buffer_0'); - }), done).toResolve(); + it('doesn\'t combine primitive that has attribute accessors that are different sizes', function() { + var gltf = { + accessors: { + accessorOneA: { + componentType: WebGLConstants.FLOAT, + count: arrayOneA.length, + type: 'SCALAR' + }, + accessorOneB: { + componentType: WebGLConstants.FLOAT, + count: arrayOneB.length, + type: 'SCALAR' + }, + accessorTwoA: { + componentType: WebGLConstants.UNSIGNED_SHORT, + count: arrayTwoA.length, + type: 'SCALAR' + }, + accessorTwoB: { + componentType: WebGLConstants.UNSIGNED_SHORT, + count: arrayTwoB.length, + type: 'SCALAR' + } + }, + meshes: { + mesh: { + primitives: [{ + attributes: { + A: 'accessorOneA', + B: 'accessorOneB' + }, + extras: { + _pipeline: {} + } + }, { + attributes: { + A: 'accessorTwoA', + B: 'accessorTwoB' + }, + extras: { + _pipeline: {} + } + }] + } + } + }; + var originalGltf = clone(gltf); + combinePrimitives(gltf); + delete gltf.meshes.mesh.primitives[0].extras._pipeline.conflicts; + delete gltf.meshes.mesh.primitives[1].extras._pipeline.conflicts; + expect(gltf).toEqual(originalGltf); }); - it('combines some primitives', function(done){ - expect(readGltf(fiveBoxPath) - .then(function(gltf){ - var fiveBox = gltf; - combinePrimitives(fiveBox); - expect(fiveBox.meshes.meshTest.primitives.length).toEqual(3); - - expect(Object.keys(fiveBox.accessors).indexOf('meshTest_INDEX_accessor_0')).not.toEqual(-1); - expect(Object.keys(fiveBox.accessors).indexOf('meshTest_POSITION_accessor_0')).not.toEqual(-1); - expect(Object.keys(fiveBox.accessors).indexOf('meshTest_INDEX_accessor_1')).not.toEqual(-1); - expect(Object.keys(fiveBox.accessors).indexOf('meshTest_POSITION_accessor_1')).not.toEqual(-1); - expect(Object.keys(fiveBox.bufferViews).indexOf('meshTest_INDEX_bufferView_0')).not.toEqual(-1); - expect(Object.keys(fiveBox.bufferViews).indexOf('meshTest_POSITION_bufferView_0')).not.toEqual(-1); - expect(Object.keys(fiveBox.bufferViews).indexOf('meshTest_INDEX_bufferView_1')).not.toEqual(-1); - expect(Object.keys(fiveBox.bufferViews).indexOf('meshTest_POSITION_bufferView_1')).not.toEqual(-1); - expect(Object.keys(fiveBox.buffers).indexOf('meshTest_INDEX_buffer_0')).not.toEqual(-1); - expect(Object.keys(fiveBox.buffers).indexOf('meshTest_POSITION_buffer_0')).not.toEqual(-1); - expect(Object.keys(fiveBox.buffers).indexOf('meshTest_INDEX_buffer_1')).not.toEqual(-1); - expect(Object.keys(fiveBox.buffers).indexOf('meshTest_POSITION_buffer_1')).not.toEqual(-1); - - expect(fiveBox.accessors.meshTest_INDEX_accessor_1.bufferView).toEqual('meshTest_INDEX_bufferView_1'); - expect(fiveBox.bufferViews.meshTest_INDEX_bufferView_1.buffer).toEqual('meshTest_INDEX_buffer_1'); - }), done).toResolve(); + it('doesn\'t combine primitives that share only a single attribute accessor', function() { + var gltf = { + accessors: { + accessorOneA: { + componentType: WebGLConstants.FLOAT, + count: arrayOneA.length, + type: 'SCALAR' + }, + accessorOneB: { + componentType: WebGLConstants.FLOAT, + count: arrayOneB.length, + type: 'SCALAR' + }, + accessorTwoB: { + componentType: WebGLConstants.UNSIGNED_SHORT, + count: arrayTwoB.length, + type: 'SCALAR' + } + }, + meshes: { + mesh: { + primitives: [{ + attributes: { + A: 'accessorOneA', + B: 'accessorTwoB' + }, + extras: { + _pipeline: {} + } + }, { + attributes: { + A: 'accessorOneA', + B: 'accessorOneB' + }, + extras: { + _pipeline: {} + } + }] + } + } + }; + var originalGltf = clone(gltf); + combinePrimitives(gltf); + delete gltf.meshes.mesh.primitives[0].extras._pipeline.conflicts; + delete gltf.meshes.mesh.primitives[1].extras._pipeline.conflicts; + expect(gltf).toEqual(originalGltf); }); - it('throws a type error', function(done) { - expect(readGltf(doubleBoxToCombinePath) - .then(function (gltf) { - var typeError = gltf; - typeError.accessors.accessor_29.type = 'VEC3'; - expect(function () { - combinePrimitives(typeError); - }).toThrowDeveloperError(); - }), done).toResolve(); + it('doesn\'t combine primitives with different materials', function() { + var gltf = { + meshes : { + mesh: { + primitives: [{ + material: 'materialOne', + extras: { + _pipeline: {} + } + }, { + material: 'materialTwo', + extras: { + _pipeline: {} + } + }] + } + } + }; + var originalGltf = clone(gltf); + combinePrimitives(gltf); + delete gltf.meshes.mesh.primitives[0].extras._pipeline.conflicts; + delete gltf.meshes.mesh.primitives[1].extras._pipeline.conflicts; + expect(gltf).toEqual(originalGltf); }); - it ('throws a componentType error', function(done) { - expect(readGltf(doubleBoxToCombinePath) - .then(function(gltf){ - var componentTypeError = gltf; - componentTypeError.accessors.accessor_29.componentType = 5126; - expect(function () { - combinePrimitives(componentTypeError); - }).toThrowDeveloperError(); - }), done).toResolve(); + it('doesn\'t combine primitives with different modes', function() { + var gltf = { + meshes : { + mesh: { + primitives: [{ + mode: 0, + extras: { + _pipeline: {} + } + }, { + mode: 1, + extras: { + _pipeline: {} + } + }] + } + } + }; + var originalGltf = clone(gltf); + combinePrimitives(gltf); + delete gltf.meshes.mesh.primitives[0].extras._pipeline.conflicts; + delete gltf.meshes.mesh.primitives[1].extras._pipeline.conflicts; + expect(gltf).toEqual(originalGltf); }); });