From 8b0d3657e54fc4a324bd068e4da3094de77a7443 Mon Sep 17 00:00:00 2001 From: paru Date: Thu, 6 May 2021 20:50:27 +0200 Subject: [PATCH] Added CPU blendshapes for GLES2 --- drivers/gles2/rasterizer_scene_gles2.cpp | 256 ++++++++++++++++++++- drivers/gles2/rasterizer_storage_gles2.cpp | 32 ++- drivers/gles2/rasterizer_storage_gles2.h | 5 + 3 files changed, 281 insertions(+), 12 deletions(-) diff --git a/drivers/gles2/rasterizer_scene_gles2.cpp b/drivers/gles2/rasterizer_scene_gles2.cpp index a5522e43ca..9fdb285b22 100644 --- a/drivers/gles2/rasterizer_scene_gles2.cpp +++ b/drivers/gles2/rasterizer_scene_gles2.cpp @@ -1385,21 +1385,269 @@ bool RasterizerSceneGLES2::_setup_material(RasterizerStorageGLES2::Material *p_m return shader_rebind; } +void static _calculate_blend_shape_buffer(RasterizerSceneGLES2::RenderList::Element *p_element, PoolVector &transform_buffer) { + RasterizerStorageGLES2::Surface *s = static_cast(p_element->geometry); + if (!s->blend_shape_data.empty()) { + if (transform_buffer.size() < s->array_byte_size) { + transform_buffer.resize(s->array_byte_size); + } + for (int i = 0; i < VS::ARRAY_MAX - 1; i++) { + if (s->attribs[i].enabled) { + const float *p_weights = p_element->instance->blend_values.ptr(); + + PoolVector::Write write = transform_buffer.write(); + PoolVector::Read read = s->data.read(); + float attrib_array[4] = { 0.0 }; + + // Read all attributes + for (int j = 0; j < s->array_len; j++) { + size_t offset = s->attribs[i].offset + (j * s->attribs[i].stride); + float base_weight = 1.0; + + if (s->mesh->blend_shape_mode == VS::BLEND_SHAPE_MODE_NORMALIZED) { + for (int ti = 0; ti < s->blend_shape_data.size(); ti++) { + base_weight -= p_weights[ti]; + } + } + + // Set the base + switch (i) { + case VS::ARRAY_VERTEX: { + if (s->format & VS::ARRAY_COMPRESS_VERTEX) { + const uint16_t *v = (const uint16_t *)(read.ptr() + offset); + attrib_array[0] = Math::halfptr_to_float(&v[0]) * base_weight; + attrib_array[1] = Math::halfptr_to_float(&v[1]) * base_weight; + attrib_array[2] = Math::halfptr_to_float(&v[2]) * base_weight; + } else { + const float *v = (const float *)(read.ptr() + offset); + attrib_array[0] = v[0] * base_weight; + attrib_array[1] = v[1] * base_weight; + attrib_array[2] = v[2] * base_weight; + } + } break; + case VS::ARRAY_NORMAL: { + if (s->format & VS::ARRAY_COMPRESS_NORMAL) { + const int8_t *v = (const int8_t *)(read.ptr() + offset); + attrib_array[0] = (v[0] / 127.0) * base_weight; + attrib_array[1] = (v[1] / 127.0) * base_weight; + attrib_array[2] = (v[2] / 127.0) * base_weight; + } else { + const float *v = (const float *)(read.ptr() + offset); + attrib_array[0] = v[0] * base_weight; + attrib_array[1] = v[1] * base_weight; + attrib_array[2] = v[2] * base_weight; + } + } break; + case VS::ARRAY_TANGENT: { + if (s->format & VS::ARRAY_COMPRESS_TANGENT) { + const int8_t *v = (const int8_t *)(read.ptr() + offset); + attrib_array[0] = (v[0] / 127.0) * base_weight; + attrib_array[1] = (v[1] / 127.0) * base_weight; + attrib_array[2] = (v[2] / 127.0) * base_weight; + attrib_array[3] = (v[3] / 127.0) * base_weight; + } else { + const float *v = (const float *)(read.ptr() + offset); + attrib_array[0] = v[0] * base_weight; + attrib_array[1] = v[1] * base_weight; + attrib_array[2] = v[2] * base_weight; + attrib_array[3] = v[3] * base_weight; + } + } break; + case VS::ARRAY_COLOR: { + if (s->format & VS::ARRAY_COMPRESS_COLOR) { + const uint8_t *v = (const uint8_t *)(read.ptr() + offset); + attrib_array[0] = (v[0] / 255.0) * base_weight; + attrib_array[1] = (v[1] / 255.0) * base_weight; + attrib_array[2] = (v[2] / 255.0) * base_weight; + attrib_array[3] = (v[3] / 255.0) * base_weight; + } else { + const float *v = (const float *)(read.ptr() + offset); + attrib_array[0] = v[0] * base_weight; + attrib_array[1] = v[1] * base_weight; + attrib_array[2] = v[2] * base_weight; + attrib_array[3] = v[3] * base_weight; + } + } break; + case VS::ARRAY_TEX_UV: { + if (s->format & VS::ARRAY_COMPRESS_TEX_UV) { + const uint16_t *v = (const uint16_t *)(read.ptr() + offset); + attrib_array[0] = Math::halfptr_to_float(&v[0]) * base_weight; + attrib_array[1] = Math::halfptr_to_float(&v[1]) * base_weight; + } else { + const float *v = (const float *)(read.ptr() + offset); + attrib_array[0] = v[0] * base_weight; + attrib_array[1] = v[1] * base_weight; + } + } break; + case VS::ARRAY_TEX_UV2: { + if (s->format & VS::ARRAY_COMPRESS_TEX_UV2) { + const uint16_t *v = (const uint16_t *)(read.ptr() + offset); + attrib_array[0] = Math::halfptr_to_float(&v[0]) * base_weight; + attrib_array[1] = Math::halfptr_to_float(&v[1]) * base_weight; + } else { + const float *v = (const float *)(read.ptr() + offset); + attrib_array[0] = v[0] * base_weight; + attrib_array[1] = v[1] * base_weight; + } + } break; + case VS::ARRAY_WEIGHTS: { + if (s->format & VS::ARRAY_COMPRESS_WEIGHTS) { + const uint16_t *v = (const uint16_t *)(read.ptr() + offset); + attrib_array[0] = (v[0] / 65535.0) * base_weight; + attrib_array[1] = (v[1] / 65535.0) * base_weight; + attrib_array[2] = (v[2] / 65535.0) * base_weight; + attrib_array[3] = (v[3] / 65535.0) * base_weight; + } else { + const float *v = (const float *)(read.ptr() + offset); + attrib_array[0] = v[0] * base_weight; + attrib_array[1] = v[1] * base_weight; + attrib_array[2] = v[2] * base_weight; + attrib_array[3] = v[3] * base_weight; + } + } break; + } + + // Add all blend shapes + for (int ti = 0; ti < s->blend_shape_data.size(); ti++) { + PoolVector::Read blend = s->blend_shape_data[ti].read(); + float weight = p_weights[ti]; + if (Math::is_zero_approx(weight)) { + continue; + } + + switch (i) { + case VS::ARRAY_VERTEX: { + if (s->format & VS::ARRAY_COMPRESS_VERTEX) { + const uint16_t *v = (const uint16_t *)(blend.ptr() + offset); + attrib_array[0] += Math::halfptr_to_float(&v[0]) * weight; + attrib_array[1] += Math::halfptr_to_float(&v[1]) * weight; + attrib_array[2] += Math::halfptr_to_float(&v[2]) * weight; + } else { + const float *v = (const float *)(blend.ptr() + offset); + attrib_array[0] += v[0] * weight; + attrib_array[1] += v[1] * weight; + attrib_array[2] += v[2] * weight; + } + } break; + case VS::ARRAY_NORMAL: { + if (s->format & VS::ARRAY_COMPRESS_NORMAL) { + const int8_t *v = (const int8_t *)(blend.ptr() + offset); + attrib_array[0] += (float(v[0]) / 127.0) * weight; + attrib_array[1] += (float(v[1]) / 127.0) * weight; + attrib_array[2] += (float(v[2]) / 127.0) * weight; + } else { + const float *v = (const float *)(blend.ptr() + offset); + attrib_array[0] += v[0] * weight; + attrib_array[1] += v[1] * weight; + attrib_array[2] += v[2] * weight; + } + } break; + case VS::ARRAY_TANGENT: { + if (s->format & VS::ARRAY_COMPRESS_TANGENT) { + const int8_t *v = (const int8_t *)(read.ptr() + offset); + attrib_array[0] += (float(v[0]) / 127.0) * weight; + attrib_array[1] += (float(v[1]) / 127.0) * weight; + attrib_array[2] += (float(v[2]) / 127.0) * weight; + attrib_array[3] = (float(v[3]) / 127.0); + } else { + const float *v = (const float *)(read.ptr() + offset); + attrib_array[0] += v[0] * weight; + attrib_array[1] += v[1] * weight; + attrib_array[2] += v[2] * weight; + attrib_array[3] = v[3]; + } + } break; + case VS::ARRAY_COLOR: { + if (s->format & VS::ARRAY_COMPRESS_COLOR) { + const uint8_t *v = (const uint8_t *)(blend.ptr() + offset); + attrib_array[0] += (v[0] / 255.0) * weight; + attrib_array[1] += (v[1] / 255.0) * weight; + attrib_array[2] += (v[2] / 255.0) * weight; + attrib_array[3] += (v[3] / 255.0) * weight; + } else { + const float *v = (const float *)(blend.ptr() + offset); + attrib_array[0] += v[0] * weight; + attrib_array[1] += v[1] * weight; + attrib_array[2] += v[2] * weight; + attrib_array[3] += v[3] * weight; + } + } break; + case VS::ARRAY_TEX_UV: { + if (s->format & VS::ARRAY_COMPRESS_TEX_UV) { + const uint16_t *v = (const uint16_t *)(blend.ptr() + offset); + attrib_array[0] += Math::halfptr_to_float(&v[0]) * weight; + attrib_array[1] += Math::halfptr_to_float(&v[1]) * weight; + } else { + const float *v = (const float *)(blend.ptr() + offset); + attrib_array[0] += v[0] * weight; + attrib_array[1] += v[1] * weight; + } + } break; + case VS::ARRAY_TEX_UV2: { + if (s->format & VS::ARRAY_COMPRESS_TEX_UV2) { + const uint16_t *v = (const uint16_t *)(blend.ptr() + offset); + attrib_array[0] += Math::halfptr_to_float(&v[0]) * weight; + attrib_array[1] += Math::halfptr_to_float(&v[1]) * weight; + } else { + const float *v = (const float *)(blend.ptr() + offset); + attrib_array[0] += v[0] * weight; + attrib_array[1] += v[1] * weight; + } + } break; + case VS::ARRAY_WEIGHTS: { + if (s->format & VS::ARRAY_COMPRESS_WEIGHTS) { + const uint16_t *v = (const uint16_t *)(blend.ptr() + offset); + attrib_array[0] += (v[0] / 65535.0) * weight; + attrib_array[1] += (v[1] / 65535.0) * weight; + attrib_array[2] += (v[2] / 65535.0) * weight; + attrib_array[3] += (v[3] / 65535.0) * weight; + } else { + const float *v = (const float *)(blend.ptr() + offset); + attrib_array[0] += v[0] * weight; + attrib_array[1] += v[1] * weight; + attrib_array[2] += v[2] * weight; + attrib_array[3] += v[3] * weight; + } + } break; + } + } + memcpy(&write[offset], attrib_array, sizeof(float) * s->attribs[i].size); + } + } + } + } +} + void RasterizerSceneGLES2::_setup_geometry(RenderList::Element *p_element, RasterizerStorageGLES2::Skeleton *p_skeleton) { switch (p_element->instance->base_type) { case VS::INSTANCE_MESH: { RasterizerStorageGLES2::Surface *s = static_cast(p_element->geometry); - glBindBuffer(GL_ARRAY_BUFFER, s->vertex_id); - if (s->index_array_len > 0) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->index_id); } + if (!s->blend_shape_data.empty()) { + _calculate_blend_shape_buffer(p_element, storage->resources.blend_shapes_transform_cpu_buffer); + storage->_update_blend_shape_transform_buffer(storage->resources.blend_shapes_transform_cpu_buffer, s->array_byte_size); + } + for (int i = 0; i < VS::ARRAY_MAX - 1; i++) { if (s->attribs[i].enabled) { - glEnableVertexAttribArray(i); - glVertexAttribPointer(s->attribs[i].index, s->attribs[i].size, s->attribs[i].type, s->attribs[i].normalized, s->attribs[i].stride, CAST_INT_TO_UCHAR_PTR(s->attribs[i].offset)); + if (!s->blend_shape_data.empty() && (i != VS::ARRAY_BONES)) { + glBindBuffer(GL_ARRAY_BUFFER, storage->resources.blend_shape_transform_buffer); + + glEnableVertexAttribArray(i); + + glVertexAttribPointer(s->attribs[i].index, s->attribs[i].size, GL_FLOAT, GL_FALSE, s->attribs[i].stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(s->attribs[i].offset * sizeof(float))); + + } else { + glBindBuffer(GL_ARRAY_BUFFER, s->vertex_id); + + glEnableVertexAttribArray(i); + + glVertexAttribPointer(s->attribs[i].index, s->attribs[i].size, s->attribs[i].type, s->attribs[i].normalized, s->attribs[i].stride, CAST_INT_TO_UCHAR_PTR(s->attribs[i].offset)); + } } else { glDisableVertexAttribArray(i); switch (i) { diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp index 8f6a47b794..22cff49b9c 100644 --- a/drivers/gles2/rasterizer_storage_gles2.cpp +++ b/drivers/gles2/rasterizer_storage_gles2.cpp @@ -2464,12 +2464,7 @@ void RasterizerStorageGLES2::mesh_add_surface(RID p_mesh, uint32_t p_format, VS: surface->aabb = p_aabb; surface->max_bone = p_bone_aabbs.size(); -#ifdef TOOLS_ENABLED surface->blend_shape_data = p_blend_shapes; - if (surface->blend_shape_data.size()) { - ERR_PRINT_ONCE("Blend shapes are not supported in OpenGL ES 2.0"); - } -#endif surface->data = array; surface->index_data = p_index_array; @@ -2674,9 +2669,6 @@ Vector> RasterizerStorageGLES2::mesh_surface_get_blend_shape const Mesh *mesh = mesh_owner.getornull(p_mesh); ERR_FAIL_COND_V(!mesh, Vector>()); ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), Vector>()); -#ifndef TOOLS_ENABLED - ERR_PRINT("OpenGL ES 2.0 does not allow retrieving blend shape data"); -#endif return mesh->surfaces[p_surface]->blend_shape_data; } @@ -3688,6 +3680,25 @@ void RasterizerStorageGLES2::skeleton_set_base_transform_2d(RID p_skeleton, cons skeleton->base_transform_2d = p_base_transform; } +void RasterizerStorageGLES2::_update_blend_shape_transform_buffer(const PoolVector &p_data, size_t p_size) { + glBindBuffer(GL_ARRAY_BUFFER, resources.blend_shape_transform_buffer); + + uint32_t buffer_size = p_size * sizeof(float); + + if (p_size > resources.blend_shape_transform_buffer_size) { + // new requested buffer is bigger, so resizing the GPU buffer + + resources.blend_shape_transform_buffer_size = p_size; + + glBufferData(GL_ARRAY_BUFFER, buffer_size, p_data.read().ptr(), GL_DYNAMIC_DRAW); + } else { + // this may not be best, it could be better to use glBufferData in both cases. + buffer_orphan_and_upload(resources.blend_shape_transform_buffer_size, 0, buffer_size, p_data.read().ptr(), GL_ARRAY_BUFFER, true); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + void RasterizerStorageGLES2::_update_skeleton_transform_buffer(const PoolVector &p_data, size_t p_size) { glBindBuffer(GL_ARRAY_BUFFER, resources.skeleton_transform_buffer); @@ -6148,6 +6159,11 @@ void RasterizerStorageGLES2::initialize() { resources.skeleton_transform_buffer_size = 0; glGenBuffers(1, &resources.skeleton_transform_buffer); } + // blend shape buffer + { + resources.blend_shape_transform_buffer_size = 0; + glGenBuffers(1, &resources.blend_shape_transform_buffer); + } // radical inverse vdc cache texture // used for cubemap filtering diff --git a/drivers/gles2/rasterizer_storage_gles2.h b/drivers/gles2/rasterizer_storage_gles2.h index cd4ecc3741..06f8314227 100644 --- a/drivers/gles2/rasterizer_storage_gles2.h +++ b/drivers/gles2/rasterizer_storage_gles2.h @@ -125,6 +125,10 @@ public: GLuint skeleton_transform_buffer; PoolVector skeleton_transform_cpu_buffer; + size_t blend_shape_transform_buffer_size; + GLuint blend_shape_transform_buffer; + PoolVector blend_shapes_transform_cpu_buffer; + } resources; mutable struct Shaders { @@ -908,6 +912,7 @@ public: virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const; virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform); + void _update_blend_shape_transform_buffer(const PoolVector &p_data, size_t p_size); void _update_skeleton_transform_buffer(const PoolVector &p_data, size_t p_size); /* Light API */