# HG changeset patch # User Matti Hamalainen # Date 1576475465 -7200 # Node ID 03aa729a9e90e0d0d90c66e01dc0221f2adb9e43 # Parent 267b3fd2c98cbf0cbb95b42f956d6a85d9f55c33 Refactor PLY file parsing from dmscene.* to dmply.* and some helper functions into dmutil.h diff -r 267b3fd2c98c -r 03aa729a9e90 Makefile.gen --- a/Makefile.gen Mon Dec 16 06:51:40 2019 +0200 +++ b/Makefile.gen Mon Dec 16 07:51:05 2019 +0200 @@ -44,10 +44,10 @@ $(COMPILE_CXX_OBJ) -$(BINPATH)gldragon$(BINEXT): $(addprefix $(OBJPATH), gldragon.o dmglrender.o dmscene.o dmutil.o) +$(BINPATH)gldragon$(BINEXT): $(addprefix $(OBJPATH), gldragon.o dmglrender.o dmscene.o dmply.o dmutil.o) $(LINK_CXX_BIN) $(LDFLAGS) $(SDL_LDFLAGS) $(GL_LDFLAGS) -$(BINPATH)ply2bin$(BINEXT): $(addprefix $(OBJPATH), ply2bin.o dmscene.o dmutil.o) +$(BINPATH)ply2bin$(BINEXT): $(addprefix $(OBJPATH), ply2bin.o dmscene.o dmply.o dmutil.o) $(LINK_CXX_BIN) $(LDFLAGS) $(TOOL_LDFLAGS) diff -r 267b3fd2c98c -r 03aa729a9e90 dmply.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmply.cpp Mon Dec 16 07:51:05 2019 +0200 @@ -0,0 +1,540 @@ +// +// GLDragon - OpenGL PLY model viewer / simple benchmark +// -- PLY file parsing +// Programmed and designed by Matti 'ccr' Hämäläinen +// (C) Copyright 2019 Tecnic Software productions (TNSP) +// +// See file "COPYING" for license information. +// +#include "dmply.h" +#include + + +static DMPLYPropType dmPLYParsePropType(const std::string &name) +{ + if (name == "list") + return PLY_TYPE_LIST; + else + if (name == "float" || name == "float32") + return PLY_TYPE_FLOAT; + else + if (name == "double" || name == "float64") + return PLY_TYPE_DOUBLE; + else + if (name == "uchar" || name == "uint8") + return PLY_TYPE_UINT8; + else + if (name == "int16") + return PLY_TYPE_INT16; + else + if (name == "uint16") + return PLY_TYPE_UINT16; + else + if (name == "int" || name == "int32") + return PLY_TYPE_INT32; + else + if (name == "uint" || name == "uint32") + return PLY_TYPE_UINT32; + else + return PLY_TYPE_NONE; +} + + +static bool dmPLYParsePropertyValueASCII(DMPLYFileInfo &info, const DMPLYPropType ptype, DMPLYPropValue &pval, size_t &pos) +{ + size_t len = 0; + std::string val = info.line.substr(pos); + + switch (ptype) + { + case PLY_TYPE_INT8: + case PLY_TYPE_INT16: + case PLY_TYPE_INT32: + pval.v_int = std::stoi(val, &len); + break; + + case PLY_TYPE_UINT8: + case PLY_TYPE_UINT16: + case PLY_TYPE_UINT32: + pval.v_uint = std::stoi(val, &len); + break; + + case PLY_TYPE_FLOAT: + pval.v_float = std::stof(val, &len); + break; + + case PLY_TYPE_DOUBLE: + pval.v_double = std::stod(val, &len); + break; + + default: + return info.textError( + "Internal error, unimplemented PLY property type"); + } + + pos += len; + + return true; +} + + +static bool dmPLYParsePropertyASCII(DMPLYFileInfo &info, DMPLYFileProperty &prop, size_t &pos) +{ + switch (prop.type) + { + case PLY_TYPE_LIST: + prop.list_values.clear(); + + if (!dmPLYParsePropertyValueASCII(info, prop.list_num_type, prop.list_num_value, pos)) + return false; + + for (unsigned int n = 0; n < prop.list_num_value.v_uint; n++) + { + DMPLYPropValue pval; + if (!dmPLYParsePropertyValueASCII(info, prop.list_values_type, pval, pos)) + return false; + + prop.list_values.push_back(pval); + } + + if (prop.list_values.size() != prop.list_num_value.v_uint) + { + return info.syntaxError( + "Number of property list '"+ prop.name +" values not equal to number specified"); + } + return true; + + default: + return dmPLYParsePropertyValueASCII(info, prop.type, prop.value, pos); + } +} + + +static bool dmPLYParseElementASCII(DMPLYFileInfo &info, DMPLYFileElement &element) +{ + size_t nprop = 0, pos = 0; + + // Read one line + if (!info.readLine()) + { + return info.textError( + "Unexpected end of file"); + } + + // Skip empty lines + if (info.line.empty()) + return true; + + // Parse the properties + for (auto const prop : element.properties) + { + if (!dmPLYParsePropertyASCII(info, *prop, pos)) + return false; + + nprop++; + } + + if (nprop != element.properties.size()) + { + return info.syntaxError( + "Expected N properties, got different number"); + } + + return true; +} + + +bool dmPLYReadPropertyValueBIN(DMPLYFileInfo &info, const DMPLYPropType ptype, DMPLYPropValue &pval) +{ + uint8_t tmpU8; + int8_t tmpS8; + uint16_t tmpU16; + int16_t tmpS16; + uint32_t tmpU32; + int32_t tmpS32; + float tmpFloat; + + switch (ptype) + { + case PLY_TYPE_INT8: + info.file.read(reinterpret_cast(&tmpS8), 1); + pval.v_int = tmpS8; + break; + + case PLY_TYPE_UINT8: + info.file.read(reinterpret_cast(&tmpU8), 1); + pval.v_uint = tmpU8; + break; + + case PLY_TYPE_INT16: + info.file.read(reinterpret_cast(&tmpS16), 2); + tmpS16 = (info.format == PLY_FMT_BIN_LE) ? SDL_SwapLE16(tmpS16) : SDL_SwapBE16(tmpS16); + pval.v_int = tmpS16; + break; + + case PLY_TYPE_UINT16: + info.file.read(reinterpret_cast(&tmpU16), 2); + tmpU16 = (info.format == PLY_FMT_BIN_LE) ? SDL_SwapLE16(tmpU16) : SDL_SwapBE16(tmpU16); + pval.v_uint = tmpU16; + break; + + case PLY_TYPE_INT32: + info.file.read(reinterpret_cast(&tmpS32), 4); + tmpS32 = (info.format == PLY_FMT_BIN_LE) ? SDL_SwapLE32(tmpS32) : SDL_SwapBE32(tmpS32); + pval.v_int = tmpS32; + break; + + case PLY_TYPE_UINT32: + info.file.read(reinterpret_cast(&tmpU32), 4); + tmpU32 = (info.format == PLY_FMT_BIN_LE) ? SDL_SwapLE32(tmpU32) : SDL_SwapBE32(tmpU32); + pval.v_uint = tmpU32; + break; + + case PLY_TYPE_FLOAT: + info.file.read(reinterpret_cast(&tmpFloat), 4); + pval.v_float = (info.format == PLY_FMT_BIN_LE) ? SDL_SwapFloatLE(tmpFloat) : SDL_SwapFloatBE(tmpFloat); + break; + + default: + return info.textError( + "Internal error, unimplemented PLY property type"); + } + + return true; +} + + +bool dmPLYReadPropertyBIN(DMPLYFileInfo &info, DMPLYFileProperty &prop) +{ + switch (prop.type) + { + case PLY_TYPE_LIST: + prop.list_values.clear(); + + if (!dmPLYReadPropertyValueBIN(info, prop.list_num_type, prop.list_num_value)) + return false; + + for (unsigned int n = 0; n < prop.list_num_value.v_uint; n++) + { + DMPLYPropValue pval; + if (!dmPLYReadPropertyValueBIN(info, prop.list_values_type, pval)) + return false; + + prop.list_values.push_back(pval); + } + + if (prop.list_values.size() != prop.list_num_value.v_uint) + { + return info.syntaxError( + "Number of property list '"+ prop.name +" values not equal to number specified"); + } + return true; + + default: + return dmPLYReadPropertyValueBIN(info, prop.type, prop.value); + } +} + + +bool dmPLYReadElementBIN(DMPLYFileInfo &info, DMPLYFileElement &element) +{ + size_t nprop = 0; + + for (auto const prop : element.properties) + { + if (!dmPLYReadPropertyBIN(info, *prop)) + return false; + + nprop++; + } + + if (nprop != element.properties.size()) + { + return info.syntaxError( + "Expected N properties, got different number"); + } + + return true; +} + + +bool dmLoadFromPLY(DMModel &model, const std::string &filename) +{ + DMPLYFileInfo info; + + return dmLoadFromPLY(model, filename, info); +} + + +bool dmLoadFromPLY(DMModel &model, const std::string &filename, DMPLYFileInfo &info) +{ + info.filename = filename; + info.nline = info.state = 0; + info.file.open(info.filename.c_str(), std::fstream::in | std::fstream::binary); + + dmMsg("Trying to read mesh from '%s'.\n", + info.filename.c_str()); + + if (!info.file.is_open()) + { + dmError("Unable to open file '%s'.\n", + info.filename.c_str()); + return false; + } + + // Parse the PLY header + while (info.state >= 0) + { + // Read one line + if (!info.readLine()) + return false; + + // Skip empty lines + if (info.line.empty()) + continue; + + // Split key and value + std::vector tokens = dmStrSplit(info.line); + std::string &key = tokens[0]; + + if (info.state == 0 && key == "ply") + { + info.state = 1; + } + else + if (info.state > 0 && key == "end_header") + { + info.state = -1; + } + else + if (info.state == 1 && key == "format") + { + if (tokens.size() < 3) + { + return info.syntaxError( + "Expected value for format"); + } + + info.format = PLY_FMT_UNKNOWN; + + if (tokens[1] == "ascii") + info.format = PLY_FMT_ASCII; + else + if (tokens[1] == "binary_little_endian") + info.format = PLY_FMT_BIN_LE; + else + if (tokens[1] == "binary_big_endian") + info.format = PLY_FMT_BIN_BE; + + if (info.format == PLY_FMT_UNKNOWN || + tokens[2] != "1.0") + { + dmError("Unknown or unsupported PLY file format '%s'.\n", + (tokens[1] +" "+ tokens[2]).c_str()); + return false; + } + + info.state = 2; + } + else + if ((info.state == 2 || info.state == 3) && key == "element") + { + if (tokens.size() < 3) + { + return info.syntaxError( + "Expected a value for element key"); + } + + std::string &el_name = tokens[1], &el_value = tokens[2]; + + if (info.elem_map.count(el_name)) + { + return info.syntaxError( + "Element '"+ el_name +"' has already been defined"); + } + + DMPLYFileElement &elem = info.elem_map[el_name]; + info.element = &elem; + info.elements.push_back(&elem); + + elem.name = el_name; + elem.value = std::stoi(el_value); + + info.state = 3; + } + else + if (info.state == 3 && key == "property") + { + if (tokens.size() < 3) + { + return info.syntaxError( + "Expected value for property"); + } + + const std::string + &pr_name = tokens.back(), + &pr_typename = tokens.at(1); + + if (!info.element) + { + // Should not happen + return info.syntaxError( + "No element defined for property '"+ pr_name +"'?"); + } + + // Check if this property has been already defined + if (info.element->prop_map.count(pr_name)) + { + return info.syntaxError( + "Element '"+ info.element->name + + "' already has property '"+ pr_name +"' defined?"); + } + + // Parse property information + DMPLYPropType pr_type = dmPLYParsePropType(pr_typename); + if (pr_type == PLY_TYPE_NONE) + { + return info.syntaxError( + "Invalid or unsupported property type '"+ pr_typename +"'"); + } + + DMPLYFileProperty &prop = info.element->prop_map[pr_name]; + info.element->properties.push_back(&prop); + + prop.name = pr_name; + prop.type = pr_type; + + if (pr_type == PLY_TYPE_LIST) + { + // List is a special case + if (tokens.size() < 5) + { + return info.syntaxError( + "Expected more values for a list property (num_type, val_type, name)"); + } + + prop.list_num_type = dmPLYParsePropType(tokens.at(2)); + prop.list_values_type = dmPLYParsePropType(tokens.at(3)); + + if (prop.list_num_type == PLY_TYPE_NONE || + prop.list_values_type == PLY_TYPE_NONE) + { + return info.syntaxError( + "Invalid or unsupported property type(s)"); + } + } + } + else + if (info.state > 0 // && (key == "comment" || key == "obj_info") + ) + { + // Ignore comments + // .. and unknown keys + } + else + { + return info.syntaxError( + "Unexpected key '"+ key +"'"); + } + } + + // Check header data + DMPLYFileElement *elem; + DMPLYFileProperty *prop; + if (info.state != -1 || + (elem = info.checkElem(PLY_ELEM_FACE)) == 0 || + (prop = elem->checkProp(PLY_PROP_VERTEX_INDICES)) == 0 || + prop->type != PLY_TYPE_LIST || + (elem = info.checkElem(PLY_ELEM_VERTEX)) == 0 || + (prop = elem->checkProp("x")) == 0 || prop->type != PLY_TYPE_FLOAT || + (prop = elem->checkProp("y")) == 0 || prop->type != PLY_TYPE_FLOAT || + (prop = elem->checkProp("z")) == 0 || prop->type != PLY_TYPE_FLOAT + ) + { + dmError("PLY file did not contain expected information.\n"); + return false; + } + + model.nvertices = info.elem_map[PLY_ELEM_VERTEX].value; + model.nfaces = info.elem_map[PLY_ELEM_FACE].value; + + + if (model.nvertices < 3 || model.nfaces < 1) + { + dmError("Invalid nvertices (%d) and/or nfaces (%d).\n", + model.nvertices, model.nfaces); + return false; + } + + dmMsg("Should have %d vertices, %d faces\n", + model.nvertices, model.nfaces); + + // Pre-allocate space + model.vertices.reserve(model.nvertices); + model.normals.reserve(model.nvertices); + model.faces.reserve(model.nfaces * 3); + + // Read the actual data (in order of the elements) + for (auto *element : info.elements) + for (int n = 0; n < element->value; n++) + { + switch (info.format) + { + case PLY_FMT_ASCII: + if (!dmPLYParseElementASCII(info, *element)) + return false; + break; + + default: + if (!dmPLYReadElementBIN(info, *element)) + return false; + break; + } + + // Check for specific elements + if (element->name == PLY_ELEM_FACE) + { + DMPLYFileProperty &prop = element->prop_map[PLY_PROP_VERTEX_INDICES]; + + if (prop.list_num_value.v_uint != 3) + { + return info.syntaxError( + "Expected 3 vertices per face"); + } + + for (unsigned int n = 0; n < prop.list_num_value.v_uint; n++) + { + model.faces.push_back(prop.list_values[n].v_uint); + } + } + else + if (element->name == PLY_ELEM_VERTEX) + { + DMVector3 vert; + vert.x = element->prop_map["x"].value.v_float; + vert.y = element->prop_map["y"].value.v_float; + vert.z = element->prop_map["z"].value.v_float; + + model.vertices.push_back(vert); + + if (element->checkProp("nx") && + element->checkProp("ny") && + element->checkProp("nz")) + { + DMVector3 normal; + normal.x = element->prop_map["nx"].value.v_float; + normal.y = element->prop_map["ny"].value.v_float; + normal.z = element->prop_map["nz"].value.v_float; + + model.normals.push_back(normal); + } + } + } + + dmMsg("Found %ld vertices, %ld normals, %ld faces\n", + model.vertices.size(), + model.normals.size(), + model.faces.size() / 3); + + return true; +} diff -r 267b3fd2c98c -r 03aa729a9e90 dmply.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmply.h Mon Dec 16 07:51:05 2019 +0200 @@ -0,0 +1,120 @@ +// +// GLDragon - OpenGL PLY model viewer / simple benchmark +// -- PLY file parsing +// Programmed and designed by Matti 'ccr' Hämäläinen +// (C) Copyright 2019 Tecnic Software productions (TNSP) +// +// See file "COPYING" for license information. +// +#ifndef DMPLY_H +#define DMPLY_H 1 + +#include "dmscene.h" +#include + + +/* Constants + */ +#define PLY_PROP_VERTEX_INDICES "vertex_indices" +#define PLY_ELEM_FACE "face" +#define PLY_ELEM_VERTEX "vertex" + + +enum DMPLYFormat +{ + PLY_FMT_UNKNOWN, + PLY_FMT_ASCII, + PLY_FMT_BIN_LE, + PLY_FMT_BIN_BE +}; + + +enum DMPLYPropType +{ + PLY_TYPE_NONE, + PLY_TYPE_LIST, + + PLY_TYPE_UINT8, + PLY_TYPE_INT8, + PLY_TYPE_INT16, + PLY_TYPE_UINT16, + PLY_TYPE_INT32, + PLY_TYPE_UINT32, + PLY_TYPE_FLOAT, + PLY_TYPE_DOUBLE +}; + + +/* Structures + */ +union DMPLYPropValue +{ + double v_double; + float v_float; + unsigned int v_uint; + int v_int; +}; + + +struct DMPLYFileProperty +{ + std::string name; + DMPLYPropType + type, + list_num_type, + list_values_type; + + DMPLYPropValue value, list_num_value; + std::vector list_values; +}; + + +struct DMPLYFileElement +{ + int value; + std::string name; + + std::unordered_map prop_map; + std::vector properties; + + DMPLYFileProperty *checkProp(const std::string &prop) + { + if (prop_map.count(prop)) + return &prop_map[prop]; + else + return 0; + } +}; + + +struct DMPLYFileInfo : DMTextFileInfo +{ + DMPLYFormat format; + + std::unordered_map elem_map; + std::vector elements; + DMPLYFileElement *element; + + DMPLYFileInfo() + { + element = 0; + format = PLY_FMT_UNKNOWN; + } + + DMPLYFileElement *checkElem(const std::string &elem) + { + if (elem_map.count(elem)) + return &elem_map[elem]; + else + return 0; + } +}; + + +/* Functions + */ +bool dmLoadFromPLY(DMModel &model, const std::string &filename); +bool dmLoadFromPLY(DMModel &model, const std::string &filename, DMPLYFileInfo &info); + + +#endif diff -r 267b3fd2c98c -r 03aa729a9e90 dmscene.cpp --- a/dmscene.cpp Mon Dec 16 06:51:40 2019 +0200 +++ b/dmscene.cpp Mon Dec 16 07:51:05 2019 +0200 @@ -1,572 +1,12 @@ // // GLDragon - OpenGL PLY model viewer / simple benchmark -// -- Scene and model handling + PLY file parsing +// -- Scene file handling // Programmed and designed by Matti 'ccr' Hämäläinen // (C) Copyright 2019 Tecnic Software productions (TNSP) // // See file "COPYING" for license information. // #include "dmscene.h" -#include - - -static bool dmTextError(DMTextFileInfo &info, const std::string &msg) -{ - dmError("%s on line #%d: %s\n", - msg.c_str(), info.nline, info.line.c_str()); - return false; -} - - -static bool dmSyntaxError(DMTextFileInfo &info, const std::string &msg) -{ - dmError("Syntax error on line #%d: %s\n", - info.nline, msg.c_str()); - return false; -} - - -static bool dmReadLine(DMTextFileInfo &info) -{ - if (!std::getline(info.file, info.line)) - return false; - - info.nline++; - - info.line = dmStrTrim(info.line); - - return true; -} - - -static DMPLYPropType dmPLYParsePropType(const std::string &name) -{ - if (name == "list") - return PLY_TYPE_LIST; - else - if (name == "float" || name == "float32") - return PLY_TYPE_FLOAT; - else - if (name == "double" || name == "float64") - return PLY_TYPE_DOUBLE; - else - if (name == "uchar" || name == "uint8") - return PLY_TYPE_UINT8; - else - if (name == "int16") - return PLY_TYPE_INT16; - else - if (name == "uint16") - return PLY_TYPE_UINT16; - else - if (name == "int" || name == "int32") - return PLY_TYPE_INT32; - else - if (name == "uint" || name == "uint32") - return PLY_TYPE_UINT32; - else - return PLY_TYPE_NONE; -} - - -static bool dmPLYParsePropertyValueASCII(DMPLYFileInfo &info, const DMPLYPropType ptype, DMPLYPropValue &pval, size_t &pos) -{ - size_t len = 0; - std::string val = info.line.substr(pos); - - switch (ptype) - { - case PLY_TYPE_INT8: - case PLY_TYPE_INT16: - case PLY_TYPE_INT32: - pval.v_int = std::stoi(val, &len); - break; - - case PLY_TYPE_UINT8: - case PLY_TYPE_UINT16: - case PLY_TYPE_UINT32: - pval.v_uint = std::stoi(val, &len); - break; - - case PLY_TYPE_FLOAT: - pval.v_float = std::stof(val, &len); - break; - - case PLY_TYPE_DOUBLE: - pval.v_double = std::stod(val, &len); - break; - - default: - return dmTextError(info, - "Internal error, unimplemented PLY property type"); - } - - pos += len; - - return true; -} - - -static bool dmPLYParsePropertyASCII(DMPLYFileInfo &info, DMPLYFileProperty &prop, size_t &pos) -{ - switch (prop.type) - { - case PLY_TYPE_LIST: - prop.list_values.clear(); - - if (!dmPLYParsePropertyValueASCII(info, prop.list_num_type, prop.list_num_value, pos)) - return false; - - for (unsigned int n = 0; n < prop.list_num_value.v_uint; n++) - { - DMPLYPropValue pval; - if (!dmPLYParsePropertyValueASCII(info, prop.list_values_type, pval, pos)) - return false; - - prop.list_values.push_back(pval); - } - - if (prop.list_values.size() != prop.list_num_value.v_uint) - { - return dmSyntaxError(info, - "Number of property list '"+ prop.name +" values not equal to number specified"); - } - return true; - - default: - return dmPLYParsePropertyValueASCII(info, prop.type, prop.value, pos); - } -} - - -static bool dmPLYParseElementASCII(DMPLYFileInfo &info, DMPLYFileElement &element) -{ - size_t nprop = 0, pos = 0; - - // Read one line - if (!dmReadLine(info)) - { - return dmTextError(info, - "Unexpected end of file"); - } - - // Skip empty lines - if (info.line.empty()) - return true; - - // Parse the properties - for (auto const prop : element.properties) - { - if (!dmPLYParsePropertyASCII(info, *prop, pos)) - return false; - - nprop++; - } - - if (nprop != element.properties.size()) - { - return dmSyntaxError(info, - "Expected N properties, got different number"); - } - - return true; -} - - -bool dmPLYReadPropertyValueBIN(DMPLYFileInfo &info, const DMPLYPropType ptype, DMPLYPropValue &pval) -{ - uint8_t tmpU8; - int8_t tmpS8; - uint16_t tmpU16; - int16_t tmpS16; - uint32_t tmpU32; - int32_t tmpS32; - float tmpFloat; - - switch (ptype) - { - case PLY_TYPE_INT8: - info.file.read(reinterpret_cast(&tmpS8), 1); - pval.v_int = tmpS8; - break; - - case PLY_TYPE_UINT8: - info.file.read(reinterpret_cast(&tmpU8), 1); - pval.v_uint = tmpU8; - break; - - case PLY_TYPE_INT16: - info.file.read(reinterpret_cast(&tmpS16), 2); - tmpS16 = (info.format == PLY_FMT_BIN_LE) ? SDL_SwapLE16(tmpS16) : SDL_SwapBE16(tmpS16); - pval.v_int = tmpS16; - break; - - case PLY_TYPE_UINT16: - info.file.read(reinterpret_cast(&tmpU16), 2); - tmpU16 = (info.format == PLY_FMT_BIN_LE) ? SDL_SwapLE16(tmpU16) : SDL_SwapBE16(tmpU16); - pval.v_uint = tmpU16; - break; - - case PLY_TYPE_INT32: - info.file.read(reinterpret_cast(&tmpS32), 4); - tmpS32 = (info.format == PLY_FMT_BIN_LE) ? SDL_SwapLE32(tmpS32) : SDL_SwapBE32(tmpS32); - pval.v_int = tmpS32; - break; - - case PLY_TYPE_UINT32: - info.file.read(reinterpret_cast(&tmpU32), 4); - tmpU32 = (info.format == PLY_FMT_BIN_LE) ? SDL_SwapLE32(tmpU32) : SDL_SwapBE32(tmpU32); - pval.v_uint = tmpU32; - break; - - case PLY_TYPE_FLOAT: - info.file.read(reinterpret_cast(&tmpFloat), 4); - pval.v_float = (info.format == PLY_FMT_BIN_LE) ? SDL_SwapFloatLE(tmpFloat) : SDL_SwapFloatBE(tmpFloat); - break; - - default: - return dmTextError(info, - "Internal error, unimplemented PLY property type"); - } - - return true; -} - - -bool dmPLYReadPropertyBIN(DMPLYFileInfo &info, DMPLYFileProperty &prop) -{ - switch (prop.type) - { - case PLY_TYPE_LIST: - prop.list_values.clear(); - - if (!dmPLYReadPropertyValueBIN(info, prop.list_num_type, prop.list_num_value)) - return false; - - for (unsigned int n = 0; n < prop.list_num_value.v_uint; n++) - { - DMPLYPropValue pval; - if (!dmPLYReadPropertyValueBIN(info, prop.list_values_type, pval)) - return false; - - prop.list_values.push_back(pval); - } - - if (prop.list_values.size() != prop.list_num_value.v_uint) - { - return dmSyntaxError(info, - "Number of property list '"+ prop.name +" values not equal to number specified"); - } - return true; - - default: - return dmPLYReadPropertyValueBIN(info, prop.type, prop.value); - } -} - - -bool dmPLYReadElementBIN(DMPLYFileInfo &info, DMPLYFileElement &element) -{ - size_t nprop = 0; - - for (auto const prop : element.properties) - { - if (!dmPLYReadPropertyBIN(info, *prop)) - return false; - - nprop++; - } - - if (nprop != element.properties.size()) - { - return dmSyntaxError(info, - "Expected N properties, got different number"); - } - - return true; -} - - -bool DMModel::loadFromPLY(const std::string &filename) -{ - DMPLYFileInfo info; - - return loadFromPLY(filename, info); -} - - -bool DMModel::loadFromPLY(const std::string &filename, DMPLYFileInfo &info) -{ - info.filename = filename; - info.nline = info.state = 0; - info.file.open(info.filename.c_str(), std::fstream::in | std::fstream::binary); - - dmMsg("Trying to read mesh from '%s'.\n", - info.filename.c_str()); - - if (!info.file.is_open()) - { - dmError("Unable to open file '%s'.\n", - info.filename.c_str()); - return false; - } - - // Parse the PLY header - while (info.state >= 0) - { - // Read one line - if (!dmReadLine(info)) - return false; - - // Skip empty lines - if (info.line.empty()) - continue; - - // Split key and value - std::vector tokens = dmStrSplit(info.line); - std::string &key = tokens[0]; - - if (info.state == 0 && key == "ply") - { - info.state = 1; - } - else - if (info.state > 0 && key == "end_header") - { - info.state = -1; - } - else - if (info.state == 1 && key == "format") - { - if (tokens.size() < 3) - { - return dmSyntaxError(info, - "Expected value for format"); - } - - info.format = PLY_FMT_UNKNOWN; - - if (tokens[1] == "ascii") - info.format = PLY_FMT_ASCII; - else - if (tokens[1] == "binary_little_endian") - info.format = PLY_FMT_BIN_LE; - else - if (tokens[1] == "binary_big_endian") - info.format = PLY_FMT_BIN_BE; - - if (info.format == PLY_FMT_UNKNOWN || - tokens[2] != "1.0") - { - dmError("Unknown or unsupported PLY file format '%s'.\n", - (tokens[1] +" "+ tokens[2]).c_str()); - return false; - } - - info.state = 2; - } - else - if ((info.state == 2 || info.state == 3) && key == "element") - { - if (tokens.size() < 3) - { - return dmSyntaxError(info, - "Expected a value for element key"); - } - - std::string &el_name = tokens[1], &el_value = tokens[2]; - - if (info.elem_map.count(el_name)) - { - return dmSyntaxError(info, - "Element '"+ el_name +"' has already been defined"); - } - - DMPLYFileElement &elem = info.elem_map[el_name]; - info.element = &elem; - info.elements.push_back(&elem); - - elem.name = el_name; - elem.value = std::stoi(el_value); - - info.state = 3; - } - else - if (info.state == 3 && key == "property") - { - if (tokens.size() < 3) - { - return dmSyntaxError(info, - "Expected value for property"); - } - - const std::string - &pr_name = tokens.back(), - &pr_typename = tokens.at(1); - - if (!info.element) - { - // Should not happen - return dmSyntaxError(info, - "No element defined for property '"+ pr_name +"'?"); - } - - // Check if this property has been already defined - if (info.element->prop_map.count(pr_name)) - { - return dmSyntaxError(info, - "Element '"+ info.element->name + - "' already has property '"+ pr_name +"' defined?"); - } - - // Parse property information - DMPLYPropType pr_type = dmPLYParsePropType(pr_typename); - if (pr_type == PLY_TYPE_NONE) - { - return dmSyntaxError(info, - "Invalid or unsupported property type '"+ pr_typename +"'"); - } - - DMPLYFileProperty &prop = info.element->prop_map[pr_name]; - info.element->properties.push_back(&prop); - - prop.name = pr_name; - prop.type = pr_type; - - if (pr_type == PLY_TYPE_LIST) - { - // List is a special case - if (tokens.size() < 5) - { - return dmSyntaxError(info, - "Expected more values for a list property (num_type, val_type, name)"); - } - - prop.list_num_type = dmPLYParsePropType(tokens.at(2)); - prop.list_values_type = dmPLYParsePropType(tokens.at(3)); - - if (prop.list_num_type == PLY_TYPE_NONE || - prop.list_values_type == PLY_TYPE_NONE) - { - return dmSyntaxError(info, - "Invalid or unsupported property type(s)"); - } - } - } - else - if (info.state > 0 // && (key == "comment" || key == "obj_info") - ) - { - // Ignore comments - // .. and unknown keys - } - else - { - return dmSyntaxError(info, - "Unexpected key '"+ key +"'"); - } - } - - // Check header data - DMPLYFileElement *elem; - DMPLYFileProperty *prop; - if (info.state != -1 || - (elem = info.checkElem(PLY_ELEM_FACE)) == 0 || - (prop = elem->checkProp(PLY_PROP_VERTEX_INDICES)) == 0 || - prop->type != PLY_TYPE_LIST || - (elem = info.checkElem(PLY_ELEM_VERTEX)) == 0 || - (prop = elem->checkProp("x")) == 0 || prop->type != PLY_TYPE_FLOAT || - (prop = elem->checkProp("y")) == 0 || prop->type != PLY_TYPE_FLOAT || - (prop = elem->checkProp("z")) == 0 || prop->type != PLY_TYPE_FLOAT - ) - { - dmError("PLY file did not contain expected information.\n"); - return false; - } - - nvertices = info.elem_map[PLY_ELEM_VERTEX].value; - nfaces = info.elem_map[PLY_ELEM_FACE].value; - - - if (nvertices < 3 || nfaces < 1) - { - dmError("Invalid nvertices (%d) and/or nfaces (%d).\n", - nvertices, nfaces); - return false; - } - - dmMsg("Should have %d vertices, %d faces\n", - nvertices, nfaces); - - // Pre-allocate space - vertices.reserve(nvertices); - normals.reserve(nvertices); - faces.reserve(nfaces * 3); - - // Read the actual data (in order of the elements) - for (auto *element : info.elements) - for (int n = 0; n < element->value; n++) - { - switch (info.format) - { - case PLY_FMT_ASCII: - if (!dmPLYParseElementASCII(info, *element)) - return false; - break; - - default: - if (!dmPLYReadElementBIN(info, *element)) - return false; - break; - } - - // Check for specific elements - if (element->name == PLY_ELEM_FACE) - { - DMPLYFileProperty &prop = element->prop_map[PLY_PROP_VERTEX_INDICES]; - - if (prop.list_num_value.v_uint != 3) - { - return dmSyntaxError(info, - "Expected 3 vertices per face"); - } - - for (unsigned int n = 0; n < prop.list_num_value.v_uint; n++) - { - faces.push_back(prop.list_values[n].v_uint); - } - } - else - if (element->name == PLY_ELEM_VERTEX) - { - DMVector3 vert; - vert.x = element->prop_map["x"].value.v_float; - vert.y = element->prop_map["y"].value.v_float; - vert.z = element->prop_map["z"].value.v_float; - - vertices.push_back(vert); - - if (element->checkProp("nx") && - element->checkProp("ny") && - element->checkProp("nz")) - { - DMVector3 normal; - normal.x = element->prop_map["nx"].value.v_float; - normal.y = element->prop_map["ny"].value.v_float; - normal.z = element->prop_map["nz"].value.v_float; - - normals.push_back(normal); - } - } - } - - dmMsg("Found %ld vertices, %ld normals, %ld faces\n", - vertices.size(), - normals.size(), - faces.size() / 3); - - return true; -} bool dmParseVector(DMTextFileInfo &info, const std::vector tokens, const size_t offs, DMVector3 &vec) @@ -584,7 +24,7 @@ } else { - return dmSyntaxError(info, + return info.syntaxError( "Expected 1/3 value vector for '"+ *info.key +"'"); } @@ -610,7 +50,7 @@ } else { - return dmSyntaxError(info, + return info.syntaxError( "Expected 1/3/4 value vector for '"+ *info.key +"'"); } @@ -643,7 +83,7 @@ while (info.state >= 0) { // Read one line - if (!dmReadLine(info)) + if (!info.readLine()) { info.state = -1; break; @@ -665,7 +105,7 @@ DMModel newmodel; if (tokens.size() != 2) { - return dmSyntaxError(info, + return info.syntaxError( "Keyword model expects a filename argument"); } @@ -680,7 +120,7 @@ { if (tokens.size() != 3) { - return dmSyntaxError(info, + return info.syntaxError( "Keyword shaderfile expects shader type (fs, vs) and filename arguments"); } @@ -695,7 +135,7 @@ model->vertShaderFile = shfile; else { - return dmSyntaxError(info, + return info.syntaxError( "Invalid shaderfile type '"+ shtype +"'"); } } @@ -733,7 +173,7 @@ { if (tokens.size() != 2) { - return dmSyntaxError(info, + return info.syntaxError( "Expected argument for shininess"); } @@ -746,7 +186,7 @@ if (lights.size() >= 4) { - return dmTextError(info, + return info.textError( "Too many lights defined (max 4)"); } @@ -802,7 +242,7 @@ } else { - return dmSyntaxError(info, + return info.syntaxError( "Unexpected key '"+ key +"'"); } } diff -r 267b3fd2c98c -r 03aa729a9e90 dmscene.h --- a/dmscene.h Mon Dec 16 06:51:40 2019 +0200 +++ b/dmscene.h Mon Dec 16 07:51:05 2019 +0200 @@ -11,116 +11,10 @@ #include "dmutil.h" #include -#include -#include - - -#define PLY_PROP_VERTEX_INDICES "vertex_indices" -#define PLY_ELEM_FACE "face" -#define PLY_ELEM_VERTEX "vertex" - - -enum DMPLYFormat -{ - PLY_FMT_UNKNOWN, - PLY_FMT_ASCII, - PLY_FMT_BIN_LE, - PLY_FMT_BIN_BE -}; - - -enum DMPLYPropType -{ - PLY_TYPE_NONE, - PLY_TYPE_LIST, - - PLY_TYPE_UINT8, - PLY_TYPE_INT8, - PLY_TYPE_INT16, - PLY_TYPE_UINT16, - PLY_TYPE_INT32, - PLY_TYPE_UINT32, - PLY_TYPE_FLOAT, - PLY_TYPE_DOUBLE -}; - - -/* Structures - */ -union DMPLYPropValue -{ - double v_double; - float v_float; - unsigned int v_uint; - int v_int; -}; -struct DMPLYFileProperty -{ - std::string name; - DMPLYPropType - type, - list_num_type, - list_values_type; - - DMPLYPropValue value, list_num_value; - std::vector list_values; -}; - - -struct DMPLYFileElement -{ - int value; - std::string name; - - std::unordered_map prop_map; - std::vector properties; - - DMPLYFileProperty *checkProp(const std::string &prop) - { - if (prop_map.count(prop)) - return &prop_map[prop]; - else - return 0; - } -}; - - -struct DMTextFileInfo -{ - int nline, state; - std::string filename; - std::string line; - std::ifstream file; - std::string *key; -}; - - -struct DMPLYFileInfo : DMTextFileInfo -{ - DMPLYFormat format; - - std::unordered_map elem_map; - std::vector elements; - DMPLYFileElement *element; - - DMPLYFileInfo() - { - element = 0; - format = PLY_FMT_UNKNOWN; - } - - DMPLYFileElement *checkElem(const std::string &elem) - { - if (elem_map.count(elem)) - return &elem_map[elem]; - else - return 0; - } -}; - - +/* Structures and classes + */ struct DMVector3 { float x, y, z; @@ -159,9 +53,6 @@ fragShaderFile, vertShaderFile, fragShaderStr, vertShaderStr; - bool loadFromPLY(const std::string &filename); - bool loadFromPLY(const std::string &filename, DMPLYFileInfo &info); - DMModel() { nfaces = nvertices = 0; diff -r 267b3fd2c98c -r 03aa729a9e90 dmutil.h --- a/dmutil.h Mon Dec 16 06:51:40 2019 +0200 +++ b/dmutil.h Mon Dec 16 07:51:05 2019 +0200 @@ -14,11 +14,14 @@ #include #include #include +#include #define DMUTIL_WHITESPACE "\t\n\v\f\r " +/* Functions + */ void dmMsg_V(const char *fmt, va_list ap); void dmMsg(const char *fmt, ...); void dmError_V(const char *fmt, va_list ap); @@ -38,4 +41,40 @@ bool dmFileExists(const std::string &filename, std::ios_base::openmode mode = std::ios_base::in); +/* Structures and classes + */ +struct DMTextFileInfo +{ + int nline, state; + std::string filename; + std::string line; + std::ifstream file; + std::string *key; + + virtual bool syntaxError(const std::string &msg) + { + dmError("Syntax error on line #%d: %s\n", + nline, msg.c_str()); + return false; + } + + virtual bool textError(const std::string &msg) + { + dmError("%s on line #%d: %s\n", + msg.c_str(), nline, line.c_str()); + return false; + } + + virtual bool readLine(void) + { + if (!std::getline(file, line)) + return false; + + nline++; + line = dmStrTrim(line); + return true; + } +}; + + #endif diff -r 267b3fd2c98c -r 03aa729a9e90 gldragon.cpp --- a/gldragon.cpp Mon Dec 16 06:51:40 2019 +0200 +++ b/gldragon.cpp Mon Dec 16 07:51:05 2019 +0200 @@ -10,6 +10,7 @@ #include #include "dmutil.h" #include "dmglrender.h" +#include "dmply.h" /* Default settings etc. constants @@ -20,8 +21,6 @@ #define SET_MAX_SHADER_SIZE (128 * 1024) - - /* Helpers */ bool dmInitSDL(DMSimpleRenderer &renderer, @@ -252,7 +251,7 @@ for (DMModel &model : scene.models) { - if (!model.loadFromPLY(basePath + model.modelFile)) + if (!dmLoadFromPLY(model, basePath + model.modelFile)) goto exit; if (optUseShaders) diff -r 267b3fd2c98c -r 03aa729a9e90 ply2bin.cpp --- a/ply2bin.cpp Mon Dec 16 06:51:40 2019 +0200 +++ b/ply2bin.cpp Mon Dec 16 07:51:05 2019 +0200 @@ -11,8 +11,8 @@ #define _SDL_main_h #define SDL_main main -#include "dmutil.h" #include "dmscene.h" +#include "dmply.h" bool dmFWriteFloatLE(FILE *fh, const float val) @@ -166,7 +166,7 @@ goto exit; } - if (!model.loadFromPLY(optInputFilename)) + if (!dmLoadFromPLY(model, optInputFilename)) goto exit; if ((outFile = fopen(optOutputFilename.c_str(), "wb")) == NULL)