Added CPU blendshapes for GLES2

This commit is contained in:
paru 2021-05-06 20:50:27 +02:00
parent 4185a22ca8
commit 8b0d3657e5
3 changed files with 281 additions and 12 deletions

View file

@ -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<float> &transform_buffer) {
RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(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<float>::Write write = transform_buffer.write();
PoolVector<uint8_t>::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<uint8_t>::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<RasterizerStorageGLES2::Surface *>(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) {

View file

@ -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<PoolVector<uint8_t>> RasterizerStorageGLES2::mesh_surface_get_blend_shape
const Mesh *mesh = mesh_owner.getornull(p_mesh);
ERR_FAIL_COND_V(!mesh, Vector<PoolVector<uint8_t>>());
ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), Vector<PoolVector<uint8_t>>());
#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<float> &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<float> &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

View file

@ -125,6 +125,10 @@ public:
GLuint skeleton_transform_buffer;
PoolVector<float> skeleton_transform_cpu_buffer;
size_t blend_shape_transform_buffer_size;
GLuint blend_shape_transform_buffer;
PoolVector<float> 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<float> &p_data, size_t p_size);
void _update_skeleton_transform_buffer(const PoolVector<float> &p_data, size_t p_size);
/* Light API */