Add Skeletons and Blend Shapes to the OpenGL renderer

This uses a similar multipass approach to blend shapes
as Godot 3.x, the major difference here is that we
need to convert the normals and tangents to octahedral
for rendering.

Skeletons work the same as the Vulkan renderer except the bones
are stored in a texture as they were in 3.x.
This commit is contained in:
clayjohn 2022-11-25 15:30:36 -08:00
parent 55dae30177
commit f33ffd9ab4
9 changed files with 870 additions and 69 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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")

View File

@ -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

View File

@ -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 */

View File

@ -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 (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) {
//s->blend_shape_buffer = RD::get_singleton()->storage_buffer_create(p_surface.blend_shape_data.size(), p_surface.blend_shape_data);
// 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());
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);
}
dirty_mesh_instance_weights.remove(&mi->weight_update_list);
mi->weights_dirty = false;
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);
}
/* OCCLUDER */
skeleton_dirty_list = skeleton->dirty_list;
void MeshStorage::occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices) {
skeleton->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_SKELETON_BONES);
skeleton->version++;
skeleton->dirty = false;
skeleton->dirty_list = nullptr;
}
skeleton_dirty_list = nullptr;
}
void MeshStorage::skeleton_update_dependency(RID p_skeleton, DependencyTracker *p_instance) {
Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton);
ERR_FAIL_COND(!skeleton);
p_instance->update_dependency(&skeleton->dependency);
}
#endif // GLES3_ENABLED

View File

@ -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

View File

@ -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();
}

View File

@ -665,7 +665,6 @@ public:
virtual void skeleton_allocate_data(RID p_skeleton, int p_bones, bool p_2d_skeleton = false) override;
virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) override;
void skeleton_set_world_transform(RID p_skeleton, bool p_enable, const Transform3D &p_world_transform);
virtual int skeleton_get_bone_count(RID p_skeleton) const override;
virtual void skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform3D &p_transform) override;
virtual Transform3D skeleton_bone_get_transform(RID p_skeleton, int p_bone) const override;