Skip to content
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

Returning conversion warnings in 3DMLoader #21639

Merged
merged 30 commits into from
May 18, 2021
Merged

Conversation

karimi
Copy link
Contributor

@karimi karimi commented Apr 12, 2021

Fixed #21638

Description

3DMLoader parse and load methods can partially fail to convert some entities of the original file for various reasons including missing render meshes. This PR captures returns warnings of this nature.

@mrdoob
Copy link
Owner

mrdoob commented Apr 12, 2021

/ping @fraguada

@fraguada
Copy link
Contributor

fraguada commented Apr 13, 2021

@mrdoob @Rada8732 @Mugen87 @karimi I'd like a bit of time to review this. @karimi and I have discussed the use case, which is valid, but I'd like to see if there are any other options to bubble up errors from workers. I haven't done the research yet, but I'm guessing this can be done through messaging. Any suggestions are welcome.

@fraguada
Copy link
Contributor

fraguada commented Apr 13, 2021

@karimi I believe the way we'd want to do this is to actually use the worker messaging like is done here: https://github.com/mrdoob/three.js/blob/dev/examples/jsm/loaders/3DMLoader.js#L811
There is a bunch of unused onError stuff, and even a case for handling errors from the worker that is never used 😳: https://github.com/mrdoob/three.js/blob/dev/examples/jsm/loaders/3DMLoader.js#L713
It feels like we should use that messaging mechanism then hit the onError here or here.

We could extend this to warnings and aggregate warnings through a similar mechanism.

Does this sound reasonable?

@karimi
Copy link
Contributor Author

karimi commented Apr 13, 2021

@fraguada are you suggesting we add a warning case in _getWorker ?

case 'warning':
	worker._callbacks[ message.id ].reject( message );
	break;

I'm not sure how that can work since warnings can be thrown at individual object level and tasks are per buffer. Of course that can change, but it's a bigger change.

@karimi
Copy link
Contributor Author

karimi commented Apr 13, 2021

@fraguada , see latest commit
It's catching the decodeObjects errors and posting them as a new error message to the worker where it's picked up by onError callback.
I'm still not sure if we can extend the same mechanism to warnings without substantial change since tasks are not per objects.

@Mugen87
Copy link
Collaborator

Mugen87 commented Apr 16, 2021

@karimi Do you mind rebasing the PR due to #21660?

@karimi karimi force-pushed the 3dmloaderwarnings branch from 38e1a96 to da176ab Compare April 16, 2021 15:59
@karimi
Copy link
Contributor Author

karimi commented Apr 16, 2021

@Mugen87 Just rebased and pushed.

@karimi
Copy link
Contributor Author

karimi commented Apr 26, 2021

@fraguada did you have a chance to review this?

Copy link
Contributor

@fraguada fraguada left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@karimi I've written some comments along with your proposed changes. I believe we can still take advantage of the worker self.postMessage( { type: 'warning', data } ); pattern by adding a warning case when setting up the worker (around line 706). When the worker finishes, you'd have a collection of warnings that can be checked along with the resulting objects. What do you think?

examples/jsm/loaders/3DMLoader.js Outdated Show resolved Hide resolved
examples/jsm/loaders/3DMLoader.js Outdated Show resolved Hide resolved
@@ -1226,6 +1241,7 @@ function Rhino3dmWorker() {

default:
console.warn( `THREE.3DMLoader: TODO: Implement ${objectType.constructor.name}` );
onWarning( `THREE.3DMLoader: TODO: Implement ${objectType.constructor.name}` );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same strategy as suggested above

examples/jsm/loaders/3DMLoader.js Outdated Show resolved Hide resolved
examples/jsm/loaders/3DMLoader.js Outdated Show resolved Hide resolved
@@ -714,6 +717,9 @@ class Rhino3dmLoader extends Loader {
const message = e.data;

switch ( message.type ) {
case 'warning':
worker._callbacks[ message.id ].warn( message );
Copy link
Contributor Author

@karimi karimi May 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fraguada We need a id to figure out which task this warning belongs to. I'm using the taskID corresponding to the file to collate the warnings.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure we should add a .warn to this callback, since the promises api only defines resolve and reject. I think this is the place we should be collecting the warning, or printing them to the console, or doing something. The worker should need to be bothered by a warning unless I am confused.

@@ -117,7 +118,7 @@ class Rhino3dmLoader extends Loader {

return new Promise( ( resolve, reject ) => {

worker._callbacks[ taskID ] = { resolve, reject };
worker._callbacks[ taskID ] = { resolve, reject, warn: warning =>warnings.push(warning) };
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a function to collate warnings thrown by workers.

@karimi
Copy link
Contributor Author

karimi commented May 3, 2021

@fraguada Thanks for your suggestions, I think posting the warnings to the workers makes sense. I was able to do that but needed an id to relate the warnings that can be thrown at any level to a specific task initiated by a file. Therefore I'm passing the taskID down to functions that create the geometry. I'm open to suggestions if you think this can be done more elaborately.

@fraguada
Copy link
Contributor

fraguada commented May 4, 2021

@karimi the warnings should only occur in a worker that has a decode message. Perhaps it would be better to have a worker level taskId that can then be added to any messages posted, instead of passing the taskId to the methods.

So for example:
(around line 773)

/* WEB WORKER */

function Rhino3dmWorker() {

	let libraryPending;
	let libraryConfig;
	let rhino;
        let taskId; //new

and then
(around line 807)

	case 'decode':

                taskId = message.id; // new

		const buffer = message.buffer;
		libraryPending.then( () => {

			const data = decodeObjects( rhino, buffer );

			self.postMessage( { type: 'decode', id: message.id, data } );

		} );

		break;

@@ -615,7 +624,7 @@ class Rhino3dmLoader extends Loader {

} else if ( geometry.isLinearLight ) {

console.warn( 'THREE.3DMLoader: No conversion exists for linear lights.' );
self.postMessage( { type: 'warning', id: taskID, message: 'THREE.3DMLoader: No conversion exists for linear lights.' } );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@karimi here we are no longer in the worker. What do you want to do with these warnings? I'm don't think posting messages is the way to go here.

@fraguada
Copy link
Contributor

fraguada commented May 7, 2021

@karimi I made some changes and updates. Thanks for giving me push access to your fork, it makes this PR process better IMO.

I think with these changes we cover a general way to communicate warnings from the worker. In the main code we need to decide how you want to use these warnings. For now, I created a class level this.warnings[] to collect all of these. We should ensure there is enough data in the message so that it is useful when converting the objects and for your use case.

@karimi karimi marked this pull request as draft May 7, 2021 21:19
@karimi
Copy link
Contributor Author

karimi commented May 7, 2021

@fraguada I turned this into a draft PR since there are still issues that need to be resolved. I pushed a commit to fix the issue with undefined this.warnings . But still need to figure out the warning with linear_lights and how we communicate these warnings using load and parse methods.

@karimi
Copy link
Contributor Author

karimi commented May 7, 2021

@fraguada I have a few ideas on how to return these warnings, one would be to just attach the array of warnings to the returned object. Another one would be to add a onWarning callback to parse and load methods. Let me know what your thoughts are.

load( url, onLoad, onProgress, onError, onWarning ) {
.
.
.
	this.decodeObjects( buffer, url )
		.then( result =>{onLoad(result); this.warnings.length>0 && onWarning(this.warnings)} )
		.catch( e => onError( e ) );

}, onProgress, onError );

}

@karimi
Copy link
Contributor Author

karimi commented May 7, 2021

Regarding the contents of the warnings I think they're already useful, but it would be definitely an improvement if we could return an object with warning type and message. As far as my workflow goes, I'd like to provide a suggestion to the user on how to fix each warning, and knowing the warning type would be useful. What I mean by warning type is for example type: "missing mesh" vs type: "missing texture"

@fraguada
Copy link
Contributor

@karimi I've reworked the warning message style, adding a guid property when the warning is related to an object. Warning types are:

  • no conversion - there is no analog type between rhino and threejs
  • not implemented - this conversion hasn't been implemented
  • missing mesh - no render mesh associated with this object
  • missing resource - there is something not embedded in the file, in the case of things like bitmaps for textures

I think we should go with your suggestion to add warnings to the objects (when possible) and not add an onWarning callback.

I suggest objects get a warning property in the object userData with the warnings associated with the object guid. This won't be relevant for things like missing textures. If that is important, we can handle it in another PR.

@karimi
Copy link
Contributor Author

karimi commented May 12, 2021

Thank @fraguada , I think the warning object is more informative now.
Regarding adding the warnings to object userData, I meant the object as in javascript object, not as in geometry. That won't be possible because in most of the warning cases there won't be a three.js geometry to attach the warnings to.
So I went ahead and added the warnings to userData of the object returned by load and parse.
I think it looks good and ready to be merged in. Let me know if you have any other thoughts.

@karimi karimi marked this pull request as ready for review May 13, 2021 01:35
@fraguada
Copy link
Contributor

Thanks for working on this @karimi! I think this is a nifty system :)

@Mugen87 @mrdoob I think this one is ready.

@mrdoob mrdoob added this to the r129 milestone May 17, 2021
@@ -76,7 +76,7 @@
// hide spinner
document.getElementById( 'loader' ).style.display = 'none';

} );
}, progress =>progress, error => ( console.log( error ) ) );
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does progress =>progress do?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cleaned that up.

@mrdoob mrdoob merged commit 8fa6227 into mrdoob:dev May 18, 2021
@mrdoob
Copy link
Owner

mrdoob commented May 18, 2021

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Retrieve 3DMLoader conversion warnings
4 participants