1 #include "obj-loader.h"
2 #include "../../nanort.h" // for float3
3
4 #define TINYOBJLOADER_IMPLEMENTATION
5 #include "tiny_obj_loader.h"
6
7 #ifdef __clang__
8 #pragma clang diagnostic push
9 #pragma clang diagnostic ignored "-Wold-style-cast"
10 #pragma clang diagnostic ignored "-Wreserved-id-macro"
11 #pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
12 #pragma clang diagnostic ignored "-Wcast-align"
13 #pragma clang diagnostic ignored "-Wpadded"
14 #pragma clang diagnostic ignored "-Wold-style-cast"
15 #pragma clang diagnostic ignored "-Wsign-conversion"
16 #pragma clang diagnostic ignored "-Wvariadic-macros"
17 #pragma clang diagnostic ignored "-Wc++11-extensions"
18 #pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
19 #pragma clang diagnostic ignored "-Wimplicit-fallthrough"
20 #if __has_warning("-Wdouble-promotion")
21 #pragma clang diagnostic ignored "-Wdouble-promotion"
22 #endif
23 #if __has_warning("-Wcomma")
24 #pragma clang diagnostic ignored "-Wcomma"
25 #endif
26 #if __has_warning("-Wcast-qual")
27 #pragma clang diagnostic ignored "-Wcast-qual"
28 #endif
29 #endif
30
31 #define STB_IMAGE_IMPLEMENTATION
32 #include "stb_image.h"
33
34 #ifdef __clang__
35 #pragma clang diagnostic pop
36 #endif
37
38 #include <iostream>
39
40 #ifdef NANOSG_USE_CXX11
41 #include <unordered_map>
42 #else
43 #include <map>
44 #endif
45
46 #define USE_TEX_CACHE 1
47
48 namespace example {
49
50 typedef nanort::real3<float> float3;
51
52 #ifdef __clang__
53 #pragma clang diagnostic push
54 #pragma clang diagnostic ignored "-Wexit-time-destructors"
55 #pragma clang diagnostic ignored "-Wglobal-constructors"
56 #endif
57
58 // TODO(LTE): Remove global static definition.
59 #ifdef NANOSG_USE_CXX11
60 static std::unordered_map<std::string, int> hashed_tex;
61 #else
62 static std::map<std::string, int> hashed_tex;
63 #endif
64
65 #ifdef __clang__
66 #pragma clang diagnostic pop
67 #endif
68
CalcNormal(float3 & N,float3 v0,float3 v1,float3 v2)69 inline void CalcNormal(float3 &N, float3 v0, float3 v1, float3 v2) {
70 float3 v10 = v1 - v0;
71 float3 v20 = v2 - v0;
72
73 N = vcross(v10, v20); // CCW
74 N = vnormalize(N);
75 }
76
GetBaseDir(const std::string & filepath)77 static std::string GetBaseDir(const std::string &filepath) {
78 if (filepath.find_last_of("/\\") != std::string::npos)
79 return filepath.substr(0, filepath.find_last_of("/\\"));
80 return "";
81 }
82
LoadTexture(const std::string & filename,std::vector<Texture> * textures)83 static int LoadTexture(const std::string &filename,
84 std::vector<Texture> *textures) {
85 int idx;
86
87 if (filename.empty()) return -1;
88
89 std::cout << " Loading texture : " << filename << std::endl;
90 Texture texture;
91
92 // tigra: find in cache. get index
93 if (USE_TEX_CACHE) {
94 if (hashed_tex.find(filename) != hashed_tex.end()) {
95 puts("from cache");
96 return hashed_tex[filename];
97 }
98 }
99
100 int w, h, n;
101 unsigned char *data = stbi_load(filename.c_str(), &w, &h, &n, 0);
102 if (data) {
103 texture.width = w;
104 texture.height = h;
105 texture.components = n;
106
107 size_t n_elem = size_t(w * h * n);
108 texture.image = new unsigned char[n_elem];
109 for (size_t i = 0; i < n_elem; i++) {
110 texture.image[i] = data[i];
111 }
112
113 free(data);
114
115 textures->push_back(texture);
116
117 idx = int(textures->size()) - 1;
118
119 // tigra: store index to cache
120 if (USE_TEX_CACHE) {
121 hashed_tex[filename] = idx;
122 }
123
124 return idx;
125 }
126
127 std::cout << " Failed to load : " << filename << std::endl;
128 return -1;
129 }
130
ComputeBoundingBoxOfMesh(float bmin[3],float bmax[3],const example::Mesh<float> & mesh)131 static void ComputeBoundingBoxOfMesh(float bmin[3], float bmax[3],
132 const example::Mesh<float> &mesh) {
133 bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max();
134 bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits<float>::max();
135
136 for (size_t i = 0; i < mesh.vertices.size() / 3; i++) {
137 bmin[0] = std::min(bmin[0], mesh.vertices[3 * i + 0]);
138 bmin[1] = std::min(bmin[1], mesh.vertices[3 * i + 1]);
139 bmin[2] = std::min(bmin[1], mesh.vertices[3 * i + 2]);
140
141 bmax[0] = std::max(bmax[0], mesh.vertices[3 * i + 0]);
142 bmax[1] = std::max(bmax[1], mesh.vertices[3 * i + 1]);
143 bmax[2] = std::max(bmax[2], mesh.vertices[3 * i + 2]);
144 }
145 }
146
LoadObj(const std::string & filename,float scale,std::vector<Mesh<float>> * meshes,std::vector<Material> * out_materials,std::vector<Texture> * out_textures)147 bool LoadObj(const std::string &filename, float scale,
148 std::vector<Mesh<float> > *meshes,
149 std::vector<Material> *out_materials,
150 std::vector<Texture> *out_textures) {
151 tinyobj::attrib_t attrib;
152 std::vector<tinyobj::shape_t> shapes;
153 std::vector<tinyobj::material_t> materials;
154 std::string err;
155
156 std::string basedir = GetBaseDir(filename) + "/";
157 const char *basepath = (basedir.compare("/") == 0) ? NULL : basedir.c_str();
158
159 // auto t_start = std::chrono::system_clock::now();
160
161 bool ret =
162 tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename.c_str(),
163 basepath, /* triangulate */ true);
164
165 // auto t_end = std::chrono::system_clock::now();
166 // std::chrono::duration<double, std::milli> ms = t_end - t_start;
167
168 if (!err.empty()) {
169 std::cerr << err << std::endl;
170 }
171
172 if (!ret) {
173 return false;
174 }
175
176 // std::cout << "[LoadOBJ] Parse time : " << ms.count() << " [msecs]"
177 // << std::endl;
178
179 std::cout << "[LoadOBJ] # of shapes in .obj : " << shapes.size() << std::endl;
180 std::cout << "[LoadOBJ] # of materials in .obj : " << materials.size()
181 << std::endl;
182
183 {
184 size_t total_num_vertices = 0;
185 size_t total_num_faces = 0;
186
187 total_num_vertices = attrib.vertices.size() / 3;
188 std::cout << " vertices : " << attrib.vertices.size() / 3 << std::endl;
189
190 for (size_t i = 0; i < shapes.size(); i++) {
191 std::cout << " shape[" << i << "].name : " << shapes[i].name
192 << std::endl;
193 std::cout << " shape[" << i
194 << "].indices : " << shapes[i].mesh.indices.size() << std::endl;
195 assert((shapes[i].mesh.indices.size() % 3) == 0);
196
197 total_num_faces += shapes[i].mesh.indices.size() / 3;
198
199 // tigra: empty name convert to _id
200 if (shapes[i].name.length() == 0) {
201 #ifdef NANOSG_USE_CXX11
202 shapes[i].name = "_" + std::to_string(i);
203 #else
204 std::stringstream ss;
205 ss << i;
206 shapes[i].name = "_" + ss.str();
207 #endif
208 std::cout << " EMPTY shape[" << i << "].name, new : " << shapes[i].name
209 << std::endl;
210 }
211 }
212 std::cout << "[LoadOBJ] # of faces: " << total_num_faces << std::endl;
213 std::cout << "[LoadOBJ] # of vertices: " << total_num_vertices << std::endl;
214 }
215
216 // TODO(LTE): Implement tangents and binormals
217
218 for (size_t i = 0; i < shapes.size(); i++) {
219 Mesh<float> mesh(/* stride */ sizeof(float) * 3);
220
221 mesh.name = shapes[i].name;
222
223 const size_t num_faces = shapes[i].mesh.indices.size() / 3;
224 mesh.faces.resize(num_faces * 3);
225 mesh.material_ids.resize(num_faces);
226 mesh.facevarying_normals.resize(num_faces * 3 * 3);
227 mesh.facevarying_uvs.resize(num_faces * 3 * 2);
228 mesh.vertices.resize(num_faces * 3 * 3);
229
230 for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) {
231 // reorder vertices. may create duplicated vertices.
232 size_t f0 = size_t(shapes[i].mesh.indices[3 * f + 0].vertex_index);
233 size_t f1 = size_t(shapes[i].mesh.indices[3 * f + 1].vertex_index);
234 size_t f2 = size_t(shapes[i].mesh.indices[3 * f + 2].vertex_index);
235
236 mesh.vertices[9 * f + 0] = scale * attrib.vertices[3 * f0 + 0];
237 mesh.vertices[9 * f + 1] = scale * attrib.vertices[3 * f0 + 1];
238 mesh.vertices[9 * f + 2] = scale * attrib.vertices[3 * f0 + 2];
239
240 mesh.vertices[9 * f + 3] = scale * attrib.vertices[3 * f1 + 0];
241 mesh.vertices[9 * f + 4] = scale * attrib.vertices[3 * f1 + 1];
242 mesh.vertices[9 * f + 5] = scale * attrib.vertices[3 * f1 + 2];
243
244 mesh.vertices[9 * f + 6] = scale * attrib.vertices[3 * f2 + 0];
245 mesh.vertices[9 * f + 7] = scale * attrib.vertices[3 * f2 + 1];
246 mesh.vertices[9 * f + 8] = scale * attrib.vertices[3 * f2 + 2];
247
248 mesh.faces[3 * f + 0] = static_cast<unsigned int>(3 * f + 0);
249 mesh.faces[3 * f + 1] = static_cast<unsigned int>(3 * f + 1);
250 mesh.faces[3 * f + 2] = static_cast<unsigned int>(3 * f + 2);
251
252 mesh.material_ids[f] =
253 static_cast<unsigned int>(shapes[i].mesh.material_ids[f]);
254 }
255
256 if (attrib.normals.size() > 0) {
257 for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) {
258 size_t f0, f1, f2;
259
260 f0 = size_t(shapes[i].mesh.indices[3 * f + 0].normal_index);
261 f1 = size_t(shapes[i].mesh.indices[3 * f + 1].normal_index);
262 f2 = size_t(shapes[i].mesh.indices[3 * f + 2].normal_index);
263
264 if (f0 > 0 && f1 > 0 && f2 > 0) {
265 float n0[3], n1[3], n2[3];
266
267 n0[0] = attrib.normals[3 * f0 + 0];
268 n0[1] = attrib.normals[3 * f0 + 1];
269 n0[2] = attrib.normals[3 * f0 + 2];
270
271 n1[0] = attrib.normals[3 * f1 + 0];
272 n1[1] = attrib.normals[3 * f1 + 1];
273 n1[2] = attrib.normals[3 * f1 + 2];
274
275 n2[0] = attrib.normals[3 * f2 + 0];
276 n2[1] = attrib.normals[3 * f2 + 1];
277 n2[2] = attrib.normals[3 * f2 + 2];
278
279 mesh.facevarying_normals[3 * (3 * f + 0) + 0] = n0[0];
280 mesh.facevarying_normals[3 * (3 * f + 0) + 1] = n0[1];
281 mesh.facevarying_normals[3 * (3 * f + 0) + 2] = n0[2];
282
283 mesh.facevarying_normals[3 * (3 * f + 1) + 0] = n1[0];
284 mesh.facevarying_normals[3 * (3 * f + 1) + 1] = n1[1];
285 mesh.facevarying_normals[3 * (3 * f + 1) + 2] = n1[2];
286
287 mesh.facevarying_normals[3 * (3 * f + 2) + 0] = n2[0];
288 mesh.facevarying_normals[3 * (3 * f + 2) + 1] = n2[1];
289 mesh.facevarying_normals[3 * (3 * f + 2) + 2] = n2[2];
290 } else { // face contains invalid normal index. calc geometric normal.
291 f0 = size_t(shapes[i].mesh.indices[3 * f + 0].vertex_index);
292 f1 = size_t(shapes[i].mesh.indices[3 * f + 1].vertex_index);
293 f2 = size_t(shapes[i].mesh.indices[3 * f + 2].vertex_index);
294
295 float3 v0, v1, v2;
296
297 v0[0] = attrib.vertices[3 * f0 + 0];
298 v0[1] = attrib.vertices[3 * f0 + 1];
299 v0[2] = attrib.vertices[3 * f0 + 2];
300
301 v1[0] = attrib.vertices[3 * f1 + 0];
302 v1[1] = attrib.vertices[3 * f1 + 1];
303 v1[2] = attrib.vertices[3 * f1 + 2];
304
305 v2[0] = attrib.vertices[3 * f2 + 0];
306 v2[1] = attrib.vertices[3 * f2 + 1];
307 v2[2] = attrib.vertices[3 * f2 + 2];
308
309 float3 N;
310 CalcNormal(N, v0, v1, v2);
311
312 mesh.facevarying_normals[3 * (3 * f + 0) + 0] = N[0];
313 mesh.facevarying_normals[3 * (3 * f + 0) + 1] = N[1];
314 mesh.facevarying_normals[3 * (3 * f + 0) + 2] = N[2];
315
316 mesh.facevarying_normals[3 * (3 * f + 1) + 0] = N[0];
317 mesh.facevarying_normals[3 * (3 * f + 1) + 1] = N[1];
318 mesh.facevarying_normals[3 * (3 * f + 1) + 2] = N[2];
319
320 mesh.facevarying_normals[3 * (3 * f + 2) + 0] = N[0];
321 mesh.facevarying_normals[3 * (3 * f + 2) + 1] = N[1];
322 mesh.facevarying_normals[3 * (3 * f + 2) + 2] = N[2];
323 }
324 }
325 } else {
326 // calc geometric normal
327 for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) {
328 size_t f0, f1, f2;
329
330 f0 = size_t(shapes[i].mesh.indices[3 * f + 0].vertex_index);
331 f1 = size_t(shapes[i].mesh.indices[3 * f + 1].vertex_index);
332 f2 = size_t(shapes[i].mesh.indices[3 * f + 2].vertex_index);
333
334 float3 v0, v1, v2;
335
336 v0[0] = attrib.vertices[3 * f0 + 0];
337 v0[1] = attrib.vertices[3 * f0 + 1];
338 v0[2] = attrib.vertices[3 * f0 + 2];
339
340 v1[0] = attrib.vertices[3 * f1 + 0];
341 v1[1] = attrib.vertices[3 * f1 + 1];
342 v1[2] = attrib.vertices[3 * f1 + 2];
343
344 v2[0] = attrib.vertices[3 * f2 + 0];
345 v2[1] = attrib.vertices[3 * f2 + 1];
346 v2[2] = attrib.vertices[3 * f2 + 2];
347
348 float3 N;
349 CalcNormal(N, v0, v1, v2);
350
351 mesh.facevarying_normals[3 * (3 * f + 0) + 0] = N[0];
352 mesh.facevarying_normals[3 * (3 * f + 0) + 1] = N[1];
353 mesh.facevarying_normals[3 * (3 * f + 0) + 2] = N[2];
354
355 mesh.facevarying_normals[3 * (3 * f + 1) + 0] = N[0];
356 mesh.facevarying_normals[3 * (3 * f + 1) + 1] = N[1];
357 mesh.facevarying_normals[3 * (3 * f + 1) + 2] = N[2];
358
359 mesh.facevarying_normals[3 * (3 * f + 2) + 0] = N[0];
360 mesh.facevarying_normals[3 * (3 * f + 2) + 1] = N[1];
361 mesh.facevarying_normals[3 * (3 * f + 2) + 2] = N[2];
362 }
363 }
364
365 if (attrib.texcoords.size() > 0) {
366 for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) {
367 size_t f0, f1, f2;
368
369 f0 = size_t(shapes[i].mesh.indices[3 * f + 0].texcoord_index);
370 f1 = size_t(shapes[i].mesh.indices[3 * f + 1].texcoord_index);
371 f2 = size_t(shapes[i].mesh.indices[3 * f + 2].texcoord_index);
372
373 if (f0 > 0 && f1 > 0 && f2 > 0) {
374 float3 n0, n1, n2;
375
376 n0[0] = attrib.texcoords[2 * f0 + 0];
377 n0[1] = attrib.texcoords[2 * f0 + 1];
378
379 n1[0] = attrib.texcoords[2 * f1 + 0];
380 n1[1] = attrib.texcoords[2 * f1 + 1];
381
382 n2[0] = attrib.texcoords[2 * f2 + 0];
383 n2[1] = attrib.texcoords[2 * f2 + 1];
384
385 mesh.facevarying_uvs[2 * (3 * f + 0) + 0] = n0[0];
386 mesh.facevarying_uvs[2 * (3 * f + 0) + 1] = n0[1];
387
388 mesh.facevarying_uvs[2 * (3 * f + 1) + 0] = n1[0];
389 mesh.facevarying_uvs[2 * (3 * f + 1) + 1] = n1[1];
390
391 mesh.facevarying_uvs[2 * (3 * f + 2) + 0] = n2[0];
392 mesh.facevarying_uvs[2 * (3 * f + 2) + 1] = n2[1];
393 }
394 }
395 }
396
397 // Compute pivot translation and add offset to the vertices.
398 float bmin[3], bmax[3];
399 ComputeBoundingBoxOfMesh(bmin, bmax, mesh);
400
401 float bcenter[3];
402 bcenter[0] = 0.5f * (bmax[0] - bmin[0]) + bmin[0];
403 bcenter[1] = 0.5f * (bmax[1] - bmin[1]) + bmin[1];
404 bcenter[2] = 0.5f * (bmax[2] - bmin[2]) + bmin[2];
405
406 for (size_t v = 0; v < mesh.vertices.size() / 3; v++) {
407 mesh.vertices[3 * v + 0] -= bcenter[0];
408 mesh.vertices[3 * v + 1] -= bcenter[1];
409 mesh.vertices[3 * v + 2] -= bcenter[2];
410 }
411
412 mesh.pivot_xform[0][0] = 1.0f;
413 mesh.pivot_xform[0][1] = 0.0f;
414 mesh.pivot_xform[0][2] = 0.0f;
415 mesh.pivot_xform[0][3] = 0.0f;
416
417 mesh.pivot_xform[1][0] = 0.0f;
418 mesh.pivot_xform[1][1] = 1.0f;
419 mesh.pivot_xform[1][2] = 0.0f;
420 mesh.pivot_xform[1][3] = 0.0f;
421
422 mesh.pivot_xform[2][0] = 0.0f;
423 mesh.pivot_xform[2][1] = 0.0f;
424 mesh.pivot_xform[2][2] = 1.0f;
425 mesh.pivot_xform[2][3] = 0.0f;
426
427 mesh.pivot_xform[3][0] = bcenter[0];
428 mesh.pivot_xform[3][1] = bcenter[1];
429 mesh.pivot_xform[3][2] = bcenter[2];
430 mesh.pivot_xform[3][3] = 1.0f;
431
432 meshes->push_back(mesh);
433 }
434
435 // material_t -> Material and Texture
436 out_materials->resize(materials.size());
437 out_textures->resize(0);
438 for (size_t i = 0; i < materials.size(); i++) {
439 (*out_materials)[i].diffuse[0] = materials[i].diffuse[0];
440 (*out_materials)[i].diffuse[1] = materials[i].diffuse[1];
441 (*out_materials)[i].diffuse[2] = materials[i].diffuse[2];
442 (*out_materials)[i].specular[0] = materials[i].specular[0];
443 (*out_materials)[i].specular[1] = materials[i].specular[1];
444 (*out_materials)[i].specular[2] = materials[i].specular[2];
445
446 (*out_materials)[i].id = int(i);
447
448 // map_Kd
449 (*out_materials)[i].diffuse_texid =
450 LoadTexture(materials[i].diffuse_texname, out_textures);
451 // map_Ks
452 (*out_materials)[i].specular_texid =
453 LoadTexture(materials[i].specular_texname, out_textures);
454 }
455
456 return true;
457 }
458
459 } // namespace example
460