Merge pull request #69325 from clayjohn/GLES3-skeletons

Add Skeletons and Blend Shapes to the OpenGL renderer
This commit is contained in:
Rémi Verschelde 2022-12-01 23:22:42 +01:00 committed by GitHub
commit 8177e5d7de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
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 (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

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;