summaryrefslogtreecommitdiff
path: root/servers/rendering
diff options
context:
space:
mode:
Diffstat (limited to 'servers/rendering')
-rw-r--r--servers/rendering/renderer_canvas_cull.cpp29
-rw-r--r--servers/rendering/renderer_canvas_render.h7
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp368
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.h10
-rw-r--r--servers/rendering/renderer_rd/renderer_storage_rd.cpp23
-rw-r--r--servers/rendering/renderer_rd/renderer_storage_rd.h25
-rw-r--r--servers/rendering/renderer_rd/shaders/canvas.glsl102
-rw-r--r--servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl10
-rw-r--r--servers/rendering/renderer_rd/shaders/particles_copy.glsl9
-rw-r--r--servers/rendering/renderer_rd/shaders/skeleton.glsl47
-rw-r--r--servers/rendering/renderer_storage.h1
-rw-r--r--servers/rendering/rendering_server_default.h1
12 files changed, 340 insertions, 292 deletions
diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp
index 7f3fc2f8f4..67003a6f64 100644
--- a/servers/rendering/renderer_canvas_cull.cpp
+++ b/servers/rendering/renderer_canvas_cull.cpp
@@ -923,10 +923,15 @@ void RendererCanvasCull::canvas_item_add_set_transform(RID p_item, const Transfo
void RendererCanvasCull::canvas_item_add_mesh(RID p_item, const RID &p_mesh, const Transform2D &p_transform, const Color &p_modulate, RID p_texture) {
Item *canvas_item = canvas_item_owner.getornull(p_item);
ERR_FAIL_COND(!canvas_item);
+ ERR_FAIL_COND(!p_mesh.is_valid());
Item::CommandMesh *m = canvas_item->alloc_command<Item::CommandMesh>();
ERR_FAIL_COND(!m);
m->mesh = p_mesh;
+ if (canvas_item->skeleton.is_valid()) {
+ m->mesh_instance = RSG::storage->mesh_instance_create(p_mesh);
+ RSG::storage->mesh_instance_set_skeleton(m->mesh_instance, canvas_item->skeleton);
+ }
m->texture = p_texture;
@@ -996,8 +1001,30 @@ void RendererCanvasCull::canvas_item_set_z_as_relative_to_parent(RID p_item, boo
void RendererCanvasCull::canvas_item_attach_skeleton(RID p_item, RID p_skeleton) {
Item *canvas_item = canvas_item_owner.getornull(p_item);
ERR_FAIL_COND(!canvas_item);
-
+ if (canvas_item->skeleton == p_skeleton) {
+ return;
+ }
canvas_item->skeleton = p_skeleton;
+
+ Item::Command *c = canvas_item->commands;
+
+ while (c) {
+ if (c->type == Item::Command::TYPE_MESH) {
+ Item::CommandMesh *cm = static_cast<Item::CommandMesh *>(c);
+ if (canvas_item->skeleton.is_valid()) {
+ if (cm->mesh_instance.is_null()) {
+ cm->mesh_instance = RSG::storage->mesh_instance_create(cm->mesh);
+ }
+ RSG::storage->mesh_instance_set_skeleton(cm->mesh_instance, canvas_item->skeleton);
+ } else {
+ if (cm->mesh_instance.is_valid()) {
+ RSG::storage->free(cm->mesh_instance);
+ cm->mesh_instance = RID();
+ }
+ }
+ }
+ c = c->next;
+ }
}
void RendererCanvasCull::canvas_item_set_copy_to_backbuffer(RID p_item, bool p_enable, const Rect2 &p_rect) {
diff --git a/servers/rendering/renderer_canvas_render.h b/servers/rendering/renderer_canvas_render.h
index f08986b021..0e9ef616cb 100644
--- a/servers/rendering/renderer_canvas_render.h
+++ b/servers/rendering/renderer_canvas_render.h
@@ -246,10 +246,16 @@ public:
RID mesh;
Transform2D transform;
Color modulate;
+ RID mesh_instance;
RID texture;
CommandMesh() { type = TYPE_MESH; }
+ ~CommandMesh() {
+ if (mesh_instance.is_valid()) {
+ RendererStorage::base_singleton->free(mesh_instance);
+ }
+ }
};
struct CommandMultiMesh : public Command {
@@ -262,7 +268,6 @@ public:
struct CommandParticles : public Command {
RID particles;
-
RID texture;
CommandParticles() { type = TYPE_PARTICLES; }
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index 377b0fd72d..f448698976 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -705,286 +705,128 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, const Item
case Item::Command::TYPE_MESH:
case Item::Command::TYPE_MULTIMESH:
case Item::Command::TYPE_PARTICLES: {
- ERR_PRINT("FIXME: Mesh, MultiMesh and Particles render commands are unimplemented currently, they need to be ported to the 4.0 rendering architecture.");
-#ifndef _MSC_VER
-#warning Item::Command types for Mesh, MultiMesh and Particles need to be implemented.
-#endif
- // See #if 0'ed code below to port from GLES3.
- } break;
-
-#if 0
- case Item::Command::TYPE_MESH: {
- Item::CommandMesh *mesh = static_cast<Item::CommandMesh *>(c);
- _set_texture_rect_mode(false);
-
- RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(mesh->texture, mesh->normal_map);
-
- if (texture) {
- Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height);
- state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size);
+ RID mesh;
+ RID mesh_instance;
+ RID texture;
+ Color modulate(1, 1, 1, 1);
+ float world_backup[6];
+ int instance_count = 1;
+
+ for (int j = 0; j < 6; j++) {
+ world_backup[j] = push_constant.world[j];
}
- state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.final_transform * mesh->transform);
-
- RasterizerStorageGLES3::Mesh *mesh_data = storage->mesh_owner.getornull(mesh->mesh);
- if (mesh_data) {
- for (int j = 0; j < mesh_data->surfaces.size(); j++) {
- RasterizerStorageGLES3::Surface *s = mesh_data->surfaces[j];
- // materials are ignored in 2D meshes, could be added but many things (ie, lighting mode, reading from screen, etc) would break as they are not meant be set up at this point of drawing
- glBindVertexArray(s->array_id);
-
- glVertexAttrib4f(RS::ARRAY_COLOR, mesh->modulate.r, mesh->modulate.g, mesh->modulate.b, mesh->modulate.a);
+ if (c->type == Item::Command::TYPE_MESH) {
+ const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(c);
+ mesh = m->mesh;
+ mesh_instance = m->mesh_instance;
+ texture = m->texture;
+ modulate = m->modulate;
+ _update_transform_2d_to_mat2x3(base_transform * m->transform, push_constant.world);
+ } 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);
+ texture = mm->texture;
+
+ if (storage->multimesh_get_transform_format(multimesh) != RS::MULTIMESH_TRANSFORM_2D) {
+ break;
+ }
- if (s->index_array_len) {
- glDrawElements(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0);
- } else {
- glDrawArrays(gl_primitive[s->primitive], 0, s->array_len);
- }
+ instance_count = storage->multimesh_get_instances_to_draw(multimesh);
- glBindVertexArray(0);
+ RID uniform_set = storage->multimesh_get_2d_uniform_set(multimesh, shader.default_version_rd_shader, TRANSFORMS_UNIFORM_SET);
+ RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, uniform_set, TRANSFORMS_UNIFORM_SET);
+ push_constant.flags |= 1; //multimesh, trails disabled
+ if (storage->multimesh_uses_colors(multimesh)) {
+ push_constant.flags |= FLAGS_INSTANCING_HAS_COLORS;
}
- }
- state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.final_transform);
+ if (storage->multimesh_uses_custom_data(multimesh)) {
+ push_constant.flags |= FLAGS_INSTANCING_HAS_CUSTOM_DATA;
+ }
+ } else if (c->type == Item::Command::TYPE_PARTICLES) {
+ const Item::CommandParticles *pt = static_cast<const Item::CommandParticles *>(c);
+ ERR_BREAK(storage->particles_get_mode(pt->particles) != RS::PARTICLES_MODE_2D);
+ if (storage->particles_is_inactive(pt->particles)) {
+ break;
+ }
+ int dpc = storage->particles_get_draw_passes(pt->particles);
+ if (dpc == 0) {
+ break; //nothing to draw
+ }
+ uint32_t divisor = 1;
+ instance_count = storage->particles_get_amount(pt->particles, divisor);
- } break;
- case Item::Command::TYPE_MULTIMESH: {
- Item::CommandMultiMesh *mmesh = static_cast<Item::CommandMultiMesh *>(c);
+ RID uniform_set = storage->particles_get_instance_buffer_uniform_set(pt->particles, shader.default_version_rd_shader, TRANSFORMS_UNIFORM_SET);
+ RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, uniform_set, TRANSFORMS_UNIFORM_SET);
- RasterizerStorageGLES3::MultiMesh *multi_mesh = storage->multimesh_owner.getornull(mmesh->multimesh);
+ push_constant.flags |= divisor;
+ instance_count /= divisor;
- if (!multi_mesh)
- break;
+ push_constant.flags |= FLAGS_INSTANCING_HAS_COLORS;
+ push_constant.flags |= FLAGS_INSTANCING_HAS_CUSTOM_DATA;
- RasterizerStorageGLES3::Mesh *mesh_data = storage->mesh_owner.getornull(multi_mesh->mesh);
+ mesh = storage->particles_get_draw_pass_mesh(pt->particles, 0); //higher ones are ignored
+ texture = pt->texture;
+ }
- if (!mesh_data)
+ if (mesh.is_null()) {
break;
+ }
- RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(mmesh->texture, mmesh->normal_map);
+ _bind_canvas_texture(p_draw_list, texture, current_filter, current_repeat, last_texture, push_constant, texpixel_size);
- state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, multi_mesh->custom_data_format != RS::MULTIMESH_CUSTOM_DATA_NONE);
- state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, true);
- //reset shader and force rebind
- state.using_texture_rect = true;
- _set_texture_rect_mode(false);
+ uint32_t surf_count = storage->mesh_get_surface_count(mesh);
+ static const PipelineVariant variant[RS::PRIMITIVE_MAX] = { PIPELINE_VARIANT_ATTRIBUTE_POINTS, PIPELINE_VARIANT_ATTRIBUTE_LINES, PIPELINE_VARIANT_ATTRIBUTE_LINES_STRIP, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLES, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLE_STRIP };
- if (texture) {
- Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height);
- state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size);
- }
+ push_constant.modulation[0] = base_color.r * modulate.r;
+ push_constant.modulation[1] = base_color.g * modulate.g;
+ push_constant.modulation[2] = base_color.b * modulate.b;
+ push_constant.modulation[3] = base_color.a * modulate.a;
- int amount = MIN(multi_mesh->size, multi_mesh->visible_instances);
-
- if (amount == -1) {
- amount = multi_mesh->size;
+ for (int j = 0; j < 4; j++) {
+ push_constant.src_rect[j] = 0;
+ push_constant.dst_rect[j] = 0;
+ push_constant.ninepatch_margins[j] = 0;
}
- for (int j = 0; j < mesh_data->surfaces.size(); j++) {
- RasterizerStorageGLES3::Surface *s = mesh_data->surfaces[j];
- // materials are ignored in 2D meshes, could be added but many things (ie, lighting mode, reading from screen, etc) would break as they are not meant be set up at this point of drawing
- glBindVertexArray(s->instancing_array_id);
+ for (uint32_t j = 0; j < surf_count; j++) {
+ void *surface = storage->mesh_get_surface(mesh, j);
- glBindBuffer(GL_ARRAY_BUFFER, multi_mesh->buffer); //modify the buffer
+ RS::PrimitiveType primitive = storage->mesh_surface_get_primitive(surface);
+ ERR_CONTINUE(primitive < 0 || primitive >= RS::PRIMITIVE_MAX);
- int stride = (multi_mesh->xform_floats + multi_mesh->color_floats + multi_mesh->custom_data_floats) * 4;
- glEnableVertexAttribArray(8);
- glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(0));
- glVertexAttribDivisor(8, 1);
- glEnableVertexAttribArray(9);
- glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(4 * 4));
- glVertexAttribDivisor(9, 1);
+ uint32_t input_mask = pipeline_variants->variants[light_mode][variant[primitive]].get_vertex_input_mask();
- int color_ofs;
+ RID vertex_array;
+ RD::VertexFormatID vertex_format = RD::INVALID_FORMAT_ID;
- if (multi_mesh->transform_format == RS::MULTIMESH_TRANSFORM_3D) {
- glEnableVertexAttribArray(10);
- glVertexAttribPointer(10, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(8 * 4));
- glVertexAttribDivisor(10, 1);
- color_ofs = 12 * 4;
+ if (mesh_instance.is_valid()) {
+ storage->mesh_instance_surface_get_vertex_arrays_and_format(mesh_instance, j, input_mask, vertex_array, vertex_format);
} else {
- glDisableVertexAttribArray(10);
- glVertexAttrib4f(10, 0, 0, 1, 0);
- color_ofs = 8 * 4;
+ storage->mesh_surface_get_vertex_arrays_and_format(surface, input_mask, vertex_array, vertex_format);
}
- int custom_data_ofs = color_ofs;
-
- switch (multi_mesh->color_format) {
- case RS::MULTIMESH_COLOR_NONE: {
- glDisableVertexAttribArray(11);
- glVertexAttrib4f(11, 1, 1, 1, 1);
- } break;
- case RS::MULTIMESH_COLOR_8BIT: {
- glEnableVertexAttribArray(11);
- glVertexAttribPointer(11, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, CAST_INT_TO_UCHAR_PTR(color_ofs));
- glVertexAttribDivisor(11, 1);
- custom_data_ofs += 4;
-
- } break;
- case RS::MULTIMESH_COLOR_FLOAT: {
- glEnableVertexAttribArray(11);
- glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(color_ofs));
- glVertexAttribDivisor(11, 1);
- custom_data_ofs += 4 * 4;
- } break;
- }
+ RID pipeline = pipeline_variants->variants[light_mode][variant[primitive]].get_render_pipeline(vertex_format, p_framebuffer_format);
+ RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline);
- switch (multi_mesh->custom_data_format) {
- case RS::MULTIMESH_CUSTOM_DATA_NONE: {
- glDisableVertexAttribArray(12);
- glVertexAttrib4f(12, 1, 1, 1, 1);
- } break;
- case RS::MULTIMESH_CUSTOM_DATA_8BIT: {
- glEnableVertexAttribArray(12);
- glVertexAttribPointer(12, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, CAST_INT_TO_UCHAR_PTR(custom_data_ofs));
- glVertexAttribDivisor(12, 1);
-
- } break;
- case RS::MULTIMESH_CUSTOM_DATA_FLOAT: {
- glEnableVertexAttribArray(12);
- glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(custom_data_ofs));
- glVertexAttribDivisor(12, 1);
- } break;
- }
+ RID index_array = storage->mesh_surface_get_index_array(surface, 0);
- if (s->index_array_len) {
- glDrawElementsInstanced(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0, amount);
- } else {
- glDrawArraysInstanced(gl_primitive[s->primitive], 0, s->array_len, amount);
+ if (index_array.is_valid()) {
+ RD::get_singleton()->draw_list_bind_index_array(p_draw_list, index_array);
}
- glBindVertexArray(0);
- }
-
- state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, false);
- state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, false);
- state.using_texture_rect = true;
- _set_texture_rect_mode(false);
-
- } break;
- case Item::Command::TYPE_PARTICLES: {
- Item::CommandParticles *particles_cmd = static_cast<Item::CommandParticles *>(c);
-
- RasterizerStorageGLES3::Particles *particles = storage->particles_owner.getornull(particles_cmd->particles);
- if (!particles)
- break;
-
- if (particles->inactive && !particles->emitting)
- break;
-
- glVertexAttrib4f(RS::ARRAY_COLOR, 1, 1, 1, 1); //not used, so keep white
-
- RenderingServerDefault::redraw_request();
-
- storage->particles_request_process(particles_cmd->particles);
- //enable instancing
-
- state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, true);
- state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_PARTICLES, true);
- state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, true);
- //reset shader and force rebind
- state.using_texture_rect = true;
- _set_texture_rect_mode(false);
-
- RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(particles_cmd->texture, particles_cmd->normal_map);
-
- if (texture) {
- Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height);
- state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size);
- } else {
- state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, Vector2(1.0, 1.0));
- }
-
- if (!particles->use_local_coords) {
- Transform2D inv_xf;
- inv_xf.set_axis(0, Vector2(particles->emission_transform.basis.get_axis(0).x, particles->emission_transform.basis.get_axis(0).y));
- inv_xf.set_axis(1, Vector2(particles->emission_transform.basis.get_axis(1).x, particles->emission_transform.basis.get_axis(1).y));
- inv_xf.set_origin(Vector2(particles->emission_transform.get_origin().x, particles->emission_transform.get_origin().y));
- inv_xf.affine_invert();
+ RD::get_singleton()->draw_list_bind_vertex_array(p_draw_list, vertex_array);
+ RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant));
- state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.final_transform * inv_xf);
+ RD::get_singleton()->draw_list_draw(p_draw_list, index_array.is_valid(), instance_count);
}
- glBindVertexArray(data.particle_quad_array); //use particle quad array
- glBindBuffer(GL_ARRAY_BUFFER, particles->particle_buffers[0]); //bind particle buffer
-
- int stride = sizeof(float) * 4 * 6;
-
- int amount = particles->amount;
-
- if (particles->draw_order != RS::PARTICLES_DRAW_ORDER_LIFETIME) {
- glEnableVertexAttribArray(8); //xform x
- glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * 3));
- glVertexAttribDivisor(8, 1);
- glEnableVertexAttribArray(9); //xform y
- glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * 4));
- glVertexAttribDivisor(9, 1);
- glEnableVertexAttribArray(10); //xform z
- glVertexAttribPointer(10, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * 5));
- glVertexAttribDivisor(10, 1);
- glEnableVertexAttribArray(11); //color
- glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, stride, nullptr);
- glVertexAttribDivisor(11, 1);
- glEnableVertexAttribArray(12); //custom
- glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * 2));
- glVertexAttribDivisor(12, 1);
-
- glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, amount);
- } else {
- //split
- int split = int(Math::ceil(particles->phase * particles->amount));
-
- if (amount - split > 0) {
- glEnableVertexAttribArray(8); //xform x
- glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * split + sizeof(float) * 4 * 3));
- glVertexAttribDivisor(8, 1);
- glEnableVertexAttribArray(9); //xform y
- glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * split + sizeof(float) * 4 * 4));
- glVertexAttribDivisor(9, 1);
- glEnableVertexAttribArray(10); //xform z
- glVertexAttribPointer(10, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * split + sizeof(float) * 4 * 5));
- glVertexAttribDivisor(10, 1);
- glEnableVertexAttribArray(11); //color
- glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * split + 0));
- glVertexAttribDivisor(11, 1);
- glEnableVertexAttribArray(12); //custom
- glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * split + sizeof(float) * 4 * 2));
- glVertexAttribDivisor(12, 1);
-
- glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, amount - split);
- }
-
- if (split > 0) {
- glEnableVertexAttribArray(8); //xform x
- glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * 3));
- glVertexAttribDivisor(8, 1);
- glEnableVertexAttribArray(9); //xform y
- glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * 4));
- glVertexAttribDivisor(9, 1);
- glEnableVertexAttribArray(10); //xform z
- glVertexAttribPointer(10, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * 5));
- glVertexAttribDivisor(10, 1);
- glEnableVertexAttribArray(11); //color
- glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, stride, nullptr);
- glVertexAttribDivisor(11, 1);
- glEnableVertexAttribArray(12); //custom
- glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * 2));
- glVertexAttribDivisor(12, 1);
-
- glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, split);
- }
+ for (int j = 0; j < 6; j++) {
+ push_constant.world[j] = world_backup[j];
}
- glBindVertexArray(0);
-
- state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, false);
- state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_PARTICLES, false);
- state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, false);
- state.using_texture_rect = true;
- _set_texture_rect_mode(false);
-
} break;
-#endif
case Item::Command::TYPE_TRANSFORM: {
const Item::CommandTransform *transform = static_cast<const Item::CommandTransform *>(c);
_update_transform_2d_to_mat2x3(base_transform * transform->xform, push_constant.world);
@@ -1437,6 +1279,8 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p
Item *canvas_group_owner = nullptr;
+ bool update_skeletons = false;
+
while (ci) {
if (ci->copy_back_buffer && canvas_group_owner == nullptr) {
backbuffer_copy = true;
@@ -1472,9 +1316,27 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p
}
}
+ if (ci->skeleton.is_valid()) {
+ const Item::Command *c = ci->commands;
+
+ while (c) {
+ if (c->type == Item::Command::TYPE_MESH) {
+ const Item::CommandMesh *cm = static_cast<const Item::CommandMesh *>(c);
+ if (cm->mesh_instance.is_valid()) {
+ storage->mesh_instance_check_for_update(cm->mesh_instance);
+ update_skeletons = true;
+ }
+ }
+ }
+ }
+
if (ci->canvas_group_owner != nullptr) {
if (canvas_group_owner == nullptr) {
//Canvas group begins here, render until before this item
+ if (update_skeletons) {
+ storage->update_mesh_instances();
+ update_skeletons = false;
+ }
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list);
item_count = 0;
@@ -1494,6 +1356,11 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p
}
if (ci == canvas_group_owner) {
+ if (update_skeletons) {
+ storage->update_mesh_instances();
+ update_skeletons = false;
+ }
+
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, true);
item_count = 0;
@@ -1506,6 +1373,10 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p
if (backbuffer_copy) {
//render anything pending, including clearing if no items
+ if (update_skeletons) {
+ storage->update_mesh_instances();
+ update_skeletons = false;
+ }
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list);
item_count = 0;
@@ -1518,6 +1389,11 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p
items[item_count++] = ci;
if (!ci->next || item_count == MAX_RENDER_ITEMS - 1) {
+ if (update_skeletons) {
+ storage->update_mesh_instances();
+ update_skeletons = false;
+ }
+
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list);
//then reset
item_count = 0;
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
index cb947d7180..8129cc6c9b 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
@@ -67,12 +67,10 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
};
enum {
- FLAGS_INSTANCING_STRIDE_MASK = 0xF,
- FLAGS_INSTANCING_ENABLED = (1 << 4),
- FLAGS_INSTANCING_HAS_COLORS = (1 << 5),
- FLAGS_INSTANCING_COLOR_8BIT = (1 << 6),
- FLAGS_INSTANCING_HAS_CUSTOM_DATA = (1 << 7),
- FLAGS_INSTANCING_CUSTOM_DATA_8_BIT = (1 << 8),
+
+ FLAGS_INSTANCING_MASK = 0x7F,
+ FLAGS_INSTANCING_HAS_COLORS = (1 << 7),
+ FLAGS_INSTANCING_HAS_CUSTOM_DATA = (1 << 8),
FLAGS_CLIP_RECT_UV = (1 << 9),
FLAGS_TRANSPOSE_RECT = (1 << 10),
diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp
index 47b9e33ca6..f419875d58 100644
--- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp
@@ -3873,6 +3873,18 @@ void RendererStorageRD::particles_initialize(RID p_rid) {
particles_owner.initialize_rid(p_rid, Particles());
}
+void RendererStorageRD::particles_set_mode(RID p_particles, RS::ParticlesMode p_mode) {
+ Particles *particles = particles_owner.getornull(p_particles);
+ ERR_FAIL_COND(!particles);
+ if (particles->mode == p_mode) {
+ return;
+ }
+
+ _particles_free_data(particles);
+
+ particles->mode = p_mode;
+}
+
void RendererStorageRD::particles_set_emitting(RID p_particles, bool p_emitting) {
Particles *particles = particles_owner.getornull(p_particles);
ERR_FAIL_COND(!particles);
@@ -4765,7 +4777,7 @@ void RendererStorageRD::particles_set_view_axis(RID p_particles, const Vector3 &
copy_push_constant.total_particles *= copy_push_constant.total_particles;
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, particles_shader.copy_pipelines[do_sort ? ParticlesShader::COPY_MODE_FILL_INSTANCES_WITH_SORT_BUFFER : ParticlesShader::COPY_MODE_FILL_INSTANCES]);
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, particles_shader.copy_pipelines[do_sort ? ParticlesShader::COPY_MODE_FILL_INSTANCES_WITH_SORT_BUFFER : (particles->mode == RS::PARTICLES_MODE_2D ? ParticlesShader::COPY_MODE_FILL_INSTANCES_2D : ParticlesShader::COPY_MODE_FILL_INSTANCES)]);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->particles_copy_uniform_set, 0);
if (do_sort) {
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->particles_sort_uniform_set, 1);
@@ -4785,8 +4797,12 @@ void RendererStorageRD::_particles_update_buffers(Particles *particles) {
if (particles->trails_enabled && particles->trail_bind_poses.size() > 1) {
total_amount *= particles->trail_bind_poses.size();
}
+
+ uint32_t xform_size = particles->mode == RS::PARTICLES_MODE_2D ? 2 : 3;
+
particles->particle_buffer = RD::get_singleton()->storage_buffer_create(sizeof(ParticleData) * total_amount);
- particles->particle_instance_buffer = RD::get_singleton()->storage_buffer_create(sizeof(float) * 4 * (3 + 1 + 1) * total_amount);
+
+ particles->particle_instance_buffer = RD::get_singleton()->storage_buffer_create(sizeof(float) * 4 * (xform_size + 1 + 1) * total_amount);
//needs to clear it
{
@@ -5004,7 +5020,7 @@ void RendererStorageRD::update_particles() {
}
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, particles_shader.copy_pipelines[ParticlesShader::COPY_MODE_FILL_INSTANCES]);
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, particles_shader.copy_pipelines[particles->mode == RS::PARTICLES_MODE_2D ? ParticlesShader::COPY_MODE_FILL_INSTANCES_2D : ParticlesShader::COPY_MODE_FILL_INSTANCES]);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->particles_copy_uniform_set, 0);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->trail_bind_pose_uniform_set, 2);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy_push_constant, sizeof(ParticlesShader::CopyPushConstant));
@@ -9220,6 +9236,7 @@ RendererStorageRD::RendererStorageRD() {
{
Vector<String> copy_modes;
copy_modes.push_back("\n#define MODE_FILL_INSTANCES\n");
+ copy_modes.push_back("\n#define MODE_FILL_INSTANCES\n#define MODE_2D\n");
copy_modes.push_back("\n#define MODE_FILL_SORT_BUFFER\n#define USE_SORT_BUFFER\n");
copy_modes.push_back("\n#define MODE_FILL_INSTANCES\n#define USE_SORT_BUFFER\n");
diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.h b/servers/rendering/renderer_rd/renderer_storage_rd.h
index 961bdfb178..49f7f3dba6 100644
--- a/servers/rendering/renderer_rd/renderer_storage_rd.h
+++ b/servers/rendering/renderer_rd/renderer_storage_rd.h
@@ -580,6 +580,7 @@ private:
RID buffer; //storage buffer
RID uniform_set_3d;
+ RID uniform_set_2d;
bool dirty = false;
MultiMesh *dirty_list = nullptr;
@@ -696,6 +697,7 @@ private:
};
struct Particles {
+ RS::ParticlesMode mode = RS::PARTICLES_MODE_3D;
bool inactive = true;
float inactive_time = 0.0;
bool emitting = false;
@@ -822,6 +824,7 @@ private:
enum {
COPY_MODE_FILL_INSTANCES,
+ COPY_MODE_FILL_INSTANCES_2D,
COPY_MODE_FILL_SORT_BUFFER,
COPY_MODE_FILL_INSTANCES_WITH_SORT_BUFFER,
COPY_MODE_MAX,
@@ -1699,6 +1702,21 @@ public:
return multimesh->uniform_set_3d;
}
+ _FORCE_INLINE_ RID multimesh_get_2d_uniform_set(RID p_multimesh, RID p_shader, uint32_t p_set) const {
+ MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh);
+ if (!multimesh->uniform_set_2d.is_valid()) {
+ Vector<RD::Uniform> uniforms;
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 0;
+ u.ids.push_back(multimesh->buffer);
+ uniforms.push_back(u);
+ multimesh->uniform_set_2d = RD::get_singleton()->uniform_set_create(uniforms, p_shader, p_set);
+ }
+
+ return multimesh->uniform_set_2d;
+ }
+
/* IMMEDIATE API */
RID immediate_allocate() { return RID(); }
@@ -2093,6 +2111,7 @@ public:
RID particles_allocate();
void particles_initialize(RID p_particles_collision);
+ void particles_set_mode(RID p_particles, RS::ParticlesMode p_mode);
void particles_set_emitting(RID p_particles, bool p_emitting);
void particles_set_amount(RID p_particles, int p_amount);
void particles_set_lifetime(RID p_particles, float p_lifetime);
@@ -2137,6 +2156,12 @@ public:
virtual bool particles_is_inactive(RID p_particles) const;
+ _FORCE_INLINE_ RS::ParticlesMode particles_get_mode(RID p_particles) {
+ Particles *particles = particles_owner.getornull(p_particles);
+ ERR_FAIL_COND_V(!particles, RS::PARTICLES_MODE_2D);
+ return particles->mode;
+ }
+
_FORCE_INLINE_ uint32_t particles_get_amount(RID p_particles, uint32_t &r_trail_divisor) {
Particles *particles = particles_owner.getornull(p_particles);
ERR_FAIL_COND_V(!particles, 0);
diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl
index 8b97ec119f..cf4c77db0d 100644
--- a/servers/rendering/renderer_rd/shaders/canvas.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas.glsl
@@ -84,40 +84,84 @@ void main() {
mat4 world_matrix = mat4(vec4(draw_data.world_x, 0.0, 0.0), vec4(draw_data.world_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(draw_data.world_ofs, 0.0, 1.0));
-#if 0
- if (draw_data.flags & FLAGS_INSTANCING_ENABLED) {
- uint offset = draw_data.flags & FLAGS_INSTANCING_STRIDE_MASK;
- offset *= gl_InstanceIndex;
- mat4 instance_xform = mat4(
- vec4(texelFetch(instancing_buffer, offset + 0), texelFetch(instancing_buffer, offset + 1), 0.0, texelFetch(instancing_buffer, offset + 3)),
- vec4(texelFetch(instancing_buffer, offset + 4), texelFetch(instancing_buffer, offset + 5), 0.0, texelFetch(instancing_buffer, offset + 7)),
- vec4(0.0, 0.0, 1.0, 0.0),
- vec4(0.0, 0.0, 0.0, 1.0));
- offset += 8;
- if (draw_data.flags & FLAGS_INSTANCING_HAS_COLORS) {
- vec4 instance_color;
- if (draw_data.flags & FLAGS_INSTANCING_COLOR_8_BIT) {
- uint bits = floatBitsToUint(texelFetch(instancing_buffer, offset));
- instance_color = unpackUnorm4x8(bits);
- offset += 1;
- } else {
- instance_color = vec4(texelFetch(instancing_buffer, offset + 0), texelFetch(instancing_buffer, offset + 1), texelFetch(instancing_buffer, offset + 2), texelFetch(instancing_buffer, offset + 3));
- offset += 4;
- }
+#define FLAGS_INSTANCING_MASK 0x7F
+#define FLAGS_INSTANCING_HAS_COLORS (1 << 7)
+#define FLAGS_INSTANCING_HAS_CUSTOM_DATA (1 << 8)
+
+ uint instancing = draw_data.flags & FLAGS_INSTANCING_MASK;
+
+#ifdef USE_ATTRIBUTES
+
+ if (instancing > 1) {
+ // trails
+
+ uint stride = 2 + 1 + 1; //particles always uses this format
- color *= instance_color;
+ uint trail_size = instancing;
+
+ uint offset = trail_size * stride * gl_InstanceIndex;
+
+ mat4 matrix;
+ vec4 pcolor;
+ {
+ uint boffset = offset + bone_attrib.x * stride;
+ matrix = 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)) * weight_attrib.x;
+ pcolor = transforms.data[boffset + 3] * weight_attrib.x;
}
- if (draw_data.flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA) {
- if (draw_data.flags & FLAGS_INSTANCING_CUSTOM_DATA_8_BIT) {
- uint bits = floatBitsToUint(texelFetch(instancing_buffer, offset));
- instance_custom = unpackUnorm4x8(bits);
- } else {
- instance_custom = vec4(texelFetch(instancing_buffer, offset + 0), texelFetch(instancing_buffer, offset + 1), texelFetch(instancing_buffer, offset + 2), texelFetch(instancing_buffer, offset + 3));
+ if (weight_attrib.y > 0.001) {
+ uint boffset = offset + bone_attrib.y * stride;
+ matrix += 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)) * weight_attrib.y;
+ pcolor += transforms.data[boffset + 3] * weight_attrib.y;
+ }
+ if (weight_attrib.z > 0.001) {
+ uint boffset = offset + bone_attrib.z * stride;
+ matrix += 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)) * weight_attrib.z;
+ pcolor += transforms.data[boffset + 3] * weight_attrib.z;
+ }
+ if (weight_attrib.w > 0.001) {
+ uint boffset = offset + bone_attrib.w * stride;
+ matrix += 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)) * weight_attrib.w;
+ pcolor += transforms.data[boffset + 3] * weight_attrib.w;
+ }
+
+ instance_custom = transforms.data[offset + 4];
+
+ color *= pcolor;
+
+ matrix = transpose(matrix);
+ world_matrix = world_matrix * matrix;
+
+ } else
+#endif // USE_ATTRIBUTES
+
+ if (instancing == 1) {
+ uint stride = 2;
+ {
+ if (bool(draw_data.flags & FLAGS_INSTANCING_HAS_COLORS)) {
+ stride += 1;
+ }
+ if (bool(draw_data.flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) {
+ stride += 1;
}
}
- }
-#endif
+ uint offset = stride * gl_InstanceIndex;
+
+ 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.flags & FLAGS_INSTANCING_HAS_COLORS)) {
+ color *= transforms.data[offset];
+ offset += 1;
+ }
+
+ if (bool(draw_data.flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) {
+ instance_custom = transforms.data[offset];
+ }
+
+ matrix = transpose(matrix);
+ world_matrix = world_matrix * matrix;
+ }
#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE)
if (bool(draw_data.flags & FLAGS_USING_PARTICLES)) {
diff --git a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
index cf7678ea31..451f9b0089 100644
--- a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
@@ -5,12 +5,10 @@
#define SDF_MAX_LENGTH 16384.0
-#define FLAGS_INSTANCING_STRIDE_MASK 0xF
-#define FLAGS_INSTANCING_ENABLED (1 << 4)
-#define FLAGS_INSTANCING_HAS_COLORS (1 << 5)
-#define FLAGS_INSTANCING_COLOR_8BIT (1 << 6)
-#define FLAGS_INSTANCING_HAS_CUSTOM_DATA (1 << 7)
-#define FLAGS_INSTANCING_CUSTOM_DATA_8_BIT (1 << 8)
+//1 means enabled, 2+ means trails in use
+#define FLAGS_INSTANCING_MASK 0x7F
+#define FLAGS_INSTANCING_HAS_COLORS (1 << 7)
+#define FLAGS_INSTANCING_HAS_CUSTOM_DATA (1 << 8)
#define FLAGS_CLIP_RECT_UV (1 << 9)
#define FLAGS_TRANSPOSE_RECT (1 << 10)
diff --git a/servers/rendering/renderer_rd/shaders/particles_copy.glsl b/servers/rendering/renderer_rd/shaders/particles_copy.glsl
index 7804d66d1c..e2bebadf1a 100644
--- a/servers/rendering/renderer_rd/shaders/particles_copy.glsl
+++ b/servers/rendering/renderer_rd/shaders/particles_copy.glsl
@@ -163,11 +163,20 @@ void main() {
txform = mat4(vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0)); //zero scale, becomes invisible
}
+#ifdef MODE_2D
+
+ instances.data[write_offset + 0] = txform[0];
+ instances.data[write_offset + 1] = txform[1];
+ instances.data[write_offset + 2] = particles.data[particle].color;
+ instances.data[write_offset + 3] = particles.data[particle].custom;
+
+#else
instances.data[write_offset + 0] = txform[0];
instances.data[write_offset + 1] = txform[1];
instances.data[write_offset + 2] = txform[2];
instances.data[write_offset + 3] = particles.data[particle].color;
instances.data[write_offset + 4] = particles.data[particle].custom;
+#endif //MODE_2D
#endif
}
diff --git a/servers/rendering/renderer_rd/shaders/skeleton.glsl b/servers/rendering/renderer_rd/shaders/skeleton.glsl
index 669ffc961d..b831005256 100644
--- a/servers/rendering/renderer_rd/shaders/skeleton.glsl
+++ b/servers/rendering/renderer_rd/shaders/skeleton.glsl
@@ -74,6 +74,53 @@ void main() {
#ifdef MODE_2D
vec2 vertex = uintBitsToFloat(uvec2(src_vertices.data[src_offset + 0], src_vertices.data[src_offset + 1]));
+
+ if (params.has_blend_shape) {
+ float blend_total = 0.0;
+ vec2 blend_vertex = vec2(0.0);
+
+ for (uint i = 0; i < params.blend_shape_count; i++) {
+ float w = blend_shape_weights.data[i];
+ if (abs(w) > 0.0001) {
+ uint base_offset = (params.vertex_count * i + index) * params.vertex_stride;
+
+ blend_vertex += uintBitsToFloat(uvec2(src_blend_shapes.data[base_offset + 0], src_blend_shapes.data[base_offset + 1])) * w;
+
+ base_offset += 2;
+
+ blend_total += w;
+ }
+ }
+
+ if (params.normalized_blend_shapes) {
+ vertex = (1.0 - blend_total) * vertex;
+ }
+
+ vertex += blend_vertex;
+ }
+
+ if (params.has_skeleton) {
+ uint skin_offset = params.skin_stride * index;
+
+ uvec2 bones = uvec2(src_bone_weights.data[skin_offset + 0], src_bone_weights.data[skin_offset + 1]);
+ uvec2 bones_01 = uvec2(bones.x & 0xFFFF, bones.x >> 16) * 3; //pre-add xform offset
+ uvec2 bones_23 = uvec2(bones.y & 0xFFFF, bones.y >> 16) * 3;
+
+ skin_offset += params.skin_weight_offset;
+
+ uvec2 weights = uvec2(src_bone_weights.data[skin_offset + 0], src_bone_weights.data[skin_offset + 1]);
+
+ vec2 weights_01 = unpackUnorm2x16(weights.x);
+ vec2 weights_23 = unpackUnorm2x16(weights.y);
+
+ mat4 m = mat4(bone_transforms.data[bones_01.x], bone_transforms.data[bones_01.x + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)) * weights_01.x;
+ m += mat4(bone_transforms.data[bones_01.y], bone_transforms.data[bones_01.y + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)) * weights_01.y;
+ m += mat4(bone_transforms.data[bones_23.x], bone_transforms.data[bones_23.x + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)) * weights_23.x;
+ m += mat4(bone_transforms.data[bones_23.y], bone_transforms.data[bones_23.y + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)) * weights_23.y;
+
+ //reverse order because its transposed
+ vertex = (vec4(vertex, 0.0, 1.0) * m).xy;
+ }
#else
vec3 vertex;
vec3 normal;
diff --git a/servers/rendering/renderer_storage.h b/servers/rendering/renderer_storage.h
index 15d99c038e..cc4ff5d55e 100644
--- a/servers/rendering/renderer_storage.h
+++ b/servers/rendering/renderer_storage.h
@@ -484,6 +484,7 @@ public:
virtual RID particles_allocate() = 0;
virtual void particles_initialize(RID p_rid) = 0;
+ virtual void particles_set_mode(RID p_particles, RS::ParticlesMode p_mode) = 0;
virtual void particles_set_emitting(RID p_particles, bool p_emitting) = 0;
virtual bool particles_get_emitting(RID p_particles) = 0;
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index c76ae1bb34..bf47d497b6 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -478,6 +478,7 @@ public:
FUNCRIDSPLIT(particles)
+ FUNC2(particles_set_mode, RID, ParticlesMode)
FUNC2(particles_set_emitting, RID, bool)
FUNC1R(bool, particles_get_emitting, RID)
FUNC2(particles_set_amount, RID, int)