Mercurial > hg > forks > gldragon
diff dmscene.cpp @ 61:7b138613e2fc
Rename dmmodel.* to dmscene.*
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sat, 14 Dec 2019 14:08:51 +0200 |
parents | dmmodel.cpp@0ae1ff609626 |
children | 03aa729a9e90 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmscene.cpp Sat Dec 14 14:08:51 2019 +0200 @@ -0,0 +1,811 @@ +// +// GLDragon - OpenGL PLY model viewer / simple benchmark +// -- Scene and model handling + PLY file parsing +// Programmed and designed by Matti 'ccr' Hämäläinen <ccr@tnsp.org> +// (C) Copyright 2019 Tecnic Software productions (TNSP) +// +// See file "COPYING" for license information. +// +#include "dmscene.h" +#include <SDL_endian.h> + + +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<char *>(&tmpS8), 1); + pval.v_int = tmpS8; + break; + + case PLY_TYPE_UINT8: + info.file.read(reinterpret_cast<char *>(&tmpU8), 1); + pval.v_uint = tmpU8; + break; + + case PLY_TYPE_INT16: + info.file.read(reinterpret_cast<char *>(&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<char *>(&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<char *>(&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<char *>(&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<char *>(&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<std::string> 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<std::string> tokens, const size_t offs, DMVector3 &vec) +{ + if (tokens.size() == offs + 1) + { + vec.x = vec.y = vec.z = std::stof(tokens[offs]); + } + else + if (tokens.size() == offs + 3) + { + vec.x = std::stof(tokens[offs]); + vec.y = std::stof(tokens[offs + 1]); + vec.z = std::stof(tokens[offs + 2]); + } + else + { + return dmSyntaxError(info, + "Expected 1/3 value vector for '"+ *info.key +"'"); + } + + return true; +} + + +bool dmParseVector(DMTextFileInfo &info, const std::vector<std::string> tokens, const size_t offs, DMVector4 &vec) +{ + if (tokens.size() == offs + 1) + { + vec.p.x = vec.p.y = vec.p.z = std::stof(tokens[offs]); + } + else + if (tokens.size() == offs + 3 || tokens.size() == offs + 4) + { + vec.p.x = std::stof(tokens[offs]); + vec.p.y = std::stof(tokens[offs + 1]); + vec.p.z = std::stof(tokens[offs + 2]); + + if (tokens.size() == offs + 4) + vec.p.w = std::stof(tokens[offs + 3]); + } + else + { + return dmSyntaxError(info, + "Expected 1/3/4 value vector for '"+ *info.key +"'"); + } + + return true; +} + + +bool DMSimpleScene::loadInfo(const std::string &filename) +{ + DMTextFileInfo info; + DMModel *model = 0; + DMLight *light = 0; + DMVector4 *ppos = 0, *ppointAt = 0; + DMMaterial *pmat = 0; + + 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 scene data 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; + } + + while (info.state >= 0) + { + // Read one line + if (!dmReadLine(info)) + { + info.state = -1; + break; + } + + // Skip empty lines and comments + if (info.line.empty() || + info.line[0] == '#' || + info.line[0] == ';') + continue; + + // Split key and values + std::vector<std::string> tokens = dmStrSplit(info.line); + std::string key = tokens[0]; + info.key = &key; + + if (key == "model") + { + DMModel newmodel; + if (tokens.size() != 2) + { + return dmSyntaxError(info, + "Keyword model expects a filename argument"); + } + + models.push_back(newmodel); + model = &models.back(); + model->modelFile = tokens[1]; + pmat = &model->material; + info.state = 1; + } + else + if (info.state == 1 && (key == "shaderfile")) + { + if (tokens.size() != 3) + { + return dmSyntaxError(info, + "Keyword shaderfile expects shader type (fs, vs) and filename arguments"); + } + + std::string + &shtype = tokens[1], + &shfile = tokens[2]; + + if (shtype == "fs") + model->fragShaderFile = shfile; + else + if (shtype == "vs") + model->vertShaderFile = shfile; + else + { + return dmSyntaxError(info, + "Invalid shaderfile type '"+ shtype +"'"); + } + } + else + if (info.state == 1 && (key == "translate" || key == "rotate" || key == "scale")) + { + DMVector3 vec; + + if (!dmParseVector(info, tokens, 1, vec)) + return false; + + if (!model) + return false; + + if (key == "translate") + { + model->translate = vec; + model->translateSet = true; + } + else + if (key == "rotate") + { + model->rotate = vec; + model->rotateSet = true; + } + else + if (key == "scale") + { + model->scale = vec; + model->scaleSet = true; + } + } + else + if (info.state == 1 && key == "shininess") + { + if (tokens.size() != 2) + { + return dmSyntaxError(info, + "Expected argument for shininess"); + } + + model->material.shininess = std::stoi(tokens[1], 0, 0); + } + else + if (key == "light") + { + DMLight newlight; + + if (lights.size() >= 4) + { + return dmTextError(info, + "Too many lights defined (max 4)"); + } + + lights.push_back(newlight); + light = &lights.back(); + ppos = &light->position; + ppointAt = &light->pointAt; + pmat = &light->color; + info.state = 2; + } + else + if ((info.state == 1 || info.state == 2) && + (key == "ambient" || key == "diffuse" || key == "specular")) + { + DMVector4 val; + val.c.a = 1.0f; + + if (!dmParseVector(info, tokens, 1, val)) + return false; + + if (key == "ambient") + pmat->ambient = val; + else + if (key == "diffuse") + pmat->diffuse = val; + else + if (key == "specular") + pmat->specular = val; + } + else + if (key == "camera") + { + info.state = 3; + + ppos = &camera.position; + ppointAt = &camera.pointAt; + } + else + if ((info.state == 3 || info.state == 2) && + (key == "position" || key == "pos" || key == "point_at")) + { + DMVector4 vec; + vec.p.w = 0; + + if (!dmParseVector(info, tokens, 1, vec)) + return false; + + if (key == "position" || key == "pos") + *ppos = vec; + else + if (key == "point_at") + *ppointAt = vec; + } + else + { + return dmSyntaxError(info, + "Unexpected key '"+ key +"'"); + } + } + + return true; +}