Merge pull request #211 from FreakTheMighty/feature/up-axis

Restore `inputUpAxis` and `outputUpAxis` axis
This commit is contained in:
Sean Lilley 2019-08-26 09:24:32 -04:00 committed by GitHub
commit f0160376a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 108 additions and 5 deletions

View File

@ -122,6 +122,18 @@ const argv = yargs
describe : 'The glTF will be saved with the KHR_materials_unlit extension.',
type : 'boolean',
default : defaults.unlit
},
inputUpAxis : {
describe: 'Up axis of the obj.',
choices: ['X', 'Y', 'Z'],
type: 'string',
default: 'Y'
},
outputUpAxis : {
describe: 'Up axis of the converted glTF.',
choices: ['X', 'Y', 'Z'],
type: 'string',
default: 'Y'
}
}).parse(args);
@ -167,7 +179,9 @@ const options = {
specularGlossiness : argv.specularGlossiness,
unlit : argv.unlit,
overridingTextures : overridingTextures,
outputDirectory : outputDirectory
outputDirectory : outputDirectory,
inputUpAxis : argv.inputUpAxis,
outputUpAxis : argv.outputUpAxis
};
console.time('Total');

View File

@ -8,6 +8,7 @@ const loadMtl = require('./loadMtl');
const outsideDirectory = require('./outsideDirectory');
const readLines = require('./readLines');
const Axis = Cesium.Axis;
const Cartesian3 = Cesium.Cartesian3;
const ComponentDatatype = Cesium.ComponentDatatype;
const CoplanarPolygonGeometryLibrary = Cesium.CoplanarPolygonGeometryLibrary;
@ -16,6 +17,7 @@ const defined = Cesium.defined;
const PolygonPipeline = Cesium.PolygonPipeline;
const RuntimeError = Cesium.RuntimeError;
const WindingOrder = Cesium.WindingOrder;
const Matrix4 = Cesium.Matrix4;
module.exports = loadObj;
@ -47,6 +49,8 @@ const normalPattern = /vn( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\
const uvPattern = /vt( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; // vt float float
const facePattern = /(-?\d+)\/?(-?\d*)\/?(-?\d*)/g; // for any face format "f v", "f v/v", "f v//v", "f v/v/v"
const scratchCartesian = new Cartesian3();
/**
* Parse an obj file.
*
@ -57,6 +61,8 @@ const facePattern = /(-?\d+)\/?(-?\d*)\/?(-?\d*)/g;
* @private
*/
function loadObj(objPath, options) {
const axisTransform = getAxisTransform(options.inputUpAxis, options.outputUpAxis);
// Global store of vertex attributes listed in the obj file
let globalPositions = new ArrayStorage(ComponentDatatype.FLOAT);
let globalNormals = new ArrayStorage(ComponentDatatype.FLOAT);
@ -354,9 +360,16 @@ function loadObj(objPath, options) {
const mtllibLine = line.substring(7).trim();
mtlPaths = mtlPaths.concat(getMtlPaths(mtllibLine));
} else if ((result = vertexPattern.exec(line)) !== null) {
globalPositions.push(parseFloat(result[1]));
globalPositions.push(parseFloat(result[2]));
globalPositions.push(parseFloat(result[3]));
const position = scratchCartesian;
position.x = parseFloat(result[1]);
position.y = parseFloat(result[2]);
position.z = parseFloat(result[3]);
if (defined(axisTransform)) {
Matrix4.multiplyByPoint(axisTransform, position, position);
}
globalPositions.push(position.x);
globalPositions.push(position.y);
globalPositions.push(position.z);
} else if ((result = normalPattern.exec(line) ) !== null) {
const normal = Cartesian3.fromElements(parseFloat(result[1]), parseFloat(result[2]), parseFloat(result[3]), scratchNormal);
if (Cartesian3.equals(normal, Cartesian3.ZERO)) {
@ -364,6 +377,9 @@ function loadObj(objPath, options) {
} else {
Cartesian3.normalize(normal, normal);
}
if (defined(axisTransform)) {
Matrix4.multiplyByPointAsVector(axisTransform, normal, normal);
}
globalNormals.push(normal.x);
globalNormals.push(normal.y);
globalNormals.push(normal.z);
@ -625,3 +641,19 @@ function cleanNodes(nodes) {
setDefaults(nodes);
return nodes;
}
function getAxisTransform(inputUpAxis, outputUpAxis) {
if (inputUpAxis === 'X' && outputUpAxis === 'Y') {
return Axis.X_UP_TO_Y_UP;
} else if (inputUpAxis === 'X' && outputUpAxis === 'Z') {
return Axis.X_UP_TO_Z_UP;
} else if (inputUpAxis === 'Y' && outputUpAxis === 'X') {
return Axis.Y_UP_TO_X_UP;
} else if (inputUpAxis === 'Y' && outputUpAxis === 'Z') {
return Axis.Y_UP_TO_Z_UP;
} else if (inputUpAxis === 'Z' && outputUpAxis === 'X') {
return Axis.Z_UP_TO_X_UP;
} else if (inputUpAxis === 'Z' && outputUpAxis === 'Y') {
return Axis.Z_UP_TO_Y_UP;
}
}

View File

@ -34,6 +34,8 @@ module.exports = obj2gltf;
* @param {String} [options.overridingTextures.baseColorTexture] Path to the baseColor/diffuse texture.
* @param {String} [options.overridingTextures.emissiveTexture] Path to the emissive texture.
* @param {String} [options.overridingTextures.alphaTexture] Path to the alpha texture.
* @param {String} [options.inputUpAxis='Y'] Up axis of the obj. Choices are 'X', 'Y', and 'Z'.
* @param {String} [options.outputUpAxis='Y'] Up axis of the converted glTF. Choices are 'X', 'Y', and 'Z'.
* @param {Logger} [options.logger] A callback function for handling logged messages. Defaults to console.log.
* @param {Writer} [options.writer] A callback function that writes files that are saved as separate resources.
* @param {String} [options.outputDirectory] Output directory for writing separate resources when options.writer is not defined.
@ -54,6 +56,8 @@ function obj2gltf(objPath, options) {
options.overridingTextures = defaultValue(options.overridingTextures, defaultValue.EMPTY_OBJECT);
options.logger = defaultValue(options.logger, getDefaultLogger());
options.writer = defaultValue(options.writer, getDefaultWriter(options.outputDirectory));
options.inputUpAxis = defaultValue(options.inputUpAxis, defaults.inputUpAxis);
options.outputUpAxis = defaultValue(options.outputUpAxis, defaults.outputUpAxis);
if (!defined(objPath)) {
throw new DeveloperError('objPath is required');
@ -164,7 +168,19 @@ obj2gltf.defaults = {
* @type Boolean
* @default false
*/
unlit : false
unlit : false,
/**
* Gets or sets the up axis of the obj.
* @type String
* @default 'Y'
*/
inputUpAxis: 'Y',
/**
* Gets or sets the up axis of the converted glTF.
* @type String
* @default 'Y'
*/
outputUpAxis: 'Y'
};
/**

View File

@ -10,6 +10,7 @@ const clone = Cesium.clone;
const RuntimeError = Cesium.RuntimeError;
const objPath = 'specs/data/box/box.obj';
const objRotatedUrl = 'specs/data/box-rotated/box-rotated.obj';
const objNormalsPath = 'specs/data/box-normals/box-normals.obj';
const objUvsPath = 'specs/data/box-uvs/box-uvs.obj';
const objPositionsOnlyPath = 'specs/data/box-positions-only/box-positions-only.obj';
@ -455,6 +456,46 @@ describe('loadObj', () => {
expect(primitives[3].indices.length).toBe(6); // 2 faces
});
function getFirstPosition(data) {
const primitive = getPrimitives(data)[0];
return new Cartesian3(primitive.positions.get(0), primitive.positions.get(1), primitive.positions.get(2));
}
function getFirstNormal(data) {
const primitive = getPrimitives(data)[0];
return new Cartesian3(primitive.normals.get(0), primitive.normals.get(1), primitive.normals.get(2));
}
async function checkAxisConversion(inputUpAxis, outputUpAxis, position, normal) {
const sameAxis = (inputUpAxis === outputUpAxis);
options.inputUpAxis = inputUpAxis;
options.outputUpAxis = outputUpAxis;
const data = await loadObj(objRotatedUrl, options);
const rotatedPosition = getFirstPosition(data);
const rotatedNormal = getFirstNormal(data);
if (sameAxis) {
expect(rotatedPosition).toEqual(position);
expect(rotatedNormal).toEqual(normal);
} else {
expect(rotatedPosition).not.toEqual(position);
expect(rotatedNormal).not.toEqual(normal);
}
}
it('performs up axis conversion', async () => {
const data = await loadObj(objRotatedUrl, options);
const position = getFirstPosition(data);
const normal = getFirstNormal(data);
const axes = ['X', 'Y', 'Z'];
const axesLength = axes.length;
for (let i = 0; i < axesLength; ++i) {
for (let j = 0; j < axesLength; ++j) {
await checkAxisConversion(axes[i], axes[j], position, normal);
}
}
});
it('throws when file has invalid contents', async () => {
let thrownError;
try {