Added CPU blendshapes for GLES2
This commit is contained in:
parent
4185a22ca8
commit
8b0d3657e5
3 changed files with 281 additions and 12 deletions
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in a new issue