cgv
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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.set_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.get_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.set_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)
361 else if (tokens[0] == "map_d" && tokens.size() > 1)
363 else if (tokens[0] == "map_Ks" && tokens.size() > 1)
365 else if (tokens[0] == "map_Ke" && tokens.size() > 1)
367 else if (tokens[0] == "Ka")
368 mtl.set_ambient(parse_color(tokens));
369 else if (tokens[0] == "Kd")
370 mtl.set_diffuse(parse_color(tokens));
371 else if (tokens[0] == "Ks")
372 mtl.set_specular(parse_color(tokens));
373 else if (tokens[0] == "Ke")
374 mtl.set_emission(parse_color(tokens));
375 else if (tokens[0] == "Ns")
376 mtl.set_shininess((float)atof(to_string(tokens.back()).c_str()));
377 else if (tokens[0] == "d")
378 mtl.set_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.set_bump_scale((float)atof(to_string(tokens[i+1]).c_str()));
384 ++i;
385 }
386 }
387 else {
389 }
390 }
391 }
392 }
393 if (in_mtl) {
394 // check if material name is new
395 if (material_index_lut.find(mtl.get_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.
>extension of a phong material with support for texture mapped color channels
const std::string & get_name() const
return name value
virtual void set_emission_texture_name(std::string o)
set emission_texture_name value
void set_bump_scale(float bs)
set scale of bumps
void set_opacity(float o)
set opacity value
virtual void set_specular_texture_name(std::string o)
set specular_texture_name value
virtual void set_bump_texture_name(std::string b)
set bump_texture_name value
virtual void set_ambient_texture_name(std::string o)
set ambient_texture_name value
void set_name(std::string o)
set name value
virtual void set_opacity_texture_name(std::string o)
set opacity_texture_name value
virtual void set_diffuse_texture_name(std::string o)
set diffuse_texture_name value
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
implements the pure reading of an obj file and calls virtual callback functions to allow a derived cl...
Definition obj_reader.h:98
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
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
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:426
the cgv namespace
Definition print.h:11