Download src/Mod/Mesh/App/Core/IO/ReaderOBJ.cpp from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 15.9 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Mesh/App/Core/IO/ReaderOBJ.cpp
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/Mesh/App/Core/IO/ReaderOBJ.cpp
-
curl -L -o ReaderOBJ.cpp https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Mesh/App/Core/IO/ReaderOBJ.cpp
15.9 kB
| // SPDX-License-Identifier: LGPL-2.1-or-later | |
| /*************************************************************************** | |
| * Copyright (c) 2022 Werner Mayer <wmayer[at]users.sourceforge.net> * | |
| * * | |
| * This file is part of the FreeCAD CAx development system. * | |
| * * | |
| * This library is free software; you can redistribute it and/or * | |
| * modify it under the terms of the GNU Library General Public * | |
| * License as published by the Free Software Foundation; either * | |
| * version 2 of the License, or (at your option) any later version. * | |
| * * | |
| * This library 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 Library General Public License for more details. * | |
| * * | |
| * You should have received a copy of the GNU Library General Public * | |
| * License along with this library; see the file COPYING.LIB. If not, * | |
| * write to the Free Software Foundation, Inc., 59 Temple Place, * | |
| * Suite 330, Boston, MA 02111-1307, USA * | |
| * * | |
| ***************************************************************************/ | |
| using namespace MeshCore; | |
| namespace | |
| { | |
| class ReaderOBJImp | |
| { | |
| public: | |
| using string_list = std::vector<std::string>; | |
| explicit ReaderOBJImp(Material* material) | |
| : _material {material} | |
| {} | |
| void Load(const std::string& line) | |
| { | |
| if (Ignore(line)) { | |
| return; | |
| } | |
| boost::char_separator<char> sep(" /\t"); | |
| boost::tokenizer<boost::char_separator<char>> tokens(line, sep); | |
| token_results.assign(tokens.begin(), tokens.end()); | |
| if (token_results.size() < 2) { | |
| return; | |
| } | |
| if (MatchVertex(token_results)) { | |
| LoadVertex(token_results); | |
| } | |
| else if (MatchVertexWithColor(token_results)) { | |
| LoadVertexWithColor(token_results); | |
| } | |
| else if (MatchGroup(token_results)) { | |
| LoadGroup(token_results); | |
| } | |
| else if (MatchLibrary(token_results)) { | |
| LoadLibrary(token_results); | |
| } | |
| else if (MatchUseMaterial(token_results)) { | |
| LoadUseMaterial(token_results); | |
| } | |
| else if (MatchFace(token_results)) { | |
| LoadFace(token_results); | |
| } | |
| else if (MatchQuad(token_results)) { | |
| LoadQuad(token_results); | |
| } | |
| } | |
| void SetupMaterial() | |
| { | |
| // Add the last added material name | |
| if (!materialName.empty()) { | |
| _materialNames.emplace_back(materialName, countMaterialFacets); | |
| } | |
| // now get back the colors from the vertex property | |
| if (rgb_value == MeshIO::PER_VERTEX) { | |
| if (_material) { | |
| _material->binding = MeshIO::PER_VERTEX; | |
| _material->diffuseColor.reserve(meshPoints.size()); | |
| for (const auto& it : meshPoints) { | |
| unsigned long prop = it._ulProp; | |
| Base::Color c; | |
| c.setPackedValue(static_cast<uint32_t>(prop)); | |
| _material->diffuseColor.push_back(c); | |
| } | |
| } | |
| } | |
| else if (!materialName.empty()) { | |
| // At this point the materials from the .mtl file are not known and will be read-in by | |
| // the calling instance but the color list is pre-filled with a default value | |
| if (_material) { | |
| _material->binding = MeshIO::PER_FACE; | |
| const float rgb = 0.8F; | |
| _material->diffuseColor.resize(meshFacets.size(), Base::Color(rgb, rgb, rgb)); | |
| } | |
| } | |
| } | |
| private: | |
| bool Ignore(const std::string& line) const | |
| { | |
| // clang-format off | |
| return boost::starts_with(line, "vn ") || | |
| boost::starts_with(line, "vt ") || | |
| boost::starts_with(line, "s ") || | |
| boost::starts_with(line, "o ") || | |
| boost::starts_with(line, "#"); | |
| // clang-format on | |
| } | |
| bool MatchVertex(const string_list& tokens) const | |
| { | |
| return tokens[0] == "v" && tokens.size() == 4; | |
| } | |
| void LoadVertex(const string_list& tokens) | |
| { | |
| float x = std::stof(tokens[1]); | |
| float y = std::stof(tokens[2]); | |
| float z = std::stof(tokens[3]); | |
| meshPoints.push_back(MeshPoint(Base::Vector3f(x, y, z))); | |
| } | |
| bool MatchVertexWithColor(const string_list& tokens) const | |
| { | |
| return tokens[0] == "v" && tokens.size() == 7; // NOLINT | |
| } | |
| void LoadVertexWithColor(const string_list& tokens) | |
| { | |
| LoadVertex(tokens); | |
| // NOLINTBEGIN | |
| float r = std::stof(tokens[4]); | |
| float g = std::stof(tokens[5]); | |
| float b = std::stof(tokens[6]); | |
| if (r > 1.0F || g > 1.0F || b > 1.0F) { | |
| r /= 255.0F; | |
| g /= 255.0F; | |
| b /= 255.0F; | |
| } | |
| // NOLINTEND | |
| SetVertexColor(Base::Color(r, g, b)); | |
| } | |
| void SetVertexColor(const Base::Color& c) | |
| { | |
| unsigned long prop = static_cast<uint32_t>(c.getPackedValue()); | |
| meshPoints.back().SetProperty(prop); | |
| rgb_value = MeshIO::PER_VERTEX; | |
| } | |
| bool MatchGroup(const string_list& tokens) const | |
| { | |
| return tokens[0] == "g" && tokens.size() == 2; | |
| } | |
| void LoadGroup(const string_list& tokens) | |
| { | |
| new_segment = true; | |
| groupName = Base::Tools::escapedUnicodeToUtf8(tokens[1]); | |
| } | |
| bool MatchLibrary(const string_list& tokens) const | |
| { | |
| return tokens[0] == "mtllib" && tokens.size() == 2; | |
| } | |
| void LoadLibrary(const string_list& tokens) | |
| { | |
| if (_material) { | |
| _material->library = Base::Tools::escapedUnicodeToUtf8(tokens[1]); | |
| } | |
| } | |
| bool MatchUseMaterial(const string_list& tokens) const | |
| { | |
| return tokens[0] == "usemtl" && tokens.size() == 2; | |
| } | |
| void LoadUseMaterial(const string_list& tokens) | |
| { | |
| if (!materialName.empty()) { | |
| _materialNames.emplace_back(materialName, countMaterialFacets); | |
| } | |
| materialName = Base::Tools::escapedUnicodeToUtf8(tokens[1]); | |
| countMaterialFacets = 0; | |
| } | |
| bool MatchFace(const string_list& tokens) const | |
| { | |
| // NOLINTBEGIN | |
| const auto num = tokens.size(); | |
| return tokens[0] == "f" && (num == 4 || num == 7 || num == 10); | |
| // NOLINTEND | |
| } | |
| void LoadFace(const string_list& tokens) | |
| { | |
| StartNewSegment(); | |
| // NOLINTBEGIN | |
| int index1 = 1; | |
| int index2 = 2; | |
| int index3 = 3; | |
| if (tokens.size() == 7) { | |
| index2 = 3; | |
| index3 = 5; | |
| } | |
| else if (tokens.size() == 10) { | |
| index2 = 4; | |
| index3 = 7; | |
| } | |
| // NOLINTEND | |
| // 3-vertex face | |
| int i1 = std::stoi(tokens[index1]); | |
| i1 = i1 > 0 ? i1 - 1 : i1 + static_cast<int>(meshPoints.size()); | |
| int i2 = std::stoi(tokens[index2]); | |
| i2 = i2 > 0 ? i2 - 1 : i2 + static_cast<int>(meshPoints.size()); | |
| int i3 = std::stoi(tokens[index3]); | |
| i3 = i3 > 0 ? i3 - 1 : i3 + static_cast<int>(meshPoints.size()); | |
| AddFace(i1, i2, i3); | |
| } | |
| bool MatchQuad(const string_list& tokens) const | |
| { | |
| // NOLINTBEGIN | |
| const auto num = tokens.size(); | |
| return tokens[0] == "f" && (num == 5 || num == 9 || num == 13); | |
| // NOLINTEND | |
| } | |
| void LoadQuad(const string_list& tokens) | |
| { | |
| StartNewSegment(); | |
| // NOLINTBEGIN | |
| int index1 = 1; | |
| int index2 = 2; | |
| int index3 = 3; | |
| int index4 = 4; | |
| if (tokens.size() == 9) { | |
| index2 = 3; | |
| index3 = 5; | |
| index4 = 7; | |
| } | |
| else if (tokens.size() == 13) { | |
| index2 = 4; | |
| index3 = 7; | |
| index4 = 10; | |
| } | |
| // NOLINTEND | |
| // 4-vertex face | |
| int i1 = std::stoi(tokens[index1]); | |
| i1 = i1 > 0 ? i1 - 1 : i1 + static_cast<int>(meshPoints.size()); | |
| int i2 = std::stoi(tokens[index2]); | |
| i2 = i2 > 0 ? i2 - 1 : i2 + static_cast<int>(meshPoints.size()); | |
| int i3 = std::stoi(tokens[index3]); | |
| i3 = i3 > 0 ? i3 - 1 : i3 + static_cast<int>(meshPoints.size()); | |
| int i4 = std::stoi(tokens[index4]); | |
| i4 = i4 > 0 ? i4 - 1 : i4 + static_cast<int>(meshPoints.size()); | |
| AddFace(i1, i2, i3); | |
| AddFace(i3, i4, i1); | |
| } | |
| void StartNewSegment() | |
| { | |
| // starts a new segment | |
| if (new_segment) { | |
| if (!groupName.empty()) { | |
| _groupNames.push_back(groupName); | |
| groupName.clear(); | |
| } | |
| new_segment = false; | |
| segment++; | |
| } | |
| } | |
| void AddFace(int i, int j, int k) | |
| { | |
| MeshFacet item; | |
| item.SetVertices(i, j, k); | |
| item.SetProperty(segment); | |
| meshFacets.push_back(item); | |
| countMaterialFacets++; | |
| } | |
| public: | |
| // NOLINTBEGIN | |
| MeshPointArray meshPoints; | |
| MeshFacetArray meshFacets; | |
| // NOLINTEND | |
| using MaterialPerSegment = std::pair<std::string, unsigned long>; | |
| std::vector<MaterialPerSegment> GetMaterialNames() const | |
| { | |
| return _materialNames; | |
| } | |
| private: | |
| string_list token_results; | |
| MeshIO::Binding rgb_value = MeshIO::OVERALL; | |
| unsigned long countMaterialFacets = 0; | |
| unsigned long segment = 0; | |
| bool new_segment = true; | |
| std::string groupName; | |
| std::string materialName; | |
| Material* _material; | |
| std::vector<std::string> _groupNames; | |
| std::vector<MaterialPerSegment> _materialNames; | |
| }; | |
| } // namespace | |
| ReaderOBJ::ReaderOBJ(MeshKernel& kernel, Material* material) | |
| : _kernel(kernel) | |
| , _material(material) | |
| {} | |
| bool ReaderOBJ::Load(const std::string& file) | |
| { | |
| Base::FileInfo fi(file); | |
| Base::ifstream str(fi, std::ios::in); | |
| return Load(str); | |
| } | |
| bool ReaderOBJ::Load(std::istream& str) | |
| { | |
| if (!str || str.bad()) { | |
| return false; | |
| } | |
| std::streambuf* buf = str.rdbuf(); | |
| if (!buf) { | |
| return false; | |
| } | |
| ReaderOBJImp reader(_material); | |
| std::string line; | |
| while (std::getline(str, line)) { | |
| boost::trim(line); | |
| reader.Load(line); | |
| } | |
| reader.SetupMaterial(); | |
| _materialNames = reader.GetMaterialNames(); | |
| _kernel.Clear(); // remove all data before | |
| MeshCleanup meshCleanup(reader.meshPoints, reader.meshFacets); | |
| if (_material) { | |
| meshCleanup.SetMaterial(_material); | |
| } | |
| meshCleanup.RemoveInvalids(); | |
| MeshPointFacetAdjacency meshAdj(reader.meshPoints.size(), reader.meshFacets); | |
| meshAdj.SetFacetNeighbourhood(); | |
| _kernel.Adopt(reader.meshPoints, reader.meshFacets); | |
| return true; | |
| } | |
| // NOLINTNEXTLINE | |
| bool ReaderOBJ::LoadMaterial(std::istream& str) | |
| { | |
| std::string line; | |
| if (!_material) { | |
| return false; | |
| } | |
| if (!str || str.bad()) { | |
| return false; | |
| } | |
| std::streambuf* buf = str.rdbuf(); | |
| if (!buf) { | |
| return false; | |
| } | |
| std::map<std::string, Base::Color> materialAmbientColor; | |
| std::map<std::string, Base::Color> materialDiffuseColor; | |
| std::map<std::string, Base::Color> materialSpecularColor; | |
| std::map<std::string, float> materialTransparency; | |
| std::string materialName; | |
| std::vector<Base::Color> ambientColor; | |
| std::vector<Base::Color> diffuseColor; | |
| std::vector<Base::Color> specularColor; | |
| std::vector<float> transparency; | |
| auto readColor = [](const std::vector<std::string>& tokens) -> Base::Color { | |
| if (tokens.size() == 2) { | |
| // If only R is given then G and B will be equal | |
| float r = std::stof(tokens[1]); | |
| return Base::Color(r, r, r); | |
| } | |
| if (tokens.size() == 4) { | |
| float r = std::stof(tokens[1]); | |
| float g = std::stof(tokens[2]); | |
| float b = std::stof(tokens[3]); | |
| return Base::Color(r, g, b); | |
| } | |
| throw std::length_error("Unexpected number of tokens"); | |
| }; | |
| while (std::getline(str, line)) { | |
| boost::char_separator<char> sep(" "); | |
| boost::tokenizer<boost::char_separator<char>> tokens(line, sep); | |
| std::vector<std::string> token_results; | |
| token_results.assign(tokens.begin(), tokens.end()); | |
| try { | |
| if (token_results.size() >= 2) { | |
| if (token_results[0] == "newmtl") { | |
| materialName = Base::Tools::escapedUnicodeToUtf8(token_results[1]); | |
| } | |
| else if (token_results[0] == "d") { | |
| float a = std::stof(token_results[1]); | |
| materialTransparency[materialName] = 1.0F - a; | |
| } | |
| // If only R is given then G and B will be equal | |
| else if (token_results[0] == "Ka") { | |
| materialAmbientColor[materialName] = readColor(token_results); | |
| } | |
| else if (token_results[0] == "Kd") { | |
| materialDiffuseColor[materialName] = readColor(token_results); | |
| } | |
| else if (token_results[0] == "Ks") { | |
| materialSpecularColor[materialName] = readColor(token_results); | |
| } | |
| } | |
| } | |
| catch (const std::exception&) { | |
| } | |
| } | |
| for (const auto& it : _materialNames) { | |
| { | |
| auto jt = materialAmbientColor.find(it.first); | |
| if (jt != materialAmbientColor.end()) { | |
| std::vector<Base::Color> mat(it.second, jt->second); | |
| ambientColor.insert(ambientColor.end(), mat.begin(), mat.end()); | |
| } | |
| } | |
| { | |
| auto jt = materialDiffuseColor.find(it.first); | |
| if (jt != materialDiffuseColor.end()) { | |
| std::vector<Base::Color> mat(it.second, jt->second); | |
| diffuseColor.insert(diffuseColor.end(), mat.begin(), mat.end()); | |
| } | |
| } | |
| { | |
| auto jt = materialSpecularColor.find(it.first); | |
| if (jt != materialSpecularColor.end()) { | |
| std::vector<Base::Color> mat(it.second, jt->second); | |
| specularColor.insert(specularColor.end(), mat.begin(), mat.end()); | |
| } | |
| } | |
| { | |
| auto jt = materialTransparency.find(it.first); | |
| if (jt != materialTransparency.end()) { | |
| std::vector<float> transp(it.second, jt->second); | |
| transparency.insert(transparency.end(), transp.begin(), transp.end()); | |
| } | |
| } | |
| } | |
| if (diffuseColor.size() == _material->diffuseColor.size()) { | |
| _material->binding = MeshIO::PER_FACE; | |
| _material->ambientColor.swap(ambientColor); | |
| _material->diffuseColor.swap(diffuseColor); | |
| _material->specularColor.swap(specularColor); | |
| _material->transparency.swap(transparency); | |
| return true; | |
| } | |
| _material->binding = MeshIO::OVERALL; | |
| _material->ambientColor.clear(); | |
| _material->diffuseColor.clear(); | |
| _material->specularColor.clear(); | |
| _material->transparency.clear(); | |
| return false; | |
| } | |