diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp index 3213fa3775c..88ee749ed66 100644 --- a/drivers/gles3/storage/mesh_storage.cpp +++ b/drivers/gles3/storage/mesh_storage.cpp @@ -217,8 +217,23 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) if (new_surface.vertex_data.size()) { glGenBuffers(1, &s->vertex_buffer); glBindBuffer(GL_ARRAY_BUFFER, s->vertex_buffer); - GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, s->vertex_buffer, new_surface.vertex_data.size(), new_surface.vertex_data.ptr(), (s->format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW, "Mesh vertex buffer"); - s->vertex_buffer_size = new_surface.vertex_data.size(); + // If we have an uncompressed surface that contains normals, but not tangents, we need to differentiate the array + // from a compressed array in the shader. To do so, we allow the the normal to read 4 components out of the buffer + // But only give it 2 components per normal. So essentially, each vertex reads the next normal in normal.zw. + // This allows us to avoid adding a shader permutation, and avoid passing dummy tangents. Since the stride is kept small + // this should still be a net win for bandwidth. + // If we do this, then the last normal will read past the end of the array. So we need to pad the array with dummy data. + if (!(new_surface.format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) && (new_surface.format & RS::ARRAY_FORMAT_NORMAL) && !(new_surface.format & RS::ARRAY_FORMAT_TANGENT)) { + // Unfortunately, we need to copy the buffer, which is fine as doing a resize triggers a CoW anyway. + Vector new_vertex_data; + new_vertex_data.resize_zeroed(new_surface.vertex_data.size() + sizeof(uint16_t) * 2); + memcpy(new_vertex_data.ptrw(), new_surface.vertex_data.ptr(), new_surface.vertex_data.size()); + GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, s->vertex_buffer, new_vertex_data.size(), new_vertex_data.ptr(), (s->format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW, "Mesh vertex buffer"); + s->vertex_buffer_size = new_vertex_data.size(); + } else { + GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, s->vertex_buffer, new_surface.vertex_data.size(), new_surface.vertex_data.ptr(), (s->format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW, "Mesh vertex buffer"); + s->vertex_buffer_size = new_surface.vertex_data.size(); + } } if (new_surface.attribute_data.size()) { @@ -461,6 +476,11 @@ RS::SurfaceData MeshStorage::mesh_get_surface(RID p_mesh, int p_surface) const { sd.format = s.format; if (s.vertex_buffer != 0) { sd.vertex_data = Utilities::buffer_get_data(GL_ARRAY_BUFFER, s.vertex_buffer, s.vertex_buffer_size); + + // When using an uncompressed buffer with normals, but without tangents, we have to trim the padding. + if (!(s.format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) && (s.format & RS::ARRAY_FORMAT_NORMAL) && !(s.format & RS::ARRAY_FORMAT_TANGENT)) { + sd.vertex_data.resize(sd.vertex_data.size() - sizeof(uint16_t) * 2); + } } if (s.attribute_buffer != 0) { diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp index 3710ac0eed3..da55b68109a 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp @@ -591,7 +591,6 @@ RS::SurfaceData MeshStorage::mesh_get_surface(RID p_mesh, int p_surface) const { sd.vertex_data = RD::get_singleton()->buffer_get_data(s.vertex_buffer); // When using an uncompressed buffer with normals, but without tangents, we have to trim the padding. if (!(s.format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) && (s.format & RS::ARRAY_FORMAT_NORMAL) && !(s.format & RS::ARRAY_FORMAT_TANGENT)) { - Vector new_vertex_data; sd.vertex_data.resize(sd.vertex_data.size() - sizeof(uint16_t) * 2); } }