in src/esp/assets/PTexMeshData.cpp [510:820]
void PTexMeshData::parsePLY(const std::string& filename,
PTexMeshData::MeshData& meshData) {
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;
CORRADE_ASSERT(s == "binary_little_endian",
"PTexMeshData::parsePLY: the file is not a binary file "
"in little endian byte order", );
} else if (token == "element") {
std::string name;
size_t size = 0;
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;
CORRADE_ASSERT(numFaces > 0,
"PTexMeshData::parsePLY: number of faces is not "
"greater than 0.", );
} else {
CORRADE_ASSERT(
false, "PTexMeshData::parsePLY: Cannot 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;
CORRADE_ASSERT(countType == "uchar" || countType == "uint8",
"PTexMeshData::parsePLY: Don't understand count type"
<< countType, );
CORRADE_ASSERT(
type == "int",
"PTexMeshData::parsePLY: Don't understand index type" << type, );
CORRADE_ASSERT(lastElement == "face",
"PTexMeshData::parsePLY: Only expecting list after "
"face element, not after"
<< lastElement, );
}
CORRADE_ASSERT(
type == "float" || type == "int" || type == "uchar" ||
type == "uint8",
"PTexMeshData::parsePLY: Don't understand type" << type, );
ls >> name;
// Collecting vertex property information
if (lastElement == "vertex") {
CORRADE_ASSERT(type != "int",
"PTexMeshData::parsePLY: Don't support 32-bit integer "
"properties", );
// Position information
if (name == "x") {
positionDimensions = 1;
vertexLayout.push_back(Properties::POSITION);
CORRADE_ASSERT(type == "float",
"PTexMeshData::parsePLY: Don't support 8-bit "
"integer positions", );
} else if (name == "y") {
CORRADE_ASSERT(lastProperty == "x",
"PTexMeshData::parsePLY: Properties should follow "
"x, y, z, (w) order", );
positionDimensions = 2;
} else if (name == "z") {
CORRADE_ASSERT(lastProperty == "y",
"PTexMeshData::parsePLY: Properties should follow "
"x, y, z, (w) order", );
positionDimensions = 3;
} else if (name == "w") {
CORRADE_ASSERT(lastProperty == "z",
"PTexMeshData::parsePLY: Properties should follow "
"x, y, z, (w) order", );
positionDimensions = 4;
}
// Normal information
if (name == "nx") {
normalDimensions = 1;
vertexLayout.push_back(Properties::NORMAL);
CORRADE_ASSERT(type == "float",
"PTexMeshData::parsePLY: Don't support 8-bit "
"integer normals", );
} else if (name == "ny") {
CORRADE_ASSERT(lastProperty == "nx",
"PTexMeshData::parsePLY: Properties should follow "
"nx, ny, nz order", );
normalDimensions = 2;
} else if (name == "nz") {
CORRADE_ASSERT(lastProperty == "ny",
"PTexMeshData::parsePLY: Properties should follow "
"nx, ny, nz order", );
normalDimensions = 3;
}
// Color information
if (name == "red") {
colorDimensions = 1;
vertexLayout.push_back(Properties::COLOR);
CORRADE_ASSERT(type == "uchar" || type == "uint8",
"PTexMeshData::parsePLY: Don't support non-8-bit "
"integer colors", );
} else if (name == "green") {
CORRADE_ASSERT(lastProperty == "red",
"PTexMeshData::parsePLY: Properties should follow "
"red, green, blue, (alpha) order", );
colorDimensions = 2;
} else if (name == "blue") {
CORRADE_ASSERT(lastProperty == "green",
"PTexMeshData::parsePLY: Properties should follow "
"red, green, blue, (alpha) order", );
colorDimensions = 3;
} else if (name == "alpha") {
CORRADE_ASSERT(lastProperty == "blue",
"PTexMeshData::parsePLY: Properties should follow "
"red, green, blue, (alpha) order", );
colorDimensions = 4;
}
} else if (lastElement == "face") {
CORRADE_ASSERT(isList,
"PTexMeshData::parsePLY: No idea what to do with "
"properties following faces", );
} else {
// No idea what to do with properties before elements
CORRADE_INTERNAL_ASSERT_UNREACHABLE();
}
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
CORRADE_INTERNAL_ASSERT_UNREACHABLE();
}
}
// Check things make sense.
CORRADE_ASSERT(
numVertices > 0,
"PTexMeshData::parsePLY: number of vertices is not greater than 0", );
CORRADE_ASSERT(positionDimensions > 0,
"PTexMeshData::parsePLY: the dimensions of the position is "
"not greater than 0", );
CORRADE_ASSERT(
positionDimensions == 3,
"PTexMeshData::parsePLY: the dimensions of the position must be 3.", );
}
meshData.vbo.resize(numVertices, vec3f(0, 0, 0));
if (normalDimensions != 0u) {
meshData.nbo.resize(numVertices, vec4f(0, 0, 0, 1));
}
if (colorDimensions != 0u) {
meshData.cbo.resize(numVertices, vec4uc(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 {
CORRADE_INTERNAL_ASSERT_UNREACHABLE();
}
}
// Close after parsing header and re-open memory mapped
const size_t postHeader = file.tellg();
file.close();
Cr::Containers::Array<const char, Cr::Utility::Directory::MapDeleter>
mmappedData = Cr::Utility::Directory::mapRead(filename);
const size_t fileSize = *Cr::Utility::Directory::fileSize(filename);
// Parse each vertex packet and unpack
const char* bytes = mmappedData + postHeader;
for (size_t i = 0; i < numVertices; i++) {
const char* nextBytes = bytes + vertexPacketSizeBytes * i;
memcpy(meshData.vbo[i].data(), &nextBytes[positionOffsetBytes],
positionBytes);
if (normalDimensions != 0u)
memcpy(meshData.nbo[i].data(), &nextBytes[normalOffsetBytes],
normalBytes);
if (colorDimensions != 0u)
memcpy(meshData.cbo[i].data(), &nextBytes[colorOffsetBytes], colorBytes);
}
const size_t bytesSoFar = postHeader + vertexPacketSizeBytes * numVertices;
bytes = mmappedData + postHeader + vertexPacketSizeBytes * numVertices;
// Read first face to get number of indices;
const uint8_t faceDimensions = *bytes;
CORRADE_ASSERT(faceDimensions == 3 || faceDimensions == 4,
"PTexMeshData::parsePLY: the dimension of a face is neither "
"3 nor 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)
// {
// ESP_DEBUG() << "Skipping" << numFaces - predictedFaces << "missing
// faces" << std::endl;
// }
// else if(numFaces < predictedFaces)
// {
// ESP_DEBUG() << "Ignoring" << predictedFaces - numFaces << "extra
// faces" << std::endl;
// }
numFaces = std::min(numFaces, predictedFaces);
meshData.ibo.resize(numFaces * faceDimensions);
for (size_t i = 0; i < numFaces; i++) {
const char* nextBytes = bytes + facePacketSizeBytes * i;
memcpy(&meshData.ibo[i * faceDimensions], &nextBytes[countBytes],
faceBytes);
}
}