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

FBXLoader: Support more texture formats. #28515

Merged
merged 9 commits into from
Jun 1, 2024

Conversation

catalin-enache
Copy link
Contributor

@catalin-enache catalin-enache commented May 28, 2024

Fixed #28514

Description

Improved the way FBXLoader calls for specific image loaders, by removing hardcodings and iterating through a set of predefined extensions.

Mugen87
Mugen87 previously approved these changes May 28, 2024
@Mugen87 Mugen87 added this to the r165 milestone May 28, 2024
@Mugen87 Mugen87 changed the title #28514 extend FBXLoader textures type May 28, 2024
Comment on lines 436 to 446
const loader = this.manager.getHandler( `.${extension}` );

if ( loader === null ) {

console.warn( 'FBXLoader: DDS loader not found, creating placeholder texture for', textureNode.RelativeFilename );
console.warn(
`FBXLoader: ${extension.toUpperCase()} loader not found, creating placeholder texture for`,
textureNode.RelativeFilename
);
texture = new Texture();

} else {
Copy link
Owner

@mrdoob mrdoob May 29, 2024

Choose a reason for hiding this comment

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

With this new code... If a fbx file uses psd as textures, we no longer log a warning...? 🤔

Copy link
Contributor Author

@catalin-enache catalin-enache May 29, 2024

Choose a reason for hiding this comment

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

Correct.
I searched globally in the repo for psd and did not find another place where it is handled specifically.
I was thinking: do we need a dedicated handling for things that are not supported?
I mean, it does not need to be just .psd.
It could be a hardcoded black list (possibly quite large) of things that we should throw a warning for if encountered.
What if we apply the same approach everywhere ?
IMO, that adds complexity without adding value.
That's why a dropped that branch.

Copy link
Owner

Choose a reason for hiding this comment

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

Ideally we could just do this:

const loader = this.manager.getHandler( `.${extension}` );

if ( loader !== null ) {

  texture = loader.load( fileName );

} else {

  console.warn(
	  `FBXLoader: ${extension.toUpperCase()} loader not found, creating placeholder texture for`,
	  textureNode.RelativeFilename
  );
  texture = new Texture();

}

But I guess for that to work we may need to add default handlers in LoadingManager?

https://github.com/mrdoob/three.js/blob/dev/src/loaders/LoadingManager.js#L140C1-L140C66

const DefaultTextureLoader = /*@__PURE__*/ new TextureLoader();

const DefaultLoadingManager = /*@__PURE__*/ new LoadingManager();
DefaultLoadingManager.setHandler( /\.png$/i, DefaultTextureLoader );
DefaultLoadingManager.setHandler( /\.jpeg$/i, DefaultTextureLoader );
DefaultLoadingManager.setHandler( /\.jpg$/i, DefaultTextureLoader );
DefaultLoadingManager.setHandler( /\.gif$/i, DefaultTextureLoader );

Or even populate this array directly?

@Mugen87 What do you think?

Copy link
Collaborator

@Mugen87 Mugen87 May 29, 2024

Choose a reason for hiding this comment

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

Um, I'm not sure I understand why it is required to add default handlers.

If a loader for an extension isn't found, an empty texture is returned and a warning is logged. That seems sufficient.

I was never a fan of the following branch in FBXLoader for processing non-supported images:

else {

    // TextureLoader#load() returns a texture in any case which can be used as placeholder if the image failed to load.
    texture = this.textureLoader.load( fileName );

}

Simply doing texture = new Texture(); is better, imo.

Copy link
Contributor Author

@catalin-enache catalin-enache May 29, 2024

Choose a reason for hiding this comment

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

Simply doing texture = new Texture(); is better, IMO.

const loader = this.manager.getHandler( .${extension} ); will return null for native images which are supposed to be handled without user adding default TextureLoader to the regex map for native extensions.
That's why there is this fallback :
texture = this.textureLoader.load( fileName );
@mrdoob would eventually like to not return null for native images, but that would require prepopulating the regex map with default TextureLoader. My only fear that I can think of with that is that the list of native extensions would not be comprehensive enough, preventing handling images that the browser could have handled.
If we'd like to set default TextureLoader for native images (which I'm afraid of possible other implications) we have to make sure to not override an eventual existing loader if it was setup by the user

Copy link
Collaborator

@Mugen87 Mugen87 May 29, 2024

Choose a reason for hiding this comment

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

Couldn't we have a second set with native extensions intended for TextureLoader (or ImageBitmapLoader). Meaning [ 'png', 'jpeg', ' jpg', 'gif', 'avif', 'webp' ]. If an extension is not in that set, we check the non-native one ( [ 'tga', 'tif', 'tiff', 'exr', 'dds', 'hdr', 'ktx2' ]). If the check is false, we return an empty texture.

My only fear that I can think of with that is that the list of native extensions would not be comprehensive enough, preventing handling images that the browser could have handled.

If we ever have an issue that something in the set is missing, we can add an entry.

Copy link
Contributor Author

@catalin-enache catalin-enache May 29, 2024

Choose a reason for hiding this comment

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

I see the will to simplify the logic especially those nested if branches. I pushed a new commit. The only reason for keeping around non native extensions is to be able to throw a warning in the console. If we won't want that warning, we don't need them around making the loader = specificLoader || defaultTextureLoader simplifying even more.
I believe though that we want that warning there, hence keeping around non native extensions set.

manager.addHandler( /\.dds$/i, new DDSLoader() );
manager.addHandler( /\.exr$/i, new EXRLoader() );
manager.addHandler( /\.hdr$/i, new RGBELoader() );
manager.addHandler( /\.tiff$/i, new TIFFLoader() );
Copy link
Collaborator

@Mugen87 Mugen87 May 30, 2024

Choose a reason for hiding this comment

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

Um, now that I look at the code I'm not so happy that the FBX example loads all the modules by default. In production, that makes the example considerably slower to load.

I vote to remove the modifications to webgl_loader_fbx and just accept that we don't have a test asset for non-native textures.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll try to make a checkbox in UI that when checked will asynchronously import the loaders. Let's see (if I manage to do that) how would you like it that way.

Copy link
Collaborator

@Mugen87 Mugen87 May 30, 2024

Choose a reason for hiding this comment

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

Wouldn't it be more simple to test for with_non_native_textures.fbx and then asynchronously load the texture loaders when the asset is selected?

Copy link
Contributor Author

@catalin-enache catalin-enache May 30, 2024

Choose a reason for hiding this comment

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

Better idea indeed. import the loaders when selecting the with_non_native_textures.fbx then when they're ready import the asset itself. Will try to do that. I have not started any changes yet. Will do soon.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I pushed a new commit, now lazily importing loaders.
Note: I had to increase ESLint ECMA version in order to not complain about import() used as a function.

Copy link
Collaborator

@Mugen87 Mugen87 May 31, 2024

Choose a reason for hiding this comment

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

Note: I had to increase ESLint ECMA version in order to not complain about import() used as a function.

This is unfortunately not possible since we have to stick to ECMA 2018 and lint addon and src code respectively. Please revert the changes to .eslintrc.json.

Sorry, I think it's best to remove the asset from the example. It makes things just too complicated.

We want to keep the example as simple as possible so devs can quickly start coding. Using the examples to test every possible code path of an addon is not top-priority.

Copy link
Contributor Author

@catalin-enache catalin-enache May 31, 2024

Choose a reason for hiding this comment

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

I understand. I reverted it. I let there though some improvements on webgl_loader_fbx.html that happened during this PR (including some changes from mrdoob). The failing E2E tests seem random. The targets for the snapshots which I believe relate to HTML examples, I didn't found them - probably a re-trigger will work this time ?

Regarding testing, if there would be a possibility to unit-test the assets we won't need to pile up to HTML examples.
However that would only be possible if in unit-tests we could leverage a mock server (something like Sinon or MSW ) so that we could load assets from disk in tests.

Maybe I will come up with such PR at some point.
Do you think a feature like that in unit tests would be welcomed?

Related to ESLint ECMA version, should I make an issue about it to increase it to something more recent, to gain access to features like async import among others? Or there is some hard reason (which escapes me) for sticking with 2018 ?

@Mugen87 Mugen87 dismissed their stale review May 31, 2024 10:55

Revert example changes.

@Mugen87 Mugen87 modified the milestones: r165, r166 May 31, 2024
@Mugen87 Mugen87 merged commit 6abb47c into mrdoob:dev Jun 1, 2024
9 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
3 participants