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

Implement TextMesh. #60507

Merged
merged 1 commit into from
May 16, 2022
Merged

Implement TextMesh. #60507

merged 1 commit into from
May 16, 2022

Conversation

bruvzg
Copy link
Member

@bruvzg bruvzg commented Apr 25, 2022

Follow up to the #60386

Implement TextMesh resource.

Implements mesh generation from the shaped text contours. Works only with the OpenType compliant dynamic fonts (without contour self-intersections, so some fonts converted by services like Google Font might fail). Will not work with bitmap fonts (regardless of font container format), e.g., color emoji fonts.

TODO:

  • Optimize generated mesh.
  • Generate a reasonable texture coordinates for the mesh.
  • Implement fallbacks for missing and faulty glyphs.

Screenshot 2022-04-26 at 13 43 52

Screenshot 2022-04-26 at 13 43 57

Screenshot 2022-04-26 at 23 25 06

Test project:
test_project.zip

@fire
Copy link
Member

fire commented Apr 25, 2022

Can you send it through the lod system?

Aka. The Meshoptimizer system? I can look into this later.

It's not so good for the mesh to be recreated every frame, but it's good for the first implementation.

If you can't generate the uvs manually, you could use xatlas.

@bruvzg bruvzg force-pushed the textmesh branch 2 times, most recently from 2936982 to d3a152a Compare April 25, 2022 14:48
@Calinou
Copy link
Member

Calinou commented Apr 25, 2022

I gave it a try 🙂

image

Testing project: test_textmesh.zip

Some comments:

  • Text is left-aligned by default, as alignment options aren't implemented yet.
  • Spaces aren't rendered yet – the text will look like as if there are no spaces.
  • The mesh fails to generate with an error message if you edit the text property to make it an empty string again. Due to this, the last character that was present in the text property will also remain visible.
  • Generating the mesh can take a lot of time, which can cause stuttering if the text changes during gameplay. The longer the text, the more noticeable this is. Increasing Curve Step in the TextMesh resource can improve mesh generation performance a lot (at the cost of worse curve quality).
    • I'm not sure if we can fully resolve this issue (given that uploading new meshes to the GPU will always take some time). Caching individual glyph triangles for each font might help a little.
    • For people looking to create 3D timers in the game world, we can probably document a way to do this with several MeshInstance3D nodes. This can be done by creating 10 MeshInstance3D nodes per digit (with all numbers in advance), and toggling the nodes' visibility at run-time. This should avoid performance caveats related to run-time mesh generation.
  • As an optimization, generating triangles on the side of the text can be skipped if depth is nearly equal to 0.
  • Property hints for curve_step and depth should be added.
    • Setting curve_step to 0 causes the engine to freeze. Values below 0.1 take a very long time to generate the mesh (even with only a few characters), so I'd limit the value between 0.2 and 10.
    • Negative depth values don't seem to behave correctly, so I'd prevent using them.

We should also look to compile some use cases for TextMesh (and where Label3D isn't a good fit). Label3D is a powerful node already, and we should be careful not to have feature creep in this aspect of the engine.

@bruvzg bruvzg force-pushed the textmesh branch 5 times, most recently from 19b3014 to d8a8734 Compare April 26, 2022 20:26
@anderlli0053
Copy link

Can this be ported to 3.x?

@bruvzg bruvzg force-pushed the textmesh branch 2 times, most recently from 56baf7f to abeef06 Compare April 27, 2022 06:50
@bruvzg
Copy link
Member Author

bruvzg commented Apr 27, 2022

  • Added glyph data caching (local only), it should be much faster (the slowest part is convex decomposition). Now mesh generation in the TextMesh should account only 10% of the mesh update time (Another 10% is taken by base Mesh update, and the rest by the RenderingServer).
  • Added alignment property.
  • Empty string and spaces should be fixed, and missing glyph replaced with quads.
  • Added property tags for curve step and depth.
  • Removed side and back faces generation when depth is zero.

Aka. The Meshoptimizer system? I can look into this later.

Not sure if it's necessary, with the reasonable curve step it should be good as is.

If you can't generate the uvs manually, you could use xatlas.

It's probably a bit of overkill to use full unwrap, something simple and applicable to any text without changing the texture probably is a more useful as a default and should cover most cases. So currently it's using a map like this to have a possibility to texture front back and sides of text independently (front and back and for the full text, and sides to loop each contour):

Screenshot 2022-04-26 at 23 25 06

But I guess adding a separate option to do unwrap on any mesh would be useful.

@bruvzg
Copy link
Member Author

bruvzg commented Apr 27, 2022

Can this be ported to 3.x?

Probably not, at least not without backporting some other stuff to the 3.x, and it won't have any of the TextServer features available.

@bruvzg bruvzg changed the title [WIP] Implement TextMesh. Implement TextMesh. Apr 27, 2022
@bruvzg bruvzg marked this pull request as ready for review April 27, 2022 07:20
@bruvzg bruvzg requested review from a team as code owners April 27, 2022 07:20
@Calinou
Copy link
Member

Calinou commented Apr 27, 2022

I tested the latest revision of this PR and can confirm the timer in the testing project now performs much better (even when it shows 4 decimals). The rotating text animation remains smooth and I still get 300+ FPS.

In practice, I'd probably limit the timer in the example to 2 decimals, but this should now be suitable for real-time use.

@golddotasksquestions
Copy link

golddotasksquestions commented Apr 29, 2022

You are amazing!

I just spend at least week on painstakingly trying to get dynamic 3D text mesh (mesh created in Blender letter by letter) in one of my projects in Godot 3 to write 4 words. It's super tedious and limited process and I can't imagine doing this ever again and was about to write a feature request proposal of exactly this!

Question: Will I be able to generate CollisionShape Siblings from this like I can with MeshInstances (or alternatively make this TextMesh the Mesh of a MeshInstance so I can generate the CollisionShape Siblings)?

@bruvzg
Copy link
Member Author

bruvzg commented Apr 29, 2022

Question: Will I be able to generate CollisionShape Siblings from this like I can with MeshInstances (or alternatively make this TextMesh the Mesh of a MeshInstance so I can generate the CollisionShape Siblings)?

I have not tested collisions, but it's a derivative of generic PrimitiveMesh not a fully separate Node, so it can be used as mesh of the MeshInstance3D (that's what happening in the test project), and should support everything any other mesh do.

Copy link
Member

@akien-mga akien-mga left a comment

Choose a reason for hiding this comment

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

The feature looks great, and the code seems fine from a quick look. Might warrant a rebase to make sure it still builds after the HashMap rewrite.

@timothyqiu
Copy link
Member

The "Pixel Size" property somehow updates its character spacing in the editor preview.

It works fine if I reopen the scene.

ksnip_20220513-103547

ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_font", "get_font");
ADD_PROPERTY(PropertyInfo(Variant::INT, "font_size", PROPERTY_HINT_RANGE, "1,127,1"), "set_font_size", "get_font_size");
ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment");
Copy link
Member

Choose a reason for hiding this comment

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

Since the documentation says only left, center, and right are supported, maybe we should remove the "Fill" option here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed pixel_size not cleaning cache, added width for Fill alignment and updated docs for this and Label3D (both support Fill alignment).

Apply simulated slant and embolden to the TextServer `gont_get_glyph_contours` results.
@akien-mga akien-mga merged commit b154f44 into godotengine:master May 16, 2022
@akien-mga
Copy link
Member

Thanks!

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

Successfully merging this pull request may close these issues.

7 participants