draco
draco copied to clipboard
[Bug] Crash on special mesh with tangents.
I integrated Draco into our game engine to compress meshes, it works fine on most of meshes except one.
I dumped the mesh data in binary and wrote a simple program to decode the data and encode in Draco, it also failed.
I tried digging into the source code, some very large number (like 2147483647 or -2147483648) was generated in the process of processing tangents attribute in prediction_scheme_->ComputeCorrectionValues
in sequential_integer_attribute_encoder.cc#L141.
Source code is below, and the binary data is in mesh.zip.
#include "draco/compression/encode.h"
#include "draco/compression/decode.h"
#include <fstream>
#include <vector>
#include <array>
enum ShaderChannel {
kShaderChannelNone = -1,
kShaderChannelVertex = 0, // Vertex (vector3)
kShaderChannelNormal, // Normal (vector3)
kShaderChannelColor, // Vertex color
kShaderChannelTexCoord0, // Texcoord 0
kShaderChannelTexCoord1, // Texcoord 1
kShaderChannelTexCoord2, // Texcoord 2
kShaderChannelTexCoord3, // Texcoord 3
kShaderChannelTangent, // Tangent (vector4)
kShaderChannelCount, // Keep this last!
};
typedef std::array<float, 2> Vector2f;
typedef std::array<float, 3> Vector3f;
typedef std::array<float, 4> Vector4f;
int main(int argc, char** argv) {
std::ifstream ifs;
ifs.open(argv[1], std::ios_base::in | std::ios_base::binary);
ifs.seekg(0, std::ios::end);
auto size = ifs.tellg();
auto buffer = new char[size];
size_t bufferIndex = 0;
ifs.seekg(0, std::ios::beg);
ifs.read(buffer, size);
draco::Mesh mesh;
auto Read = [&](auto& x) -> void {
x = *reinterpret_cast<decltype(&x)>(buffer + bufferIndex);
bufferIndex += sizeof(x);
};
int trianglesCount;
int vertexCount;
Read(trianglesCount);
Read(vertexCount);
mesh.SetNumFaces(trianglesCount);
mesh.set_num_points(vertexCount);
std::array<int8_t, kShaderChannelCount + 1> attrIDs{};
for (auto& attrID : attrIDs) {
Read(attrID);
}
std::vector<uint32_t> triangles(trianglesCount * 3, 0);
for (auto &it : triangles) { Read(it); }
for (draco::FaceIndex index(0); index < trianglesCount; ++index) {
draco::Mesh::Face face;
face[0] = triangles[index.value() * 3];
face[1] = triangles[index.value() * 3 + 1];
face[2] = triangles[index.value() * 3 + 2];
mesh.SetFace(index, face);
}
std::vector<Vector3f> vertices;
if (attrIDs[kShaderChannelVertex] >= 0) {
vertices.resize(vertexCount);
draco::GeometryAttribute va;
va.Init(draco::GeometryAttribute::POSITION, nullptr, 3, draco::DT_FLOAT32,
false, sizeof(float) * 3, 0);
auto a = mesh.AddAttribute(va, true, vertexCount);
auto attr = mesh.attribute(a);
draco::AttributeValueIndex index(0);
for (auto& v : vertices) {
for (auto& it : v) {
Read(it);
}
attr->SetAttributeValue(index, &v);
++index;
}
}
std::array<std::vector<Vector2f>, 4> uvs;
for (auto i = 0; i < 4; ++i) {
if (attrIDs[kShaderChannelTexCoord0 + i] >= 0) {
uvs[i].resize(vertexCount);
draco::GeometryAttribute va;
va.Init(draco::GeometryAttribute::TEX_COORD, nullptr, 2, draco::DT_FLOAT32,
false, sizeof(float) * 2, 0);
auto a = mesh.AddAttribute(va, true, vertexCount);
auto attr = mesh.attribute(a);
draco::AttributeValueIndex index(0);
for (auto& it : uvs[i]) {
for (auto& j : it) {
Read(j);
}
attr->SetAttributeValue(index, &it);
++index;
}
}
}
std::vector<Vector3f> normals;
if (attrIDs[kShaderChannelNormal] >= 0) {
normals.resize(vertexCount);
draco::GeometryAttribute va;
va.Init(draco::GeometryAttribute::NORMAL, nullptr, 3, draco::DT_FLOAT32,
false, sizeof(float) * 3, 0);
auto a = mesh.AddAttribute(va, true, vertexCount);
auto attr = mesh.attribute(a);
draco::AttributeValueIndex index(0);
for (auto& it : normals) {
for (auto& i : it) {
Read(i);
}
attr->SetAttributeValue(index, &it);
++index;
}
}
std::vector<std::array<uint8_t, 4>> colors;
if (attrIDs[kShaderChannelColor] >= 0) {
colors.resize(vertexCount);
draco::GeometryAttribute va;
va.Init(draco::GeometryAttribute::COLOR, nullptr, 4, draco::DT_UINT8,
false, sizeof(uint8_t) * 4, 0);
auto a = mesh.AddAttribute(va, true, vertexCount);
auto attr = mesh.attribute(a);
draco::AttributeValueIndex index(0);
for (auto& it : colors) {
for (auto& i : it) {
Read(i);
}
attr->SetAttributeValue(index, &it);
++index;
}
}
std::vector<Vector4f> tangents;
if (attrIDs[kShaderChannelTangent] >= 0) {
tangents.resize(vertexCount);
draco::GeometryAttribute va;
va.Init(draco::GeometryAttribute::GENERIC, nullptr, 4, draco::DT_FLOAT32,
false, sizeof(float) * 4, 0);
auto a = mesh.AddAttribute(va, true, vertexCount);
auto attr = mesh.attribute(a);
draco::AttributeValueIndex index(0);
for (auto& it : tangents) {
for (auto& i : it) {
Read(i);
}
attr->SetAttributeValue(index, &it);
++index;
}
}
draco::Encoder encoder;
draco::EncoderBuffer encBuffer;
encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, 12);
encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, 10);
encoder.SetAttributeQuantization(draco::GeometryAttribute::COLOR, 8);
encoder.SetAttributeQuantization(draco::GeometryAttribute::TEX_COORD, 10);
encoder.SetAttributeQuantization(draco::GeometryAttribute::GENERIC, 12);
encoder.SetSpeedOptions(7, 7);
auto encOk = encoder.EncodeMeshToBuffer(mesh, &encBuffer);
std::cout << encOk.ok() << " " << encOk.error_msg() << std::endl;
}
I looked into it but the parsing doesn't seem to work properly. After this portion of code:
std::vector<uint32_t> triangles(trianglesCount * 3, 0);
for (auto &it : triangles) { Read(it); }
the first three indices in triangles
were 4294967295
(uint32_t max). The remaining indices seemed to be ok though.
Sorry I got ShaderChannel
wrong. It should be :
enum ShaderChannel {
kShaderChannelNone = -1,
kShaderChannelVertex = 0, // Vertex (vector3)
kShaderChannelNormal, // Normal (vector3)
kShaderChannelColor, // Vertex color
kShaderChannelTexCoord0, // Texcoord 0
kShaderChannelTexCoord1, // Texcoord 1
kShaderChannelTexCoord2, // Texcoord 2
kShaderChannelTexCoord3, // Texcoord 3
kShaderChannelTangent, // Tangent (vector4)
kShaderChannelAttrib0, // Attrib 0 (vector4)
kShaderChannelAttrib1, // Attrib 1 (vector4)
kShaderChannelAttrib2, // Attrib 2 (vector4)
kShaderChannelAttrib3, // Attrib 3 (vector4)
kShaderChannelAttrib4, // Attrib 4 (vector4)
kShaderChannelAttrib5, // Attrib 5 (vector4)
kShaderChannelAttrib6, // Attrib 6 (vector4)
kShaderChannelAttrib7, // Attrib 7 (vector4)
kShaderChannelAttrib8, // Attrib 8 (vector4)
kShaderChannelAttrib9, // Attrib 9 (vector4)
kShaderChannelAttrib10, // Attrib 10 (vector4)
kShaderChannelAttrib11, // Attrib 11 (vector4)
kShaderChannelCount, // Keep this last!
};
Which means kShaderChannelCount = 20
, and this mistake advances the order in which the indices are read in.