mirror of
https://github.com/CesiumGS/obj2gltf.git
synced 2024-11-23 16:44:05 -05:00
Merge pull request #211 from FreakTheMighty/feature/up-axis
Restore `inputUpAxis` and `outputUpAxis` axis
This commit is contained in:
commit
f0160376a3
@ -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');
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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'
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user