mirror of
https://github.com/CesiumGS/obj2gltf.git
synced 2024-11-30 03:50:14 -05:00
Added up axis options back
This commit is contained in:
parent
1d0bb5eb35
commit
3cf338107d
@ -66,62 +66,21 @@ const argv = yargs
|
||||
default : defaults.checkTransparency
|
||||
},
|
||||
secure : {
|
||||
describe : 'Prevent the converter from reading textures or mtl files outside of the input obj directory.',
|
||||
type : 'boolean',
|
||||
default : defaults.secure
|
||||
describe: 'Prevent the converter from reading image or mtl files outside of the input obj directory.',
|
||||
type: 'boolean',
|
||||
default: defaults.secure
|
||||
},
|
||||
packOcclusion : {
|
||||
describe : 'Pack the occlusion texture in the red channel of metallic-roughness texture.',
|
||||
type : 'boolean',
|
||||
default : defaults.packOcclusion
|
||||
inputUpAxis : {
|
||||
describe: 'Up axis of the obj.',
|
||||
choices: ['X', 'Y', 'Z'],
|
||||
type: 'string',
|
||||
default: 'Y'
|
||||
},
|
||||
metallicRoughness : {
|
||||
describe : 'The values in the .mtl file are already metallic-roughness PBR values and no conversion step should be applied. Metallic is stored in the Ks and map_Ks slots and roughness is stored in the Ns and map_Ns slots.',
|
||||
type : 'boolean',
|
||||
default : defaults.metallicRoughness
|
||||
},
|
||||
specularGlossiness : {
|
||||
describe : 'The values in the .mtl file are already specular-glossiness PBR values and no conversion step should be applied. Specular is stored in the Ks and map_Ks slots and glossiness is stored in the Ns and map_Ns slots. The glTF will be saved with the KHR_materials_pbrSpecularGlossiness extension.',
|
||||
type : 'boolean',
|
||||
default : defaults.specularGlossiness
|
||||
},
|
||||
metallicRoughnessOcclusionTexture : {
|
||||
describe : 'Path to the metallic-roughness-occlusion texture that should override textures in the .mtl file, where occlusion is stored in the red channel, roughness is stored in the green channel, and metallic is stored in the blue channel. The model will be saved with a pbrMetallicRoughness material. This is often convenient in workflows where the .mtl does not exist or is not set up to use PBR materials. Intended for models with a single material',
|
||||
type : 'string',
|
||||
normalize : true
|
||||
},
|
||||
specularGlossinessTexture : {
|
||||
describe : 'Path to the specular-glossiness texture that should override textures in the .mtl file, where specular color is stored in the red, green, and blue channels and specular glossiness is stored in the alpha channel. The model will be saved with a material using the KHR_materials_pbrSpecularGlossiness extension.',
|
||||
type : 'string',
|
||||
normalize : true
|
||||
},
|
||||
occlusionTexture : {
|
||||
describe : 'Path to the occlusion texture that should override textures in the .mtl file.',
|
||||
type : 'string',
|
||||
normalize : true
|
||||
},
|
||||
normalTexture : {
|
||||
describe : 'Path to the normal texture that should override textures in the .mtl file.',
|
||||
type : 'string',
|
||||
normalize : true
|
||||
},
|
||||
baseColorTexture : {
|
||||
describe : 'Path to the baseColor/diffuse texture that should override textures in the .mtl file.',
|
||||
type : 'string',
|
||||
normalize : true
|
||||
},
|
||||
emissiveTexture : {
|
||||
describe : 'Path to the emissive texture that should override textures in the .mtl file.',
|
||||
type : 'string',
|
||||
normalize : true
|
||||
},
|
||||
alphaTexture : {
|
||||
describe : 'Path to the alpha texture that should override textures in the .mtl file.'
|
||||
},
|
||||
unlit : {
|
||||
describe : 'The glTF will be saved with the KHR_materials_unlit extension.',
|
||||
type : 'boolean',
|
||||
default : defaults.unlit
|
||||
outputUpAxis : {
|
||||
describe: 'Up axis of the converted glTF.',
|
||||
choices: ['X', 'Y', 'Z'],
|
||||
type: 'string',
|
||||
default: 'Y'
|
||||
}
|
||||
}).parse(args);
|
||||
|
||||
@ -162,12 +121,8 @@ const options = {
|
||||
separateTextures : argv.separateTextures,
|
||||
checkTransparency : argv.checkTransparency,
|
||||
secure : argv.secure,
|
||||
packOcclusion : argv.packOcclusion,
|
||||
metallicRoughness : argv.metallicRoughness,
|
||||
specularGlossiness : argv.specularGlossiness,
|
||||
unlit : argv.unlit,
|
||||
overridingTextures : overridingTextures,
|
||||
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,8 @@ const defined = Cesium.defined;
|
||||
const PolygonPipeline = Cesium.PolygonPipeline;
|
||||
const RuntimeError = Cesium.RuntimeError;
|
||||
const WindingOrder = Cesium.WindingOrder;
|
||||
const Matrix4 = Cesium.Matrix4;
|
||||
|
||||
|
||||
module.exports = loadObj;
|
||||
|
||||
@ -47,16 +50,31 @@ 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"
|
||||
|
||||
var scratchCartesian = new Cartesian3();
|
||||
|
||||
/**
|
||||
* Parse an obj file.
|
||||
*
|
||||
* @param {String} objPath Path to the obj file.
|
||||
<<<<<<< HEAD
|
||||
* @param {Object} options The options object passed along from lib/obj2gltf.js
|
||||
* @returns {Promise} A promise resolving to the obj data, which includes an array of nodes containing geometry information and an array of materials.
|
||||
=======
|
||||
* @param {Object} options An object with the following properties:
|
||||
* @param {Boolean} options.checkTransparency Do a more exhaustive check for texture transparency by looking at the alpha channel of each pixel.
|
||||
* @param {Boolean} options.secure Prevent the converter from reading image or mtl files outside of the input obj directory.
|
||||
* @param {String} options.inputUpAxis Up axis of the obj.
|
||||
* @param {String} options.outputUpAxis Up axis of the converted glTF.
|
||||
* @param {Boolean} options.logger A callback function for handling logged messages. Defaults to console.log.
|
||||
* @returns {Promise} A promise resolving to the obj data.
|
||||
* @exception {RuntimeError} The file does not have any geometry information in it.
|
||||
>>>>>>> 93dac5e... Convert up axis
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function loadObj(objPath, options) {
|
||||
var 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,15 +372,23 @@ 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)) {
|
||||
Cartesian3.clone(Cartesian3.UNIT_Z, normal);
|
||||
} else {
|
||||
Cartesian3.normalize(normal, normal);
|
||||
const normal = scratchCartesian;
|
||||
normal.x = parseFloat(result[1]);
|
||||
normal.y = parseFloat(result[2]);
|
||||
normal.z = parseFloat(result[3]);
|
||||
if (defined(axisTransform)) {
|
||||
Matrix4.multiplyByPointAsVector(axisTransform, normal, normal);
|
||||
}
|
||||
globalNormals.push(normal.x);
|
||||
globalNormals.push(normal.y);
|
||||
@ -625,3 +651,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='Z'] 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',
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -445,6 +445,7 @@ describe('loadObj', () => {
|
||||
expect(baseColorTexture.source).toBeDefined();
|
||||
});
|
||||
|
||||
<<<<<<< HEAD
|
||||
it('separates faces that don\'t use the same attributes as other faces in the primitive', async () => {
|
||||
const data = await loadObj(objMixedAttributesPath, options);
|
||||
const primitives = getPrimitives(data);
|
||||
@ -453,6 +454,57 @@ describe('loadObj', () => {
|
||||
expect(primitives[1].indices.length).toBe(6); // 2 faces
|
||||
expect(primitives[2].indices.length).toBe(6); // 2 faces
|
||||
expect(primitives[3].indices.length).toBe(6); // 2 faces
|
||||
=======
|
||||
function getFirstPosition(data) {
|
||||
var positions = data.nodes[0].meshes[0].positions;
|
||||
return new Cartesian3(positions.get(0), positions.get(1), positions.get(2));
|
||||
}
|
||||
|
||||
function getFirstNormal(data) {
|
||||
var normals = data.nodes[0].meshes[0].normals;
|
||||
return new Cartesian3(normals.get(0), normals.get(1), normals.get(2));
|
||||
}
|
||||
|
||||
function checkAxisConversion(inputUpAxis, outputUpAxis, position, normal) {
|
||||
var sameAxis = (inputUpAxis === outputUpAxis);
|
||||
var options = clone(defaultOptions);
|
||||
options.inputUpAxis = inputUpAxis;
|
||||
options.outputUpAxis = outputUpAxis;
|
||||
return loadObj(objRotatedUrl, options)
|
||||
.then(function(data) {
|
||||
var rotatedPosition = getFirstPosition(data);
|
||||
var 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', function(done) {
|
||||
expect(loadObj(objRotatedUrl, defaultOptions)
|
||||
.then(function(data) {
|
||||
var position = getFirstPosition(data);
|
||||
var normal = getFirstNormal(data);
|
||||
|
||||
var axes = ['X', 'Y', 'Z'];
|
||||
var axesLength = axes.length;
|
||||
var promises = [];
|
||||
for (var i = 0; i < axesLength; ++i) {
|
||||
for (var j = 0; j < axesLength; ++j) {
|
||||
promises.push(checkAxisConversion(axes[i], axes[j], position, normal));
|
||||
}
|
||||
}
|
||||
return Promise.all(promises);
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('throws when file has invalid contents', function(done) {
|
||||
expect(loadObj(objInvalidContentsUrl, defaultOptions), done).toRejectWith(RuntimeError);
|
||||
>>>>>>> 93dac5e... Convert up axis
|
||||
});
|
||||
|
||||
it('throws when file has invalid contents', async () => {
|
||||
|
Loading…
Reference in New Issue
Block a user