| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| | #include "Core/Iterator.h" |
| | #include <Base/Console.h> |
| | #include <Base/Sequencer.h> |
| | #include <Base/Tools.h> |
| |
|
| | #include "WriterOBJ.h" |
| |
|
| |
|
| | using namespace MeshCore; |
| |
|
| | struct WriterOBJ::Color_Less |
| | { |
| | bool operator()(const Base::Color& x, const Base::Color& y) const |
| | { |
| | if (x.r != y.r) { |
| | return x.r < y.r; |
| | } |
| | if (x.g != y.g) { |
| | return x.g < y.g; |
| | } |
| | if (x.b != y.b) { |
| | return x.b < y.b; |
| | } |
| | return false; |
| | } |
| | }; |
| |
|
| | WriterOBJ::WriterOBJ(const MeshKernel& kernel, const Material* material) |
| | : _kernel(kernel) |
| | , _material(material) |
| | {} |
| |
|
| | void WriterOBJ::SetGroups(const std::vector<Group>& g) |
| | { |
| | _groups = g; |
| | } |
| |
|
| | void WriterOBJ::SetTransform(const Base::Matrix4D& mat) |
| | { |
| | _transform = mat; |
| | if (mat != Base::Matrix4D()) { |
| | apply_transform = true; |
| | } |
| | } |
| |
|
| | bool WriterOBJ::Save(std::ostream& out) |
| | { |
| | const MeshPointArray& rPoints = _kernel.GetPoints(); |
| | const MeshFacetArray& rFacets = _kernel.GetFacets(); |
| |
|
| | if (!out || out.bad()) { |
| | return false; |
| | } |
| |
|
| | Base::SequencerLauncher seq("saving...", _kernel.CountPoints() + _kernel.CountFacets()); |
| | bool exportColorPerVertex = false; |
| | bool exportColorPerFace = false; |
| |
|
| | if (_material) { |
| | if (_material->binding == MeshIO::PER_FACE) { |
| | if (_material->diffuseColor.size() != rFacets.size()) { |
| | Base::Console().warning( |
| | "Cannot export color information because there is a " |
| | "different number of faces and colors" |
| | ); |
| | } |
| | else { |
| | exportColorPerFace = true; |
| | } |
| | } |
| | else if (_material->binding == MeshIO::PER_VERTEX) { |
| | if (_material->diffuseColor.size() != rPoints.size()) { |
| | Base::Console().warning( |
| | "Cannot export color information because there is a " |
| | "different number of points and colors" |
| | ); |
| | } |
| | else { |
| | exportColorPerVertex = true; |
| | } |
| | } |
| | else if (_material->binding == MeshIO::OVERALL) { |
| | if (_material->diffuseColor.empty()) { |
| | Base::Console().warning( |
| | "Cannot export color information because there is no color defined" |
| | ); |
| | } |
| | else { |
| | exportColorPerVertex = true; |
| | } |
| | } |
| | } |
| |
|
| | |
| | out << "# Created by FreeCAD <https://www.freecad.org>\n"; |
| | if (exportColorPerFace) { |
| | out << "mtllib " << _material->library << '\n'; |
| | } |
| |
|
| | out.precision(6); |
| | out.setf(std::ios::fixed | std::ios::showpoint); |
| |
|
| | |
| | Base::Vector3f pt; |
| | std::size_t index = 0; |
| | for (auto it = rPoints.begin(); it != rPoints.end(); ++it, ++index) { |
| | if (this->apply_transform) { |
| | pt = this->_transform * *it; |
| | } |
| | else { |
| | pt.Set(it->x, it->y, it->z); |
| | } |
| |
|
| | if (exportColorPerVertex) { |
| | Base::Color c; |
| | if (_material->binding == MeshIO::PER_VERTEX) { |
| | c = _material->diffuseColor[index]; |
| | } |
| | else { |
| | c = _material->diffuseColor.front(); |
| | } |
| |
|
| | int r = static_cast<int>(c.r * 255.0F); |
| | int g = static_cast<int>(c.g * 255.0F); |
| | int b = static_cast<int>(c.b * 255.0F); |
| |
|
| | out << "v " << pt.x << " " << pt.y << " " << pt.z << " " << r << " " << g << " " << b |
| | << '\n'; |
| | } |
| | else { |
| | out << "v " << pt.x << " " << pt.y << " " << pt.z << '\n'; |
| | } |
| | seq.next(true); |
| | } |
| | |
| | MeshFacetIterator clIter(_kernel), clEnd(_kernel); |
| | const MeshGeomFacet* pclFacet {}; |
| |
|
| | clIter.Begin(); |
| | clEnd.End(); |
| |
|
| | while (clIter < clEnd) { |
| | pclFacet = &(*clIter); |
| | out << "vn " << pclFacet->GetNormal().x << " " << pclFacet->GetNormal().y << " " |
| | << pclFacet->GetNormal().z << '\n'; |
| | ++clIter; |
| | seq.next(true); |
| | } |
| |
|
| | if (_groups.empty()) { |
| | if (exportColorPerFace) { |
| | |
| |
|
| | |
| | std::vector<Base::Color> colors = _material->diffuseColor; |
| | std::sort(colors.begin(), colors.end(), Color_Less()); |
| | colors.erase(std::unique(colors.begin(), colors.end()), colors.end()); |
| |
|
| | std::size_t index = 0; |
| | Base::Color prev; |
| | int faceIdx = 1; |
| | const std::vector<Base::Color>& Kd = _material->diffuseColor; |
| | for (auto it = rFacets.begin(); it != rFacets.end(); ++it, index++) { |
| | if (index == 0 || prev != Kd[index]) { |
| | prev = Kd[index]; |
| | if (auto c_it = std::ranges::find(colors, prev); c_it != colors.end()) { |
| | out << "usemtl material_" << (c_it - colors.begin()) << '\n'; |
| | } |
| | } |
| | out << "f " << it->_aulPoints[0] + 1 << "//" << faceIdx << " " |
| | << it->_aulPoints[1] + 1 << "//" << faceIdx << " " << it->_aulPoints[2] + 1 |
| | << "//" << faceIdx << '\n'; |
| | seq.next(true); |
| | faceIdx++; |
| | } |
| | } |
| | else { |
| | |
| | std::size_t faceIdx = 1; |
| | for (const auto& it : rFacets) { |
| | out << "f " << it._aulPoints[0] + 1 << "//" << faceIdx << " " << it._aulPoints[1] + 1 |
| | << "//" << faceIdx << " " << it._aulPoints[2] + 1 << "//" << faceIdx << '\n'; |
| | seq.next(true); |
| | faceIdx++; |
| | } |
| | } |
| | } |
| | else { |
| | if (exportColorPerFace) { |
| | |
| | std::vector<Base::Color> colors = _material->diffuseColor; |
| | std::sort(colors.begin(), colors.end(), Color_Less()); |
| | colors.erase(std::unique(colors.begin(), colors.end()), colors.end()); |
| |
|
| | bool first = true; |
| | Base::Color prev; |
| | const std::vector<Base::Color>& Kd = _material->diffuseColor; |
| |
|
| | for (const auto& gt : _groups) { |
| | out << "g " << Base::Tools::escapedUnicodeFromUtf8(gt.name.c_str()) << '\n'; |
| | for (FacetIndex it : gt.indices) { |
| | const MeshFacet& f = rFacets[it]; |
| | if (first || prev != Kd[it]) { |
| | first = false; |
| | prev = Kd[it]; |
| | if (auto c_it = std::ranges::find(colors, prev); c_it != colors.end()) { |
| | out << "usemtl material_" << (c_it - colors.begin()) << '\n'; |
| | } |
| | } |
| |
|
| | out << "f " << f._aulPoints[0] + 1 << "//" << it + 1 << " " << f._aulPoints[1] + 1 |
| | << "//" << it + 1 << " " << f._aulPoints[2] + 1 << "//" << it + 1 << '\n'; |
| | seq.next(true); |
| | } |
| | } |
| | } |
| | else { |
| | for (const auto& gt : _groups) { |
| | out << "g " << Base::Tools::escapedUnicodeFromUtf8(gt.name.c_str()) << '\n'; |
| | for (FacetIndex it : gt.indices) { |
| | const MeshFacet& f = rFacets[it]; |
| | out << "f " << f._aulPoints[0] + 1 << "//" << it + 1 << " " << f._aulPoints[1] + 1 |
| | << "//" << it + 1 << " " << f._aulPoints[2] + 1 << "//" << it + 1 << '\n'; |
| | seq.next(true); |
| | } |
| | } |
| | } |
| | } |
| |
|
| | return true; |
| | } |
| |
|
| | bool WriterOBJ::SaveMaterial(std::ostream& out) |
| | { |
| | if (!out || out.bad()) { |
| | return false; |
| | } |
| |
|
| | if (_material) { |
| | if (_material->binding == MeshIO::PER_FACE) { |
| |
|
| | std::vector<Base::Color> Kd = _material->diffuseColor; |
| | std::sort(Kd.begin(), Kd.end(), Color_Less()); |
| | Kd.erase(std::unique(Kd.begin(), Kd.end()), Kd.end()); |
| |
|
| | out.precision(6); |
| | out.setf(std::ios::fixed | std::ios::showpoint); |
| | out << "# Created by FreeCAD <https://www.freecad.org>: 'None'\n"; |
| | out << "# Material Count: " << Kd.size() << '\n'; |
| |
|
| | for (std::size_t i = 0; i < Kd.size(); i++) { |
| | out << '\n'; |
| | out << "newmtl material_" << i << '\n'; |
| | out << " Ka 0.200000 0.200000 0.200000\n"; |
| | out << " Kd " << Kd[i].r << " " << Kd[i].g << " " << Kd[i].b << '\n'; |
| | out << " Ks 1.000000 1.000000 1.000000\n"; |
| | out << " d 1.000000" << '\n'; |
| | out << " illum 2" << '\n'; |
| | out << " Ns 0.000000" << '\n'; |
| | } |
| |
|
| | return true; |
| | } |
| | } |
| |
|
| | return false; |
| | } |
| |
|