Merge branch 'master' into missing-attributes

This commit is contained in:
Sean Lilley 2019-10-26 20:17:31 -04:00
commit 52ce2938f5
9 changed files with 150 additions and 14 deletions

View File

@ -1,7 +0,0 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="PROJECT_PROFILE" value="Project Default" />
<option name="USE_PROJECT_PROFILE" value="true" />
<version value="1.0" />
</settings>
</component>

View File

@ -1,6 +1,14 @@
Change Log Change Log
========== ==========
### 3.?.? - 2019-??-??
* Added back `inputUpAxis` and `outputUpAxis`. [#211](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/211)
### 3.0.4 - 2019-07-22
* No longer printing texture decode warning if the diffuse and alpha textures are the same. [#205](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/205)
### 3.0.3 2019-06-26 ### 3.0.3 2019-06-26
* Fixed parsing of negative face indices. [#191](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/191) * Fixed parsing of negative face indices. [#191](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/191)

View File

@ -122,6 +122,18 @@ const argv = yargs
describe : 'The glTF will be saved with the KHR_materials_unlit extension.', describe : 'The glTF will be saved with the KHR_materials_unlit extension.',
type : 'boolean', type : 'boolean',
default : defaults.unlit 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); }).parse(args);
@ -167,7 +179,9 @@ const options = {
specularGlossiness : argv.specularGlossiness, specularGlossiness : argv.specularGlossiness,
unlit : argv.unlit, unlit : argv.unlit,
overridingTextures : overridingTextures, overridingTextures : overridingTextures,
outputDirectory : outputDirectory outputDirectory : outputDirectory,
inputUpAxis : argv.inputUpAxis,
outputUpAxis : argv.outputUpAxis
}; };
console.time('Total'); console.time('Total');

View File

@ -387,6 +387,10 @@ function createDiffuseAlphaTexture(diffuseTexture, alphaTexture, options) {
return diffuseTexture; return diffuseTexture;
} }
if (diffuseTexture === alphaTexture) {
return diffuseTexture;
}
if (!defined(diffuseTexture.pixels) || !defined(alphaTexture.pixels)) { if (!defined(diffuseTexture.pixels) || !defined(alphaTexture.pixels)) {
options.logger('Could not get decoded texture data for ' + diffuseTexture.path + ' or ' + alphaTexture.path + '. The material will be created without an alpha texture.'); options.logger('Could not get decoded texture data for ' + diffuseTexture.path + ' or ' + alphaTexture.path + '. The material will be created without an alpha texture.');
return diffuseTexture; return diffuseTexture;

View File

@ -8,6 +8,7 @@ const loadMtl = require('./loadMtl');
const outsideDirectory = require('./outsideDirectory'); const outsideDirectory = require('./outsideDirectory');
const readLines = require('./readLines'); const readLines = require('./readLines');
const Axis = Cesium.Axis;
const Cartesian3 = Cesium.Cartesian3; const Cartesian3 = Cesium.Cartesian3;
const ComponentDatatype = Cesium.ComponentDatatype; const ComponentDatatype = Cesium.ComponentDatatype;
const CoplanarPolygonGeometryLibrary = Cesium.CoplanarPolygonGeometryLibrary; const CoplanarPolygonGeometryLibrary = Cesium.CoplanarPolygonGeometryLibrary;
@ -16,6 +17,7 @@ const defined = Cesium.defined;
const PolygonPipeline = Cesium.PolygonPipeline; const PolygonPipeline = Cesium.PolygonPipeline;
const RuntimeError = Cesium.RuntimeError; const RuntimeError = Cesium.RuntimeError;
const WindingOrder = Cesium.WindingOrder; const WindingOrder = Cesium.WindingOrder;
const Matrix4 = Cesium.Matrix4;
module.exports = loadObj; 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 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 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. * Parse an obj file.
* *
@ -57,6 +61,8 @@ const facePattern = /(-?\d+)\/?(-?\d*)\/?(-?\d*)/g;
* @private * @private
*/ */
function loadObj(objPath, options) { function loadObj(objPath, options) {
const axisTransform = getAxisTransform(options.inputUpAxis, options.outputUpAxis);
// Global store of vertex attributes listed in the obj file // Global store of vertex attributes listed in the obj file
let globalPositions = new ArrayStorage(ComponentDatatype.FLOAT); let globalPositions = new ArrayStorage(ComponentDatatype.FLOAT);
let globalNormals = new ArrayStorage(ComponentDatatype.FLOAT); let globalNormals = new ArrayStorage(ComponentDatatype.FLOAT);
@ -354,9 +360,16 @@ function loadObj(objPath, options) {
const mtllibLine = line.substring(7).trim(); const mtllibLine = line.substring(7).trim();
mtlPaths = mtlPaths.concat(getMtlPaths(mtllibLine)); mtlPaths = mtlPaths.concat(getMtlPaths(mtllibLine));
} else if ((result = vertexPattern.exec(line)) !== null) { } else if ((result = vertexPattern.exec(line)) !== null) {
globalPositions.push(parseFloat(result[1])); const position = scratchCartesian;
globalPositions.push(parseFloat(result[2])); position.x = parseFloat(result[1]);
globalPositions.push(parseFloat(result[3])); 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) { } else if ((result = normalPattern.exec(line) ) !== null) {
const normal = Cartesian3.fromElements(parseFloat(result[1]), parseFloat(result[2]), parseFloat(result[3]), scratchNormal); const normal = Cartesian3.fromElements(parseFloat(result[1]), parseFloat(result[2]), parseFloat(result[3]), scratchNormal);
if (Cartesian3.equals(normal, Cartesian3.ZERO)) { if (Cartesian3.equals(normal, Cartesian3.ZERO)) {
@ -364,6 +377,9 @@ function loadObj(objPath, options) {
} else { } else {
Cartesian3.normalize(normal, normal); Cartesian3.normalize(normal, normal);
} }
if (defined(axisTransform)) {
Matrix4.multiplyByPointAsVector(axisTransform, normal, normal);
}
globalNormals.push(normal.x); globalNormals.push(normal.x);
globalNormals.push(normal.y); globalNormals.push(normal.y);
globalNormals.push(normal.z); globalNormals.push(normal.z);
@ -625,3 +641,19 @@ function cleanNodes(nodes) {
setDefaults(nodes); setDefaults(nodes);
return 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.baseColorTexture] Path to the baseColor/diffuse texture.
* @param {String} [options.overridingTextures.emissiveTexture] Path to the emissive texture. * @param {String} [options.overridingTextures.emissiveTexture] Path to the emissive texture.
* @param {String} [options.overridingTextures.alphaTexture] Path to the alpha 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 {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 {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. * @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.overridingTextures = defaultValue(options.overridingTextures, defaultValue.EMPTY_OBJECT);
options.logger = defaultValue(options.logger, getDefaultLogger()); options.logger = defaultValue(options.logger, getDefaultLogger());
options.writer = defaultValue(options.writer, getDefaultWriter(options.outputDirectory)); 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)) { if (!defined(objPath)) {
throw new DeveloperError('objPath is required'); throw new DeveloperError('objPath is required');
@ -164,7 +168,19 @@ obj2gltf.defaults = {
* @type Boolean * @type Boolean
* @default false * @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

@ -1,6 +1,6 @@
{ {
"name": "obj2gltf", "name": "obj2gltf",
"version": "3.0.3", "version": "3.0.4",
"description": "Convert OBJ model format to glTF", "description": "Convert OBJ model format to glTF",
"license": "Apache-2.0", "license": "Apache-2.0",
"contributors": [ "contributors": [
@ -32,7 +32,7 @@
"jpeg-js": "^0.3.5", "jpeg-js": "^0.3.5",
"mime": "^2.4.4", "mime": "^2.4.4",
"pngjs": "^3.4.0", "pngjs": "^3.4.0",
"yargs": "^13.2.4" "yargs": "^14.2.0"
}, },
"devDependencies": { "devDependencies": {
"cloc": "^2.5.0", "cloc": "^2.5.0",

View File

@ -321,6 +321,20 @@ describe('loadMtl', () => {
expect(material.alphaMode).toBe('BLEND'); expect(material.alphaMode).toBe('BLEND');
expect(material.doubleSided).toBe(true); expect(material.doubleSided).toBe(true);
}); });
it('uses diffuse texture if diffuse and alpha are the same', () => {
options.metallicRoughness = true;
const material = loadMtl._createMaterial({
diffuseTexture : diffuseTexture,
alphaTexture : diffuseTexture
}, options);
const pbr = material.pbrMetallicRoughness;
expect(pbr.baseColorTexture).toBe(diffuseTexture);
expect(material.alphaMode).toBe('BLEND');
expect(material.doubleSided).toBe(true);
});
}); });
describe('specularGlossiness', () => { describe('specularGlossiness', () => {
@ -416,5 +430,19 @@ describe('loadMtl', () => {
expect(material.alphaMode).toBe('BLEND'); expect(material.alphaMode).toBe('BLEND');
expect(material.doubleSided).toBe(true); expect(material.doubleSided).toBe(true);
}); });
it('uses diffuse texture if diffuse and alpha are the same', () => {
options.specularGlossiness = true;
const material = loadMtl._createMaterial({
diffuseTexture : diffuseTexture,
alphaTexture : diffuseTexture
}, options);
const pbr = material.extensions.KHR_materials_pbrSpecularGlossiness;
expect(pbr.diffuseTexture).toEqual(diffuseTexture);
expect(material.alphaMode).toBe('BLEND');
expect(material.doubleSided).toBe(true);
});
}); });
}); });

View File

@ -10,6 +10,7 @@ const clone = Cesium.clone;
const RuntimeError = Cesium.RuntimeError; const RuntimeError = Cesium.RuntimeError;
const objPath = 'specs/data/box/box.obj'; 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 objNormalsPath = 'specs/data/box-normals/box-normals.obj';
const objUvsPath = 'specs/data/box-uvs/box-uvs.obj'; const objUvsPath = 'specs/data/box-uvs/box-uvs.obj';
const objPositionsOnlyPath = 'specs/data/box-positions-only/box-positions-only.obj'; const objPositionsOnlyPath = 'specs/data/box-positions-only/box-positions-only.obj';
@ -456,6 +457,46 @@ describe('loadObj', () => {
expect(primitives[3].indices.length).toBe(6); // 2 faces 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('does not add missing normals and uvs', async() => { it('does not add missing normals and uvs', async() => {
const data = await loadObj(objMissingAttributesPath, options); const data = await loadObj(objMissingAttributesPath, options);
const primitive = getPrimitives(data)[0]; const primitive = getPrimitives(data)[0];