Merge pull request #18200 from wwmayer/refactor_ply

Refactor and optimize PLY reader
This commit is contained in:
Chris Hennes
2024-12-13 11:40:29 -05:00
committed by GitHub
4 changed files with 810 additions and 511 deletions

View File

@@ -102,6 +102,8 @@ SET(Core_SRCS
Core/IO/Reader3MF.h
Core/IO/ReaderOBJ.cpp
Core/IO/ReaderOBJ.h
Core/IO/ReaderPLY.cpp
Core/IO/ReaderPLY.h
Core/IO/Writer3MF.cpp
Core/IO/Writer3MF.h
Core/IO/WriterInventor.cpp

View File

@@ -0,0 +1,664 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/***************************************************************************
* Copyright (c) 2024 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
#include <boost/lexical_cast.hpp>
#include <istream>
#endif
#include "Core/MeshIO.h"
#include "Core/MeshKernel.h"
#include <Base/Stream.h>
#include <Base/Tools.h>
#include "ReaderPLY.h"
using namespace MeshCore;
// http://local.wasp.uwa.edu.au/~pbourke/dataformats/ply/
ReaderPLY::ReaderPLY(MeshKernel& kernel, Material* material)
: _kernel(kernel)
, _material(material)
{}
bool ReaderPLY::CheckHeader(std::istream& input) const
{
if (!input || input.bad()) {
return false;
}
std::streambuf* buf = input.rdbuf();
if (!buf) {
return false;
}
// read in the first three characters
std::array<char, 3> ply {};
input.read(ply.data(), ply.size());
input.ignore(1);
if (!input) {
return false;
}
return ((ply[0] == 'p') && (ply[1] == 'l') && (ply[2] == 'y'));
}
bool ReaderPLY::ReadFormat(std::istream& str)
{
std::string format_string;
std::string version;
char space_format_string {};
char space_format_version {};
str >> space_format_string >> std::ws >> format_string >> space_format_version >> std::ws
>> version;
// clang-format off
if (std::isspace(static_cast<unsigned char>(space_format_string)) == 0 ||
std::isspace(static_cast<unsigned char>(space_format_version)) == 0) {
return false;
}
// clang-format on
if (format_string == "ascii") {
format = ascii;
}
else if (format_string == "binary_big_endian") {
format = binary_big_endian;
}
else if (format_string == "binary_little_endian") {
format = binary_little_endian;
}
else {
// wrong format version
return false;
}
return (version == "1.0");
}
bool ReaderPLY::ReadElement(std::istream& str, std::string& element)
{
std::string name;
std::size_t count {};
char space_element_name {};
char space_name_count {};
str >> space_element_name >> std::ws >> name >> space_name_count >> std::ws >> count;
// clang-format off
if (std::isspace(static_cast<unsigned char>(space_element_name)) == 0 ||
std::isspace(static_cast<unsigned char>(space_name_count)) == 0) {
return false;
}
// clang-format on
if (name == "vertex") {
element = name;
v_count = count;
meshPoints.reserve(count);
}
else if (name == "face") {
element = name;
f_count = count;
meshFacets.reserve(count);
}
else {
element.clear();
}
return true;
}
ReaderPLY::Property ReaderPLY::propertyOfName(const std::string& name)
{
if (name == "x") {
return coord_x;
}
if (name == "y") {
return coord_y;
}
if (name == "z") {
return coord_z;
}
if (name == "red" || name == "diffuse_red") {
return color_r;
}
if (name == "green" || name == "diffuse_green") {
return color_g;
}
if (name == "blue" || name == "diffuse_blue") {
return color_b;
}
return generic;
}
bool ReaderPLY::ReadVertexProperty(std::istream& str)
{
std::string type;
std::string name;
char space {};
str >> space >> std::ws >> type >> space >> std::ws >> name >> std::ws;
Number number {};
if (type == "char" || type == "int8") {
number = int8;
}
else if (type == "uchar" || type == "uint8") {
number = uint8;
}
else if (type == "short" || type == "int16") {
number = int16;
}
else if (type == "ushort" || type == "uint16") {
number = uint16;
}
else if (type == "int" || type == "int32") {
number = int32;
}
else if (type == "uint" || type == "uint32") {
number = uint32;
}
else if (type == "float" || type == "float32") {
number = float32;
}
else if (type == "double" || type == "float64") {
number = float64;
}
else {
// no valid number type
return false;
}
// store the property name and type
vertex_props.emplace_back(propertyOfName(name), number);
return true;
}
bool ReaderPLY::ReadFaceProperty(std::istream& str)
{
std::string type;
std::string name;
char space {};
std::string list;
std::string uchr;
str >> space >> std::ws >> list >> std::ws;
if (list == "list") {
str >> uchr >> std::ws >> type >> std::ws >> name >> std::ws;
}
else {
// not a 'list'
type = list;
str >> name;
}
if (name != "vertex_indices" && name != "vertex_index") {
Number number {};
if (type == "char" || type == "int8") {
number = int8;
}
else if (type == "uchar" || type == "uint8") {
number = uint8;
}
else if (type == "short" || type == "int16") {
number = int16;
}
else if (type == "ushort" || type == "uint16") {
number = uint16;
}
else if (type == "int" || type == "int32") {
number = int32;
}
else if (type == "uint" || type == "uint32") {
number = uint32;
}
else if (type == "float" || type == "float32") {
number = float32;
}
else if (type == "double" || type == "float64") {
number = float64;
}
else {
// no valid number type
return false;
}
// store the property name and type
face_props.push_back(number);
}
return true;
}
bool ReaderPLY::ReadProperty(std::istream& str, const std::string& element)
{
if (element == "vertex") {
if (!ReadVertexProperty(str)) {
return false;
}
}
else if (element == "face") {
if (!ReadFaceProperty(str)) {
return false;
}
}
return true;
}
bool ReaderPLY::ReadHeader(std::istream& input)
{
std::string line;
std::string element;
while (std::getline(input, line)) {
std::istringstream str(line);
str.unsetf(std::ios_base::skipws);
str >> std::ws;
if (str.eof()) {
continue; // empty line
}
std::string kw;
str >> kw;
if (kw == "format") {
if (!ReadFormat(str)) {
return false;
}
}
else if (kw == "element") {
if (!ReadElement(str, element)) {
return false;
}
}
else if (kw == "property") {
if (!ReadProperty(str, element)) {
return false;
}
}
else if (kw == "end_header") {
break; // end of the header, now read the data
}
}
return true;
}
bool ReaderPLY::VerifyVertexProperty()
{
// check if valid 3d points
PropertyComp property;
std::size_t num_x = std::count_if(vertex_props.begin(),
vertex_props.end(),
[&property](const std::pair<Property, int>& prop) {
return property(prop, coord_x);
});
std::size_t num_y = std::count_if(vertex_props.begin(),
vertex_props.end(),
[&property](const std::pair<Property, int>& prop) {
return property(prop, coord_y);
});
std::size_t num_z = std::count_if(vertex_props.begin(),
vertex_props.end(),
[&property](const std::pair<Property, int>& prop) {
return property(prop, coord_z);
});
return ((num_x == 1) && (num_y == 1) && (num_z == 1));
}
bool ReaderPLY::VerifyColorProperty()
{
// check if valid colors are set
PropertyComp property;
std::size_t num_r = std::count_if(vertex_props.begin(),
vertex_props.end(),
[&property](const std::pair<Property, int>& prop) {
return property(prop, color_r);
});
std::size_t num_g = std::count_if(vertex_props.begin(),
vertex_props.end(),
[&property](const std::pair<Property, int>& prop) {
return property(prop, color_g);
});
std::size_t num_b = std::count_if(vertex_props.begin(),
vertex_props.end(),
[&property](const std::pair<Property, int>& prop) {
return property(prop, color_b);
});
std::size_t rgb_colors = num_r + num_g + num_b;
if (rgb_colors != 0 && rgb_colors != 3) {
return false;
}
// only if set per vertex
if (rgb_colors == 3) {
if (_material) {
_material->binding = MeshIO::PER_VERTEX;
_material->diffuseColor.reserve(v_count);
}
}
return true;
}
bool ReaderPLY::Load(std::istream& input)
{
if (!CheckHeader(input)) {
return false;
}
if (!ReadHeader(input)) {
return false;
}
if (!VerifyVertexProperty()) {
return false;
}
if (!VerifyColorProperty()) {
return false;
}
// clang-format off
return format == ascii ? LoadAscii(input)
: LoadBinary(input);
// clang-format on
}
void ReaderPLY::CleanupMesh()
{
_kernel.Clear(); // remove all data before
MeshCleanup meshCleanup(meshPoints, meshFacets);
if (_material) {
meshCleanup.SetMaterial(_material);
}
meshCleanup.RemoveInvalids();
MeshPointFacetAdjacency meshAdj(meshPoints.size(), meshFacets);
meshAdj.SetFacetNeighbourhood();
_kernel.Adopt(meshPoints, meshFacets);
}
bool ReaderPLY::ReadVertexes(std::istream& input)
{
std::string line;
for (std::size_t i = 0; i < v_count && std::getline(input, line); i++) {
std::istringstream str(line);
str.unsetf(std::ios_base::skipws);
str >> std::ws;
// go through the vertex properties
PropertyArray prop_values {};
std::size_t count_props = vertex_props.size();
for (const auto& it : vertex_props) {
switch (it.second) {
case int8:
case int16:
case int32: {
int vt {};
str >> vt >> std::ws;
prop_values[it.first] = static_cast<float>(vt);
} break;
case uint8:
case uint16:
case uint32: {
unsigned int vt {};
str >> vt >> std::ws;
prop_values[it.first] = static_cast<float>(vt);
} break;
case float32: {
float vt {};
str >> vt >> std::ws;
prop_values[it.first] = vt;
} break;
case float64: {
double vt {};
str >> vt >> std::ws;
prop_values[it.first] = static_cast<float>(vt);
} break;
default:
return false;
}
// does line contain all properties
if (--count_props > 0 && str.eof()) {
return false;
}
}
addVertexProperty(prop_values);
}
return true;
}
bool ReaderPLY::ReadFaces(std::istream& input)
{
constexpr const std::size_t count_props = 4;
std::string line;
for (std::size_t i = 0; i < f_count && std::getline(input, line); i++) {
std::istringstream str(line);
str.unsetf(std::ios_base::skipws);
str >> std::ws;
std::array<int, count_props> v_indices {};
std::size_t index = count_props;
for (int& vt : v_indices) {
str >> vt >> std::ws;
if (--index > 0 && str.eof()) {
return false;
}
}
if (v_indices[0] != 3) {
return false;
}
meshFacets.push_back(MeshFacet(v_indices[1], v_indices[2], v_indices[3]));
}
return true;
}
bool ReaderPLY::LoadAscii(std::istream& input)
{
if (!ReadVertexes(input)) {
return false;
}
if (!ReadFaces(input)) {
return false;
}
CleanupMesh();
return true;
}
void ReaderPLY::addVertexProperty(const PropertyArray& prop)
{
Base::Vector3f pt;
pt.x = (prop[coord_x]);
pt.y = (prop[coord_y]);
pt.z = (prop[coord_z]);
meshPoints.push_back(pt);
if (_material && _material->binding == MeshIO::PER_VERTEX) {
// NOLINTBEGIN
float r = (prop[color_r]) / 255.0F;
float g = (prop[color_g]) / 255.0F;
float b = (prop[color_b]) / 255.0F;
// NOLINTEND
_material->diffuseColor.emplace_back(r, g, b);
}
}
bool ReaderPLY::ReadVertexes(Base::InputStream& is)
{
for (std::size_t i = 0; i < v_count; i++) {
// go through the vertex properties
PropertyArray prop_values {};
for (const auto& it : vertex_props) {
switch (it.second) {
case int8: {
int8_t vt {};
is >> vt;
prop_values[it.first] = static_cast<float>(vt);
} break;
case uint8: {
uint8_t vt {};
is >> vt;
prop_values[it.first] = static_cast<float>(vt);
} break;
case int16: {
int16_t vt {};
is >> vt;
prop_values[it.first] = static_cast<float>(vt);
} break;
case uint16: {
uint16_t vt {};
is >> vt;
prop_values[it.first] = static_cast<float>(vt);
} break;
case int32: {
int32_t vt {};
is >> vt;
prop_values[it.first] = static_cast<float>(vt);
} break;
case uint32: {
uint32_t vt {};
is >> vt;
prop_values[it.first] = static_cast<float>(vt);
} break;
case float32: {
float vt {};
is >> vt;
prop_values[it.first] = vt;
} break;
case float64: {
double vt {};
is >> vt;
prop_values[it.first] = static_cast<float>(vt);
} break;
default:
return false;
}
}
addVertexProperty(prop_values);
}
return true;
}
bool ReaderPLY::ReadFaces(Base::InputStream& is)
{
unsigned char num {};
uint32_t f1 {};
uint32_t f2 {};
uint32_t f3 {};
for (std::size_t i = 0; i < f_count; i++) {
is >> num;
if (num == 3) {
is >> f1 >> f2 >> f3;
if (f1 < v_count && f2 < v_count && f3 < v_count) {
meshFacets.push_back(MeshFacet(f1, f2, f3));
}
for (auto it : face_props) {
switch (it) {
case int8: {
int8_t vt {};
is >> vt;
} break;
case uint8: {
uint8_t vt {};
is >> vt;
} break;
case int16: {
int16_t vt {};
is >> vt;
} break;
case uint16: {
uint16_t vt {};
is >> vt;
} break;
case int32: {
int32_t vt {};
is >> vt;
} break;
case uint32: {
uint32_t vt {};
is >> vt;
} break;
case float32: {
unsigned char cnt {};
is >> cnt;
float vt {};
for (unsigned char j = 0; j < cnt; j++) {
is >> vt;
}
} break;
case float64: {
unsigned char cnt {};
is >> cnt;
double vt {};
for (unsigned char j = 0; j < cnt; j++) {
is >> vt;
}
} break;
default:
return false;
}
}
}
}
return true;
}
bool ReaderPLY::LoadBinary(std::istream& input)
{
Base::InputStream is(input);
if (format == binary_little_endian) {
is.setByteOrder(Base::Stream::LittleEndian);
}
else {
is.setByteOrder(Base::Stream::BigEndian);
}
if (!ReadVertexes(is)) {
return false;
}
if (!ReadFaces(is)) {
return false;
}
CleanupMesh();
return true;
}

View File

@@ -0,0 +1,141 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/***************************************************************************
* Copyright (c) 2024 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef MESH_IO_READER_PLY_H
#define MESH_IO_READER_PLY_H
#include <Mod/Mesh/App/Core/MeshKernel.h>
#include <Mod/Mesh/MeshGlobal.h>
#include <iosfwd>
namespace Base
{
class InputStream;
}
namespace MeshCore
{
class MeshKernel;
struct Material;
/** Loads the mesh object from data in PLY format. */
class MeshExport ReaderPLY
{
public:
/*!
* \brief ReaderPLY
*/
explicit ReaderPLY(MeshKernel& kernel, Material* = nullptr);
/*!
* \brief Load the mesh from the input stream
* \return true on success and false otherwise
*/
bool Load(std::istream& input);
private:
bool CheckHeader(std::istream& input) const;
bool ReadHeader(std::istream& input);
bool VerifyVertexProperty();
bool VerifyColorProperty();
bool ReadFormat(std::istream& str);
bool ReadElement(std::istream& str, std::string& element);
bool ReadProperty(std::istream& str, const std::string& element);
bool ReadVertexProperty(std::istream& str);
bool ReadFaceProperty(std::istream& str);
bool ReadVertexes(std::istream& input);
bool ReadFaces(std::istream& input);
bool ReadVertexes(Base::InputStream& is);
bool ReadFaces(Base::InputStream& is);
bool LoadAscii(std::istream& input);
bool LoadBinary(std::istream& input);
void CleanupMesh();
private:
enum Property
{
coord_x,
coord_y,
coord_z,
color_r,
color_g,
color_b,
generic,
num_props
};
static Property propertyOfName(const std::string& name);
using PropertyArray = std::array<float, num_props>;
void addVertexProperty(const PropertyArray& prop);
enum Number
{
int8,
uint8,
int16,
uint16,
int32,
uint32,
float32,
float64
};
struct PropertyComp
{
using argument_type_1st = std::pair<Property, int>;
using argument_type_2nd = Property;
using result_type = bool;
// clang-format off
bool operator()(const argument_type_1st& x,
const argument_type_2nd& y) const
{
return x.first == y;
}
// clang-format on
};
enum Format
{
unknown,
ascii,
binary_little_endian,
binary_big_endian
} format = unknown;
std::vector<std::pair<Property, Number>> vertex_props;
std::vector<Number> face_props;
std::size_t v_count = 0;
std::size_t f_count = 0;
MeshPointArray meshPoints;
MeshFacetArray meshFacets;
MeshKernel& _kernel;
Material* _material;
};
} // namespace MeshCore
#endif // MESH_IO_READER_PLY_H

View File

@@ -38,6 +38,7 @@
#include "IO/Reader3MF.h"
#include "IO/ReaderOBJ.h"
#include "IO/ReaderPLY.h"
#include "IO/Writer3MF.h"
#include "IO/WriterInventor.h"
#include "IO/WriterOBJ.h"
@@ -657,519 +658,10 @@ bool MeshInput::LoadOFF(std::istream& input)
return true;
}
namespace MeshCore
{
namespace Ply
{
enum Number
{
int8,
uint8,
int16,
uint16,
int32,
uint32,
float32,
float64
};
struct Property
{
using first_argument_type = std::pair<std::string, int>;
using second_argument_type = std::string;
using result_type = bool;
bool operator()(const std::pair<std::string, int>& x, const std::string& y) const
{
return x.first == y;
}
};
} // namespace Ply
using namespace Ply;
} // namespace MeshCore
bool MeshInput::LoadPLY(std::istream& input)
{
// http://local.wasp.uwa.edu.au/~pbourke/dataformats/ply/
std::size_t v_count = 0, f_count = 0;
MeshPointArray meshPoints;
MeshFacetArray meshFacets;
enum
{
unknown,
ascii,
binary_little_endian,
binary_big_endian
} format = unknown;
if (!input || input.bad()) {
return false;
}
std::streambuf* buf = input.rdbuf();
if (!buf) {
return false;
}
// read in the first three characters
char ply[3];
input.read(ply, 3);
input.ignore(1);
if (!input) {
return false;
}
if ((ply[0] != 'p') || (ply[1] != 'l') || (ply[2] != 'y')) {
return false; // wrong header
}
std::vector<std::pair<std::string, Ply::Number>> vertex_props;
std::vector<Ply::Number> face_props;
std::string line, element;
MeshIO::Binding rgb_value = MeshIO::OVERALL;
while (std::getline(input, line)) {
std::istringstream str(line);
str.unsetf(std::ios_base::skipws);
str >> std::ws;
if (str.eof()) {
continue; // empty line
}
std::string kw;
str >> kw;
if (kw == "format") {
std::string format_string, version;
char space_format_string {}, space_format_version {};
str >> space_format_string >> std::ws >> format_string >> space_format_version
>> std::ws >> version;
if (/*!str || !str.eof() ||*/
!std::isspace(space_format_string) || !std::isspace(space_format_version)) {
return false;
}
if (format_string == "ascii") {
format = ascii;
}
else if (format_string == "binary_big_endian") {
format = binary_big_endian;
}
else if (format_string == "binary_little_endian") {
format = binary_little_endian;
}
else {
// wrong format version
return false;
}
if (version != "1.0") {
// wrong version
return false;
}
}
else if (kw == "element") {
std::string name;
std::size_t count {};
char space_element_name {}, space_name_count {};
str >> space_element_name >> std::ws >> name >> space_name_count >> std::ws >> count;
if (/*!str || !str.eof() ||*/
!std::isspace(space_element_name) || !std::isspace(space_name_count)) {
return false;
}
if (name == "vertex") {
element = name;
v_count = count;
meshPoints.reserve(count);
}
else if (name == "face") {
element = name;
f_count = count;
meshFacets.reserve(count);
}
else {
element.clear();
}
}
else if (kw == "property") {
std::string type, name;
char space {};
if (element == "vertex") {
str >> space >> std::ws >> type >> space >> std::ws >> name >> std::ws;
Ply::Number number {};
if (type == "char" || type == "int8") {
number = int8;
}
else if (type == "uchar" || type == "uint8") {
number = uint8;
}
else if (type == "short" || type == "int16") {
number = int16;
}
else if (type == "ushort" || type == "uint16") {
number = uint16;
}
else if (type == "int" || type == "int32") {
number = int32;
}
else if (type == "uint" || type == "uint32") {
number = uint32;
}
else if (type == "float" || type == "float32") {
number = float32;
}
else if (type == "double" || type == "float64") {
number = float64;
}
else {
// no valid number type
return false;
}
// store the property name and type
vertex_props.emplace_back(name, number);
}
else if (element == "face") {
std::string list, uchr;
str >> space >> std::ws >> list >> std::ws;
if (list == "list") {
str >> uchr >> std::ws >> type >> std::ws >> name >> std::ws;
}
else {
// not a 'list'
type = list;
str >> name;
}
if (name != "vertex_indices" && name != "vertex_index") {
Number number {};
if (type == "char" || type == "int8") {
number = int8;
}
else if (type == "uchar" || type == "uint8") {
number = uint8;
}
else if (type == "short" || type == "int16") {
number = int16;
}
else if (type == "ushort" || type == "uint16") {
number = uint16;
}
else if (type == "int" || type == "int32") {
number = int32;
}
else if (type == "uint" || type == "uint32") {
number = uint32;
}
else if (type == "float" || type == "float32") {
number = float32;
}
else if (type == "double" || type == "float64") {
number = float64;
}
else {
// no valid number type
return false;
}
// store the property name and type
face_props.push_back(number);
}
}
}
else if (kw == "end_header") {
break; // end of the header, now read the data
}
}
// check if valid 3d points
Property property;
std::size_t num_x = std::count_if(vertex_props.begin(),
vertex_props.end(),
[&property](const std::pair<std::string, int>& p) {
return property(p, "x");
});
if (num_x != 1) {
return false;
}
std::size_t num_y = std::count_if(vertex_props.begin(),
vertex_props.end(),
[&property](const std::pair<std::string, int>& p) {
return property(p, "y");
});
if (num_y != 1) {
return false;
}
std::size_t num_z = std::count_if(vertex_props.begin(),
vertex_props.end(),
[&property](const std::pair<std::string, int>& p) {
return property(p, "z");
});
if (num_z != 1) {
return false;
}
for (auto& it : vertex_props) {
if (it.first == "diffuse_red") {
it.first = "red";
}
else if (it.first == "diffuse_green") {
it.first = "green";
}
else if (it.first == "diffuse_blue") {
it.first = "blue";
}
}
// check if valid colors are set
std::size_t num_r = std::count_if(vertex_props.begin(),
vertex_props.end(),
[&property](const std::pair<std::string, int>& p) {
return property(p, "red");
});
std::size_t num_g = std::count_if(vertex_props.begin(),
vertex_props.end(),
[&property](const std::pair<std::string, int>& p) {
return property(p, "green");
});
std::size_t num_b = std::count_if(vertex_props.begin(),
vertex_props.end(),
[&property](const std::pair<std::string, int>& p) {
return property(p, "blue");
});
std::size_t rgb_colors = num_r + num_g + num_b;
if (rgb_colors != 0 && rgb_colors != 3) {
return false;
}
// only if set per vertex
if (rgb_colors == 3) {
rgb_value = MeshIO::PER_VERTEX;
if (_material) {
_material->binding = MeshIO::PER_VERTEX;
_material->diffuseColor.reserve(v_count);
}
}
if (format == ascii) {
boost::regex rx_d("(([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?))\\s*");
boost::regex rx_s("\\b([-+]?[0-9]+)\\s*");
boost::regex rx_u("\\b([0-9]+)\\s*");
boost::regex rx_f(R"(^\s*3\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s*)");
boost::smatch what;
for (std::size_t i = 0; i < v_count && std::getline(input, line); i++) {
// go through the vertex properties
std::map<std::string, float> prop_values;
for (const auto& it : vertex_props) {
switch (it.second) {
case int8:
case int16:
case int32: {
if (boost::regex_search(line, what, rx_s)) {
int v {};
v = boost::lexical_cast<int>(what[1]);
prop_values[it.first] = static_cast<float>(v);
line = line.substr(what[0].length());
}
else {
return false;
}
} break;
case uint8:
case uint16:
case uint32: {
if (boost::regex_search(line, what, rx_u)) {
int v {};
v = boost::lexical_cast<int>(what[1]);
prop_values[it.first] = static_cast<float>(v);
line = line.substr(what[0].length());
}
else {
return false;
}
} break;
case float32:
case float64: {
if (boost::regex_search(line, what, rx_d)) {
double v {};
v = boost::lexical_cast<double>(what[1]);
prop_values[it.first] = static_cast<float>(v);
line = line.substr(what[0].length());
}
else {
return false;
}
} break;
default:
return false;
}
}
Base::Vector3f pt;
pt.x = (prop_values["x"]);
pt.y = (prop_values["y"]);
pt.z = (prop_values["z"]);
meshPoints.push_back(pt);
if (_material && (rgb_value == MeshIO::PER_VERTEX)) {
float r = (prop_values["red"]) / 255.0F;
float g = (prop_values["green"]) / 255.0F;
float b = (prop_values["blue"]) / 255.0F;
_material->diffuseColor.emplace_back(r, g, b);
}
}
int f1 {}, f2 {}, f3 {};
for (std::size_t i = 0; i < f_count && std::getline(input, line); i++) {
if (boost::regex_search(line, what, rx_f)) {
f1 = boost::lexical_cast<int>(what[1]);
f2 = boost::lexical_cast<int>(what[2]);
f3 = boost::lexical_cast<int>(what[3]);
meshFacets.push_back(MeshFacet(f1, f2, f3));
}
}
}
// binary
else {
Base::InputStream is(input);
if (format == binary_little_endian) {
is.setByteOrder(Base::Stream::LittleEndian);
}
else {
is.setByteOrder(Base::Stream::BigEndian);
}
for (std::size_t i = 0; i < v_count; i++) {
// go through the vertex properties
std::map<std::string, float> prop_values;
for (const auto& it : vertex_props) {
switch (it.second) {
case int8: {
int8_t v {};
is >> v;
prop_values[it.first] = static_cast<float>(v);
} break;
case uint8: {
uint8_t v {};
is >> v;
prop_values[it.first] = static_cast<float>(v);
} break;
case int16: {
int16_t v {};
is >> v;
prop_values[it.first] = static_cast<float>(v);
} break;
case uint16: {
uint16_t v {};
is >> v;
prop_values[it.first] = static_cast<float>(v);
} break;
case int32: {
int32_t v {};
is >> v;
prop_values[it.first] = static_cast<float>(v);
} break;
case uint32: {
uint32_t v {};
is >> v;
prop_values[it.first] = static_cast<float>(v);
} break;
case float32: {
float v {};
is >> v;
prop_values[it.first] = v;
} break;
case float64: {
double v {};
is >> v;
prop_values[it.first] = static_cast<float>(v);
} break;
default:
return false;
}
}
Base::Vector3f pt;
pt.x = (prop_values["x"]);
pt.y = (prop_values["y"]);
pt.z = (prop_values["z"]);
meshPoints.push_back(pt);
if (_material && (rgb_value == MeshIO::PER_VERTEX)) {
float r = (prop_values["red"]) / 255.0F;
float g = (prop_values["green"]) / 255.0F;
float b = (prop_values["blue"]) / 255.0F;
_material->diffuseColor.emplace_back(r, g, b);
}
}
unsigned char n {};
uint32_t f1 {}, f2 {}, f3 {};
for (std::size_t i = 0; i < f_count; i++) {
is >> n;
if (n == 3) {
is >> f1 >> f2 >> f3;
if (f1 < v_count && f2 < v_count && f3 < v_count) {
meshFacets.push_back(MeshFacet(f1, f2, f3));
}
for (auto it : face_props) {
switch (it) {
case int8: {
int8_t v {};
is >> v;
} break;
case uint8: {
uint8_t v {};
is >> v;
} break;
case int16: {
int16_t v {};
is >> v;
} break;
case uint16: {
uint16_t v {};
is >> v;
} break;
case int32: {
int32_t v {};
is >> v;
} break;
case uint32: {
uint32_t v {};
is >> v;
} break;
case float32: {
is >> n;
float v {};
for (unsigned char j = 0; j < n; j++) {
is >> v;
}
} break;
case float64: {
is >> n;
double v {};
for (unsigned char j = 0; j < n; j++) {
is >> v;
}
} break;
default:
return false;
}
}
}
}
}
this->_rclMesh.Clear(); // remove all data before
MeshCleanup meshCleanup(meshPoints, meshFacets);
if (_material) {
meshCleanup.SetMaterial(_material);
}
meshCleanup.RemoveInvalids();
MeshPointFacetAdjacency meshAdj(meshPoints.size(), meshFacets);
meshAdj.SetFacetNeighbourhood();
this->_rclMesh.Adopt(meshPoints, meshFacets);
return true;
ReaderPLY reader(this->_rclMesh, this->_material);
return reader.Load(input);
}
bool MeshInput::LoadMeshNode(std::istream& input)