-
Notifications
You must be signed in to change notification settings - Fork 311
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor and Tests #49
Conversation
Fixes #2 - power plant converts with the |
Can you elaborate on this? Because even without out-of-core processing, this seems like an architectural issue somewhere in the code. |
Yes maybe, I'm doing some more tests but the actual numbers do look better than that. |
Maybe obj size isn't the best metric, but I ran through two highly tessellated spheres, one with smooth normals and one with face normals, both 3.2 GB. The first one converts while the second has enough indices that the index array exceeds the maximum allowable typed array length. This could be solved by splitting primitives, something I was trying to avoid - also maybe future work because I want to get these changes in pretty soon. I actually don't think I have any "real" objs that fail at the moment, @shehzan10 had one that I still need to try out sometime tomorrow. |
Thanks, that makes sense and sounds like a perfectly fine limitation until we need to handle massive obj files. |
@likangning93 can you please review this? |
41956dd allows @shehzan10's model to be converted, but |
Ready now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at obj.js
and spec to start off.
var vertexPattern = /v( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; // v float float float | ||
var normalPattern = /vn( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; // vn float float float | ||
var uvPattern = /vt( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; // vt float float | ||
var facePattern1 = /f( +-?\d+)\/?( +-?\d+)\/?( +-?\d+)\/?( +-?\d+)?\/?/; // f vertex vertex vertex ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty inexperienced with regexes, so I could have used a note that the facePattern
regexes return something like [generatedVertexID, position, texCoord, Normal]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh wait, actually just saw the comments to the side... nevermind.
lib/obj.js
Outdated
} | ||
|
||
function useMaterial(name) { | ||
// Look to see if this material has already been used by a primitive in the mesh |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be better to use a per-Mesh
dictionary for materials?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I could have done a material -> primitive dictionary. Since nodes and meshes are also arrays I just wanted to keep it consistent.
lib/obj.js
Outdated
for (var i = 0; i < primitivesLength; ++i) { | ||
primitive = primitives[i]; // Sets the active primitive in case of returning early | ||
if (primitive.material === material) { | ||
return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clearer to do
primitive = primitives[i]; // Sets the active primitive in case of returning early
here, does that break anything?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that is clearer.
lib/obj.js
Outdated
.then(function(materials) { | ||
var imagePaths = getImagePaths(materials); | ||
return loadImages(imagePaths, objPath) | ||
.then(function(images) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
less indentation:
return loadMaterials(...)
.then(function(materials) {
///
})
.then(function(images) {
///
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The downside here is then materials
is not in the closure of the final return (without a helper var).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah that's right... ok.
lib/obj.js
Outdated
// Collect all the image files from the materials | ||
var images = []; | ||
function getImagePaths(materials) { | ||
var imagePaths = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does indexOf
do a linear search? Maybe a dictionary would be better here for an .obj
with a huge number of textures.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, will fix that.
specs/lib/objSpec.js
Outdated
|
||
describe('obj', function() { | ||
it('loads obj with positions, normals, and uvs', function(done) { | ||
expect(loadObj(objUrl) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of curiosity, is there a difference between wrapping in expect(...).toResolve()
and something like:
loadObj(..)
.then(function(data)) {
///
done();
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that way if the promise failed it wouldn't reach done()
. The test would still fail due to a timeout, but the version in objSpec
would fail faster.
specs/lib/objSpec.js
Outdated
}), done).toResolve(); | ||
}); | ||
|
||
it('loads obj with triangle faces', function(done) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
duplicate, was this supposed to be quads?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, good catch. The other tests all use quads.
lib/image.js
Outdated
.then(function(data) { | ||
var extension = path.extname(imagePath); | ||
var uriType = getUriType(extension); | ||
var uri = uriType + ';base64,' + data.toString('base64'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this include a check on the buffer length for large textures? Otherwise, maybe we should add a troubleshooting section in the README.md with things to try, like writing out separate images if there's a buffer.toString
error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah a common check like this could be in a few places.
lib/image.js
Outdated
info.format = getFormat(channels); | ||
|
||
if (channels === 4) { | ||
// Need to do a finer grained check over the pixels to see if the image is actually transparent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed offline, a lot of images may be 4-channel PNGs that don't have any transparent pixels. I wonder if it's okay to just make transparency a flag option, since a full pixel level check might be kind of slow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea for adding an option for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With one model I noticed a difference from 200 ms to 6000 ms just by checking this. (It had lots of textures though).
Updated. The uri handling is better now. |
specs/lib/convertSpec.js
Outdated
}), done).toResolve(); | ||
}); | ||
|
||
it('bypassPipeline flag bypasses gltf-pipeline', function(done) { | ||
var spy1 = spyOn(convert, '_outputJson'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to create these variables, just call spyOn
.
specs/lib/convertSpec.js
Outdated
expect(convert(objPath, gltfPath, options) | ||
.then(function() { | ||
expect(spy1.calls.count()).toBe(1); | ||
expect(spy2.calls.count()).toBe(0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is better written as expect(GltfPipeline.processJSONToDisk).not.toHaveBeenCalled();
specs/lib/convertSpec.js
Outdated
}; | ||
expect(convert(objPath, gltfPath, options) | ||
.then(function() { | ||
expect(spy1.calls.count()).toBe(1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is better written as expect(convert._outputJson).toHaveBeenCalled();
specs/lib/convertSpec.js
Outdated
}), done).toResolve(); | ||
}); | ||
|
||
it('uses a custom logger', function(done) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test isn't really useful (and is also fragile if we change what we're logging). We should be testing for specific log messages in each instance we expect one to be generated, not just running something and seeing if the logger gets called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed this.
I checked and the all the actually places things are logged are accounted for.
specs/lib/gltfSpec.js
Outdated
var fsExtra = require('fs-extra'); | ||
var path = require('path'); | ||
var Promise = require('bluebird'); | ||
var clone = require('../../lib/clone.js'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove .js
from these requires.
specs/lib/gltfSpec.js
Outdated
var diffuseTexture; | ||
var transparentDiffuseTexture; | ||
|
||
beforeAll(function(done) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change this to a beforeEach
and get rid of all of the clone
usage. You can then delete the clone.js
.
lib/image.js
Outdated
|
||
var fsReadFile = Promise.promisify(fs.readFile); | ||
var defaultValue = Cesium.defaultValue; | ||
var WebGLConstants = Cesium.WebGLConstants; | ||
|
||
module.exports = loadImage; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Filename and function name don't match. Shouldn't the file be called loadImage
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a problem with most of the files in lib
. They should all be fixed. We can do that in a quick separate PR after this is merged if you want, but please write up an issue.
lib/image.js
Outdated
* @private | ||
*/ | ||
function loadImage(imagePath, options) { | ||
options = defaultValue(options, defaultValue.EMPTY_OBJECT); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function should check if imagePath
is a string and throw a DeveloperError
before doing anything else (also add a test for it).
specs/lib/mtlSpec.js
Outdated
return path.normalize(path.join(path.dirname(objPath), relativePath)); | ||
} | ||
|
||
describe('mtl', function() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be a separate PR, but there are no tests here for how we handle invalid mtl files and we don't do any sort of parameter validation either. Please write up an issue to review all functions for error handling and parameter validation.
OK, that's all I have for the first go around. Given the size of this PR, there are definitely things we should do as separate PRs after this is merged, but let's make sure we write issues so nothing drops on the floor. Thanks @lilleyse! |
Thanks for the thorough review @mramato, I'll get to these soon. |
I got through a lot of the comments here. I may briefly scan everything again but I believe this is ready. Some things I haven't addressed in this PR but will handle in others:
Breaking change |
@lilleyse please make sure there are issues for everything in #49 (comment). |
@lilleyse please comment close all the issues in #49 (comment). |
@lilleyse when you get a chance, it would be great to publish a new version to npm as well. |
This refactor fixes a few long-standing issues with this project:
To-do:
--bypassPipeline
, so there are some issues to look into for gltf-pipeline