From 9d041d8497e5f6fc64d977480d65358bd2af5065 Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 26 Aug 2022 12:39:04 +0200 Subject: [PATCH] Base: implement loading of OpenInventor files (currently only SoIndexedFaceSet is supported) --- src/Base/Builder3D.cpp | 167 +++++++++++++++++++++++++++++++++++++++++ src/Base/Builder3D.h | 54 +++++++++++++ src/Base/PreCompiled.h | 3 + 3 files changed, 224 insertions(+) diff --git a/src/Base/Builder3D.cpp b/src/Base/Builder3D.cpp index 63fff62420..8d219ad42f 100644 --- a/src/Base/Builder3D.cpp +++ b/src/Base/Builder3D.cpp @@ -24,9 +24,13 @@ #include "PreCompiled.h" #ifndef _PreComp_ +# include # include # include # include +# include +# include +# include #endif #include "Builder3D.h" @@ -920,3 +924,166 @@ void InventorBuilder::addTransformation(const Vector3f& translation, const Vecto << " " << fAngle << std::endl; result << Base::blanks(indent) << "}" << std::endl; } + +// ----------------------------------------------------------------------------- + +template +std::vector InventorLoader::readData(const char* fieldName) const +{ + std::vector fieldValues; + std::string str; + + // search for 'fieldName' and '[' + bool found = false; + while (std::getline(inp, str)) { + std::string::size_type point = str.find(fieldName); + std::string::size_type open = str.find("["); + if (point != std::string::npos && open > point) { + str = str.substr(open); + found = true; + break; + } + } + + if (!found) + return {}; + + do { + boost::char_separator sep(" ,"); + boost::tokenizer > tokens(str, sep); + std::vector token_results; + token_results.assign(tokens.begin(),tokens.end()); + + for (const auto& it : token_results) { + try { + T value = boost::lexical_cast(it); + fieldValues.emplace_back(value); + } + catch (const boost::bad_lexical_cast&) { + } + } + + // search for ']' to finish the reading + if (str.find("]") != std::string::npos) + break; + } + while (std::getline(inp, str)); + + return fieldValues; +} + +std::vector InventorLoader::convert(const std::vector& data) const +{ + if (data.size() % 3 != 0) + throw std::string("Reading failed"); + + std::size_t len = data.size() / 3; + std::vector points; + points.reserve(len); + + for (std::size_t i = 0; i < len; i++) { + float x = data[3 * i]; + float y = data[3 * i + 1]; + float z = data[3 * i + 2]; + points.emplace_back(x, y, z); + } + + return points; +} + +std::vector> InventorLoader::split(const std::vector& data) +{ + std::vector> splitdata; + std::vector::const_iterator begin = data.cbegin(); + std::vector::const_iterator it = begin; + + while ((it = std::find(begin, data.cend(), -1)) != data.cend()) { + splitdata.emplace_back(begin, it); + begin = it; + std::advance(begin, 1); + } + return splitdata; +} + +std::vector InventorLoader::convert(const std::vector& data) const +{ + std::vector> coordIndex = split(data); + std::vector faces; + faces.reserve(coordIndex.size()); + for (const auto it : coordIndex) { + if (it.size() == 3) { + faces.emplace_back(it[0], it[1], it[2]); + } + else if (it.size() == 4) { + faces.emplace_back(it[0], it[1], it[2]); + faces.emplace_back(it[0], it[2], it[3]); + } + } + return faces; +} + +void InventorLoader::readNormals() +{ + auto data = readData("vector"); + vector = convert(data); +} + +void InventorLoader::readCoords() +{ + auto data = readData("point"); + points = convert(data); +} + +void InventorLoader::readFaceSet() +{ + auto data = readData("coordIndex"); + faces = convert(data); +} + +bool InventorLoader::read() +{ + if (!inp || inp.bad()) + return false; + + std::string line; + + // Verify it's an Inventor 2.1 file + std::getline(inp, line); + if (line.find("#Inventor V2.1 ascii") == std::string::npos) + return false; + + while (std::getline(inp, line)) { + // read the normals if they are defined + if (line.find("Normal {") != std::string::npos) { + readNormals(); + } + else if (line.find("Coordinate3 {") != std::string::npos) { + readCoords(); + } + else if (line.find("IndexedFaceSet {") != std::string::npos) { + readFaceSet(); + break; + } + } + return true; +} + +bool InventorLoader::isValid() const +{ + int32_t value{static_cast(points.size())}; + auto inRange = [value](const Face& f) { + if (f.p1 < 0 || f.p1 >= value) + return false; + if (f.p2 < 0 || f.p2 >= value) + return false; + if (f.p3 < 0 || f.p3 >= value) + return false; + return true; + }; + for (auto it : faces) { + if (!inRange(it)) + return false; + } + + return true; +} diff --git a/src/Base/Builder3D.h b/src/Base/Builder3D.h index 59161ecc00..c94b70dcf5 100644 --- a/src/Base/Builder3D.h +++ b/src/Base/Builder3D.h @@ -340,6 +340,60 @@ private: int indent; }; +/** + * Loads an OpenInventor file. + * @author Werner Mayer + */ +class BaseExport InventorLoader { +public: + struct Face { + Face(int32_t p1, int32_t p2, int32_t p3) + : p1(p1), p2(p2), p3(p3) {} + int32_t p1, p2, p3; + }; + + explicit InventorLoader(std::istream &inp) : inp(inp) { + } + + /// Start the read process. Returns true if successful and false otherwise. + /// The obtained data can be accessed with the appropriate getter functions. + bool read(); + + /// Checks if the loaded data are valid + bool isValid() const; + + /// Return the vectors of an SoNormal node + const std::vector& getVector() { + return vector; + } + + /// Return the points of an SoCoordinate3 node + const std::vector& getPoints() { + return points; + } + + /// Return the faces of an SoIndexedFaceSet node + const std::vector& getFaces() { + return faces; + } + +private: + void readNormals(); + void readCoords(); + void readFaceSet(); + template + std::vector readData(const char*) const; + std::vector convert(const std::vector&) const; + std::vector convert(const std::vector&) const; + static std::vector> split(const std::vector&); + +private: + std::vector vector; + std::vector points; + std::vector faces; + std::istream &inp; +}; + } //namespace Base #endif // BASE_BUILDER3D_H diff --git a/src/Base/PreCompiled.h b/src/Base/PreCompiled.h index 79566cc4d0..1e4bc9ef17 100644 --- a/src/Base/PreCompiled.h +++ b/src/Base/PreCompiled.h @@ -114,7 +114,10 @@ #include #include #include +#include +#include #include +#include // QtCore #include