diff options
author | RĂ©mi Verschelde <remi@verschelde.fr> | 2022-12-01 23:22:42 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-12-01 23:22:42 +0100 |
commit | 8177e5d7de36bc77953f9d1495f973d39b3f0068 (patch) | |
tree | 1351b60bb750a238c43f36de305a035679db2e84 /drivers/gles3 | |
parent | 1e3919c409fa1f24db8ecde58f723fd59adfeccb (diff) | |
parent | f33ffd9ab4948e0fa649de9bbe490a1d4f977691 (diff) |
Merge pull request #69325 from clayjohn/GLES3-skeletons
Add Skeletons and Blend Shapes to the OpenGL renderer
Diffstat (limited to 'drivers/gles3')
-rw-r--r-- | drivers/gles3/rasterizer_canvas_gles3.cpp | 33 | ||||
-rw-r--r-- | drivers/gles3/rasterizer_scene_gles3.cpp | 5 | ||||
-rw-r--r-- | drivers/gles3/shaders/SCsub | 1 | ||||
-rw-r--r-- | drivers/gles3/shaders/canvas.glsl | 9 | ||||
-rw-r--r-- | drivers/gles3/shaders/skeleton.glsl | 269 | ||||
-rw-r--r-- | drivers/gles3/storage/mesh_storage.cpp | 581 | ||||
-rw-r--r-- | drivers/gles3/storage/mesh_storage.h | 38 | ||||
-rw-r--r-- | drivers/gles3/storage/utilities.cpp | 2 |
8 files changed, 870 insertions, 68 deletions
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 07d56b156c..83c20e6fa7 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -106,6 +106,7 @@ void RasterizerCanvasGLES3::_update_transform_to_mat4(const Transform3D &p_trans void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_light_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) { GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton(); Transform2D canvas_transform_inverse = p_canvas_transform.affine_inverse(); @@ -384,6 +385,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ Rect2 back_buffer_rect; bool backbuffer_copy = false; bool backbuffer_gen_mipmaps = false; + bool update_skeletons = false; Item *ci = p_item_list; Item *canvas_group_owner = nullptr; @@ -425,8 +427,27 @@ void RasterizerCanvasGLES3::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()) { + mesh_storage->mesh_instance_check_for_update(cm->mesh_instance); + update_skeletons = true; + } + } + c = c->next; + } + } + if (ci->canvas_group_owner != nullptr) { if (canvas_group_owner == nullptr) { + if (update_skeletons) { + mesh_storage->update_mesh_instances(); + update_skeletons = false; + } // Canvas group begins here, render until before this item _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, false); item_count = 0; @@ -455,6 +476,10 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ } if (ci == canvas_group_owner) { + if (update_skeletons) { + mesh_storage->update_mesh_instances(); + update_skeletons = false; + } _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, true); item_count = 0; @@ -468,6 +493,10 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ } if (backbuffer_copy) { + if (update_skeletons) { + mesh_storage->update_mesh_instances(); + update_skeletons = false; + } //render anything pending, including clearing if no items _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, false); @@ -492,6 +521,10 @@ void RasterizerCanvasGLES3::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) { + mesh_storage->update_mesh_instances(); + update_skeletons = false; + } _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, false); //then reset item_count = 0; diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 026ec85e6b..8250140c3f 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -420,6 +420,11 @@ void RasterizerSceneGLES3::_geometry_instance_update(RenderGeometryInstance *p_g } } else if (ginstance->data->base_type == RS::INSTANCE_MESH) { + if (mesh_storage->skeleton_is_valid(ginstance->data->skeleton)) { + if (ginstance->data->dirty_dependencies) { + mesh_storage->skeleton_update_dependency(ginstance->data->skeleton, &ginstance->data->dependency_tracker); + } + } } ginstance->store_transform_cache = store_transform; diff --git a/drivers/gles3/shaders/SCsub b/drivers/gles3/shaders/SCsub index 2686b1aa48..34713e7e29 100644 --- a/drivers/gles3/shaders/SCsub +++ b/drivers/gles3/shaders/SCsub @@ -21,3 +21,4 @@ if "GLES3_GLSL" in env["BUILDERS"]: env.GLES3_GLSL("canvas_sdf.glsl") env.GLES3_GLSL("particles.glsl") env.GLES3_GLSL("particles_copy.glsl") + env.GLES3_GLSL("skeleton.glsl") diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl index cdae05a516..60139de472 100644 --- a/drivers/gles3/shaders/canvas.glsl +++ b/drivers/gles3/shaders/canvas.glsl @@ -19,9 +19,6 @@ layout(location = 0) in vec2 vertex_attrib; layout(location = 3) in vec4 color_attrib; 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 = 1) in highp vec4 instance_xform0; @@ -81,8 +78,6 @@ void main() { uv = draw_data[draw_data_instance].uv_c; color = vec4(unpackHalf2x16(draw_data[draw_data_instance].color_c_rg), unpackHalf2x16(draw_data[draw_data_instance].color_c_ba)); } - uvec4 bones = uvec4(0, 0, 0, 0); - vec4 bone_weights = vec4(0.0); #elif defined(USE_ATTRIBUTES) draw_data_instance = gl_InstanceID; @@ -93,9 +88,6 @@ void main() { 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 vec4 instance_color = vec4(unpackHalf2x16(instance_color_custom_data.x), unpackHalf2x16(instance_color_custom_data.y)); color *= instance_color; @@ -110,7 +102,6 @@ void main() { vec2 uv = draw_data[draw_data_instance].src_rect.xy + abs(draw_data[draw_data_instance].src_rect.zw) * ((draw_data[draw_data_instance].flags & FLAGS_TRANSPOSE_RECT) != uint(0) ? vertex_base.yx : vertex_base.xy); vec4 color = draw_data[draw_data_instance].modulation; vec2 vertex = draw_data[draw_data_instance].dst_rect.xy + abs(draw_data[draw_data_instance].dst_rect.zw) * mix(vertex_base, vec2(1.0, 1.0) - vertex_base, lessThan(draw_data[draw_data_instance].src_rect.zw, vec2(0.0, 0.0))); - uvec4 bones = uvec4(0, 0, 0, 0); #endif diff --git a/drivers/gles3/shaders/skeleton.glsl b/drivers/gles3/shaders/skeleton.glsl new file mode 100644 index 0000000000..a1e3c098f4 --- /dev/null +++ b/drivers/gles3/shaders/skeleton.glsl @@ -0,0 +1,269 @@ +/* clang-format off */ +#[modes] + +mode_base_pass = +mode_blend_pass = #define MODE_BLEND_PASS + +#[specializations] + +MODE_2D = true +USE_BLEND_SHAPES = false +USE_SKELETON = false +USE_NORMAL = false +USE_TANGENT = false +FINAL_PASS = false +USE_EIGHT_WEIGHTS = false + +#[vertex] + +#include "stdlib_inc.glsl" + +#ifdef MODE_2D +#define VFORMAT vec2 +#else +#define VFORMAT vec3 +#endif + +#ifdef FINAL_PASS +#define OFORMAT vec2 +#else +#define OFORMAT uvec2 +#endif + +// These come from the source mesh and the output from previous passes. +layout(location = 0) in highp VFORMAT in_vertex; +#ifdef MODE_BLEND_PASS +#ifdef USE_NORMAL +layout(location = 1) in highp uvec2 in_normal; +#endif +#ifdef USE_TANGENT +layout(location = 2) in highp uvec2 in_tangent; +#endif +#else // MODE_BLEND_PASS +#ifdef USE_NORMAL +layout(location = 1) in highp vec2 in_normal; +#endif +#ifdef USE_TANGENT +layout(location = 2) in highp vec2 in_tangent; +#endif +#endif // MODE_BLEND_PASS + +#ifdef USE_SKELETON +#ifdef USE_EIGHT_WEIGHTS +layout(location = 10) in highp uvec4 in_bone_attrib; +layout(location = 11) in highp uvec4 in_bone_attrib2; +layout(location = 12) in mediump vec4 in_weight_attrib; +layout(location = 13) in mediump vec4 in_weight_attrib2; +#else +layout(location = 10) in highp uvec4 in_bone_attrib; +layout(location = 11) in mediump vec4 in_weight_attrib; +#endif + +uniform mediump sampler2D skeleton_texture; // texunit:0 +#endif + +/* clang-format on */ +#ifdef MODE_BLEND_PASS +layout(location = 3) in highp VFORMAT blend_vertex; +#ifdef USE_NORMAL +layout(location = 4) in highp vec2 blend_normal; +#endif +#ifdef USE_TANGENT +layout(location = 5) in highp vec2 blend_tangent; +#endif +#endif // MODE_BLEND_PASS + +out highp VFORMAT out_vertex; //tfb: + +#ifdef USE_NORMAL +flat out highp OFORMAT out_normal; //tfb:USE_NORMAL +#endif +#ifdef USE_TANGENT +flat out highp OFORMAT out_tangent; //tfb:USE_TANGENT +#endif + +#ifdef USE_BLEND_SHAPES +uniform highp float blend_weight; +uniform lowp float blend_shape_count; +#endif + +vec2 signNotZero(vec2 v) { + return mix(vec2(-1.0), vec2(1.0), greaterThanEqual(v.xy, vec2(0.0))); +} + +vec3 oct_to_vec3(vec2 oct) { + oct = oct * 2.0 - 1.0; + vec3 v = vec3(oct.xy, 1.0 - abs(oct.x) - abs(oct.y)); + if (v.z < 0.0) { + v.xy = (1.0 - abs(v.yx)) * signNotZero(v.xy); + } + return normalize(v); +} + +vec2 vec3_to_oct(vec3 e) { + e /= abs(e.x) + abs(e.y) + abs(e.z); + vec2 oct = e.z >= 0.0f ? e.xy : (vec2(1.0f) - abs(e.yx)) * signNotZero(e.xy); + return oct * 0.5f + 0.5f; +} + +vec4 oct_to_tang(vec2 oct_sign_encoded) { + // Binormal sign encoded in y component + vec2 oct = vec2(oct_sign_encoded.x, abs(oct_sign_encoded.y) * 2.0 - 1.0); + return vec4(oct_to_vec3(oct), sign(oct_sign_encoded.y)); +} + +vec2 tang_to_oct(vec4 base) { + vec2 oct = vec3_to_oct(base.xyz); + // Encode binormal sign in y component + oct.y = oct.y * 0.5f + 0.5f; + oct.y = base.w >= 0.0f ? oct.y : 1.0 - oct.y; + return oct; +} + +// Our original input for normals and tangents is 2 16-bit floats. +// Transform Feedback has to write out 32-bits per channel. +// Octahedral compression requires normalized vectors, but we need to store +// non-normalized vectors until the very end. +// Therefore, we will compress our normals into 16 bits using signed-normalized +// fixed point precision. This works well, because we know that each normal +// is no larger than |1| so we can normalize by dividing by the number of blend +// shapes. +uvec2 vec4_to_vec2(vec4 p_vec) { + return uvec2(packSnorm2x16(p_vec.xy), packSnorm2x16(p_vec.zw)); +} + +vec4 vec2_to_vec4(uvec2 p_vec) { + return vec4(unpackSnorm2x16(p_vec.x), unpackSnorm2x16(p_vec.y)); +} + +void main() { +#ifdef MODE_2D + out_vertex = in_vertex; + +#ifdef USE_BLEND_SHAPES +#ifdef MODE_BLEND_PASS + out_vertex = in_vertex + blend_vertex * blend_weight; +#else + out_vertex = in_vertex * blend_weight; +#endif +#ifdef FINAL_PASS + out_vertex = normalize(out_vertex); +#endif +#endif // USE_BLEND_SHAPES + +#ifdef USE_SKELETON + +#define TEX(m) texelFetch(skeleton_texture, ivec2(m % 256u, m / 256u), 0) +#define GET_BONE_MATRIX(a, b, w) mat2x4(TEX(a), TEX(b)) * w + + uvec4 bones = in_bone_attrib * uvec4(2u); + uvec4 bones_a = bones + uvec4(1u); + + highp mat2x4 m = GET_BONE_MATRIX(bones.x, bones_a.x, in_weight_attrib.x); + m += GET_BONE_MATRIX(bones.y, bones_a.y, in_weight_attrib.y); + m += GET_BONE_MATRIX(bones.z, bones_a.z, in_weight_attrib.z); + m += GET_BONE_MATRIX(bones.w, bones_a.w, in_weight_attrib.w); + + mat4 bone_matrix = mat4(m[0], m[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)); + + //reverse order because its transposed + out_vertex = (vec4(out_vertex, 0.0, 1.0) * bone_matrix).xy; +#endif // USE_SKELETON + +#else // MODE_2D + +#ifdef USE_BLEND_SHAPES +#ifdef MODE_BLEND_PASS + out_vertex = in_vertex + blend_vertex * blend_weight; + +#ifdef USE_NORMAL + vec3 normal = vec2_to_vec4(in_normal).xyz * blend_shape_count; + vec3 normal_blend = oct_to_vec3(blend_normal) * blend_weight; +#ifdef FINAL_PASS + out_normal = vec3_to_oct(normalize(normal + normal_blend)); +#else + out_normal = vec4_to_vec2(vec4(normal + normal_blend, 0.0) / blend_shape_count); +#endif +#endif // USE_NORMAL + +#ifdef USE_TANGENT + vec4 tangent = vec2_to_vec4(in_tangent) * blend_shape_count; + vec4 tangent_blend = oct_to_tang(blend_tangent) * blend_weight; +#ifdef FINAL_PASS + out_tangent = tang_to_oct(vec4(normalize(tangent.xyz + tangent_blend.xyz), tangent.w)); +#else + out_tangent = vec4_to_vec2(vec4((tangent.xyz + tangent_blend.xyz) / blend_shape_count, tangent.w)); +#endif +#endif // USE_TANGENT + +#else // MODE_BLEND_PASS + out_vertex = in_vertex * blend_weight; + +#ifdef USE_NORMAL + vec3 normal = oct_to_vec3(in_normal); + out_normal = vec4_to_vec2(vec4(normal * blend_weight / blend_shape_count, 0.0)); +#endif +#ifdef USE_TANGENT + vec4 tangent = oct_to_tang(in_tangent); + out_tangent = vec4_to_vec2(vec4(tangent.rgb * blend_weight / blend_shape_count, tangent.w)); +#endif +#endif // MODE_BLEND_PASS +#else // USE_BLEND_SHAPES + + // Make attributes available to the skeleton shader if not written by blend shapes. + out_vertex = in_vertex; +#ifdef USE_NORMAL + out_normal = in_normal; +#endif +#ifdef USE_TANGENT + out_tangent = in_tangent; +#endif +#endif // USE_BLEND_SHAPES + +#ifdef USE_SKELETON + +#define TEX(m) texelFetch(skeleton_texture, ivec2(m % 256u, m / 256u), 0) +#define GET_BONE_MATRIX(a, b, c, w) mat4(TEX(a), TEX(b), TEX(c), vec4(0.0, 0.0, 0.0, 1.0)) * w + + uvec4 bones = in_bone_attrib * uvec4(3); + uvec4 bones_a = bones + uvec4(1); + uvec4 bones_b = bones + uvec4(2); + + highp mat4 m; + m = GET_BONE_MATRIX(bones.x, bones_a.x, bones_b.x, in_weight_attrib.x); + m += GET_BONE_MATRIX(bones.y, bones_a.y, bones_b.y, in_weight_attrib.y); + m += GET_BONE_MATRIX(bones.z, bones_a.z, bones_b.z, in_weight_attrib.z); + m += GET_BONE_MATRIX(bones.w, bones_a.w, bones_b.w, in_weight_attrib.w); + +#ifdef USE_EIGHT_WEIGHTS + bones = in_bone_attrib2 * uvec4(3); + bones_a = bones + uvec4(1); + bones_b = bones + uvec4(2); + + m += GET_BONE_MATRIX(bones.x, bones_a.x, bones_b.x, in_weight_attrib2.x); + m += GET_BONE_MATRIX(bones.y, bones_a.y, bones_b.y, in_weight_attrib2.y); + m += GET_BONE_MATRIX(bones.z, bones_a.z, bones_b.z, in_weight_attrib2.z); + m += GET_BONE_MATRIX(bones.w, bones_a.w, bones_b.w, in_weight_attrib2.w); +#endif + + // Reverse order because its transposed. + out_vertex = (vec4(out_vertex, 1.0) * m).xyz; +#ifdef USE_NORMAL + vec3 vertex_normal = oct_to_vec3(out_normal); + out_normal = vec3_to_oct(normalize((vec4(vertex_normal, 0.0) * m).xyz)); +#endif // USE_NORMAL +#ifdef USE_TANGENT + vec4 vertex_tangent = oct_to_tang(out_tangent); + out_tangent = tang_to_oct(vec4(normalize((vec4(vertex_tangent.xyz, 0.0) * m).xyz), vertex_tangent.w)); +#endif // USE_TANGENT +#endif // USE_SKELETON +#endif // MODE_2D +} + +/* clang-format off */ +#[fragment] + +void main() { + +} +/* clang-format on */ diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp index a47df42500..285f32f1a5 100644 --- a/drivers/gles3/storage/mesh_storage.cpp +++ b/drivers/gles3/storage/mesh_storage.cpp @@ -44,10 +44,16 @@ MeshStorage *MeshStorage::get_singleton() { MeshStorage::MeshStorage() { singleton = this; + + { + skeleton_shader.shader.initialize(); + skeleton_shader.shader_version = skeleton_shader.shader.version_create(); + } } MeshStorage::~MeshStorage() { singleton = nullptr; + skeleton_shader.shader.version_free(skeleton_shader.shader_version); } /* MESH API */ @@ -88,10 +94,6 @@ void MeshStorage::mesh_set_blend_shape_count(RID p_mesh, int p_blend_shape_count ERR_FAIL_COND(mesh->surface_count > 0); //surfaces already exist mesh->blend_shape_count = p_blend_shape_count; - - if (p_blend_shape_count > 0) { - WARN_PRINT_ONCE("blend shapes not supported by GLES3 renderer yet"); - } } bool MeshStorage::mesh_needs_instance(RID p_mesh, bool p_has_skeleton) { @@ -114,7 +116,6 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) uint32_t attrib_stride = 0; uint32_t skin_stride = 0; - // TODO: I think this should be <=, but it is copied from RendererRD, will have to verify later for (int i = 0; i < RS::ARRAY_WEIGHTS; i++) { if ((p_surface.format & (1 << i))) { switch (i) { @@ -248,8 +249,77 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) s->aabb = p_surface.aabb; s->bone_aabbs = p_surface.bone_aabbs; //only really useful for returning them. - if (mesh->blend_shape_count > 0) { - //s->blend_shape_buffer = RD::get_singleton()->storage_buffer_create(p_surface.blend_shape_data.size(), p_surface.blend_shape_data); + if (p_surface.skin_data.size() || mesh->blend_shape_count > 0) { + // Size must match the size of the vertex array. + int size = p_surface.vertex_data.size(); + int vertex_size = 0; + int stride = 0; + int normal_offset = 0; + int tangent_offset = 0; + if ((p_surface.format & (1 << RS::ARRAY_VERTEX))) { + if (p_surface.format & RS::ARRAY_FLAG_USE_2D_VERTICES) { + vertex_size = 2; + } else { + vertex_size = 3; + } + stride = sizeof(float) * vertex_size; + } + if ((p_surface.format & (1 << RS::ARRAY_NORMAL))) { + normal_offset = stride; + stride += sizeof(uint16_t) * 2; + } + if ((p_surface.format & (1 << RS::ARRAY_TANGENT))) { + tangent_offset = stride; + stride += sizeof(uint16_t) * 2; + } + + if (mesh->blend_shape_count > 0) { + // Blend shapes are passed as one large array, for OpenGL, we need to split each of them into their own buffer + s->blend_shapes = memnew_arr(Mesh::Surface::BlendShape, mesh->blend_shape_count); + + for (uint32_t i = 0; i < mesh->blend_shape_count; i++) { + glGenVertexArrays(1, &s->blend_shapes[i].vertex_array); + glBindVertexArray(s->blend_shapes[i].vertex_array); + glGenBuffers(1, &s->blend_shapes[i].vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, s->blend_shapes[i].vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, size, p_surface.blend_shape_data.ptr() + i * size, (s->format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); + + if ((p_surface.format & (1 << RS::ARRAY_VERTEX))) { + glEnableVertexAttribArray(RS::ARRAY_VERTEX + 3); + glVertexAttribPointer(RS::ARRAY_VERTEX + 3, vertex_size, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(0)); + } + if ((p_surface.format & (1 << RS::ARRAY_NORMAL))) { + glEnableVertexAttribArray(RS::ARRAY_NORMAL + 3); + glVertexAttribPointer(RS::ARRAY_NORMAL + 3, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, CAST_INT_TO_UCHAR_PTR(normal_offset)); + } + if ((p_surface.format & (1 << RS::ARRAY_TANGENT))) { + glEnableVertexAttribArray(RS::ARRAY_TANGENT + 3); + glVertexAttribPointer(RS::ARRAY_TANGENT + 3, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, CAST_INT_TO_UCHAR_PTR(tangent_offset)); + } + } + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + // Create a vertex array to use for skeleton/blend shapes. + glGenVertexArrays(1, &s->skeleton_vertex_array); + glBindVertexArray(s->skeleton_vertex_array); + glBindBuffer(GL_ARRAY_BUFFER, s->vertex_buffer); + + if ((p_surface.format & (1 << RS::ARRAY_VERTEX))) { + glEnableVertexAttribArray(RS::ARRAY_VERTEX); + glVertexAttribPointer(RS::ARRAY_VERTEX, vertex_size, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(0)); + } + if ((p_surface.format & (1 << RS::ARRAY_NORMAL))) { + glEnableVertexAttribArray(RS::ARRAY_NORMAL); + glVertexAttribPointer(RS::ARRAY_NORMAL, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, CAST_INT_TO_UCHAR_PTR(normal_offset)); + } + if ((p_surface.format & (1 << RS::ARRAY_TANGENT))) { + glEnableVertexAttribArray(RS::ARRAY_TANGENT); + glVertexAttribPointer(RS::ARRAY_TANGENT, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, CAST_INT_TO_UCHAR_PTR(tangent_offset)); + } + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); } if (mesh->surface_count == 0) { @@ -412,7 +482,13 @@ RS::SurfaceData MeshStorage::mesh_get_surface(RID p_mesh, int p_surface) const { } sd.bone_aabbs = s.bone_aabbs; - glBindBuffer(GL_ARRAY_BUFFER, 0); + + if (mesh->blend_shape_count) { + sd.blend_shape_data = Vector<uint8_t>(); + for (uint32_t i = 0; i < mesh->blend_shape_count; i++) { + sd.blend_shape_data.append_array(Utilities::buffer_get_data(GL_ARRAY_BUFFER, s.blend_shapes[i].vertex_buffer, s.vertex_buffer_size)); + } + } return sd; } @@ -608,6 +684,24 @@ void MeshStorage::mesh_clear(RID p_mesh) { memdelete_arr(s.lods); } + if (mesh->blend_shape_count) { + for (uint32_t j = 0; j < mesh->blend_shape_count; j++) { + if (s.blend_shapes[j].vertex_buffer != 0) { + glDeleteBuffers(1, &s.blend_shapes[j].vertex_buffer); + s.blend_shapes[j].vertex_buffer = 0; + } + if (s.blend_shapes[j].vertex_array != 0) { + glDeleteVertexArrays(1, &s.blend_shapes[j].vertex_array); + s.blend_shapes[j].vertex_array = 0; + } + } + memdelete_arr(s.blend_shapes); + } + if (s.skeleton_vertex_array != 0) { + glDeleteVertexArrays(1, &s.skeleton_vertex_array); + s.skeleton_vertex_array = 0; + } + memdelete(mesh->surfaces[i]); } if (mesh->surfaces) { @@ -663,15 +757,15 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V case RS::ARRAY_NORMAL: { attribs[i].offset = vertex_stride; attribs[i].size = 2; - attribs[i].type = GL_UNSIGNED_SHORT; - vertex_stride += sizeof(uint16_t) * 2; + attribs[i].type = (mis ? GL_FLOAT : GL_UNSIGNED_SHORT); + vertex_stride += sizeof(uint16_t) * 2 * (mis ? 2 : 1); attribs[i].normalized = GL_TRUE; } break; case RS::ARRAY_TANGENT: { attribs[i].offset = vertex_stride; attribs[i].size = 2; - attribs[i].type = GL_UNSIGNED_SHORT; - vertex_stride += sizeof(uint16_t) * 2; + attribs[i].type = (mis ? GL_FLOAT : GL_UNSIGNED_SHORT); + vertex_stride += sizeof(uint16_t) * 2 * (mis ? 2 : 1); attribs[i].normalized = GL_TRUE; } break; case RS::ARRAY_COLOR: { @@ -716,7 +810,7 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V attribs[i].offset = skin_stride; attribs[i].size = 4; attribs[i].type = GL_UNSIGNED_SHORT; - attributes_stride += 4 * sizeof(uint16_t); + skin_stride += 4 * sizeof(uint16_t); attribs[i].normalized = GL_FALSE; attribs[i].integer = true; } break; @@ -724,7 +818,7 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V attribs[i].offset = skin_stride; attribs[i].size = 4; attribs[i].type = GL_UNSIGNED_SHORT; - attributes_stride += 4 * sizeof(uint16_t); + skin_stride += 4 * sizeof(uint16_t); attribs[i].normalized = GL_TRUE; } break; } @@ -815,7 +909,7 @@ void MeshStorage::mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int ERR_FAIL_COND(!mi); ERR_FAIL_INDEX(p_shape, (int)mi->blend_weights.size()); mi->blend_weights[p_shape] = p_weight; - mi->weights_dirty = true; + mi->dirty = true; } void MeshStorage::_mesh_instance_clear(MeshInstance *mi) { @@ -827,38 +921,65 @@ void MeshStorage::_mesh_instance_clear(MeshInstance *mi) { } memfree(mi->surfaces[i].versions); } + + if (mi->surfaces[i].vertex_buffers[0] != 0) { + glDeleteBuffers(2, mi->surfaces[i].vertex_buffers); + mi->surfaces[i].vertex_buffers[0] = 0; + mi->surfaces[i].vertex_buffers[1] = 0; + } + if (mi->surfaces[i].vertex_buffer != 0) { glDeleteBuffers(1, &mi->surfaces[i].vertex_buffer); mi->surfaces[i].vertex_buffer = 0; } } mi->surfaces.clear(); - - if (mi->blend_weights_buffer != 0) { - glDeleteBuffers(1, &mi->blend_weights_buffer); - mi->blend_weights_buffer = 0; - } mi->blend_weights.clear(); - mi->weights_dirty = false; mi->skeleton_version = 0; } void MeshStorage::_mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint32_t p_surface) { - if (mesh->blend_shape_count > 0 && mi->blend_weights_buffer == 0) { + if (mesh->blend_shape_count > 0) { mi->blend_weights.resize(mesh->blend_shape_count); for (uint32_t i = 0; i < mi->blend_weights.size(); i++) { - mi->blend_weights[i] = 0; + mi->blend_weights[i] = 0.0; } - // Todo allocate buffer for blend_weights and copy data to it - //mi->blend_weights_buffer = RD::get_singleton()->storage_buffer_create(sizeof(float) * mi->blend_weights.size(), mi->blend_weights.to_byte_array()); - - mi->weights_dirty = true; } MeshInstance::Surface s; - if (mesh->blend_shape_count > 0 || (mesh->surfaces[p_surface]->format & RS::ARRAY_FORMAT_BONES)) { - //surface warrants transform - //s.vertex_buffer = RD::get_singleton()->vertex_buffer_create(mesh->surfaces[p_surface]->vertex_buffer_size, Vector<uint8_t>(), true); + if ((mesh->blend_shape_count > 0 || (mesh->surfaces[p_surface]->format & RS::ARRAY_FORMAT_BONES)) && mesh->surfaces[p_surface]->vertex_buffer_size > 0) { + // Cache surface properties + s.format_cache = mesh->surfaces[p_surface]->format; + if ((s.format_cache & (1 << RS::ARRAY_VERTEX))) { + if (s.format_cache & RS::ARRAY_FLAG_USE_2D_VERTICES) { + s.vertex_size_cache = 2; + } else { + s.vertex_size_cache = 3; + } + s.vertex_stride_cache = sizeof(float) * s.vertex_size_cache; + } + if ((s.format_cache & (1 << RS::ARRAY_NORMAL))) { + s.vertex_normal_offset_cache = s.vertex_stride_cache; + s.vertex_stride_cache += sizeof(uint32_t) * 2; + } + if ((s.format_cache & (1 << RS::ARRAY_TANGENT))) { + s.vertex_tangent_offset_cache = s.vertex_stride_cache; + s.vertex_stride_cache += sizeof(uint32_t) * 2; + } + + // Buffer to be used for rendering. Final output of skeleton and blend shapes. + glGenBuffers(1, &s.vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, s.vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, s.vertex_stride_cache * mesh->surfaces[p_surface]->vertex_count, nullptr, GL_DYNAMIC_DRAW); + if (mesh->blend_shape_count > 0) { + // Ping-Pong buffers for processing blendshapes. + glGenBuffers(2, s.vertex_buffers); + for (uint32_t i = 0; i < 2; i++) { + glBindBuffer(GL_ARRAY_BUFFER, s.vertex_buffers[i]); + glBufferData(GL_ARRAY_BUFFER, s.vertex_stride_cache * mesh->surfaces[p_surface]->vertex_count, nullptr, GL_DYNAMIC_DRAW); + } + } + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind } mi->surfaces.push_back(s); @@ -870,11 +991,6 @@ void MeshStorage::mesh_instance_check_for_update(RID p_mesh_instance) { bool needs_update = mi->dirty; - if (mi->weights_dirty && !mi->weight_update_list.in_list()) { - dirty_mesh_instance_weights.add(&mi->weight_update_list); - needs_update = true; - } - if (mi->array_update_list.in_list()) { return; } @@ -891,22 +1007,223 @@ void MeshStorage::mesh_instance_check_for_update(RID p_mesh_instance) { } } -void MeshStorage::update_mesh_instances() { - while (dirty_mesh_instance_weights.first()) { - MeshInstance *mi = dirty_mesh_instance_weights.first()->self(); +void MeshStorage::_blend_shape_bind_mesh_instance_buffer(MeshInstance *p_mi, uint32_t p_surface) { + glBindBuffer(GL_ARRAY_BUFFER, p_mi->surfaces[p_surface].vertex_buffers[0]); - if (mi->blend_weights_buffer != 0) { - //RD::get_singleton()->buffer_update(mi->blend_weights_buffer, 0, mi->blend_weights.size() * sizeof(float), mi->blend_weights.ptr()); - } - dirty_mesh_instance_weights.remove(&mi->weight_update_list); - mi->weights_dirty = false; + if ((p_mi->surfaces[p_surface].format_cache & (1 << RS::ARRAY_VERTEX))) { + glEnableVertexAttribArray(RS::ARRAY_VERTEX); + glVertexAttribPointer(RS::ARRAY_VERTEX, p_mi->surfaces[p_surface].vertex_size_cache, GL_FLOAT, GL_FALSE, p_mi->surfaces[p_surface].vertex_stride_cache, CAST_INT_TO_UCHAR_PTR(0)); + } else { + glDisableVertexAttribArray(RS::ARRAY_VERTEX); } + if ((p_mi->surfaces[p_surface].format_cache & (1 << RS::ARRAY_NORMAL))) { + glEnableVertexAttribArray(RS::ARRAY_NORMAL); + glVertexAttribIPointer(RS::ARRAY_NORMAL, 2, GL_UNSIGNED_INT, p_mi->surfaces[p_surface].vertex_stride_cache, CAST_INT_TO_UCHAR_PTR(p_mi->surfaces[p_surface].vertex_normal_offset_cache)); + } else { + glDisableVertexAttribArray(RS::ARRAY_NORMAL); + } + if ((p_mi->surfaces[p_surface].format_cache & (1 << RS::ARRAY_TANGENT))) { + glEnableVertexAttribArray(RS::ARRAY_TANGENT); + glVertexAttribIPointer(RS::ARRAY_TANGENT, 2, GL_UNSIGNED_INT, p_mi->surfaces[p_surface].vertex_stride_cache, CAST_INT_TO_UCHAR_PTR(p_mi->surfaces[p_surface].vertex_tangent_offset_cache)); + } else { + glDisableVertexAttribArray(RS::ARRAY_TANGENT); + } +} + +void MeshStorage::_compute_skeleton(MeshInstance *p_mi, Skeleton *p_sk, uint32_t p_surface) { + glBindBuffer(GL_ARRAY_BUFFER, 0); + + // Add in the bones and weights. + glBindBuffer(GL_ARRAY_BUFFER, p_mi->mesh->surfaces[p_surface]->skin_buffer); + + bool use_8_weights = p_mi->surfaces[p_surface].format_cache & RS::ARRAY_FLAG_USE_8_BONE_WEIGHTS; + int skin_stride = sizeof(int16_t) * (use_8_weights ? 16 : 8); + glEnableVertexAttribArray(RS::ARRAY_BONES); + glVertexAttribIPointer(RS::ARRAY_BONES, 4, GL_UNSIGNED_SHORT, skin_stride, CAST_INT_TO_UCHAR_PTR(0)); + if (use_8_weights) { + glEnableVertexAttribArray(11); + glVertexAttribIPointer(11, 4, GL_UNSIGNED_SHORT, skin_stride, CAST_INT_TO_UCHAR_PTR(4 * sizeof(uint16_t))); + glEnableVertexAttribArray(12); + glVertexAttribPointer(12, 4, GL_UNSIGNED_SHORT, GL_TRUE, skin_stride, CAST_INT_TO_UCHAR_PTR(8 * sizeof(uint16_t))); + glEnableVertexAttribArray(13); + glVertexAttribPointer(13, 4, GL_UNSIGNED_SHORT, GL_TRUE, skin_stride, CAST_INT_TO_UCHAR_PTR(12 * sizeof(uint16_t))); + } else { + glEnableVertexAttribArray(RS::ARRAY_WEIGHTS); + glVertexAttribPointer(RS::ARRAY_WEIGHTS, 4, GL_UNSIGNED_SHORT, GL_TRUE, skin_stride, CAST_INT_TO_UCHAR_PTR(4 * sizeof(uint16_t))); + } + + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, p_mi->surfaces[p_surface].vertex_buffer); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, p_sk->transforms_texture); + + glBeginTransformFeedback(GL_POINTS); + glDrawArrays(GL_POINTS, 0, p_mi->mesh->surfaces[p_surface]->vertex_count); + glEndTransformFeedback(); + + glDisableVertexAttribArray(RS::ARRAY_BONES); + glDisableVertexAttribArray(RS::ARRAY_WEIGHTS); + glDisableVertexAttribArray(RS::ARRAY_BONES + 2); + glDisableVertexAttribArray(RS::ARRAY_WEIGHTS + 2); + glBindVertexArray(0); + glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0); +} + +void MeshStorage::update_mesh_instances() { if (dirty_mesh_instance_arrays.first() == nullptr) { return; //nothing to do } + glEnable(GL_RASTERIZER_DISCARD); // Process skeletons and blend shapes using transform feedback - // TODO: Implement when working on skeletons and blend shapes + while (dirty_mesh_instance_arrays.first()) { + MeshInstance *mi = dirty_mesh_instance_arrays.first()->self(); + + Skeleton *sk = skeleton_owner.get_or_null(mi->skeleton); + + // Precompute base weight if using blend shapes. + float base_weight = 1.0; + if (mi->mesh->blend_shape_count && mi->mesh->blend_shape_mode == RS::BLEND_SHAPE_MODE_NORMALIZED) { + for (uint32_t i = 0; i < mi->mesh->blend_shape_count; i++) { + base_weight -= mi->blend_weights[i]; + } + } + + for (uint32_t i = 0; i < mi->surfaces.size(); i++) { + if (mi->surfaces[i].vertex_buffer == 0 || mi->mesh->surfaces[i]->skeleton_vertex_array == 0) { + continue; + } + + bool array_is_2d = mi->surfaces[i].format_cache & RS::ARRAY_FLAG_USE_2D_VERTICES; + bool can_use_skeleton = sk != nullptr && sk->use_2d == array_is_2d && (mi->surfaces[i].format_cache & RS::ARRAY_FORMAT_BONES); + bool use_8_weights = mi->surfaces[i].format_cache & RS::ARRAY_FLAG_USE_8_BONE_WEIGHTS; + + // Always process blend shapes first. + if (mi->mesh->blend_shape_count) { + SkeletonShaderGLES3::ShaderVariant variant = SkeletonShaderGLES3::MODE_BASE_PASS; + uint64_t specialization = 0; + specialization |= array_is_2d ? SkeletonShaderGLES3::MODE_2D : 0; + specialization |= SkeletonShaderGLES3::USE_BLEND_SHAPES; + if (!array_is_2d) { + if ((mi->surfaces[i].format_cache & (1 << RS::ARRAY_NORMAL))) { + specialization |= SkeletonShaderGLES3::USE_NORMAL; + } + if ((mi->surfaces[i].format_cache & (1 << RS::ARRAY_TANGENT))) { + specialization |= SkeletonShaderGLES3::USE_TANGENT; + } + } + + bool success = skeleton_shader.shader.version_bind_shader(skeleton_shader.shader_version, variant, specialization); + if (!success) { + continue; + } + + skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::BLEND_WEIGHT, base_weight, skeleton_shader.shader_version, variant, specialization); + skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::BLEND_SHAPE_COUNT, float(mi->mesh->blend_shape_count), skeleton_shader.shader_version, variant, specialization); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(mi->mesh->surfaces[i]->skeleton_vertex_array); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mi->surfaces[i].vertex_buffers[0]); + glBeginTransformFeedback(GL_POINTS); + glDrawArrays(GL_POINTS, 0, mi->mesh->surfaces[i]->vertex_count); + glEndTransformFeedback(); + + variant = SkeletonShaderGLES3::MODE_BLEND_PASS; + success = skeleton_shader.shader.version_bind_shader(skeleton_shader.shader_version, variant, specialization); + if (!success) { + continue; + } + + //Do the last blend shape separately, as it can be combined with the skeleton pass. + for (uint32_t bs = 0; bs < mi->mesh->blend_shape_count - 1; bs++) { + float weight = mi->blend_weights[bs]; + + if (Math::is_zero_approx(weight)) { + //not bother with this one + continue; + } + skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::BLEND_WEIGHT, weight, skeleton_shader.shader_version, variant, specialization); + skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::BLEND_SHAPE_COUNT, float(mi->mesh->blend_shape_count), skeleton_shader.shader_version, variant, specialization); + + glBindVertexArray(mi->mesh->surfaces[i]->blend_shapes[bs].vertex_array); + _blend_shape_bind_mesh_instance_buffer(mi, i); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mi->surfaces[i].vertex_buffers[1]); + + glBeginTransformFeedback(GL_POINTS); + glDrawArrays(GL_POINTS, 0, mi->mesh->surfaces[i]->vertex_count); + glEndTransformFeedback(); + + SWAP(mi->surfaces[i].vertex_buffers[0], mi->surfaces[i].vertex_buffers[1]); + } + uint32_t bs = mi->mesh->blend_shape_count - 1; + + float weight = mi->blend_weights[bs]; + + glBindVertexArray(mi->mesh->surfaces[i]->blend_shapes[bs].vertex_array); + _blend_shape_bind_mesh_instance_buffer(mi, i); + + specialization |= can_use_skeleton ? SkeletonShaderGLES3::USE_SKELETON : 0; + specialization |= (can_use_skeleton && use_8_weights) ? SkeletonShaderGLES3::USE_EIGHT_WEIGHTS : 0; + specialization |= SkeletonShaderGLES3::FINAL_PASS; + success = skeleton_shader.shader.version_bind_shader(skeleton_shader.shader_version, variant, specialization); + if (!success) { + continue; + } + + skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::BLEND_WEIGHT, weight, skeleton_shader.shader_version, variant, specialization); + skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::BLEND_SHAPE_COUNT, float(mi->mesh->blend_shape_count), skeleton_shader.shader_version, variant, specialization); + + if (can_use_skeleton) { + // Do last blendshape in the same pass as the Skeleton. + _compute_skeleton(mi, sk, i); + can_use_skeleton = false; + } else { + // Do last blendshape by itself and prepare vertex data for use by the renderer. + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mi->surfaces[i].vertex_buffer); + + glBeginTransformFeedback(GL_POINTS); + glDrawArrays(GL_POINTS, 0, mi->mesh->surfaces[i]->vertex_count); + glEndTransformFeedback(); + } + + glBindVertexArray(0); + glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0); + } + + // This branch should only execute when Skeleton is run by itself. + if (can_use_skeleton) { + SkeletonShaderGLES3::ShaderVariant variant = SkeletonShaderGLES3::MODE_BASE_PASS; + uint64_t specialization = 0; + specialization |= array_is_2d ? SkeletonShaderGLES3::MODE_2D : 0; + specialization |= SkeletonShaderGLES3::USE_SKELETON; + specialization |= SkeletonShaderGLES3::FINAL_PASS; + specialization |= use_8_weights ? SkeletonShaderGLES3::USE_EIGHT_WEIGHTS : 0; + if (!array_is_2d) { + if ((mi->surfaces[i].format_cache & (1 << RS::ARRAY_NORMAL))) { + specialization |= SkeletonShaderGLES3::USE_NORMAL; + } + if ((mi->surfaces[i].format_cache & (1 << RS::ARRAY_TANGENT))) { + specialization |= SkeletonShaderGLES3::USE_TANGENT; + } + } + + bool success = skeleton_shader.shader.version_bind_shader(skeleton_shader.shader_version, variant, specialization); + if (!success) { + continue; + } + + glBindVertexArray(mi->mesh->surfaces[i]->skeleton_vertex_array); + _compute_skeleton(mi, sk, i); + } + } + mi->dirty = false; + if (sk) { + mi->skeleton_version = sk->version; + } + dirty_mesh_instance_arrays.remove(&mi->array_update_list); + } + glDisable(GL_RASTERIZER_DISCARD); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0); } /* MULTIMESH API */ @@ -1577,45 +1894,207 @@ void MeshStorage::_update_dirty_multimeshes() { /* SKELETON API */ RID MeshStorage::skeleton_allocate() { - return RID(); + return skeleton_owner.allocate_rid(); } void MeshStorage::skeleton_initialize(RID p_rid) { + skeleton_owner.initialize_rid(p_rid, Skeleton()); } void MeshStorage::skeleton_free(RID p_rid) { + _update_dirty_skeletons(); + skeleton_allocate_data(p_rid, 0); + Skeleton *skeleton = skeleton_owner.get_or_null(p_rid); + skeleton->dependency.deleted_notify(p_rid); + skeleton_owner.free(p_rid); +} + +void MeshStorage::_skeleton_make_dirty(Skeleton *skeleton) { + if (!skeleton->dirty) { + skeleton->dirty = true; + skeleton->dirty_list = skeleton_dirty_list; + skeleton_dirty_list = skeleton; + } } void MeshStorage::skeleton_allocate_data(RID p_skeleton, int p_bones, bool p_2d_skeleton) { + Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); + ERR_FAIL_COND(!skeleton); + ERR_FAIL_COND(p_bones < 0); + + if (skeleton->size == p_bones && skeleton->use_2d == p_2d_skeleton) { + return; + } + + skeleton->size = p_bones; + skeleton->use_2d = p_2d_skeleton; + skeleton->height = (p_bones * (p_2d_skeleton ? 2 : 3)) / 256; + if ((p_bones * (p_2d_skeleton ? 2 : 3)) % 256) { + skeleton->height++; + } + + if (skeleton->transforms_texture != 0) { + glDeleteTextures(1, &skeleton->transforms_texture); + skeleton->transforms_texture = 0; + skeleton->data.clear(); + } + + if (skeleton->size) { + skeleton->data.resize(256 * skeleton->height * 4); + glGenTextures(1, &skeleton->transforms_texture); + glBindTexture(GL_TEXTURE_2D, skeleton->transforms_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 256, skeleton->height, 0, GL_RGBA, GL_FLOAT, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_2D, 0); + + memset(skeleton->data.ptrw(), 0, skeleton->data.size() * sizeof(float)); + + _skeleton_make_dirty(skeleton); + } + + skeleton->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_SKELETON_DATA); } void MeshStorage::skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) { + Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); + + ERR_FAIL_NULL(skeleton); + ERR_FAIL_COND(!skeleton->use_2d); + + skeleton->base_transform_2d = p_base_transform; } int MeshStorage::skeleton_get_bone_count(RID p_skeleton) const { - return 0; + Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); + ERR_FAIL_COND_V(!skeleton, 0); + + return skeleton->size; } void MeshStorage::skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform3D &p_transform) { + Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); + + ERR_FAIL_COND(!skeleton); + ERR_FAIL_INDEX(p_bone, skeleton->size); + ERR_FAIL_COND(skeleton->use_2d); + + float *dataptr = skeleton->data.ptrw() + p_bone * 12; + + dataptr[0] = p_transform.basis.rows[0][0]; + dataptr[1] = p_transform.basis.rows[0][1]; + dataptr[2] = p_transform.basis.rows[0][2]; + dataptr[3] = p_transform.origin.x; + dataptr[4] = p_transform.basis.rows[1][0]; + dataptr[5] = p_transform.basis.rows[1][1]; + dataptr[6] = p_transform.basis.rows[1][2]; + dataptr[7] = p_transform.origin.y; + dataptr[8] = p_transform.basis.rows[2][0]; + dataptr[9] = p_transform.basis.rows[2][1]; + dataptr[10] = p_transform.basis.rows[2][2]; + dataptr[11] = p_transform.origin.z; + + _skeleton_make_dirty(skeleton); } Transform3D MeshStorage::skeleton_bone_get_transform(RID p_skeleton, int p_bone) const { - return Transform3D(); + Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); + + ERR_FAIL_COND_V(!skeleton, Transform3D()); + ERR_FAIL_INDEX_V(p_bone, skeleton->size, Transform3D()); + ERR_FAIL_COND_V(skeleton->use_2d, Transform3D()); + + const float *dataptr = skeleton->data.ptr() + p_bone * 12; + + Transform3D t; + + t.basis.rows[0][0] = dataptr[0]; + t.basis.rows[0][1] = dataptr[1]; + t.basis.rows[0][2] = dataptr[2]; + t.origin.x = dataptr[3]; + t.basis.rows[1][0] = dataptr[4]; + t.basis.rows[1][1] = dataptr[5]; + t.basis.rows[1][2] = dataptr[6]; + t.origin.y = dataptr[7]; + t.basis.rows[2][0] = dataptr[8]; + t.basis.rows[2][1] = dataptr[9]; + t.basis.rows[2][2] = dataptr[10]; + t.origin.z = dataptr[11]; + + return t; } void MeshStorage::skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) { + Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); + + ERR_FAIL_COND(!skeleton); + ERR_FAIL_INDEX(p_bone, skeleton->size); + ERR_FAIL_COND(!skeleton->use_2d); + + float *dataptr = skeleton->data.ptrw() + p_bone * 8; + + dataptr[0] = p_transform.columns[0][0]; + dataptr[1] = p_transform.columns[1][0]; + dataptr[2] = 0; + dataptr[3] = p_transform.columns[2][0]; + dataptr[4] = p_transform.columns[0][1]; + dataptr[5] = p_transform.columns[1][1]; + dataptr[6] = 0; + dataptr[7] = p_transform.columns[2][1]; + + _skeleton_make_dirty(skeleton); } Transform2D MeshStorage::skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const { - return Transform2D(); + Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); + + ERR_FAIL_COND_V(!skeleton, Transform2D()); + ERR_FAIL_INDEX_V(p_bone, skeleton->size, Transform2D()); + ERR_FAIL_COND_V(!skeleton->use_2d, Transform2D()); + + const float *dataptr = skeleton->data.ptr() + p_bone * 8; + + Transform2D t; + t.columns[0][0] = dataptr[0]; + t.columns[1][0] = dataptr[1]; + t.columns[2][0] = dataptr[3]; + t.columns[0][1] = dataptr[4]; + t.columns[1][1] = dataptr[5]; + t.columns[2][1] = dataptr[7]; + + return t; } -void MeshStorage::skeleton_update_dependency(RID p_base, DependencyTracker *p_instance) { +void MeshStorage::_update_dirty_skeletons() { + while (skeleton_dirty_list) { + Skeleton *skeleton = skeleton_dirty_list; + + if (skeleton->size) { + glBindTexture(GL_TEXTURE_2D, skeleton->transforms_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 256, skeleton->height, 0, GL_RGBA, GL_FLOAT, skeleton->data.ptr()); + glBindTexture(GL_TEXTURE_2D, 0); + } + + skeleton_dirty_list = skeleton->dirty_list; + + skeleton->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_SKELETON_BONES); + + skeleton->version++; + + skeleton->dirty = false; + skeleton->dirty_list = nullptr; + } + + skeleton_dirty_list = nullptr; } -/* OCCLUDER */ +void MeshStorage::skeleton_update_dependency(RID p_skeleton, DependencyTracker *p_instance) { + Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); + ERR_FAIL_COND(!skeleton); -void MeshStorage::occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices) { + p_instance->update_dependency(&skeleton->dependency); } #endif // GLES3_ENABLED diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h index 1aef3cbf78..0f30814928 100644 --- a/drivers/gles3/storage/mesh_storage.h +++ b/drivers/gles3/storage/mesh_storage.h @@ -33,6 +33,7 @@ #ifdef GLES3_ENABLED +#include "../shaders/skeleton.glsl.gen.h" #include "core/templates/local_vector.h" #include "core/templates/rid_owner.h" #include "core/templates/self_list.h" @@ -102,7 +103,13 @@ struct Mesh { Vector<AABB> bone_aabbs; - GLuint blend_shape_buffer = 0; + struct BlendShape { + GLuint vertex_buffer = 0; + GLuint vertex_array = 0; + }; + + BlendShape *blend_shapes = nullptr; + GLuint skeleton_vertex_array = 0; RID material; }; @@ -136,7 +143,14 @@ struct MeshInstance { Mesh *mesh = nullptr; RID skeleton; struct Surface { + GLuint vertex_buffers[2] = { 0, 0 }; + GLuint vertex_arrays[2] = { 0, 0 }; GLuint vertex_buffer = 0; + int vertex_stride_cache = 0; + int vertex_size_cache = 0; + int vertex_normal_offset_cache = 0; + int vertex_tangent_offset_cache = 0; + uint32_t format_cache = 0; Mesh::Surface::Version *versions = nullptr; //allocated on demand uint32_t version_count = 0; @@ -144,7 +158,6 @@ struct MeshInstance { LocalVector<Surface> surfaces; LocalVector<float> blend_weights; - GLuint blend_weights_buffer = 0; List<MeshInstance *>::Element *I = nullptr; //used to erase itself uint64_t skeleton_version = 0; bool dirty = false; @@ -186,13 +199,15 @@ struct MultiMesh { struct Skeleton { bool use_2d = false; int size = 0; + int height = 0; Vector<float> data; - GLuint buffer = 0; bool dirty = false; Skeleton *dirty_list = nullptr; Transform2D base_transform_2d; + GLuint transforms_texture = 0; + uint64_t version = 1; Dependency dependency; @@ -202,6 +217,11 @@ class MeshStorage : public RendererMeshStorage { private: static MeshStorage *singleton; + struct { + SkeletonShaderGLES3 shader; + RID shader_version; + } skeleton_shader; + /* Mesh */ mutable RID_Owner<Mesh, true> mesh_owner; @@ -214,6 +234,7 @@ private: void _mesh_instance_clear(MeshInstance *mi); void _mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint32_t p_surface); + void _blend_shape_bind_mesh_instance_buffer(MeshInstance *p_mi, uint32_t p_surface); SelfList<MeshInstance>::List dirty_mesh_instance_weights; SelfList<MeshInstance>::List dirty_mesh_instance_arrays; @@ -232,9 +253,10 @@ private: mutable RID_Owner<Skeleton, true> skeleton_owner; - Skeleton *skeleton_dirty_list = nullptr; - _FORCE_INLINE_ void _skeleton_make_dirty(Skeleton *skeleton); + void _compute_skeleton(MeshInstance *p_mi, Skeleton *p_sk, uint32_t p_surface); + + Skeleton *skeleton_dirty_list = nullptr; public: static MeshStorage *get_singleton(); @@ -534,9 +556,11 @@ public: virtual void skeleton_update_dependency(RID p_base, DependencyTracker *p_instance) override; - /* OCCLUDER */ + void _update_dirty_skeletons(); - void occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices); + _FORCE_INLINE_ bool skeleton_is_valid(RID p_skeleton) { + return skeleton_owner.get_or_null(p_skeleton) != nullptr; + } }; } // namespace GLES3 diff --git a/drivers/gles3/storage/utilities.cpp b/drivers/gles3/storage/utilities.cpp index 393093c2a7..fe900c7cfb 100644 --- a/drivers/gles3/storage/utilities.cpp +++ b/drivers/gles3/storage/utilities.cpp @@ -281,7 +281,7 @@ String Utilities::get_captured_timestamp_name(uint32_t p_index) const { void Utilities::update_dirty_resources() { MaterialStorage::get_singleton()->_update_global_shader_uniforms(); MaterialStorage::get_singleton()->_update_queued_materials(); - //MeshStorage::get_singleton()->_update_dirty_skeletons(); + MeshStorage::get_singleton()->_update_dirty_skeletons(); MeshStorage::get_singleton()->_update_dirty_multimeshes(); TextureStorage::get_singleton()->update_texture_atlas(); } |