Implement 2D Meshes and MultiMeshes in GLES3 backend

This commit is contained in:
clayjohn 2022-05-25 13:19:45 -07:00
parent 3e20c1347d
commit fb860265e0
7 changed files with 282 additions and 236 deletions

View file

@ -445,6 +445,9 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
}
void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights, uint32_t &r_index) {
// Used by Polygon and Mesh.
static const GLenum prim[5] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP };
RS::CanvasItemTextureFilter current_filter = state.default_filter;
RS::CanvasItemTextureRepeat current_repeat = state.default_repeat;
@ -474,7 +477,10 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
continue;
}
_update_transform_2d_to_mat2x3(base_transform * draw_transform, state.instance_data_array[r_index].world);
if (c->type != Item::Command::TYPE_MESH) {
// For Meshes, this gets updated below.
_update_transform_2d_to_mat2x3(base_transform * draw_transform, state.instance_data_array[r_index].world);
}
for (int i = 0; i < 4; i++) {
state.instance_data_array[r_index].modulation[i] = 0.0;
@ -680,30 +686,9 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
state.instance_data_array[r_index].ninepatch_margins[j] = 0;
}
// If the previous operation is not done yet, allocate a new buffer
if (state.fences[state.current_buffer] != GLsync()) {
GLint syncStatus;
glGetSynciv(state.fences[state.current_buffer], GL_SYNC_STATUS, sizeof(GLint), nullptr, &syncStatus);
if (syncStatus == GL_UNSIGNALED) {
_allocate_instance_data_buffer();
} else {
glDeleteSync(state.fences[state.current_buffer]);
}
}
glBindBufferBase(GL_UNIFORM_BUFFER, INSTANCE_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer]);
#ifdef JAVASCRIPT_ENABLED
//WebGL 2.0 does not support mapping buffers, so use slow glBufferData instead
glBufferData(GL_UNIFORM_BUFFER, sizeof(InstanceData), &state.instance_data_array[0], GL_DYNAMIC_DRAW);
#else
void *ubo = glMapBufferRange(GL_UNIFORM_BUFFER, 0, sizeof(InstanceData), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
memcpy(ubo, &state.instance_data_array[0], sizeof(InstanceData));
glUnmapBuffer(GL_UNIFORM_BUFFER);
#endif
_bind_instance_data_buffer(1);
glBindVertexArray(pb->vertex_array);
static const GLenum prim[5] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP };
if (pb->index_buffer != 0) {
glDrawElements(prim[polygon->primitive], pb->count, GL_UNSIGNED_INT, nullptr);
} else {
@ -764,12 +749,18 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
case Item::Command::TYPE_MESH:
case Item::Command::TYPE_MULTIMESH:
case Item::Command::TYPE_PARTICLES: {
/*
GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton();
RID mesh;
RID mesh_instance;
RID texture;
Color modulate(1, 1, 1, 1);
int instance_count = 1;
uint32_t instance_count = 1;
GLuint multimesh_buffer = 0;
uint32_t multimesh_stride = 0;
uint32_t multimesh_color_offset = 0;
uint32_t multimesh_custom_data_offset = 0;
bool multimesh_uses_color = false;
bool multimesh_uses_custom_data = false;
if (c->type == Item::Command::TYPE_MESH) {
const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(c);
@ -781,26 +772,25 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
} else if (c->type == Item::Command::TYPE_MULTIMESH) {
const Item::CommandMultiMesh *mm = static_cast<const Item::CommandMultiMesh *>(c);
RID multimesh = mm->multimesh;
mesh = storage->multimesh_get_mesh(multimesh);
mesh = mesh_storage->multimesh_get_mesh(multimesh);
texture = mm->texture;
if (storage->multimesh_get_transform_format(multimesh) != RS::MULTIMESH_TRANSFORM_2D) {
if (mesh_storage->multimesh_get_transform_format(multimesh) != RS::MULTIMESH_TRANSFORM_2D) {
break;
}
instance_count = storage->multimesh_get_instances_to_draw(multimesh);
instance_count = mesh_storage->multimesh_get_instances_to_draw(multimesh);
if (instance_count == 0) {
break;
}
state.instance_data_array[r_index].flags |= 1; //multimesh, trails disabled
if (storage->multimesh_uses_colors(multimesh)) {
state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_COLORS;
}
if (storage->multimesh_uses_custom_data(multimesh)) {
state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_CUSTOM_DATA;
}
multimesh_buffer = mesh_storage->multimesh_get_gl_buffer(multimesh);
multimesh_stride = mesh_storage->multimesh_get_stride(multimesh);
multimesh_color_offset = mesh_storage->multimesh_get_color_offset(multimesh);
multimesh_custom_data_offset = mesh_storage->multimesh_get_custom_data_offset(multimesh);
multimesh_uses_color = mesh_storage->multimesh_uses_colors(multimesh);
multimesh_uses_custom_data = mesh_storage->multimesh_uses_custom_data(multimesh);
}
// TODO: implement particles here
@ -816,8 +806,15 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
}
_bind_canvas_texture(texture, current_filter, current_repeat, r_index);
if (instance_count == 1) {
GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_ATTRIBUTES);
} else if (instance_count > 1) {
GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_INSTANCED);
} else {
ERR_PRINT("Must have at least one mesh instance to draw mesh");
}
uint32_t surf_count = storage->mesh_get_surface_count(mesh);
uint32_t surf_count = mesh_storage->mesh_get_surface_count(mesh);
state.instance_data_array[r_index].modulation[0] = base_color.r * modulate.r;
state.instance_data_array[r_index].modulation[1] = base_color.g * modulate.g;
@ -829,19 +826,79 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
state.instance_data_array[r_index].dst_rect[j] = 0;
state.instance_data_array[r_index].ninepatch_margins[j] = 0;
}
_bind_instance_data_buffer(1);
for (uint32_t j = 0; j < surf_count; j++) {
RS::SurfaceData *surface = storage->mesh_get_surface(mesh, j);
void *surface = mesh_storage->mesh_get_surface(mesh, j);
RS::PrimitiveType primitive = storage->mesh_surface_get_primitive(surface);
RS::PrimitiveType primitive = mesh_storage->mesh_surface_get_primitive(surface);
ERR_CONTINUE(primitive < 0 || primitive >= RS::PRIMITIVE_MAX);
glBindVertexArray(surface->vertex_array);
static const GLenum prim[5] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP };
GLuint vertex_array_gl = 0;
GLuint index_array_gl = 0;
// Draw directly, no need to batch
uint32_t input_mask = 0; // 2D meshes always use the same vertex format
if (mesh_instance.is_valid()) {
mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(mesh_instance, j, input_mask, vertex_array_gl);
} else {
mesh_storage->mesh_surface_get_vertex_arrays_and_format(surface, input_mask, vertex_array_gl);
}
index_array_gl = mesh_storage->mesh_surface_get_index_buffer(surface, 0);
bool use_index_buffer = false;
glBindVertexArray(vertex_array_gl);
if (index_array_gl != 0) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_array_gl);
use_index_buffer = true;
}
if (instance_count > 1) {
// Bind instance buffers.
glBindBuffer(GL_ARRAY_BUFFER, multimesh_buffer);
glEnableVertexAttribArray(5);
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(0));
glVertexAttribDivisor(5, 1);
glEnableVertexAttribArray(6);
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(4 * 4));
glVertexAttribDivisor(6, 1);
if (multimesh_uses_color) {
glEnableVertexAttribArray(7);
glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(multimesh_color_offset * sizeof(float)));
glVertexAttribDivisor(7, 1);
}
if (multimesh_uses_custom_data) {
glEnableVertexAttribArray(8);
glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(multimesh_custom_data_offset * sizeof(float)));
glVertexAttribDivisor(8, 1);
}
}
GLenum primitive_gl = prim[int(primitive)];
if (instance_count == 1) {
if (use_index_buffer) {
glDrawElements(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), mesh_storage->mesh_surface_get_index_type(surface), 0);
} else {
glDrawArrays(primitive_gl, 0, mesh_storage->mesh_surface_get_vertices_drawn_count(surface));
}
} else if (instance_count > 1) {
if (use_index_buffer) {
glDrawElementsInstanced(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), mesh_storage->mesh_surface_get_index_type(surface), 0, instance_count);
} else {
glDrawArraysInstanced(primitive_gl, 0, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), instance_count);
}
}
state.fences[state.current_buffer] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
state.current_buffer = (state.current_buffer + 1) % state.canvas_instance_data_buffers.size();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
if (instance_count > 1) {
glDisableVertexAttribArray(5);
glDisableVertexAttribArray(6);
glDisableVertexAttribArray(7);
glDisableVertexAttribArray(8);
}
}
*/
} break;
case Item::Command::TYPE_TRANSFORM: {
const Item::CommandTransform *transform = static_cast<const Item::CommandTransform *>(c);
@ -886,26 +943,7 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
void RasterizerCanvasGLES3::_render_batch(uint32_t &r_index) {
if (r_index > 0) {
// If the previous operation is not done yet, allocate a new buffer
if (state.fences[state.current_buffer] != GLsync()) {
GLint syncStatus;
glGetSynciv(state.fences[state.current_buffer], GL_SYNC_STATUS, sizeof(GLint), nullptr, &syncStatus);
if (syncStatus == GL_UNSIGNALED) {
_allocate_instance_data_buffer();
} else {
glDeleteSync(state.fences[state.current_buffer]);
}
}
glBindBufferBase(GL_UNIFORM_BUFFER, INSTANCE_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer]);
#ifdef JAVASCRIPT_ENABLED
//WebGL 2.0 does not support mapping buffers, so use slow glBufferData instead
glBufferData(GL_UNIFORM_BUFFER, sizeof(InstanceData) * r_index, state.instance_data_array, GL_DYNAMIC_DRAW);
#else
void *ubo = glMapBufferRange(GL_UNIFORM_BUFFER, 0, sizeof(InstanceData) * r_index, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
memcpy(ubo, state.instance_data_array, sizeof(InstanceData) * r_index);
glUnmapBuffer(GL_UNIFORM_BUFFER);
#endif
_bind_instance_data_buffer(r_index);
glBindVertexArray(data.canvas_quad_array);
if (state.current_primitive_points == 0) {
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, r_index);
@ -939,6 +977,32 @@ void RasterizerCanvasGLES3::_render_batch(uint32_t &r_index) {
}
}
void RasterizerCanvasGLES3::_bind_instance_data_buffer(uint32_t p_max_index) {
if (p_max_index == 0) {
return;
}
// If the previous operation is not done yet, allocate a new buffer
if (state.fences[state.current_buffer] != GLsync()) {
GLint syncStatus;
glGetSynciv(state.fences[state.current_buffer], GL_SYNC_STATUS, sizeof(GLint), nullptr, &syncStatus);
if (syncStatus == GL_UNSIGNALED) {
_allocate_instance_data_buffer();
} else {
glDeleteSync(state.fences[state.current_buffer]);
}
}
glBindBufferBase(GL_UNIFORM_BUFFER, INSTANCE_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer]);
#ifdef JAVASCRIPT_ENABLED
//WebGL 2.0 does not support mapping buffers, so use slow glBufferData instead
glBufferData(GL_UNIFORM_BUFFER, sizeof(InstanceData) * p_max_index, state.instance_data_array, GL_DYNAMIC_DRAW);
#else
void *ubo = glMapBufferRange(GL_UNIFORM_BUFFER, 0, sizeof(InstanceData) * p_max_index, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
memcpy(ubo, state.instance_data_array, sizeof(InstanceData) * p_max_index);
glUnmapBuffer(GL_UNIFORM_BUFFER);
#endif
}
RID RasterizerCanvasGLES3::light_create() {
return RID();
}

View file

@ -252,6 +252,7 @@ public:
void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool p_to_backbuffer = false);
void _render_item(RID p_render_target, const Item *p_item, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights, uint32_t &r_index);
void _render_batch(uint32_t &p_max_index);
void _bind_instance_data_buffer(uint32_t p_max_index);
void _allocate_instance_data_buffer();
void set_time(double p_time);

View file

@ -5,6 +5,7 @@ mode_quad =
mode_ninepatch = #define USE_NINEPATCH
mode_primitive = #define USE_PRIMITIVE
mode_attributes = #define USE_ATTRIBUTES
mode_instanced = #define USE_ATTRIBUTES \n#define USE_INSTANCING
#[specializations]
@ -20,6 +21,15 @@ layout(location = 4) in vec2 uv_attrib;
layout(location = 10) in uvec4 bone_attrib;
layout(location = 11) in vec4 weight_attrib;
#ifdef USE_INSTANCING
layout(location = 5) in highp vec4 instance_xform0;
layout(location = 6) in highp vec4 instance_xform1;
layout(location = 7) in lowp vec4 instance_color;
layout(location = 8) in highp vec4 instance_custom_data;
#endif
#endif
// This needs to be outside clang-format so the ubo comment is in the right place
@ -77,13 +87,21 @@ void main() {
vec4 bone_weights = vec4(0.0);
#elif defined(USE_ATTRIBUTES)
#ifdef USE_INSTANCING
draw_data_instance = 0;
#endif
vec2 vertex = vertex_attrib;
vec4 color = color_attrib * draw_data[draw_data_instance].modulation;
vec2 uv = uv_attrib;
uvec4 bones = bone_attrib;
vec4 bone_weights = weight_attrib;
#ifdef USE_INSTANCING
color *= instance_color;
instance_custom = instance_custom_data;
#endif
#else
vec2 vertex_base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
@ -98,81 +116,10 @@ void main() {
mat4 model_matrix = mat4(vec4(draw_data[draw_data_instance].world_x, 0.0, 0.0), vec4(draw_data[draw_data_instance].world_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(draw_data[draw_data_instance].world_ofs, 0.0, 1.0));
// MultiMeshes don't batch, so always read from draw_data[0]
uint instancing = draw_data[0].flags & FLAGS_INSTANCING_MASK;
#ifdef USE_INSTANCING
model_matrix = model_matrix * transpose(mat4(instance_xform0, instance_xform1, vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)));
#endif // USE_INSTANCING
#ifdef USE_ATTRIBUTES
/*
if (instancing > 1) {
// trails
uint stride = 2 + 1 + 1; //particles always uses this format
uint trail_size = instancing;
uint offset = trail_size * stride * gl_InstanceID;
vec4 pcolor;
vec2 new_vertex;
{
uint boffset = offset + bone_attrib.x * stride;
new_vertex = (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.x;
pcolor = transforms.data[boffset + 2] * weight_attrib.x;
}
if (weight_attrib.y > 0.001) {
uint boffset = offset + bone_attrib.y * stride;
new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.y;
pcolor += transforms.data[boffset + 2] * weight_attrib.y;
}
if (weight_attrib.z > 0.001) {
uint boffset = offset + bone_attrib.z * stride;
new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.z;
pcolor += transforms.data[boffset + 2] * weight_attrib.z;
}
if (weight_attrib.w > 0.001) {
uint boffset = offset + bone_attrib.w * stride;
new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.w;
pcolor += transforms.data[boffset + 2] * weight_attrib.w;
}
instance_custom = transforms.data[offset + 3];
vertex = new_vertex;
color *= pcolor;
} else*/
#endif // USE_ATTRIBUTES
/*
{
if (instancing == 1) {
uint stride = 2;
{
if (bool(draw_data[0].flags & FLAGS_INSTANCING_HAS_COLORS)) {
stride += 1;
}
if (bool(draw_data[0].flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) {
stride += 1;
}
}
uint offset = stride * gl_InstanceID;
mat4 matrix = mat4(transforms.data[offset + 0], transforms.data[offset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));
offset += 2;
if (bool(draw_data[0].flags & FLAGS_INSTANCING_HAS_COLORS)) {
color *= transforms.data[offset];
offset += 1;
}
if (bool(draw_data[0].flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) {
instance_custom = transforms.data[offset];
}
matrix = transpose(matrix);
model_matrix = model_matrix * matrix;
}
}
*/
#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE)
if (bool(draw_data[draw_data_instance].flags & FLAGS_USING_PARTICLES)) {
//scale by texture size

View file

@ -722,6 +722,18 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
for (int i = 0; i < RS::ARRAY_INDEX; i++) {
if (!attribs[i].enabled) {
glDisableVertexAttribArray(i);
if (s->format & RS::ARRAY_FLAG_USE_2D_VERTICES) {
if (i == RS::ARRAY_COLOR) {
glVertexAttrib4f(i, 1, 1, 1, 1);
} else if (i == RS::ARRAY_TEX_UV) {
glVertexAttrib2f(i, 1, 1);
} else if (i == RS::ARRAY_BONES) {
glVertexAttrib4f(i, 1, 1, 1, 1);
} else if (i == RS::ARRAY_WEIGHTS) {
glVertexAttrib4f(i, 1, 1, 1, 1);
}
}
continue;
}
if (i <= RS::ARRAY_TANGENT) {
@ -941,7 +953,6 @@ void MeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS::
multimesh->stride_cache = multimesh->custom_data_offset_cache + (p_use_custom_data ? 4 : 0);
multimesh->buffer_set = false;
//print_line("allocate, elements: " + itos(p_instances) + " 2D: " + itos(p_transform_format == RS::MULTIMESH_TRANSFORM_2D) + " colors " + itos(multimesh->uses_colors) + " data " + itos(multimesh->uses_custom_data) + " stride " + itos(multimesh->stride_cache) + " total size " + itos(multimesh->stride_cache * multimesh->instances));
multimesh->data_cache = Vector<float>();
multimesh->aabb = AABB();
multimesh->aabb_dirty = false;

View file

@ -493,6 +493,26 @@ public:
return multimesh->instances;
}
_FORCE_INLINE_ GLuint multimesh_get_gl_buffer(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
return multimesh->buffer;
}
_FORCE_INLINE_ uint32_t multimesh_get_stride(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
return multimesh->stride_cache;
}
_FORCE_INLINE_ uint32_t multimesh_get_color_offset(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
return multimesh->color_offset_cache;
}
_FORCE_INLINE_ uint32_t multimesh_get_custom_data_offset(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
return multimesh->custom_data_offset_cache;
}
/* SKELETON API */
Skeleton *get_skeleton(RID p_rid) { return skeleton_owner.get_or_null(p_rid); };

View file

@ -29,3 +29,102 @@
/*************************************************************************/
#include "renderer_canvas_render.h"
#include "servers/rendering/rendering_server_globals.h"
const Rect2 &RendererCanvasRender::Item::get_rect() const {
if (custom_rect || (!rect_dirty && !update_when_visible)) {
return rect;
}
//must update rect
if (commands == nullptr) {
rect = Rect2();
rect_dirty = false;
return rect;
}
Transform2D xf;
bool found_xform = false;
bool first = true;
const Item::Command *c = commands;
while (c) {
Rect2 r;
switch (c->type) {
case Item::Command::TYPE_RECT: {
const Item::CommandRect *crect = static_cast<const Item::CommandRect *>(c);
r = crect->rect;
} break;
case Item::Command::TYPE_NINEPATCH: {
const Item::CommandNinePatch *style = static_cast<const Item::CommandNinePatch *>(c);
r = style->rect;
} break;
case Item::Command::TYPE_POLYGON: {
const Item::CommandPolygon *polygon = static_cast<const Item::CommandPolygon *>(c);
r = polygon->polygon.rect_cache;
} break;
case Item::Command::TYPE_PRIMITIVE: {
const Item::CommandPrimitive *primitive = static_cast<const Item::CommandPrimitive *>(c);
for (uint32_t j = 0; j < primitive->point_count; j++) {
if (j == 0) {
r.position = primitive->points[0];
} else {
r.expand_to(primitive->points[j]);
}
}
} break;
case Item::Command::TYPE_MESH: {
const Item::CommandMesh *mesh = static_cast<const Item::CommandMesh *>(c);
AABB aabb = RSG::mesh_storage->mesh_get_aabb(mesh->mesh, RID());
r = Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y);
} break;
case Item::Command::TYPE_MULTIMESH: {
const Item::CommandMultiMesh *multimesh = static_cast<const Item::CommandMultiMesh *>(c);
AABB aabb = RSG::mesh_storage->multimesh_get_aabb(multimesh->multimesh);
r = Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y);
} break;
case Item::Command::TYPE_PARTICLES: {
const Item::CommandParticles *particles_cmd = static_cast<const Item::CommandParticles *>(c);
if (particles_cmd->particles.is_valid()) {
AABB aabb = RendererRD::ParticlesStorage::get_singleton()->particles_get_aabb(particles_cmd->particles);
r = Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y);
}
} break;
case Item::Command::TYPE_TRANSFORM: {
const Item::CommandTransform *transform = static_cast<const Item::CommandTransform *>(c);
xf = transform->xform;
found_xform = true;
[[fallthrough]];
}
default: {
c = c->next;
continue;
}
}
if (found_xform) {
r = xf.xform(r);
}
if (first) {
rect = r;
first = false;
} else {
rect = rect.merge(r);
}
c = c->next;
}
rect_dirty = false;
return rect;
}

View file

@ -356,103 +356,7 @@ public:
Rect2 global_rect_cache;
const Rect2 &get_rect() const {
if (custom_rect || (!rect_dirty && !update_when_visible)) {
return rect;
}
//must update rect
if (commands == nullptr) {
rect = Rect2();
rect_dirty = false;
return rect;
}
Transform2D xf;
bool found_xform = false;
bool first = true;
const Item::Command *c = commands;
while (c) {
Rect2 r;
switch (c->type) {
case Item::Command::TYPE_RECT: {
const Item::CommandRect *crect = static_cast<const Item::CommandRect *>(c);
r = crect->rect;
} break;
case Item::Command::TYPE_NINEPATCH: {
const Item::CommandNinePatch *style = static_cast<const Item::CommandNinePatch *>(c);
r = style->rect;
} break;
case Item::Command::TYPE_POLYGON: {
const Item::CommandPolygon *polygon = static_cast<const Item::CommandPolygon *>(c);
r = polygon->polygon.rect_cache;
} break;
case Item::Command::TYPE_PRIMITIVE: {
const Item::CommandPrimitive *primitive = static_cast<const Item::CommandPrimitive *>(c);
for (uint32_t j = 0; j < primitive->point_count; j++) {
if (j == 0) {
r.position = primitive->points[0];
} else {
r.expand_to(primitive->points[j]);
}
}
} break;
case Item::Command::TYPE_MESH: {
const Item::CommandMesh *mesh = static_cast<const Item::CommandMesh *>(c);
AABB aabb = RendererRD::MeshStorage::get_singleton()->mesh_get_aabb(mesh->mesh, RID());
r = Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y);
} break;
case Item::Command::TYPE_MULTIMESH: {
const Item::CommandMultiMesh *multimesh = static_cast<const Item::CommandMultiMesh *>(c);
AABB aabb = RendererRD::MeshStorage::get_singleton()->multimesh_get_aabb(multimesh->multimesh);
r = Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y);
} break;
case Item::Command::TYPE_PARTICLES: {
const Item::CommandParticles *particles_cmd = static_cast<const Item::CommandParticles *>(c);
if (particles_cmd->particles.is_valid()) {
AABB aabb = RendererRD::ParticlesStorage::get_singleton()->particles_get_aabb(particles_cmd->particles);
r = Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y);
}
} break;
case Item::Command::TYPE_TRANSFORM: {
const Item::CommandTransform *transform = static_cast<const Item::CommandTransform *>(c);
xf = transform->xform;
found_xform = true;
[[fallthrough]];
}
default: {
c = c->next;
continue;
}
}
if (found_xform) {
r = xf.xform(r);
}
if (first) {
rect = r;
first = false;
} else {
rect = rect.merge(r);
}
c = c->next;
}
rect_dirty = false;
return rect;
}
const Rect2 &get_rect() const;
Command *commands = nullptr;
Command *last_command = nullptr;