mirror of
https://github.com/CesiumGS/obj2gltf.git
synced 2024-11-30 03:50:14 -05:00
Merge branch 'master' into missing-attributes
This commit is contained in:
commit
52ce2938f5
@ -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>
|
|
@ -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)
|
||||||
|
@ -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');
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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'
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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",
|
||||||
|
@ -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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -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];
|
||||||
|
Loading…
Reference in New Issue
Block a user