cgv
Loading...
Searching...
No Matches
obj_reader.cxx
1#include "obj_reader.h"
2#include <cgv/utils/file.h>
3#include <cgv/base/import.h>
4#include <cgv/type/standard_types.h>
6#include <cgv/utils/tokenizer.h>
7#include <cgv/base/import.h>
8
9using namespace cgv::math;
10using namespace cgv::type;
11using namespace cgv::utils;
12using namespace cgv::media::illum;
13
14#ifdef WIN32
15#pragma warning(disable:4996)
16#endif
17
18namespace cgv {
19 namespace media {
20 namespace mesh {
21
22 bool is_double_impl(const char* begin, const char* end, float& value)
23 {
24 double valued;
25 bool res = cgv::utils::is_double(begin, end, valued);
26 value = (float)valued;
27 return res;
28 }
29
30 bool is_double_impl(const char* begin, const char* end, double& value)
31 {
32 return cgv::utils::is_double(begin, end, value);
33 }
34
36template <typename T>
37bool obj_reader_generic<T>::is_double(const char* begin, const char* end, coord_type& value)
38{
39 return is_double_impl(begin, end, value);
40}
41
42template <typename T>
44{
45 vec2_type v(0.0);
46 t.size() > 2 &&
47 is_double(t[1].begin,t[1].end, v(0)) &&
48 is_double(t[2].begin,t[2].end, v(1));
49 return v;
50}
51
52template <typename T>
54{
55 vec3_type v(0,0,0);
56 t.size() > 3 &&
57 is_double(t[1].begin,t[1].end, v(0)) &&
58 is_double(t[2].begin,t[2].end, v(1)) &&
59 is_double(t[3].begin,t[3].end, v(2));
60 return v;
61}
62
63obj_reader_base::color_type obj_reader_base::parse_color(const std::vector<token>& t, unsigned off) const
64{
65 float v[4] = {0,0,0,1};
66 (t.size() > 3+off) &&
67 is_double_impl(t[1+off].begin,t[1+off].end, v[0]) &&
68 is_double_impl(t[2+off].begin,t[2+off].end, v[1]) &&
69 is_double_impl(t[3+off].begin,t[3+off].end, v[2]);
70 if (t.size() > 4+off)
71 is_double_impl(t[4+off].begin,t[4+off].end, v[3]);
72 return color_type((float)v[0],(float)v[1],(float)v[2],(float)v[3]);
73}
74
77{
78 return group_index;
79}
80
83{
84 return material_index;
85}
86
87obj_reader_base::obj_reader_base()
88{
89 clear();
90}
91
93{
94 mtl_lib_files.clear();
95 material_index_lut.clear();
96 nr_materials = 0;
97 nr_groups = 0;
98 minus = 1;
99 nr_normals = nr_texcoords = 0;
100 material_index = -1;
101 have_default_material = false;
102}
103
104template <typename T>
108
110void obj_reader_base::process_comment(const std::string& comment)
111{
112}
113
115template <typename T>
119
121template <typename T>
125
127template <typename T>
131
136
138void obj_reader_base::convert_to_positive(unsigned vcount, int *vertices,
139 int *texcoords, int *normals,
140 unsigned v, unsigned n, unsigned t)
141{
142 for (unsigned int i=0; i<vcount; ++i) {
143 if (vertices[i] < 0)
144 vertices[i] += v;
145 if (texcoords) {
146 if (texcoords[i] < 0)
147 texcoords[i] += t;
148 }
149 if (normals) {
150 if (normals[i] < 0)
151 normals[i] += n;
152 }
153 }
154}
155
157void obj_reader_base::process_line(unsigned vcount, int* vertices, int* texcoords, int* normals)
158{
159}
160
162void obj_reader_base::process_face(unsigned vcount, int *vertices, int *texcoords, int *normals)
163{
164}
165
167void obj_reader_base::process_group(const std::string& name, const std::string& parameters)
168{
169}
170
175
177template <typename T>
178void obj_reader_generic<T>::parse_and_process_vertex(const std::vector<cgv::utils::token>& tokens)
179{
180 process_vertex(parse_vec3(tokens));
181}
183template <typename T>
184void obj_reader_generic<T>::parse_and_process_normal(const std::vector<cgv::utils::token>& tokens)
185{
186 process_normal(parse_vec3(tokens));
187}
189template <typename T>
190void obj_reader_generic<T>::parse_and_process_texcoord(const std::vector<cgv::utils::token>& tokens)
191{
192 process_texcoord(parse_vec2(tokens));
193}
194
195bool obj_reader_base::read_obj(const std::string& file_name)
196{
197 std::string content;
198 if (!cgv::base::read_data_file(file_name, content, true))
199 return false;
200
201 path_name = file::get_path(file_name);
202 if (!path_name.empty())
203 path_name += "/";
204 return parse_obj(content, path_name);
205}
206
207bool obj_reader_base::parse_obj(const std::string & content, const std::string path_name)
208{
209 std::vector<line> lines;
210 split_to_lines(content,lines);
211
212 minus = 1;
213 material_index = -1;
214 group_index = -1;
215 nr_groups = 0;
216 nr_normals = nr_texcoords = 0;
217 std::map<std::string,unsigned> group_index_lut;
218 std::vector<token> tokens;
219 for (unsigned li=0; li<lines.size(); ++li) {
220 if(li % 1000 == 0)
221 printf("%d Percent done.\r", (int)(100.0*li/(lines.size()-1)) );
222
223 tokenizer(lines[li]).bite_all(tokens);
224 if (tokens.size() == 0)
225 continue;
226
227 switch (tokens[0][0]) {
228 case 'v' :
229 if (tokens[0].size() == 1) {
230 parse_and_process_vertex(tokens);
231 if (tokens.size() >= 7)
232 process_color(parse_color(tokens, 3));
233 }
234 else {
235 switch (tokens[0][1]) {
236 case 'n' :
237 parse_and_process_normal(tokens);
238 ++nr_normals;
239 break;
240 case 't' :
241 parse_and_process_texcoord(tokens);
242 ++nr_texcoords;
243 break;
244 case 'c' :
245 process_color(parse_color(tokens));
246 break;
247 }
248 }
249 break;
250 case 'f' :
251 if (group_index == -1) {
252 group_index = 0;
253 nr_groups = 1;
254 process_group("main","");
255 group_index_lut["main"] = group_index;
256 }
257 if (material_index == -1) {
258 obj_material m;
259 m.name = "default";
260 material_index = 0;
261 nr_materials = 1;
262 process_material(m, 0);
264 have_default_material = true;
265 }
266 parse_face(tokens);
267 break;
268 case 'l':
269 if (group_index == -1) {
270 group_index = 0;
271 nr_groups = 1;
272 process_group("main", "");
273 group_index_lut["main"] = group_index;
274 }
275 parse_face(tokens, true);
276 break;
277 case 'g' :
278 if (tokens.size() > 1) {
279 std::string name = to_string(tokens[1]);
280 std::string parameters;
281 if (tokens.size() > 2)
282 parameters.assign(tokens[2].begin, tokens.back().end - tokens[2].begin);
283
284 std::map<std::string,unsigned>::iterator it =
285 group_index_lut.find(name);
286
287 if (it != group_index_lut.end())
288 group_index = it->second;
289 else {
291 ++nr_groups;
292 process_group(name, parameters);
293 group_index_lut[name] = group_index;
294 }
295 }
296 break;
297 default:
298 if (to_string(tokens[0]) == "usemtl")
299 parse_material(tokens);
300 else if (to_string(tokens[0]) == "mtllib") {
301 if (tokens.size() > 1)
302 read_mtl(to_string(tokens[1]));
303 }
304 }
305 tokens.clear();
306 }
307 printf("\n");
308 return true;
309}
310
311bool obj_reader_base::read_mtl(const std::string& file_name)
312{
313 std::string fn = cgv::base::find_data_file(file_name, "McpD", "", path_name);
314 if (path_name.empty()) {
315 path_name = file::get_path(file_name);
316 if (!path_name.empty())
317 path_name += "/";
318 }
319 if (mtl_lib_files.find(fn) != mtl_lib_files.end())
320 return true;
321
322 std::string content;
323 if (!file::read(fn, content, true))
324 return false;
325
326 mtl_lib_files.insert(fn);
327
328 std::vector<line> lines;
329 split_to_lines(content,lines);
330
331 std::vector<token> tokens;
332 obj_material mtl;
333 bool in_mtl = false;
334
335 for (unsigned li=0; li<lines.size(); ++li) {
336 tokens.clear();
337 tokenizer(lines[li]).bite_all(tokens);
338 if (tokens.size() == 0)
339 continue;
340 if (tokens[0] == "newmtl") {
341 if (in_mtl) {
342 // check if material name is new
343 if (material_index_lut.find(mtl.name) == material_index_lut.end()) {
346 ++nr_materials;
347 }
348 // if not overwrite old definition
349 else
351 }
352 in_mtl = true;
353 mtl = obj_material();
354 if (tokens.size() > 1)
355 mtl.name = to_string(tokens[1]);
356 }
357 else if (tokens[0] == "map_Ka" && tokens.size() > 1)
359 else if (tokens[0] == "map_Kd" && tokens.size() > 1)
360 mtl.diffuse_texture_name = path_name + to_string(tokens[1]);
361 else if (tokens[0] == "map_d" && tokens.size() > 1)
362 mtl.opacity_texture_name = path_name + to_string(tokens[1]);
363 else if (tokens[0] == "map_Ks" && tokens.size() > 1)
364 mtl.specular_texture_name = path_name + to_string(tokens[1]);
365 else if (tokens[0] == "map_Ke" && tokens.size() > 1)
366 mtl.emission_texture_name = path_name + to_string(tokens[1]);
367 else if (tokens[0] == "Ka")
368 mtl.ambient = parse_color(tokens);
369 else if (tokens[0] == "Kd")
370 mtl.diffuse = parse_color(tokens);
371 else if (tokens[0] == "Ks")
372 mtl.specular = parse_color(tokens);
373 else if (tokens[0] == "Ke")
374 mtl.emission = parse_color(tokens);
375 else if (tokens[0] == "Ns")
376 mtl.shininess = (float)atof(to_string(tokens.back()).c_str());
377 else if (tokens[0] == "d")
378 mtl.opacity = (float)atof(to_string(tokens.back()).c_str());
379 else if (tokens[0] == "bump") {
380 for (unsigned i=1; i<tokens.size(); ++i) {
381 if (tokens[i] == "-bm") {
382 if (i+1 < tokens.size()) {
383 mtl.bump_scale = (float)atof(to_string(tokens[i+1]).c_str());
384 ++i;
385 }
386 }
387 else {
388 mtl.bump_texture_name = path_name+to_string(tokens[i]);
389 }
390 }
391 }
392 }
393 if (in_mtl) {
394 // check if material name is new
395 if (material_index_lut.find(mtl.name) == material_index_lut.end()) {
398 ++nr_materials;
399 }
400 // if not overwrite old definition
401 else
403 }
404 return true;
405}
406
407void obj_reader_base::parse_material(const std::vector<token>& tokens)
408{
409 if (tokens.size() < 2)
410 return;
411
412 std::map<std::string,unsigned>::iterator it =
413 material_index_lut.find(to_string(tokens[1]));
414
415 if(it != material_index_lut.end())
416 material_index = it->second;
417}
418
419void obj_reader_base::parse_face(const std::vector<token>& tokens, bool is_line)
420{
421 std::vector<int> vertex_indices;
422 std::vector<int> normal_indices;
423 std::vector<int> texcoord_indices;
424
425 for(unsigned i = 1; i < tokens.size(); i++) {
426 std::vector<token> smaller_tokens;
427 tokenizer(tokens[i]).set_sep("/").bite_all(smaller_tokens);
428 if (smaller_tokens.size() < 1)
429 continue;
430 int vi = atoi(to_string(smaller_tokens[0]).c_str());
431 if (vi > 0)
432 vi -= minus;
433 vertex_indices.push_back(vi);
434 if (smaller_tokens.size() == 1) {
435 if ((int)nr_normals > vi)
436 normal_indices.push_back(vi);
437 if ((int)nr_texcoords > vi)
438 texcoord_indices.push_back(vi);
439 continue;
440 }
441 if (smaller_tokens.size() < 3)
442 continue;
443 unsigned j = 2;
444 if (smaller_tokens[j] != "/") {
445 int ti = atoi(to_string(smaller_tokens[j]).c_str());
446 if (ti > 0)
447 ti -= minus;
448 if ((int)nr_texcoords > ti)
449 texcoord_indices.push_back(ti);
450 ++j;
451 }
452 if (smaller_tokens.size() < j+2)
453 continue;
454 int ni = atoi(to_string(smaller_tokens[j+1]).c_str());
455 if (ni > 0)
456 ni -= minus;
457 if ((int)nr_normals > ni)
458 normal_indices.push_back(ni);
459 }
460 int* nml_ptr = 0;
461 if (normal_indices.size() == vertex_indices.size())
462 nml_ptr = &normal_indices[0];
463 int* tex_ptr = 0;
464 if (texcoord_indices.size() == vertex_indices.size())
465 tex_ptr = &texcoord_indices[0];
466 if (is_line)
467 process_line((unsigned)vertex_indices.size(), &vertex_indices[0], tex_ptr, nml_ptr);
468 else
469 process_face((unsigned) vertex_indices.size(), &vertex_indices[0], tex_ptr, nml_ptr);
470}
471
472
473template class obj_reader_generic < float >;
474template class obj_reader_generic < double >;
475
476 }
477 }
478}
More advanced text processing for splitting text into lines or tokens.
virtual bool read_mtl(const std::string &file_name)
read a material file
virtual void process_line(unsigned vcount, int *vertices, int *texcoords=0, int *normals=0)
overide this function to process a line strip, the indices start with 0
virtual bool parse_obj(const std::string &content, const std::string path_name="")
parse the content of an obj file already read to memory, where path_name is used to find material fil...
color_type parse_color(const std::vector< cgv::utils::token > &t, unsigned off=0) const
parse a color, if alpha not given it defaults to 1
virtual void process_comment(const std::string &comment)
overide this function to process a comment
virtual void process_color(const color_type &c)
overide this function to process a color (this called for vc prefixes which is is not in the standard...
unsigned material_index
keep track of current material index
Definition obj_reader.h:29
virtual void process_material(const cgv::media::illum::obj_material &mtl, unsigned idx)
process a material definition. If a material with a certain name is overwritten, it will receive the ...
illum::obj_material::color_type color_type
type used for rgba colors
Definition obj_reader.h:22
std::string path_name
store the path name
Definition obj_reader.h:52
unsigned group_index
keep track of the current group
Definition obj_reader.h:25
unsigned nr_materials
number of materials
Definition obj_reader.h:31
virtual bool read_obj(const std::string &file_name)
read an obj file
virtual void process_group(const std::string &name, const std::string &parameters)
overide this function to process a group given by name and parameter string
std::map< std::string, unsigned > material_index_lut
mapping from material names to material indices
Definition obj_reader.h:33
unsigned get_current_group() const
return the index of the currently selected group or -1 if no group is defined
virtual void process_face(unsigned vcount, int *vertices, int *texcoords=0, int *normals=0)
overide this function to process a face, the indices start with 0
unsigned get_current_material() const
return the index of the currently selected material or -1 if no material is defined
virtual void clear()
clear the reader such that a new file can be read
unsigned nr_groups
number of groups
Definition obj_reader.h:27
void convert_to_positive(unsigned vcount, int *vertices, int *texcoords, int *normals, unsigned v, unsigned n, unsigned t)
convert negative indices to positive ones by adding the number of elements
vec3_type parse_vec3(const std::vector< cgv::utils::token > &t) const
parse 3d vector
virtual void process_vertex(const vec3_type &p)
overide this function to process a vertex
virtual void process_texcoord(const vec2_type &t)
overide this function to process a texcoord
virtual void process_normal(const vec3_type &n)
overide this function to process a normal
obj_reader_generic()
default constructor
cgv::math::fvec< T, 3 > vec3_type
type used to store positions and normal vectors
Definition obj_reader.h:105
cgv::math::fvec< T, 2 > vec2_type
type used to store texture coordinates
Definition obj_reader.h:103
vec2_type parse_vec2(const std::vector< cgv::utils::token > &t) const
parse 2d vector
the tokenizer allows to split text into tokens in a convenient way.
Definition tokenizer.h:68
tokenizer & set_sep(const std::string &sep, bool merge)
set the list of separators and specify whether succeeding separators are merged into single tokens
Definition tokenizer.cxx:59
bool read_data_file(const std::string &file_name, std::string &content, bool ascii)
read ascii file into a string
Definition import.cxx:388
std::string find_data_file(const std::string &file_name, const std::string &strategy, const std::string &sub_directory, const std::string &master_path)
Find a file with the given strategy and return the file name extended by the necessary path.
Definition import.cxx:59
namespace for compile time type information
Definition bool32_t.h:7
namespace that holds tools that dont fit any other namespace
std::string to_string(const std::string &v, unsigned int w, unsigned int p, bool)
specialization of conversion from string to strings
void split_to_lines(const char *global_begin, const char *global_end, std::vector< line > &lines, bool truncate_trailing_spaces)
this function splits a text range at the newline characters into single lines.
bool is_double(const char *begin, const char *end, double &value)
check if the text range (begin,end( defines a double value. If yes, store the value in the passed ref...
Definition scan.cxx:508
this header is dependency free
Definition print.h:11
Extension of a phong material with support for texture-mapped color channels.
float bump_scale
scaling factor for bump map
std::string emission_texture_name
file name of emission texture
std::string bump_texture_name
file name of bump map texture
std::string diffuse_texture_name
file name of diffuse texture
std::string specular_texture_name
file name of specular texture
std::string ambient_texture_name
file name of ambient texture
std::string name
name of material
std::string opacity_texture_name
file name of opacity texture
color_type ambient
ambient color component
color_type emission
emissive color component
color_type diffuse
diffuse color component
color_type specular
specular color component
float shininess
exponent of the specular cosine term