SpriteMesh
SpriteMesh copied to clipboard
Performance considerations of `SpriteMesh`
Hi, I've recently noticed your work and it looks great, if you feel like GDScript is slow for procedural stuff with SpriteMesh
perhaps some performance critical stuff could be implemented in https://github.com/goostengine/goost.
I don't necessarily say that you need to implement SpriteMesh
in Goost (although voxel-based games are quite popular nowadays, the way I see it, if you feel like SpriteMesh
could be a nice addition to Goost, feel free to contribute), but there are certain techniques which could be integrated into your plugin without requiring users to depend on Goost features at all, something like:
if ClassDB.class_exists("GoostGeometry3D"):
Engine.get_singleton("GoostGeometry3D").do_stuff()
else:
# GDScript code.
What I'm proposing is like optional performance boost. I know you've contributed to ImageTools/Goost before, therefore I presume this could be useful for you (and others!).
This is more like a question/discussion to see what kind of bottlenecks you stumble upon exactly, and whether these underlying algorithms could be seen useful for more than what your plugin accomplishes.
Hi! I'm glad you liked it! I would start talking about the bottlenecks. Right now, if you use the editor, only the developer would execute the algorithm. So it would not affect the performance of the final game. The only case where performance may be an issue is if the game generates many meshes procedurally. In this case, implementing the algorithm in C++ would certainly be noticeable.
Implementing the algorithm in Goost is quite challenging because it takes many parameters. However, many of these properties' only purpose is to transform the mesh's vertices.
So what I would do first is to implement many methods to transform meshes, something like:
GoostGeometry3D.scale(mesh, scale) -> mesh
So there would be a method for position, rotation, scale, and transformation. Then, the function for SpriteMesh could receive a lot fewer parameters:
GoostGeometry3D.sprite_mesh(texture, double_sided, alpha_threshold, uv_correction) -> mesh
I'm not really familiar with 3D transformations much, but note that you can transform a bunch of PoolVector3Array
with Transform.xform()
method, so perhaps you could already speed this up even via GDScript if we're speaking about things like transforming vertices.
Speaking of:
GoostGeometry3D.sprite_mesh(texture, double_sided, alpha_threshold, uv_correction) -> mesh
I'm afraid this kind of API would be too specific, I think geometry's singleton scope should mostly deal with built-in data structures, not objects. I'd rather break this down into functions that have single responsibility. But again, as I said, perhaps all you really need is to use Transform.xform()
!
However, Godot's editor has some methods to convert Sprite
to MeshInstance2D
similarly. I don't see anything like that exposed in Godot either. So in theory both functionalities could be exposed/implemented, and it would no longer be that much specific I guess.
You can use Godot's profiler to first identify which parts take considerable amount of resources when doing this via code. It may just happen that there's not much which could be done to achieve considerable performance improvements... By "considerable" I mean performance gains that improve current speed by ~30-50%.
I have been looking a bit into Transform.xform()
and, as you said, it can be used to apply the transformations I was talking about. I could generate the mesh using ArrayMesh
instead of SurfaceTool
. This would improve performance slightly as the documentation says.
However, I think the major performance bust could be achieved by implementing the algorithm itself because it needs to iterate through the image multiple times. Right now, the transformations I was talking about are implemented in each step of the algorithm, so removing them would not reduce the number of iterations, just reduce the time per step. The algorithm has to generate both the vertices and the UV mapping at the same time, so I don't really know how to implement the API, as you already said than returning a mesh would be too specific. Maybe a method that returns a PoolVector3Array
with the vertices and another that returns a PoolVector2Array
with the UV mapping?
Lastly, I would not be able to do any benchmark for a while to check any of these ideas as I would not be at home, but I'm fine just discussing them!
You've mentioned SurfaceTool
which is used to procedurally generate meshes. I think the best approach to solve this would be to create MeshTool
class that will be responsible for generating all kinds of mesh geometry, including sprite mesh:
MeshTool.create_from_texture(texture, double_sided, alpha_threshold, uv_correction) -> Mesh
But at the same time, it must be useful for generating 2D meshes from texture... (as done with Godot's Sprite
→ MeshInstance2D
conversion tool, see source code https://github.com/godotengine/godot/blob/140350d/editor/plugins/sprite_editor_plugin.cpp#L159-L297). Perhaps it would be necessary to make proper separation of 2D/3D here, such as MeshTool2D
and MeshTool3D
.
I've even noticed some other modules like https://github.com/EternalColor/Godot-Planet-Generator-Module, so doing it this way could make the class more useful for other stuff in the future. But instead MeshTool
would generate meshes rather than actual mesh instances.
That seems promising! Then the implementation could look something like this:
- Create the basic mesh using
MeshTool.create_from_texture
. - Obtain the
PoolVector3Array
of vertices using the methodsurface_get_arrays
fromMesh
. - Use the
Transform.xform
method to apply the necessary transformations to thePoolVector3Array
of vertices. - Use
ArrayMesh
to generate the transformed mesh.
I would need to consider how to handle flipping the mesh, as it can be challenging. As to whether or not to create separate classes to generate 3D or 2D meshes, I would name it MeshTool3D
, even if there is no 2D counterpart at the moment, to avoid compatibilities issues in the future.
Sounds good to me! Yes, creating MeshTool3D
should be fine, also consistency with potential MeshTool2D
API shouldn't matter much I guess.