3232
3333#include " scene/resources/atlas_texture.h"
3434
35+ HashMap<SpriteMeshKey, SpriteBase3D *, SpriteMeshHasher> SpriteBase3D::shared_sprites;
36+
3537Color SpriteBase3D::_get_color_accum () {
3638 if (!color_dirty) {
3739 return color_accum;
@@ -221,6 +223,73 @@ void SpriteBase3D::draw_texture_rect(Ref<Texture2D> p_texture, Rect2 p_dst_rect,
221223 uint8_t (CLAMP (color.a * 255.0 , 0.0 , 255.0 ))
222224 };
223225
226+ SpriteMeshKey sprite_mesh_key;
227+ memcpy (&sprite_mesh_key.vertices , vertices, sizeof (Vector2) * 4 );
228+ memcpy (&sprite_mesh_key.uvs , uvs, sizeof (Vector2) * 4 );
229+ sprite_mesh_key.v_color = *(uint32_t *)v_color;
230+ sprite_mesh_key.v_normal = v_normal;
231+ sprite_mesh_key.alpha_cut_disabled = get_alpha_cut_mode () == ALPHA_CUT_DISABLED;
232+ if (!using_sprite) {
233+ // Not using another sprite -> this sprite owns its current mesh.
234+ if (last_sprite_mesh_key != sprite_mesh_key) {
235+ // Sprite mesh data changed.
236+ shared_sprites.erase (last_sprite_mesh_key);
237+ if (!users.is_empty ()) {
238+ // Select a successor and make every other user use that successor's mesh instead.
239+ SpriteBase3D *successor = nullptr ;
240+ int i = 0 ;
241+ for (; i < users.size (); i++) {
242+ // There may be sprites that have changed the sprite they're using earlier this frame, so we have to filter them out.
243+ if (users[i]->using_sprite == this ) {
244+ successor = users[i];
245+ shared_sprites.insert (last_sprite_mesh_key, successor);
246+ // Copy mesh data to successor. Need to store data in vertex and attribute buffer and not just update the RenderingServer mesh directly so they can be copied again later.
247+ // memcpy is used directly because buffer sizes are guaranteed to be identical across all SpriteBase3Ds.
248+ memcpy (successor->vertex_buffer .ptrw (), vertex_buffer.ptr (), vertex_buffer.size ());
249+ memcpy (successor->attribute_buffer .ptrw (), attribute_buffer.ptr (), attribute_buffer.size ());
250+ RS::get_singleton ()->mesh_surface_update_vertex_region (successor->mesh , 0 , 0 , successor->vertex_buffer );
251+ RS::get_singleton ()->mesh_surface_update_attribute_region (successor->mesh , 0 , 0 , successor->attribute_buffer );
252+ RS::get_singleton ()->mesh_set_custom_aabb (successor->mesh , aabb);
253+ if (last_sprite_mesh_key.alpha_cut_disabled ) {
254+ RS::get_singleton ()->material_set_render_priority (get_material (), get_render_priority ());
255+ RS::get_singleton ()->mesh_surface_set_material (successor->mesh , 0 , get_material ());
256+ }
257+ successor->using_sprite = nullptr ;
258+ successor->set_base (successor->mesh );
259+ break ;
260+ }
261+ }
262+ // Propagate the change to remaining users that haven't changed their using_sprite.
263+ // Note: This works even if the same user is registered twice. Very rare but can still happen.
264+ for (; i < users.size (); i++) {
265+ if (users[i]->using_sprite == this ) {
266+ users[i]->set_base (successor->mesh );
267+ users[i]->using_sprite = successor;
268+ }
269+ }
270+ // Now every user has moved on, we can clear the users list.
271+ users.clear ();
272+ }
273+ }
274+ }
275+ // Try to see if there's any sprite whose mesh can be used instead.
276+ SpriteBase3D **sprite_ptr = shared_sprites.getptr (sprite_mesh_key);
277+ last_sprite_mesh_key = sprite_mesh_key;
278+ if (sprite_ptr) {
279+ if (*sprite_ptr != using_sprite) {
280+ // Found new sprite that can be reused.
281+ using_sprite = *sprite_ptr;
282+ set_base (using_sprite->mesh );
283+ using_sprite->users .push_back (this );
284+ // We don't need to remove this sprite from the previous shared sprite's users list, as they will be detected and filtered out later.
285+ return ;
286+ } else {
287+ // Keep using same sprite.
288+ return ;
289+ }
290+ }
291+ // Otherwise, setup mesh data and register this sprite's mesh for sharing.
292+ shared_sprites.insert (sprite_mesh_key, this );
224293 for (int i = 0 ; i < 4 ; i++) {
225294 Vector3 vtx;
226295 vtx[x_axis] = vertices[i][0 ];
@@ -243,7 +312,8 @@ void SpriteBase3D::draw_texture_rect(Ref<Texture2D> p_texture, Rect2 p_dst_rect,
243312 memcpy (&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_COLOR]], v_color, 4 );
244313 }
245314
246- RID mesh_new = get_mesh ();
315+ RID mesh_new = mesh;
316+ set_base (mesh_new);
247317 RS::get_singleton ()->mesh_surface_update_vertex_region (mesh_new, 0 , 0 , vertex_buffer);
248318 RS::get_singleton ()->mesh_surface_update_attribute_region (mesh_new, 0 , 0 , attribute_buffer);
249319
@@ -753,6 +823,10 @@ SpriteBase3D::~SpriteBase3D() {
753823 ERR_FAIL_NULL (RenderingServer::get_singleton ());
754824 RenderingServer::get_singleton ()->free (mesh);
755825 RenderingServer::get_singleton ()->free (material);
826+
827+ if (!using_sprite) {
828+ shared_sprites.erase (last_sprite_mesh_key);
829+ }
756830}
757831
758832// /////////////////////////////////////////
0 commit comments