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

BufferGeometryUtils: Fixes for MikkTSpace tangents #23802

Merged

Conversation

donmccurdy
Copy link
Collaborator

@donmccurdy donmccurdy commented Mar 28, 2022

Related issues:

Description

  • Avoid top-level await.
  • Fix error on non-indexed geometry.

/cc @gkjohnson do you mind checking that this works with your Parcel setup?

- Avoid top-level await.
- Fix error on non-indexed geometry.
@@ -10,10 +10,15 @@ import {
TrianglesDrawMode,
Vector3,
} from 'three';
import { generateTangents } from '../libs/mikktspace.module.js';
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this import can stay now that there is no top-level await anymore? The wasm variable will contain the instance once the request has been fulfilled.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It technically could stay, it's a question of the preferred API / usage:

import * as MikkTSpace from 'three/examples/jsm/libs/mikktspace.module.js';
import { computeTangents } from 'three/examples/jsm/utils/BufferGeometryUtils.js';

await MikkTSpace.ready;

// (A)
geometry = computeTangents( geometry, MikkTSpace );

// (B)
geometry = computeTangents( geometry );

I was thinking (A) might be better because it is more explicit – you have to know about this MikkTSpace module, because you're passing it into the function, so there's perhaps less chance of forgetting to check .ready and having a subtle race condition. This also allows users to provide the mikktspace npm package instead, which is more optimized for modern bundlers (it doesn't embed the WASM as a base64 string as required here).

I'm open to either (A) or (B), though.

Copy link
Contributor

Choose a reason for hiding this comment

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

Right, if it makes users able to use the wasm version, it makes sense.

@gkjohnson
Copy link
Collaborator

gkjohnson commented Mar 28, 2022

Works great! I recommend having a code blurb in the documentation showing how to import the MikkTSpace object since it wasn't immediately clear to me:

import * as MikkTSpace from './lib/mikktspace.module.js';
import { computeTangents } from './lib/BufferGeometryUtils.js';

// ...

await MikktSpace.ready;
computeTangents( geometry, MikktSpace );

Here's a demo of a model that had to have tangents generated to use the normal map correctly:

image

@gkjohnson
Copy link
Collaborator

Also not sure if this is a requirement of MikkTSpace computation or not but it would be nice if vertices were not implicitly unmerged while computing tangents. But I guess it's not a huge deal since you can remerge after.

@donmccurdy
Copy link
Collaborator Author

donmccurdy commented Mar 28, 2022

MikkTSpace does require non-indexed input, although re-indexing after computing tangents is fine. Would it be better to throw an error for indexed input rather than rebuilding the input geometry?

@WestLangley
Copy link
Collaborator

I find it confusing to have two methods called .computeTangents(). Maybe .computeMikkTSpaceTangents()?

@gkjohnson
Copy link
Collaborator

MikkTSpace does require non-indexed input, although re-indexing after computing tangents is fine. Would it be better to throw an error for indexed input rather than rebuilding the input geometry?

No I think as-is is fine. Just thought I'd mention it in case there was a better way to handle it. I think requiring the user to merge after makes sense. I'd just make sure it's well-noted in the docs.

@WestLangley
Copy link
Collaborator

Here's a demo of a model that had to have tangents generated to use the normal map correctly

@gkjohnson Since the docs are not clear as to when the existing method may not work, can you please use VertexTangentsHelper and show us where the differences lie? Is it the mirrored UVs... or what?

@gkjohnson
Copy link
Collaborator

can you please use VertexTangentsHelper and show us where the differences lie? Is it the mirrored UVs... or what?

The model above just does not have tangents at all. For my use case (path traced ao map baking) I need tangents in order to correctly use the normal map and the existing view-space generation doesn't work. I want to generate tangents in-shader for this use case, as well, (which I think is doable) but that's a longer term effort.

@WestLangley
Copy link
Collaborator

@gkjohnson But you compute tangents, and the existing method is inadequate in your example. Right?

I'd like to know where the existing method fails.

@gkjohnson
Copy link
Collaborator

Oh tbh I didn't realize that BufferGeometry.computeTangents had been implemented. I do see that GLTF recommends MikkTSpace normals if no tangents are provided in a model, though.

When tangents are not specified, client implementations SHOULD calculate tangents using default MikkTSpace algorithms with the specified vertex positions, normals, and texture coordinates associated with the normal texture.

But here's an image with the different compute tangent methods. Notice on the BufferGeometry.computeTangents that the normal map seems inverted -- this is likely due to the negateSign argument that is included in the MikkT function in this PR. Perhaps a negate argument could be added to the BufferGeometry version? Perhaps the built in version would be good enough in most cases, then. I'll have to look at the tangents helper another time, though.

MikkT computeTangents
image image

@Mugen87 Mugen87 added this to the r140 milestone Mar 29, 2022
@gkjohnson
Copy link
Collaborator

gkjohnson commented Mar 29, 2022

It might also be worth throwing a more human-readable error if there are no normals / uvs present on the geometry since they're required for generating tangents.

@marcofugaro
Copy link
Contributor

@mrdoob we might want to publish a patch with this.. Some bundlers are affected even if not importing directly from BufferGeometryUtils.

image

@mrdoob
Copy link
Owner

mrdoob commented Mar 31, 2022

Thanks!

@mrdoob
Copy link
Owner

mrdoob commented Mar 31, 2022

@marcofugaro Done. Cherry-picked this PR into master and published 0.139.1.

@donmccurdy donmccurdy deleted the hotfix/mikktspace-tangents-top-level-await branch March 31, 2022 02:08
@donmccurdy
Copy link
Collaborator Author

donmccurdy commented Mar 31, 2022

I find it confusing to have two methods called .computeTangents(). Maybe .computeMikkTSpaceTangents()?

It might also be worth throwing a more human-readable error if there are no normals / uvs present on the geometry since they're required for generating tangents.

Made both changes in #23815, for r140.

@Methuselah96
Copy link
Contributor

Methuselah96 commented Mar 31, 2022

@mrdoob I'm not sure that this PR was successfully included in 0.139.1. When I look at the code in the published package it appears to match the code before this PR and my build is still failing due to the top-level await issue. Maybe someone else can verify as well?

@gkjohnson
Copy link
Collaborator

I'm not sure that this PR was successfully included in 0.139.1.

Looks like the old version, still: https://unpkg.com/browse/three@0.139.1/examples/jsm/utils/BufferGeometryUtils.js

@mrdoob
Copy link
Owner

mrdoob commented Mar 31, 2022

Eek... I cherry-picked the wrong PR...

mrdoob pushed a commit that referenced this pull request Mar 31, 2022
- Avoid top-level await.
- Fix error on non-indexed geometry.
@mrdoob
Copy link
Owner

mrdoob commented Mar 31, 2022

Alright, 0.139.2 is out 😅

@Mugen87 Mugen87 modified the milestones: r140, r139 Apr 1, 2022
mrdoob referenced this pull request Apr 1, 2022
abernier pushed a commit to abernier/three.js that referenced this pull request Sep 16, 2022
- Avoid top-level await.
- Fix error on non-indexed geometry.
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.

Build error with 0.139.0 BufferGeometryUtils.computeTangents( geometry ) fails with non-indexed geometry
7 participants