Skip to content

Commit

Permalink
Importing .obj Files in OpenSCAD (#4542)
Browse files Browse the repository at this point in the history
Add OBJ import.
  • Loading branch information
gsohler committed Mar 9, 2023
1 parent 2452055 commit 4b835ad
Show file tree
Hide file tree
Showing 10 changed files with 214 additions and 0 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Expand Up @@ -661,6 +661,7 @@ set(CORE_SOURCES
src/io/import_3mf.cc
src/io/import_amf.cc
src/io/import_stl.cc
src/io/import_obj.cc
src/io/import_off.cc
src/io/import_svg.cc
src/io/import_json.cc
Expand Down
5 changes: 5 additions & 0 deletions src/core/ImportNode.cc
Expand Up @@ -91,6 +91,7 @@ static std::shared_ptr<AbstractNode> do_import(const ModuleInstantiation *inst,
else if (ext == ".3mf") actualtype = ImportType::_3MF;
else if (ext == ".amf") actualtype = ImportType::AMF;
else if (ext == ".svg") actualtype = ImportType::SVG;
else if (ext == ".obj") actualtype = ImportType::OBJ;
}

auto node = std::make_shared<ImportNode>(inst, actualtype);
Expand Down Expand Up @@ -192,6 +193,10 @@ const Geometry *ImportNode::createGeometry() const
g = import_off(this->filename, loc);
break;
}
case ImportType::OBJ: {
g = import_obj(this->filename, loc);
break;
}
case ImportType::SVG: {
g = import_svg(this->fn, this->fs, this->fa, this->filename, this->id, this->layer, this->dpi, this->center, loc);
break;
Expand Down
1 change: 1 addition & 0 deletions src/core/ImportNode.h
Expand Up @@ -14,6 +14,7 @@ enum class ImportType {
SVG,
DXF,
NEF3,
OBJ,
};

class ImportNode : public LeafNode
Expand Down
1 change: 1 addition & 0 deletions src/gui/MainWindow.cc
Expand Up @@ -269,6 +269,7 @@ MainWindow::MainWindow(const QStringList& filenames)
const QString surfaceStatement = "surface(\"%1\");\n";
const QString importFunction = "data = import(\"%1\");\n";
knownFileExtensions["stl"] = importStatement;
knownFileExtensions["obj"] = importStatement;
knownFileExtensions["3mf"] = importStatement;
knownFileExtensions["off"] = importStatement;
knownFileExtensions["dxf"] = importStatement;
Expand Down
1 change: 1 addition & 0 deletions src/io/import.h
Expand Up @@ -6,6 +6,7 @@
#include "AST.h"

class PolySet *import_stl(const std::string& filename, const Location& loc);
class PolySet *import_obj(const std::string& filename, const Location& loc);

PolySet *import_off(const std::string& filename, const Location& loc);

Expand Down
85 changes: 85 additions & 0 deletions src/io/import_obj.cc
@@ -0,0 +1,85 @@
#include "import.h"
#include "PolySet.h"
#include <fstream>
#include <vector>
#include <boost/regex.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/split.hpp>

PolySet *import_obj(const std::string& filename, const Location& loc) {
std::unique_ptr<PolySet> p = std::make_unique<PolySet>(3);


std::ifstream f(filename.c_str(), std::ios::in | std::ios::binary );
if (!f.good()) {
LOG(message_group::Warning, Location::NONE, "",
"Can't open import file '%1$s', import() at line %2$d",
filename, loc.firstLine());
return p.release();
}
std::vector<Vector3d> pts;
boost::regex ex_comment(R"(^\s*#)");
boost::regex ex_v( R"(^\s*v\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)\s*$)");
boost::regex ex_f( R"(^\s*f\s+(.*)$)");
boost::regex ex_vt( R"(^\s*vt)");
boost::regex ex_vn( R"(^\s*vn)");
boost::regex ex_mtllib( R"(^\s*mtllib)");
boost::regex ex_usemtl( R"(^\s*usemtl)");
boost::regex ex_o( R"(^\s*o)");
boost::regex ex_s( R"(^\s*s)");
boost::regex ex_g( R"(^\s*g)");
int lineno = 1;
std::string line;

auto AsciiError = [&](const auto& errstr){
LOG(message_group::Error, loc, "",
"OBJ File line %1$s, %2$s line '%3$s' importing file '%4$s'",
lineno, errstr, line, filename);
};

while (!f.eof()) {
lineno++;
std::getline(f, line);
boost::trim(line);

boost::smatch results;
if (line.length() == 0 || boost::regex_search(line, ex_comment)) {
continue;
} else if (boost::regex_search(line, results, ex_v) && results.size() >= 4) {
try {
Vector3d v;
for (int i = 0; i < 3; i++) {
v[i]= boost::lexical_cast<double>(results[i + 1]);
}
pts.push_back(v);
} catch (const boost::bad_lexical_cast& blc) {
AsciiError("can't parse vertex");
return new PolySet(3);
}
} else if (boost::regex_search(line, results, ex_f) && results.size() >= 2) {
p->append_poly();
std::string args=results[1];
std::vector<std::string> words;
boost::split(words, results[1], boost::is_any_of(" \t"));
for (const std::string& word : words) {
int ind=boost::lexical_cast<int>(word);
if(ind >= 1 && ind <= pts.size())
p->append_vertex(pts[ind-1][0], pts[ind-1][1], pts[ind-1][2]);
else
LOG(message_group::Warning, Location::NONE, "", "Index %1$d out of range in Line %2$d", filename, lineno);
}

} else if (boost::regex_search(line, results, ex_vt)) { // ignore texture coords
} else if (boost::regex_search(line, results, ex_vn)) { // ignore normal coords
} else if (boost::regex_search(line, results, ex_mtllib)) { // ignore material lib
} else if (boost::regex_search(line, results, ex_usemtl)) { // ignore usemtl
} else if (boost::regex_search(line, results, ex_o)) { // ignore object name
} else if (boost::regex_search(line, results, ex_s)) { // ignore smooting
} else if (boost::regex_search(line, results, ex_g)) { // ignore group name
} else {
LOG(message_group::Warning, Location::NONE, "", "Unrecognized Line %1$s in line Line %2$d", line, lineno);
}
}
return p.release();
}
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Expand Up @@ -708,6 +708,7 @@ list(APPEND CGALSTLSANITYTEST_FILES ${TEST_SCAD_DIR}/misc/normal-nan.scad)
list(APPEND EXPORT_STL_TEST_FILES ${TEST_SCAD_DIR}/stl/stl-export.scad)

list(APPEND EXPORT_OBJ_TEST_FILES ${TEST_SCAD_DIR}/obj/obj-export.scad)
list(APPEND EXPORT_OBJ_TEST_FILES ${TEST_SCAD_DIR}/obj/obj-import-export.scad)

list(APPEND EXPORT_3MF_TEST_FILES ${TEST_SCAD_DIR}/3mf/3mf-export.scad)

Expand Down
61 changes: 61 additions & 0 deletions tests/data/obj/dodecahedron.obj
@@ -0,0 +1,61 @@
# Author: Guenther Sohler, create_dod.py
# Date: 2023-03-08

g Dodecahedron

v -0.57735 -0.57735 0.57735
v 0.934172 0.356822 0
v 0.934172 -0.356822 0
v -0.934172 0.356822 0
v -0.934172 -0.356822 0
v 0 0.934172 0.356822
v 0 0.934172 -0.356822
v 0.356822 0 -0.934172
v -0.356822 0 -0.934172
v 0 -0.934172 -0.356822
v 0 -0.934172 0.356822
v 0.356822 0 0.934172
v -0.356822 0 0.934172
v 0.57735 0.57735 -0.57735
v 0.57735 0.57735 0.57735
v -0.57735 0.57735 -0.57735
v -0.57735 0.57735 0.57735
v 0.57735 -0.57735 -0.57735
v 0.57735 -0.57735 0.57735
v -0.57735 -0.57735 -0.57735
f 19 3 2
f 12 19 2
f 15 12 2
f 8 14 2
f 18 8 2
f 3 18 2
f 20 5 4
f 9 20 4
f 16 9 4
f 13 17 4
f 1 13 4
f 5 1 4
f 7 16 4
f 6 7 4
f 17 6 4
f 6 15 2
f 7 6 2
f 14 7 2
f 10 18 3
f 11 10 3
f 19 11 3
f 11 1 5
f 10 11 5
f 20 10 5
f 20 9 8
f 10 20 8
f 18 10 8
f 9 16 7
f 8 9 7
f 14 8 7
f 12 15 6
f 13 12 6
f 17 13 6
f 13 1 11
f 12 13 11
f 19 12 11
1 change: 1 addition & 0 deletions tests/data/scad/obj/obj-import-export.scad
@@ -0,0 +1 @@
import("../../obj/dodecahedron.obj");
57 changes: 57 additions & 0 deletions tests/regression/objexport/obj-import-export-expected.obj
@@ -0,0 +1,57 @@
# OpenSCAD obj exporter
v 0.57735 -0.57735 0.57735
v 0.934172 -0.356822 0
v 0.934172 0.356822 0
v 0.356822 0 0.934172
v 0.57735 0.57735 0.57735
v 0.356822 0 -0.934172
v 0.57735 0.57735 -0.57735
v 0.57735 -0.57735 -0.57735
v -0.57735 -0.57735 -0.57735
v -0.934172 -0.356822 0
v -0.934172 0.356822 0
v -0.356822 0 -0.934172
v -0.57735 0.57735 -0.57735
v -0.356822 0 0.934172
v -0.57735 0.57735 0.57735
v -0.57735 -0.57735 0.57735
v 0 0.934172 -0.356822
v 0 0.934172 0.356822
v 0 -0.934172 -0.356822
v 0 -0.934172 0.356822
f 1 2 3
f 4 1 3
f 5 4 3
f 6 7 3
f 8 6 3
f 2 8 3
f 9 10 11
f 12 9 11
f 13 12 11
f 14 15 11
f 16 14 11
f 10 16 11
f 17 13 11
f 18 17 11
f 15 18 11
f 18 5 3
f 17 18 3
f 7 17 3
f 19 8 2
f 20 19 2
f 1 20 2
f 20 16 10
f 19 20 10
f 9 19 10
f 9 12 6
f 19 9 6
f 8 19 6
f 12 13 17
f 6 12 17
f 7 6 17
f 4 5 18
f 14 4 18
f 15 14 18
f 14 16 20
f 4 14 20
f 1 4 20

0 comments on commit 4b835ad

Please sign in to comment.