CellModules
obj3D.hpp
Go to the documentation of this file.
1#ifndef MECACELL_3DOBJ_HPP
2#define MECACELL_3DOBJ_HPP
3#include <algorithm>
4#include <fstream>
5#include <iostream>
6#include <string>
7#include <utility>
8#include <vector>
11#include "utils.hpp"
12
13namespace MecaCell {
14
19struct Obj3D {
20 enum TriangleData { V, VT, VN };
21 using UV = std::array<double, 2>;
22 using triangle_t = std::array<size_t, 3>;
23
28};
29
34struct Scene3D {
36 using UV = Obj3D::UV;
37
39 std::unordered_map<std::string, Obj3D> originalObjects;
40 std::unordered_map<std::string, Obj3D> transformedObjects;
41
52 for (auto &o : transformedObjects) {
53 std::pair<std::pair<size_t, double>, std::pair<size_t, double>> hits{
54 {0, std::numeric_limits<double>::max()},
55 {0, std::numeric_limits<double>::max()}};
56 auto &obj = o.second;
57
58 // nb of hits with the closest squared distance in one direction and its opposite.
59 // If both are uneven, the point v is inside the object. The object with the closest
60 // pair of hits is the closest containing one.
61
62 for (auto &f : obj.faces) {
63 auto raycast = rayInTriangle(obj.vertices[f[Obj3D::TriangleData::V][0]],
64 obj.vertices[f[Obj3D::TriangleData::V][1]],
65 obj.vertices[f[Obj3D::TriangleData::V][2]], v,
66 Vector3D(0, 0, 1));
67 if (raycast.first) {
68 ++hits.first.first;
69 auto sqdist = (v - raycast.second).sqlength();
70 if (sqdist < hits.first.second) {
71 hits.first.second = sqdist;
72 }
73 } else {
74 // opposite direction
75 raycast = rayInTriangle(obj.vertices[f[Obj3D::TriangleData::V][0]],
76 obj.vertices[f[Obj3D::TriangleData::V][1]],
77 obj.vertices[f[Obj3D::TriangleData::V][2]], v,
78 Vector3D(0, 0, -1));
79 if (raycast.first) {
80 ++hits.second.first;
81 auto sqdist = (v - raycast.second).sqlength();
82 if (sqdist < hits.second.second) hits.second.second = sqdist;
83 }
84 }
85 }
86 if (hits.first.first % 2 == 1 && hits.second.first % 2 == 1) { // we're inside
87 containers.push_back(std::pair<std::string, double>(
88 o.first, hits.first.second + hits.second.second));
89 }
90 }
91 std::sort(containers.begin(), containers.end(),
92 [](const auto &a, const auto &b) { return a.second < b.second; });
94 for (auto &c : containers) result.push_back(c.first);
95 return result;
96 }
97
99 for (auto &oo : originalObjects) {
100 if (!transformedObjects.count(oo.first)) transformedObjects[oo.first] = oo.second;
101 Obj3D &to = transformedObjects[oo.first];
102 for (size_t i = 0; i < to.vertices.size(); ++i)
103 to.vertices[i] = transformation * oo.second.vertices[i];
104 for (size_t i = 0; i < to.normals.size(); ++i)
105 to.normals[i] = (transformation * oo.second.normals[i]).normalized();
106 }
107 }
108
109 void scale(const Vec &s) {
112 }
113
114 void translate(const Vec &t) {
117 }
118
119 void rotate(const Rotation<Vec> &r) {
122 }
123
125 Scene3D(const string &filename) { load(filename); }
131 void load(const string &filepath) {
132 std::ifstream file(filepath);
133 if (!file.is_open()) {
134 throw std::runtime_error("Unable to open scene3D file");
135 }
136
137 string line;
138 Obj3D *currentObjPtr = nullptr;
139 std::string currentObj = "";
140 size_t currentObjVertexId = 0, currentObjUVId = 0, currentObjNormalId = 0;
141 while (std::getline(file, line)) {
142 vector<string> vs = splitStr(line, ' ');
143 if (vs.size() > 1) {
144 if (vs[0] == "o") {
145 // new object
146 if (currentObjPtr) {
147 currentObjVertexId += currentObjPtr->vertices.size();
148 currentObjUVId += currentObjPtr->uv.size();
149 currentObjNormalId += currentObjPtr->normals.size();
150 }
151 currentObj = vs[1];
152 originalObjects.insert({{currentObj, Obj3D()}});
153 currentObjPtr = &originalObjects[currentObj];
154
155 } else {
156 if (currentObjPtr) {
157 if (vs[0] == "v" && vs.size() > 3) { // vertex coordinates
158 currentObjPtr->vertices.push_back(
159 Vec(stod(vs[1]), stod(vs[2]), stod(vs[3])));
160 } else if (vs[0] == "vt" && vs.size() > 2) { // UV coordinates
161 currentObjPtr->uv.push_back({{stod(vs[1]), stod(vs[2])}});
162 } else if (vs[0] == "vn" && vs.size() > 3) { // normal coordinates
163 currentObjPtr->normals.push_back(
164 Vec(stod(vs[1]), stod(vs[2]), stod(vs[3])));
165 } else if (vs[0] == "f" &&
166 vs.size() == 4) { // face (only triangles allowed for now)
167 std::array<triangle_t, 3> face;
168 for (size_t i = 1; i < vs.size(); ++i) {
169 std::vector<std::string> index = splitStr(vs[i], '/');
170 assert(index.size() == 3);
171 auto vid = stoul(index[0]) - 1 - currentObjVertexId;
172 assert(vid < currentObjPtr->vertices.size() && vid >= 0);
173 face[Obj3D::TriangleData::V][i - 1] = vid;
174 if (!index[1].empty()) {
175 auto uvid = stoul(index[1]) - 1 - currentObjUVId;
176 assert(uvid < currentObjPtr->uv.size() && uvid >= 0);
177 face[Obj3D::TriangleData::VT][i - 1] = uvid;
178 }
179 auto nid = stoul(index[2]) - 1 - currentObjNormalId;
180 assert(nid < currentObjPtr->normals.size() && nid >= 0);
181 face[Obj3D::TriangleData::VN][i - 1] = stoul(index[2]) - 1;
182 }
183 currentObjPtr->faces.push_back(face);
184 }
185 }
186 }
187 }
188 }
190 }
191};
192}
193
194#endif
general purpose 3D vector/point class.
Definition: vector3D.h:20
A simple vector class template.
Definition: std.hpp:290
void push_back(const T &value)
Adds an element to the end of the vector.
Definition: std.hpp:304
iterator begin()
Returns an iterator to the first element.
Definition: std.hpp:409
iterator end()
Returns an iterator to the last element.
Definition: std.hpp:418
size_t size() const
Returns the number of elements in the vector.
Definition: std.hpp:320
this file contains various miscellanious utility functions & helpers *
std::pair< bool, Vec > rayInTriangle(const Vec &v0, const Vec &v1, const Vec &v2, const Vec &o, const Vec &r, const double tolerance=0)
test if a ray hits a triangle
Definition: geometry.hpp:19
Vector3D Vec
alias for Vector3D
Definition: utils.hpp:22
std::vector< std::string > splitStr(const std::string &s, char delim)
String spliting.
Definition: utils.hpp:80
void scale(const V &s)
Multiplies this matrix by another that scales coordinates by the components of the given vector.
Definition: matrix4x4.h:22
void rotate(const R &r)
Multiplies this matrix by another that rotates coordinates by the according to the given rotation.
Definition: matrix4x4.h:48
void translate(const V &v)
Multiplies this matrix by another that translates coordinates by the components of the given vector.
Definition: matrix4x4.h:35
3D object representation: contains the vertices, UV coordinates, normals and faces
Definition: obj3D.hpp:19
std::array< size_t, 3 > triangle_t
Definition: obj3D.hpp:22
std::array< double, 2 > UV
Definition: obj3D.hpp:21
std::vector< Vector3D > vertices
Definition: obj3D.hpp:24
std::vector< UV > uv
Definition: obj3D.hpp:25
std::vector< std::array< triangle_t, 3 > > faces
Definition: obj3D.hpp:27
std::vector< Vector3D > normals
Definition: obj3D.hpp:26
Class allowing to load a 3D scene from an obj file. It contains one or several Obj3D as well as vario...
Definition: obj3D.hpp:34
std::unordered_map< std::string, Obj3D > originalObjects
Definition: obj3D.hpp:39
Obj3D::triangle_t triangle_t
Definition: obj3D.hpp:35
Obj3D::UV UV
Definition: obj3D.hpp:36
std::vector< std::string > isInside(const Vector3D &v) const
usefull to test if a point v is inside an object of the scene
Definition: obj3D.hpp:50
void updateFromTransformation()
Definition: obj3D.hpp:98
void scale(const Vec &s)
Definition: obj3D.hpp:109
Scene3D(const string &filename)
Definition: obj3D.hpp:125
void translate(const Vec &t)
Definition: obj3D.hpp:114
void rotate(const Rotation< Vec > &r)
Definition: obj3D.hpp:119
Matrix4x4 transformation
Definition: obj3D.hpp:38
void load(const string &filepath)
Loads an obj (wavefront) file.
Definition: obj3D.hpp:131
std::unordered_map< std::string, Obj3D > transformedObjects
Definition: obj3D.hpp:40