draco icon indicating copy to clipboard operation
draco copied to clipboard

[Bug] Crash on special mesh with tangents.

Open xxzl0130 opened this issue 2 years ago • 2 comments

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;
}

xxzl0130 avatar Mar 24 '22 03:03 xxzl0130

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.

ondys avatar Apr 27 '22 19:04 ondys

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.

xxzl0130 avatar Apr 28 '22 01:04 xxzl0130