Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gltfPrimitiveToCesiumGeometry fixes #100

Merged
merged 7 commits into from
Jun 30, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions lib/cacheOptimization.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
'use strict';
var gltfPrimitiveToCesiumGeometry = require('./gltfPrimitiveToCesiumGeometry');
var cesiumGeometryToGltfPrimitive = require('./cesiumGeometryToGltfPrimitive');
var createAccessorUsageTables = require('./createAccessorUsageTables');
var Cesium = require('cesium');
var GeometryPipeline = Cesium.GeometryPipeline;
var defined = Cesium.defined;

module.exports = cacheOptimization;

// Helper method to map accessor collections from the usageTable to an independent primitive (if there is one)
function createIndicesToAttributeDictionary(gltf) {
var dictionary = {};
var meshes = gltf.meshes;
for (var meshId in meshes) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this use NodeHelpers.forEachPrimitiveInScene?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this currently in master? I cannot seem to find this function.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not; it is in the ao branch. I think forego using it for now since it is async and doesn't have a way to notify when it completes currently.

if (meshes.hasOwnProperty(meshId)) {
var mesh = meshes[meshId];
var primitives = mesh.primitives;
var primitivesLength = primitives.length;
for (var i = 0; i < primitivesLength; i++) {
var primitive = primitives[i];
var indicesId = primitive.indices;

var primitivesOfIndicesId;
if (!dictionary[indicesId]) {
primitivesOfIndicesId = [];
} else {
primitivesOfIndicesId = dictionary[indicesId];
}
primitivesOfIndicesId.push(primitive);

dictionary[indicesId] = primitivesOfIndicesId;
}
}
}
return dictionary;
}

function getIndependentPrimitive(dictionary, indicesId, attributeAccessors) {
var primitivesOfIndicesId = dictionary[indicesId];
for (var primitiveId in primitivesOfIndicesId) {
if (primitivesOfIndicesId.hasOwnProperty(primitiveId)) {
var primitive = primitivesOfIndicesId[primitiveId];
var attributes = primitive.attributes;

var equalCount = 0;
// Naive check to see if this primitive's attributes match
for (var semantic in attributes) {
if (attributes.hasOwnProperty(semantic)) {
var accessorId = attributes[semantic];
if (defined(attributeAccessors[accessorId])) {
equalCount++;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This second loop can be avoided. Just do something like this:

for (var attributeSemantic in attributes) {
    if (attributes.hasOwnProperty(attributeSemantic)) {
        var accessorId = attributes[attributeSemantic];
        if (defined(attributeAccessors[accessorId])) {
            ...
        }
    }
}

}
}
var accessorsLength = Object.keys(attributeAccessors).length;
// Ensure that this is an independent set of accessors
if (equalCount === accessorsLength) {
return primitive;
}
}
}
}

function cacheOptimization(gltf, cacheSize) {
// perform post vertex cache optimization
var primitive;
var geometry;
var meshes = gltf.meshes;
for (var meshId in meshes) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment.

if (meshes.hasOwnProperty(meshId)) {
var mesh = meshes[meshId];
var primitives = mesh.primitives;
var primitivesLength = primitives.length;
for (var i = 0; i < primitivesLength; i++) {
primitive = primitives[i];
geometry = gltfPrimitiveToCesiumGeometry(gltf, primitive);
GeometryPipeline.reorderForPostVertexCache(geometry, cacheSize);

cesiumGeometryToGltfPrimitive(gltf, primitive, geometry);
}
}
}
// perform pre vertex cache optimization on each independent primitive
var usageTables = createAccessorUsageTables(gltf);
var dictionary = createIndicesToAttributeDictionary(gltf);
// Iterate through each usage table
for (var j = 0; j < usageTables.length; j++) {
var indexAccessors = usageTables[j].indexAccessors;
var attributeAccessors = usageTables[j].attributeAccessors;
var keys = Object.keys(indexAccessors);
// If a set of attributes accessors is only used by one indices accessor
if (keys.length === 1) {
var indicesId = keys[0];
// look through to see if an independent primitive exists for those attributes
primitive = getIndependentPrimitive(dictionary, indicesId, attributeAccessors);
// If there is, it can be safely pre vertex cache optimized
if (defined(primitive)) {
geometry = gltfPrimitiveToCesiumGeometry(gltf, primitive);
GeometryPipeline.reorderForPreVertexCache(geometry);
cesiumGeometryToGltfPrimitive(gltf, primitive, geometry);
}
}
}
}
31 changes: 0 additions & 31 deletions lib/cacheOptimizeIndices.js

This file was deleted.

55 changes: 55 additions & 0 deletions lib/cesiumGeometryToGltfPrimitive.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use strict';
var writeAccessor = require('./writeAccessor');

module.exports = cesiumGeometryToGltfPrimitive;

// Get the first gltf attribute semantic of that type
function getFirstAttributeSemantic(primitive, semantic) {
var attributes = primitive.attributes;
for (var attributeSemantic in attributes) {
if (attributes.hasOwnProperty(attributeSemantic)) {
if (attributeSemantic.indexOf(semantic) === 0) {
return attributeSemantic;
}
}
}
}

// Helper method to write attributes to gltf primitive from cesium geometry
function mapGeometryAttributeToPrimitive(gltf, primitive, geometry, semantic) {
var attributeSemantic;
var values;

switch(semantic) {
case 'position':
attributeSemantic = getFirstAttributeSemantic(primitive, 'POSITION');
values = geometry.attributes.position.values;
break;
case 'normal':
attributeSemantic = getFirstAttributeSemantic(primitive, 'NORMAL');
values = geometry.attributes.normal.values;
break;
case 'st':
attributeSemantic = getFirstAttributeSemantic(primitive, 'TEXCOORD');
values = geometry.attributes.st.values;
break;
default:
attributeSemantic = semantic;
values = geometry.attributes[semantic].values;
}
var accessorId = primitive.attributes[attributeSemantic];
var accessor = gltf.accessors[accessorId];
writeAccessor(gltf, accessor, values);
}

function cesiumGeometryToGltfPrimitive(gltf, primitive, geometry) {
var attributes = geometry.attributes;
for (var semantic in attributes) {
if (attributes.hasOwnProperty(semantic)) {
mapGeometryAttributeToPrimitive(gltf, primitive, geometry, semantic);
}
}
var indicesId = primitive.indices;
var indicesAccessor = gltf.accessors[indicesId];
writeAccessor(gltf, indicesAccessor, geometry.indices);
}
4 changes: 2 additions & 2 deletions lib/createAccessorUsageTables.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ function createAccessorUsageTables(gltf) {
for (attribute in attributes) {
if (attributes.hasOwnProperty(attribute)) {
attributeAccessorId = attributes[attribute];
table.attributeAccessors[attributeAccessorId] = attribute;
table.attributeAccessors[attributeAccessorId] = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for correcting that

accessorTableMapping[attributeAccessorId] = mappedTableIndex;
}
}
Expand All @@ -147,7 +147,7 @@ function createAccessorUsageTables(gltf) {
// Merge them into this one if they have been
mergeUsageTables(tables, accessorTableMapping, tableIndex, attributeTableIndex);
}
table.attributeAccessors[attributeAccessorId] = attribute;
table.attributeAccessors[attributeAccessorId] = true;
accessorTableMapping[attributeAccessorId] = tableIndex;
}
}
Expand Down
24 changes: 0 additions & 24 deletions lib/geometryToPrimitive.js

This file was deleted.

4 changes: 2 additions & 2 deletions lib/gltfPipeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ var writeBinaryGltf = require('./writeBinaryGltf');
var readGltf = require('./readGltf');
var loadGltfUris = require('./loadGltfUris');
var quantizeAttributes = require('./quantizeAttributes');
var cacheOptimizeIndices = require('./cacheOptimizeIndices');
var cacheOptimization = require('./cacheOptimization');
var encodeImages = require('./encodeImages');
var writeSource = require('./writeSource');
var Cesium = require('cesium');
Expand Down Expand Up @@ -71,7 +71,7 @@ function processJSONWithExtras(gltfWithExtras, options, callback) {
// combinePrimitives(gltfWithExtras);
// Merging duplicate vertices again to prevent repeat data in newly combined primitives
// mergeDuplicateVertices(gltfWithExtras);
cacheOptimizeIndices(gltfWithExtras);
cacheOptimization(gltfWithExtras);
// Run removeUnused stage again after all pipeline stages have been run to remove objects that become unused
removeUnused(gltfWithExtras);
if (options.quantize) {
Expand Down
119 changes: 119 additions & 0 deletions lib/gltfPrimitiveToCesiumGeometry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
'use strict';
var readAccessor = require('./readAccessor');
var numberOfComponentsForType = require('./numberOfComponentsForType');
var Cesium = require('cesium');
var Geometry = Cesium.Geometry;
var GeometryAttribute = Cesium.GeometryAttribute;
var ComponentDatatype = Cesium.ComponentDatatype;
var Cartesian2 = Cesium.Cartesian2;
var Cartesian3 = Cesium.Cartesian3;
var Cartesian4 = Cesium.Cartesian4;
var defined = Cesium.defined;

module.exports = gltfPrimitiveToCesiumGeometry;

// Helper method for packing Cartesians to arrays
function packArray(values) {
var length = values.data.length;
var packed;
var i;
switch (values.type) {
case 'Cartesian2':
packed = new Array(length * 2);
for (i = 0; i < length; ++i) {
Cartesian2.pack(values.data[i], packed, i * 2);
}
return packed;
case 'Cartesian3':
packed = new Array(length * 3);
for (i = 0; i < length; ++i) {
Cartesian3.pack(values.data[i], packed, i * 3);
}
return packed;
case 'Cartesian4':
packed = new Array(length * 4);
for (i = 0; i < length; ++i) {
Cartesian4.pack(values.data[i], packed, i * 4);
}
return packed;
}
}

// Helper method for taking a gltf primitive attribute and returning a geometry attribute
function mapPrimitiveAttributeToGeometry(gltf, primitive, semantic, geometryAttributes) {
var accessorId = primitive.attributes[semantic];
var accessor = gltf.accessors[accessorId];
var componentsPerAttribute = numberOfComponentsForType(accessor.type);
var values = readAccessor(gltf, accessor);
var packed = packArray(values);

var attributeName;
var finalValues;
// Strip semantic of its index
switch(semantic.replace(/([0-9]|_)/g, '')) {
case 'POSITION':
// Only use geometry semantic for one attribute, otherwise just create a generic attribute
if (!defined(geometryAttributes.position)) {
attributeName = 'position';
finalValues = new Float64Array(packed);
} else {
attributeName = semantic;
finalValues = new Float64Array(packed);
}
break;
case 'NORMAL':
if (!defined(geometryAttributes.normal)) {
attributeName = 'normal';
finalValues = new Float32Array(packed);
} else {
attributeName = semantic;
finalValues = new Float32Array(packed);
}
break;
case 'TEXCOORD':
if (!defined(geometryAttributes.st)) {
attributeName = 'st';
finalValues = new Float32Array(packed);
} else {
attributeName = semantic;
finalValues = new Float32Array(packed);
}
break;
default:
attributeName = semantic;
finalValues = new Float64Array(packed);
}
var options = {
componentDatatype : ComponentDatatype.FLOAT,
componentsPerAttribute : componentsPerAttribute,
values : finalValues
};
return {
name : attributeName,
attribute : new GeometryAttribute(options)
};
}

function gltfPrimitiveToCesiumGeometry(gltf, primitive) {
var geometryAttributes = {};
var attributes = primitive.attributes;
for (var semantic in attributes) {
if (attributes.hasOwnProperty(semantic)) {
var nameAndAttribute = mapPrimitiveAttributeToGeometry(gltf, primitive, semantic, geometryAttributes);
geometryAttributes[nameAndAttribute.name] = nameAndAttribute.attribute;
}
}

var indicesId = primitive.indices;
var indicesAccessor = gltf.accessors[indicesId];
var indices = readAccessor(gltf, indicesAccessor);
var primitiveType = primitive.mode;

var geometry = new Geometry({
attributes : geometryAttributes,
indices : new Uint32Array(indices.data),
primitiveType : primitiveType
});

return geometry;
}
Loading