mirror of https://github.com/godotengine/godot
Reuse Sprite3D meshes across nodes when possible.
This commit is contained in:
parent
53faed5351
commit
8e33549969
|
|
@ -32,6 +32,8 @@
|
|||
|
||||
#include "scene/resources/atlas_texture.h"
|
||||
|
||||
HashMap<SpriteMeshKey, SpriteBase3D *, SpriteMeshHasher> SpriteBase3D::shared_sprites;
|
||||
|
||||
Color SpriteBase3D::_get_color_accum() {
|
||||
if (!color_dirty) {
|
||||
return color_accum;
|
||||
|
|
@ -221,6 +223,68 @@ void SpriteBase3D::draw_texture_rect(Ref<Texture2D> p_texture, Rect2 p_dst_rect,
|
|||
uint8_t(CLAMP(color.a * 255.0, 0.0, 255.0))
|
||||
};
|
||||
|
||||
SpriteMeshKey sprite_mesh_key;
|
||||
memcpy(&sprite_mesh_key.vertices, vertices, sizeof(Vector2) * 4);
|
||||
memcpy(&sprite_mesh_key.uvs, uvs, sizeof(Vector2) * 4);
|
||||
sprite_mesh_key.v_color = *(uint32_t *)v_color;
|
||||
sprite_mesh_key.v_normal = v_normal;
|
||||
sprite_mesh_key.alpha_cut_disabled = get_alpha_cut_mode() == ALPHA_CUT_DISABLED;
|
||||
if (!using_sprite) {
|
||||
// Not using another sprite -> this sprite owns its current mesh.
|
||||
if (last_sprite_mesh_key != sprite_mesh_key) {
|
||||
// Sprite mesh data changed.
|
||||
shared_sprites.erase(last_sprite_mesh_key);
|
||||
if (!users.is_empty()) {
|
||||
// Select a successor and make every other user use that successor's mesh instead.
|
||||
SpriteBase3D *successor = nullptr;
|
||||
int i = 0;
|
||||
for (; i < users.size(); i++) {
|
||||
// There may be sprites that have changed the sprite they're using earlier this frame, so we have to filter them out.
|
||||
if (users[i]->using_sprite == this) {
|
||||
successor = users[i];
|
||||
shared_sprites.insert(last_sprite_mesh_key, successor);
|
||||
// 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.
|
||||
// memcpy is used directly because buffer sizes are guaranteed to be identical across all SpriteBase3Ds.
|
||||
memcpy(successor->vertex_buffer.ptrw(), vertex_buffer.ptr(), vertex_buffer.size());
|
||||
memcpy(successor->attribute_buffer.ptrw(), attribute_buffer.ptr(), attribute_buffer.size());
|
||||
RS::get_singleton()->mesh_surface_update_vertex_region(successor->mesh, 0, 0, successor->vertex_buffer);
|
||||
RS::get_singleton()->mesh_surface_update_attribute_region(successor->mesh, 0, 0, successor->attribute_buffer);
|
||||
RS::get_singleton()->mesh_set_custom_aabb(successor->mesh, aabb);
|
||||
if (last_sprite_mesh_key.alpha_cut_disabled) {
|
||||
RS::get_singleton()->material_set_render_priority(get_material(), get_render_priority());
|
||||
RS::get_singleton()->mesh_surface_set_material(successor->mesh, 0, get_material());
|
||||
}
|
||||
successor->using_sprite = nullptr;
|
||||
successor->set_base(successor->mesh);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Propagate the change to remaining users that haven't changed their using_sprite.
|
||||
// Note: This works even if the same user is registered twice. Very rare but can still happen.
|
||||
for (; i < users.size(); i++) {
|
||||
if (users[i]->using_sprite == this) {
|
||||
users[i]->set_base(successor->mesh);
|
||||
users[i]->using_sprite = successor;
|
||||
}
|
||||
}
|
||||
// Now every user has moved on, so we can clear the users list.
|
||||
users.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Try to see if there's any sprite whose mesh can be used instead.
|
||||
SpriteBase3D **sprite_ptr = shared_sprites.getptr(sprite_mesh_key);
|
||||
last_sprite_mesh_key = sprite_mesh_key;
|
||||
if (sprite_ptr && *sprite_ptr != using_sprite) {
|
||||
// Found sprite that can be reused.
|
||||
using_sprite = *sprite_ptr;
|
||||
set_base(using_sprite->mesh);
|
||||
using_sprite->users.push_back(this);
|
||||
// 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.
|
||||
return;
|
||||
}
|
||||
// Otherwise, setup mesh data and register this sprite's mesh for sharing.
|
||||
shared_sprites.insert(sprite_mesh_key, this);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Vector3 vtx;
|
||||
vtx[x_axis] = vertices[i][0];
|
||||
|
|
@ -243,7 +307,8 @@ void SpriteBase3D::draw_texture_rect(Ref<Texture2D> p_texture, Rect2 p_dst_rect,
|
|||
memcpy(&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_COLOR]], v_color, 4);
|
||||
}
|
||||
|
||||
RID mesh_new = get_mesh();
|
||||
RID mesh_new = mesh;
|
||||
set_base(mesh_new);
|
||||
RS::get_singleton()->mesh_surface_update_vertex_region(mesh_new, 0, 0, vertex_buffer);
|
||||
RS::get_singleton()->mesh_surface_update_attribute_region(mesh_new, 0, 0, attribute_buffer);
|
||||
|
||||
|
|
@ -753,6 +818,10 @@ SpriteBase3D::~SpriteBase3D() {
|
|||
ERR_FAIL_NULL(RenderingServer::get_singleton());
|
||||
RenderingServer::get_singleton()->free(mesh);
|
||||
RenderingServer::get_singleton()->free(material);
|
||||
|
||||
if (!using_sprite) {
|
||||
shared_sprites.erase(last_sprite_mesh_key);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -34,6 +34,26 @@
|
|||
#include "scene/3d/visual_instance_3d.h"
|
||||
#include "scene/resources/sprite_frames.h"
|
||||
|
||||
struct SpriteMeshKey {
|
||||
Vector2 vertices[4];
|
||||
Vector2 uvs[4];
|
||||
uint32_t v_color = 0;
|
||||
uint32_t v_normal = 0;
|
||||
bool alpha_cut_disabled = false;
|
||||
bool operator==(const SpriteMeshKey &other) const {
|
||||
return memcmp(this, &other, sizeof(SpriteMeshKey)) == 0;
|
||||
}
|
||||
bool operator!=(const SpriteMeshKey &other) const {
|
||||
return memcmp(this, &other, sizeof(SpriteMeshKey)) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct SpriteMeshHasher {
|
||||
static _FORCE_INLINE_ uint32_t hash(const SpriteMeshKey &p_mesh_key) {
|
||||
return hash_murmur3_buffer(&p_mesh_key, sizeof(SpriteMeshKey));
|
||||
}
|
||||
};
|
||||
|
||||
class SpriteBase3D : public GeometryInstance3D {
|
||||
GDCLASS(SpriteBase3D, GeometryInstance3D);
|
||||
|
||||
|
|
@ -59,6 +79,11 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
static HashMap<SpriteMeshKey, SpriteBase3D *, SpriteMeshHasher> shared_sprites;
|
||||
Vector<SpriteBase3D *> users;
|
||||
SpriteMeshKey last_sprite_mesh_key;
|
||||
SpriteBase3D *using_sprite = nullptr;
|
||||
|
||||
bool color_dirty = true;
|
||||
Color color_accum;
|
||||
|
||||
|
|
@ -105,7 +130,7 @@ protected:
|
|||
virtual void _draw() = 0;
|
||||
void draw_texture_rect(Ref<Texture2D> p_texture, Rect2 p_dst_rect, Rect2 p_src_rect);
|
||||
_FORCE_INLINE_ void set_aabb(const AABB &p_aabb) { aabb = p_aabb; }
|
||||
_FORCE_INLINE_ RID &get_mesh() { return mesh; }
|
||||
_FORCE_INLINE_ RID &get_mesh() { return using_sprite ? using_sprite->mesh : mesh; }
|
||||
_FORCE_INLINE_ RID &get_material() { return material; }
|
||||
|
||||
uint32_t mesh_surface_offsets[RS::ARRAY_MAX];
|
||||
|
|
|
|||
Loading…
Reference in New Issue