void PLYParse()

in ReplicaSDK/ptex/PLYParser.cpp [12:292]


void PLYParse(MeshData& meshData, const std::string& filename) {
  std::vector<std::string> comments;
  std::vector<std::string> objInfo;

  std::string lastElement;
  std::string lastProperty;

  enum Properties { POSITION = 0, NORMAL, COLOR, NUM_PROPERTIES };

  size_t numVertices = 0;

  size_t positionDimensions = 0;
  size_t normalDimensions = 0;
  size_t colorDimensions = 0;

  std::vector<Properties> vertexLayout;

  size_t numFaces = 0;

  std::ifstream file(filename, std::ios::binary);

  // Header parsing
  {
    std::string line;

    while (std::getline(file, line)) {
      std::istringstream ls(line);
      std::string token;
      ls >> token;

      if (token == "ply" || token == "PLY" || token == "") {
        // Skip preamble line
        continue;
      } else if (token == "comment") {
        // Just store these incase
        comments.push_back(line.erase(0, 8));
      } else if (token == "format") {
        // We can only parse binary data, so check that's what it is
        std::string s;
        ls >> s;
        ASSERT(
            s == "binary_little_endian",
            "Can only parse binary files... why are you using ASCII anyway?");
      } else if (token == "element") {
        std::string name;
        size_t size;
        ls >> name >> size;

        if (name == "vertex") {
          // Pull out the number of vertices
          numVertices = size;
        } else if (name == "face") {
          // Pull out number of faces
          numFaces = size;
        } else {
          ASSERT(false, "Can't parse element (%)", name);
        }

        // Keep track of what element we parsed last to associate the properties that follow
        lastElement = name;
      } else if (token == "property") {
        std::string type, name;
        ls >> type;

        // Special parsing for list properties (e.g. faces)
        bool isList = false;

        if (type == "list") {
          isList = true;

          std::string countType;
          ls >> countType >> type;

          ASSERT(
              countType == "uchar" || countType == "uint8",
              "Don't understand count type (%)",
              countType);

          ASSERT(type == "int", "Don't understand index type (%)", type);

          ASSERT(
              lastElement == "face",
              "Only expecting list after face element, not after (%)",
              lastElement);
        }

        ASSERT(
            type == "float" || type == "int" || type == "uchar" || type == "uint8",
            "Don't understand type (%)",
            type);

        ls >> name;

        // Collecting vertex property information
        if (lastElement == "vertex") {
          ASSERT(type != "int", "Don't support 32-bit integer properties");

          // Position information
          if (name == "x") {
            positionDimensions = 1;
            vertexLayout.push_back(Properties::POSITION);
            ASSERT(type == "float", "Don't support 8-bit integer positions");
          } else if (name == "y") {
            ASSERT(lastProperty == "x", "Properties should follow x, y, z, (w) order");
            positionDimensions = 2;
          } else if (name == "z") {
            ASSERT(lastProperty == "y", "Properties should follow x, y, z, (w) order");
            positionDimensions = 3;
          } else if (name == "w") {
            ASSERT(lastProperty == "z", "Properties should follow x, y, z, (w) order");
            positionDimensions = 4;
          }

          // Normal information
          if (name == "nx") {
            normalDimensions = 1;
            vertexLayout.push_back(Properties::NORMAL);
            ASSERT(type == "float", "Don't support 8-bit integer normals");
          } else if (name == "ny") {
            ASSERT(lastProperty == "nx", "Properties should follow nx, ny, nz order");
            normalDimensions = 2;
          } else if (name == "nz") {
            ASSERT(lastProperty == "ny", "Properties should follow nx, ny, nz order");
            normalDimensions = 3;
          }

          // Color information
          if (name == "red") {
            colorDimensions = 1;
            vertexLayout.push_back(Properties::COLOR);
            ASSERT(type == "uchar" || type == "uint8", "Don't support non-8-bit integer colors");
          } else if (name == "green") {
            ASSERT(
                lastProperty == "red", "Properties should follow red, green, blue, (alpha) order");
            colorDimensions = 2;
          } else if (name == "blue") {
            ASSERT(
                lastProperty == "green",
                "Properties should follow red, green, blue, (alpha) order");
            colorDimensions = 3;
          } else if (name == "alpha") {
            ASSERT(
                lastProperty == "blue", "Properties should follow red, green, blue, (alpha) order");
            colorDimensions = 4;
          }
        } else if (lastElement == "face") {
          ASSERT(isList, "No idea what to do with properties following faces");
        } else {
          ASSERT(false, "No idea what to do with properties before elements");
        }

        lastProperty = name;
      } else if (token == "obj_info") {
        // Just store these incase
        objInfo.push_back(line.erase(0, 9));
      } else if (token == "end_header") {
        // Done reading!
        break;
      } else {
        // Something unrecognised
        ASSERT(false);
      }
    }

    // Check things make sense.
    ASSERT(numVertices > 0);
    ASSERT(positionDimensions > 0);
  }

  meshData.vbo.Reinitialise(numVertices, 1);
  meshData.vbo.Fill(Eigen::Vector4f(0, 0, 0, 1));

  if (normalDimensions) {
    meshData.nbo.Reinitialise(numVertices, 1);
    meshData.nbo.Fill(Eigen::Vector4f(0, 0, 0, 1));
  }

  if (colorDimensions) {
    meshData.cbo.Reinitialise(numVertices, 1);
    meshData.cbo.Fill(Eigen::Matrix<unsigned char, 4, 1>(0, 0, 0, 255));
  }

  // Can only be FLOAT32 or UINT8
  const size_t positionBytes = positionDimensions * sizeof(float); // floats
  const size_t normalBytes = normalDimensions * sizeof(float); // floats
  const size_t colorBytes = colorDimensions * sizeof(uint8_t); // bytes

  const size_t vertexPacketSizeBytes = positionBytes + normalBytes + colorBytes;

  size_t positionOffsetBytes = 0;
  size_t normalOffsetBytes = 0;
  size_t colorOffsetBytes = 0;

  size_t offsetSoFarBytes = 0;

  for (size_t i = 0; i < vertexLayout.size(); i++) {
    if (vertexLayout[i] == Properties::POSITION) {
      positionOffsetBytes = offsetSoFarBytes;
      offsetSoFarBytes += positionBytes;
    } else if (vertexLayout[i] == Properties::NORMAL) {
      normalOffsetBytes = offsetSoFarBytes;
      offsetSoFarBytes += normalBytes;
    } else if (vertexLayout[i] == Properties::COLOR) {
      colorOffsetBytes = offsetSoFarBytes;
      offsetSoFarBytes += colorBytes;
    } else {
      ASSERT(false);
    }
  }

  // Close after parsing header and re-open memory mapped
  const size_t postHeader = file.tellg();

  file.close();

  const size_t fileSize = std::experimental::filesystem::file_size(filename);

  int fd = open(filename.c_str(), O_RDONLY, 0);
  void* mmappedData = mmap(NULL, fileSize, PROT_READ, MAP_PRIVATE | MAP_POPULATE, fd, 0);

  // Parse each vertex packet and unpack
  char* bytes = &(((char*)mmappedData)[postHeader]);

  for (size_t i = 0; i < numVertices; i++) {
    char* nextBytes = &bytes[vertexPacketSizeBytes * i];

    memcpy(meshData.vbo[i].data(), &nextBytes[positionOffsetBytes], positionBytes);

    if (normalDimensions)
      memcpy(meshData.nbo[i].data(), &nextBytes[normalOffsetBytes], normalBytes);

    if (colorDimensions)
      memcpy(meshData.cbo[i].data(), &nextBytes[colorOffsetBytes], colorBytes);
  }

  const size_t bytesSoFar = postHeader + vertexPacketSizeBytes * numVertices;

  bytes = &(((char*)mmappedData)[postHeader + vertexPacketSizeBytes * numVertices]);

  if (numFaces > 0) {
    // Read first face to get number of indices;
    const uint8_t faceDimensions = *bytes;

    ASSERT(faceDimensions == 3 || faceDimensions == 4);

    const size_t countBytes = 1;
    const size_t faceBytes = faceDimensions * sizeof(uint32_t); // uint32_t
    const size_t facePacketSizeBytes = countBytes + faceBytes;

    const size_t predictedFaces = (fileSize - bytesSoFar) / facePacketSizeBytes;

    // Not sure what to do here
    //    if(predictedFaces < numFaces)
    //    {
    //        std::cout << "Skipping " << numFaces - predictedFaces << " missing faces" <<
    //        std::endl;
    //    }
    //    else if(numFaces < predictedFaces)
    //    {
    //        std::cout << "Ignoring " << predictedFaces - numFaces << " extra faces" << std::endl;
    //    }

    numFaces = std::min(numFaces, predictedFaces);

    meshData.ibo.Reinitialise(numFaces * faceDimensions, 1);

    for (size_t i = 0; i < numFaces; i++) {
      char* nextBytes = &bytes[facePacketSizeBytes * i];

      memcpy(&meshData.ibo[i * faceDimensions], &nextBytes[countBytes], faceBytes);
    }

    meshData.polygonStride = faceDimensions;
  } else {
    meshData.polygonStride = 0;
  }

  munmap(mmappedData, fileSize);

  close(fd);
}