Visualization Library 2.1.0

A lightweight C++ OpenGL middleware for 2D/3D graphics

VL     Star     Watch     Fork     Issue

[Download] [Tutorials] [All Classes] [Grouped Classes]
ioOBJ.cpp
Go to the documentation of this file.
1 /**************************************************************************************/
2 /* */
3 /* Visualization Library */
4 /* http://visualizationlibrary.org */
5 /* */
6 /* Copyright (c) 2005-2020, Michele Bosi */
7 /* All rights reserved. */
8 /* */
9 /* Redistribution and use in source and binary forms, with or without modification, */
10 /* are permitted provided that the following conditions are met: */
11 /* */
12 /* - Redistributions of source code must retain the above copyright notice, this */
13 /* list of conditions and the following disclaimer. */
14 /* */
15 /* - Redistributions in binary form must reproduce the above copyright notice, this */
16 /* list of conditions and the following disclaimer in the documentation and/or */
17 /* other materials provided with the distribution. */
18 /* */
19 /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND */
20 /* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED */
21 /* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE */
22 /* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR */
23 /* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES */
24 /* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; */
25 /* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON */
26 /* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */
27 /* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
28 /* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
29 /* */
30 /**************************************************************************************/
31 
32 #include "ioOBJ.hpp"
33 #include <vlCore/TextStream.hpp>
34 #include <vlCore/VirtualFile.hpp>
37 #include <vlCore/FileSystem.hpp>
40 #include <vlGraphics/Effect.hpp>
41 #include <vlGraphics/Actor.hpp>
42 #include <string>
43 #include <vector>
44 #include <stdio.h>
45 
46 using namespace vl;
47 
48 namespace
49 {
50  /*
51  * Utility function
52  */
53  template <class T>
54  void append(std::vector<T>& vec, const T& data, const int& alloc_step = 1024*10)
55  {
56  if (vec.size() == vec.capacity())
57  vec.reserve( vec.size() + alloc_step );
58  vec.push_back(data);
59  }
60 }
61 //-----------------------------------------------------------------------------
62 // ObjTexture
63 //-----------------------------------------------------------------------------
65 {
66  mBlendU = false;
67  mBlendV = false;
68  mCC = false;
69  mClamp = false;
70  mMM_Base = 0.0f;
71  mMM_Gain = 0.0f;
72  mO_UVW[0] = 0.0f; mO_UVW[1] = 0.0f; mO_UVW[2] = 0.0f;
73  mS_UVW[0] = 0.0f; mS_UVW[1] = 0.0f; mS_UVW[2] = 0.0f;
74  mT_UVW[0] = 0.0f; mT_UVW[1] = 0.0f; mT_UVW[2] = 0.0f;
75  mTexres_Value = 0.0f;
76  mImfchan = 0;
77  mBM = 0.0f;
78 }
79 //-----------------------------------------------------------------------------
80 const ObjTexture& ObjTexture::parseLine(const String& line, const String& file)
81 {
82  std::vector<String> tokens;
83  line.split(" \t",tokens,true);
84 
85  if (!tokens.empty())
86  {
87  tokens.erase(tokens.begin());
88  mFileName = tokens.back();
89  }
90  for(int i=0; i<(int)tokens.size(); ++i)
91  {
92  if (tokens[i] == "-blendu")
93  {
94  mBlendU = tokens[i+1] == "on";
95  ++i;
96  }
97  else
98  if (tokens[i] == "-blendv")
99  {
100  mBlendV = tokens[i+1] == "on";
101  ++i;
102  }
103  else
104  if (tokens[i] == "-cc")
105  {
106  mCC = tokens[i+1] == "on";
107  ++i;
108  }
109  else
110  if (tokens[i] == "-clamp")
111  {
112  mClamp = tokens[i+1] == "on";
113  ++i;
114  }
115  else
116  if (tokens[i] == "-mm")
117  {
118  mMM_Base = (float)tokens[i+1].toDouble();
119  mMM_Gain = (float)tokens[i+2].toDouble();
120  i+=2;
121  }
122  else
123  if (tokens[i] == "-o")
124  {
125  mO_UVW[0] = (float)tokens[i+1].toDouble();
126  mO_UVW[1] = (float)tokens[i+2].toDouble();
127  mO_UVW[2] = (float)tokens[i+3].toDouble();
128  i+=3;
129  }
130  else
131  if (tokens[i] == "-s")
132  {
133  mS_UVW[0] = (float)tokens[i+1].toDouble();
134  mS_UVW[1] = (float)tokens[i+2].toDouble();
135  mS_UVW[2] = (float)tokens[i+3].toDouble();
136  i+=3;
137  }
138  else
139  if (tokens[i] == "-t")
140  {
141  mT_UVW[0] = (float)tokens[i+1].toDouble();
142  mT_UVW[1] = (float)tokens[i+2].toDouble();
143  mT_UVW[2] = (float)tokens[i+3].toDouble();
144  i+=3;
145  }
146  else
147  if (tokens[i] == "-texres")
148  {
149  mTexres_Value = (float)tokens[i+1].toDouble();
150  ++i;
151  }
152  else
153  if (tokens[i] == "-imfchan")
154  {
155  mImfchan = (unsigned char)tokens[i+1][0];
156  ++i;
157  }
158  else
159  if (tokens[i] == "-bm")
160  {
161  mBM = (float)tokens[i+1].toDouble();
162  ++i;
163  }
164  else
165  {
166  if ( i != (int)tokens.size()-1 )
167  Log::error( Say("Unknown map option '%s' in file '%s'.\n") << tokens[i] << file );
168  }
169  }
170  return *this;
171 }
172 //-----------------------------------------------------------------------------
174 {
175  Log::print(
176  Say("-blendu %s -blendv %s -cc %s -clamp %s -mm %n %n -o %n %n %n -s %n %n %n -t %n %n %n -texres %n -imfchan '%c' -bm %n %s\n")
177  << (mBlendU ? "on" : "off")
178  << (mBlendV ? "on" : "off")
179  << (mCC ? "on" : "off")
180  << (mClamp ? "on" : "off")
181  << mMM_Base << mMM_Gain
182  << mO_UVW[0] << mO_UVW[1] << mO_UVW[2]
183  << mS_UVW[0] << mS_UVW[1] << mS_UVW[2]
184  << mT_UVW[0] << mT_UVW[1] << mT_UVW[2]
185  << mTexres_Value
186  << mImfchan
187  << mBM
188  << mFileName.toStdString().c_str()
189  );
190 }
191 //-----------------------------------------------------------------------------
192 // loadObjMaterials
193 //-----------------------------------------------------------------------------
194 void ObjLoader::loadObjMaterials(VirtualFile* input, std::vector<ObjMaterial>& materials )
195 {
196  ref<TextStream> line_reader = new TextStream(input);
197 
198  String line;
199  String file = input->path();
200  while(line_reader->readLine(line))
201  {
202  line = line.trim();
203  if (line.empty() || line[0] == '#')
204  continue;
205  else
206  {
207  if (line.startsWith("newmtl"))
208  {
209  materials.push_back(ObjMaterial());
210  materials.back().setObjectName( line.field(' ', 1).toStdString().c_str() );
211  }
212  else
213  if (line.startsWith("Ns"))
214  {
215  materials.back().setNs (line.field(' ', 1).toFloat());
216  }
217  else
218  if (line.startsWith("Ka"))
219  {
220  fvec3 col;
221  col.r() = (float)line.field(' ', 1).toDouble();
222  col.g() = (float)line.field(' ', 2).toDouble();
223  col.b() = (float)line.field(' ', 3).toDouble();
224  materials.back().setKa(col);
225  }
226  else
227  if (line.startsWith("Kd"))
228  {
229  fvec3 col;
230  col.r() = (float)line.field(' ', 1).toDouble();
231  col.g() = (float)line.field(' ', 2).toDouble();
232  col.b() = (float)line.field(' ', 3).toDouble();
233  materials.back().setKd(col);
234  }
235  else
236  if (line.startsWith("Ks"))
237  {
238  fvec3 col;
239  col.r() = (float)line.field(' ', 1).toDouble();
240  col.g() = (float)line.field(' ', 2).toDouble();
241  col.b() = (float)line.field(' ', 3).toDouble();
242  materials.back().setKs(col);
243  }
244  else
245  if (line.startsWith("Ke"))
246  {
247  fvec3 col;
248  col.r() = (float)line.field(' ', 1).toDouble();
249  col.g() = (float)line.field(' ', 2).toDouble();
250  col.b() = (float)line.field(' ', 3).toDouble();
251  materials.back().setKe(col);
252  }
253  else
254  if (line.startsWith("Tf"))
255  {
256  // skip transmission filter
257  }
258  else
259  if (line.startsWith("Ni"))
260  {
261  materials.back().setNi(line.field(' ', 1).toFloat());
262  }
263  else
264  if (line.startsWith("d") || line.startsWith("Tr"))
265  {
266  materials.back().setTr(line.field(' ', 1).toFloat());
267  }
268  else
269  if (line.startsWith("illum"))
270  {
271  materials.back().setIllum(line.field(' ', 1).toInt());
272  }
273  else
274  if (line.startsWith("map_Kd"))
275  materials.back().setMap_Kd(ObjTexture().parseLine(line,file));
276  else
277  if (line.startsWith("map_Ks"))
278  materials.back().setMap_Ks(ObjTexture().parseLine(line,file));
279  else
280  if (line.startsWith("map_Ka"))
281  materials.back().setMap_Ka(ObjTexture().parseLine(line,file));
282  else
283  if (line.startsWith("map_Ns"))
284  materials.back().setMap_Ns(ObjTexture().parseLine(line,file));
285  else
286  if (line.startsWith("map_d"))
287  materials.back().setMap_d(ObjTexture().parseLine(line,file));
288  else
289  if (line.startsWith("decal"))
290  materials.back().setMap_Decal(ObjTexture().parseLine(line,file));
291  else
292  if (line.startsWith("disp"))
293  materials.back().setMap_Disp(ObjTexture().parseLine(line,file));
294  else
295  if (line.startsWith("bump") || line.startsWith("map_bump"))
296  materials.back().setMap_Bump(ObjTexture().parseLine(line,file));
297  else
298  Log::error( Say("Unknown field '%s' in file %s'.\n") << line << file );
299  }
300  }
301 }
302 //-----------------------------------------------------------------------------
304 {
305  if (!file)
306  {
307  Log::error("loadOBJ() called with NULL argument.\n");
308  return NULL;
309  }
310  ref<TextStream> stream = new TextStream(file);
311  if ( !stream->inputFile()->open(OM_ReadOnly) )
312  {
313  Log::error( Say("loadOBJ(): could not open source file.\n") );
314  return NULL;
315  }
316 
317  mCoords.clear();
318  mNormals.clear();
319  mTexCoords.clear();
320  mMaterials.clear();
321  mMeshes.clear();
322 
323  ref<ObjMaterial> cur_material;
324  ref<ObjMesh> cur_mesh;
325 
326  const int BUF_SIZE = 1024;
327  char cmd[BUF_SIZE];
328 
329  int line_count = 0;
330  int f_format_type = 0;
331  std::string object_name;
332  bool starts_new_geom = true;
333  #if 0
334  bool smoothing_group = false;
335  #endif
336 
337  std::string stdstr_line;
338  while( stream->readLine(stdstr_line) )
339  {
340  ++line_count;
341  std::string line = String::trimStdString(stdstr_line);
342  if (line.empty() || line[0] == '#')
343  continue;
344 
345  // note: comments cannot be multiline
346  while( line[line.length()-1] == '\\' && stream->readLine(stdstr_line) )
347  {
348  ++line_count;
349  // remove "\"
350  line[line.length()-1] = ' ';
351  // remove spaces before \ and insert a single ' ' space
352  line = String::trimStdString(line) + ' ';
353  // appends new line
354  line += String::trimStdString(stdstr_line);
355  }
356 
357  cmd[0] = 0;
358  sscanf(line.c_str(), "%s ", cmd);
359  if ( !cmd[0] )
360  continue;
361 // ----------------------------------------------------------------------------
362  // Vertex data:
363  if (strcmp(cmd,"v") == 0) // Geometric vertices
364  {
365  float x=0,y=0,z=0,w=1.0f;
366  sscanf(line.c_str()+2,"%f %f %f", &x, &y, &z);
367  append(mCoords,fvec4(x,y,z,w));
368  /*append(mCoords,x);
369  append(mCoords,y);
370  append(mCoords,z);
371  append(mCoords,w);*/
372  }
373  else
374  if (strcmp(cmd,"vt") == 0) // Texture vertices
375  {
376  // note, this might have less than 3 mCoords
377  float x=0,y=0,z=0;
378  sscanf(line.c_str()+3,"%f %f %f", &x, &y, &z);
379  append(mTexCoords,fvec3(x,y,z));
380  /*append(mTexCoords,x);
381  append(mTexCoords,y);
382  append(mTexCoords,z);*/
383  }
384  else
385  if (strcmp(cmd,"vn") == 0) // Vertex mNormals
386  {
387  float x=0,y=0,z=0;
388  sscanf(line.c_str()+3,"%f %f %f", &x, &y, &z);
389  append(mNormals,fvec3(x,y,z));
390  /*append(mNormals,x);
391  append(mNormals,y);
392  append(mNormals,z);*/
393  }
394  else
395  /*if (strcmp(cmd,"vp") == 0) // Parameter space vertices
396  {
397  }
398  else*/
399 // ----------------------------------------------------------------------------
400  // Elements:
401  if (strcmp(cmd,"f") == 0) // Face
402  {
403  // starts new geometry if necessary
404  if (starts_new_geom)
405  {
406  cur_mesh = new ObjMesh;
407  cur_mesh->setObjectName(object_name.c_str());
408  mMeshes.push_back( cur_mesh );
409  starts_new_geom = false;
410  cur_mesh->setMaterial(cur_material.get());
411 
412  // detect vertex format
413 
414  int i=1;
415  while( line[i] == ' ' || line[i] == '\t' ) ++i;
416  int slash1 = 0;
417  int slash2 = 0;
418  while( i < (int)line.size() && line[i] != ' ' && line[i] != '\t' )
419  {
420  if (line[i] == '/')
421  {
422  if (!slash1)
423  slash1 = i;
424  else
425  if (!slash2)
426  {
427  slash2 = i;
428  break;
429  }
430  }
431  ++i;
432  }
433  if (!slash1 && !slash2)
434  f_format_type = 0;
435  else
436  if (slash1 && !slash2)
437  f_format_type = 1;
438  else
439  {
440  VL_CHECK(slash1)
441  VL_CHECK(slash2)
442  if (slash2 == slash1+1)
443  f_format_type = 2;
444  else
445  f_format_type = 3;
446  }
447  }
448 
449  int face_type = 0;
450  // divide into tokens
451  for(size_t i=0; i < line.size(); ++i)
452  {
453  if (line[i] == ' ')
454  {
455  ++face_type;
456 
457  // eat all the spaces
458  while( line[i] == ' ' )
459  ++i;
460 
461  if (line[i] == 0)
462  break;
463 
464  int iv=-1,ivt=-1,ivn=-1;
465  // 0 = f v v v
466  // 1 = f v/vt v/vt v/vt
467  // 2 = f v//vn v//vn v//vn
468  // 3 = f v/vt/vn v/vt/vn v/vt/vn
469  switch(f_format_type)
470  {
471  case 0:
472  sscanf(line.c_str()+i, "%d", &iv);
473  if (iv>0) --iv; else iv = (int)mCoords.size() - iv;
474  append(cur_mesh->facePositionIndex(), iv);
475  break;
476  case 1:
477  sscanf(line.c_str()+i, "%d/%d", &iv,&ivt);
478  if (iv>0) --iv; else iv = (int)mCoords.size() - iv;
479  if (ivt>0) --ivt; else ivt = (int)mTexCoords.size() - ivt;
480  append(cur_mesh->facePositionIndex(), iv);
481  append(cur_mesh->faceTexCoordIndex(), ivt);
482  break;
483  case 2:
484  sscanf(line.c_str()+i, "%d//%d", &iv,&ivn);
485  if (iv>0) --iv; else iv = (int)mCoords.size() - iv;
486  if (ivn>0) --ivn; else ivn = (int)mNormals.size() - ivn;
487  append(cur_mesh->facePositionIndex(), iv);
488  append(cur_mesh->faceNormalIndex(), ivn);
489  break;
490  case 3:
491  sscanf(line.c_str()+i, "%d/%d/%d", &iv,&ivt,&ivn);
492  if (iv>0) --iv; else iv = (int)mCoords.size() - iv;
493  if (ivt>0) --ivt; else ivt = (int)mTexCoords.size() - ivt;
494  if (ivn>0) --ivn; else ivn = (int)mNormals.size() - ivn;
495  append(cur_mesh->facePositionIndex(), iv);
496  append(cur_mesh->faceTexCoordIndex(), ivt);
497  append(cur_mesh->faceNormalIndex(), ivn);
498  break;
499  default:
500  break;
501  }
502  }
503  }
504  VL_CHECK(face_type > 2)
505  // track the face type in order to triangulate it later
506  append(cur_mesh->face_type(), face_type);
507  }
508  else
509  /*if (strcmp(cmd,"p") == 0) // Point
510  {
511  }
512  else
513  if (strcmp(cmd,"l") == 0) // Line
514  {
515  }
516  else
517  if (strcmp(cmd,"curv") == 0) // Curve
518  {
519  }
520  else
521  if (strcmp(cmd,"curv2") == 0) // 2D Curve
522  {
523  }
524  else
525  if (strcmp(cmd,"surf") == 0) // Surface
526  {
527  }
528  else
529 // ----------------------------------------------------------------------------
530  // Free-form curve/surface attributes:
531  if (strcmp(cmd,"deg") == 0) // Degree
532  {
533  }
534  else
535  if (strcmp(cmd,"bmat") == 0) // Basis matrix
536  {
537  }
538  else
539  if (strcmp(cmd,"step") == 0) // Step size
540  {
541  }
542  else
543  if (strcmp(cmd,"cstype") == 0) // Curve or surface type
544  {
545  }
546  else
547 // ----------------------------------------------------------------------------
548  // Free-form curve/surface body statements:
549  if (strcmp(cmd,"parm") == 0) // Parameter values
550  {
551  }
552  else
553  if (strcmp(cmd,"trim") == 0) // Outer trimming loop
554  {
555  }
556  else
557  if (strcmp(cmd,"hole") == 0) // Inner trimming loop
558  {
559  }
560  else
561  if (strcmp(cmd,"scrv") == 0) // Special curve
562  {
563  }
564  else
565  if (strcmp(cmd,"sp") == 0) // Special point
566  {
567  }
568  else
569  if (strcmp(cmd,"end") == 0) // End statement
570  {
571  }
572  else
573 // ----------------------------------------------------------------------------
574  // Connectivity between free-form surfaces:
575  if (strcmp(cmd,"con") == 0) // Connect
576  {
577  }
578  else
579  // Grouping:
580  if (strcmp(cmd,"g") == 0) // Group name
581  {
582  }
583  else*/
584  if (strcmp(cmd,"s") == 0) // Smoothing group
585  {
586  // not used
587  #if 0
588  if(String::trim(line+1) == "off" || String::trim(line+1) == "0")
589  smoothing_group = false;
590  else
591  smoothing_group = true;
592  #endif
593  }
594  else
595  /*if (strcmp(cmd,"mg") == 0) // Merging group
596  {
597  }
598  else*/
599  if (strcmp(cmd,"o") == 0) // Object name
600  {
601  starts_new_geom = true;
602  object_name = String::trimStdString(line.c_str()+1);
603  }
604  else
605 // ----------------------------------------------------------------------------
606  // Display/render attributes:
607  /*if (strcmp(cmd,"bevel") == 0) // Bevel interpolation
608  {
609  }
610  else
611  if (strcmp(cmd,"c_interp") == 0) // Color interpolation
612  {
613  }
614  else
615  if (strcmp(cmd,"d_interp") == 0) // Dissolve interpolation
616  {
617  }
618  else
619  if (strcmp(cmd,"lod") == 0) // Level of detail
620  {
621  }
622  else*/
623  if (strcmp(cmd,"usemtl") == 0) // Material name
624  {
625  starts_new_geom = true;
626  std::string mat_name = String(line.c_str()+6).trim().toStdString();
627  // can also become NULL
628  cur_material = mMaterials[mat_name];
629  }
630  else
631  if (strcmp(cmd,"mtllib") == 0) // Material library
632  {
633  // creates the path for the mtl
634  String path = file->path().extractPath() + String(line.c_str()+7).trim();
635  ref<VirtualFile> vfile = defFileSystem()->locateFile(path, file->path().extractPath());
636  std::string str_file = file->path().toStdString();
637  std::string str_path = path.toStdString();
638  if (vfile)
639  {
640  // reads the material
641  std::vector<ObjMaterial> mats;
642  loadObjMaterials(vfile.get(), mats);
643  // updates the material library
644  for(size_t i=0; i < mats.size(); ++i)
645  mMaterials[mats[i].objectName()] = new ObjMaterial(mats[i]);
646  }
647  else
648  {
649  Log::error( Say("Could not find OBJ material file '%s'.\n") << path );
650  }
651  }
652  /*else
653  if (strcmp(cmd,"shadow_obj") == 0) // Shadow casting
654  {
655  }
656  else
657  if (strcmp(cmd,"trace_obj") == 0) // Ray tracing
658  {
659  }
660  else
661  if (strcmp(cmd,"ctech") == 0) // Curve approximation technique
662  {
663  }
664  else
665  if (strcmp(cmd,"stech") == 0) // Surface approximation technique
666  {
667  }*/
668  }
669 
671 
672  // compile the material/effect library
673 
674  std::map< ObjMaterial*, ref<Effect> > material_map;
675  for (std::map< std::string, ref<ObjMaterial> >::iterator it = mMaterials.begin();
676  it != mMaterials.end();
677  ++it)
678  {
679  ref<Effect> effect = new Effect;
680  res_db->resources().push_back(effect.get());
681 
682  ref<ObjMaterial> obj_mat = it->second;
683  material_map[it->second.get()] = effect;
684  res_db->resources().push_back(effect);
685 
686  effect->shader()->enable(EN_DEPTH_TEST);
687  effect->shader()->disable(EN_CULL_FACE);
688  effect->shader()->enable(EN_LIGHTING);
689  effect->shader()->gocLightModel()->setTwoSide(true);
690 
691  if (obj_mat)
692  {
693  // sets the name
694  effect->shader()->gocMaterial()->setObjectName(obj_mat->objectName().c_str());
695  // add the Material to the ResourceDatabase
696  res_db->resources().push_back(effect->shader()->gocMaterial());
697  // setup the material
698  fvec4 diffuse = fvec4( obj_mat->kd(), 1.0f - obj_mat->tr() );
699  fvec4 ambient = fvec4( obj_mat->ka(), 1.0f - obj_mat->tr() );
700  fvec4 specular = fvec4( obj_mat->ks(), 1.0f - obj_mat->tr() );
701  fvec4 emission = fvec4( obj_mat->ke(), 1.0f - obj_mat->tr() );
702  effect->shader()->gocMaterial()->setDiffuse( diffuse );
703  effect->shader()->gocMaterial()->setAmbient( ambient );
704  effect->shader()->gocMaterial()->setSpecular( specular );
705  effect->shader()->gocMaterial()->setEmission( emission );
706  effect->shader()->gocMaterial()->setShininess( obj_mat->ns());
707 
708  // setup transparency
709  if (obj_mat->tr() > 0 || obj_mat->map_d().valid())
710  {
711  effect->shader()->enable(EN_BLEND);
712 
713  // if it has a mask map use alpha testing (typically for vegetation).
714  if ( obj_mat->map_d().valid() )
715  {
716  effect->shader()->gocAlphaFunc()->set(FU_GEQUAL, 0.5);
717  effect->shader()->enable(EN_ALPHA_TEST);
718  // disable cull face
719  // enable two side
720  }
721  else
722  {
723  effect->shader()->enable(EN_CULL_FACE);
724  effect->shader()->gocLightModel()->setTwoSide(false);
725  }
726  }
727 
728  // setup texture
729  if (obj_mat->map_Kd().valid())
730  {
731  // diffuse
732  ref<VirtualFile> diff_file = defFileSystem()->locateFile(obj_mat->map_Kd().path(), file->path().extractPath());
733  ref<Image> diff_img;
734  if (diff_file)
735  diff_img = loadImage( diff_file.get() );
736 
737  // mask
738  if (obj_mat->map_d().valid())
739  {
740  ref<VirtualFile> mask_file = defFileSystem()->locateFile(obj_mat->map_d().path(), file->path().extractPath());
741  ref<Image> mask_img;
742  if (mask_file)
743  mask_img = loadImage( mask_file.get() );
744  if (mask_img)
745  {
746  if (mask_img->width() == diff_img->width() && mask_img->height() == diff_img->height())
747  {
748  diff_img = diff_img->convertFormat(IF_RGBA); VL_CHECK(diff_img)
749  diff_img = diff_img->convertType(IT_UNSIGNED_BYTE); VL_CHECK(diff_img)
750  mask_img = mask_img->convertType(IT_UNSIGNED_BYTE);
751  int bpp = mask_img->bitsPerPixel() / 8;
752  unsigned char* mask_px = mask_img->pixels();
753  unsigned char* mask_end = mask_img->pixels() + mask_img->requiredMemory();
754  unsigned char* diff_px = diff_img->pixels() + 3;
755  while( mask_px != mask_end )
756  {
757  diff_px[0] = mask_px[0];
758  mask_px += bpp;
759  diff_px += 4;
760  }
761  }
762  }
763  }
764 
765  if ( diff_img )
766  {
767  ref<Texture> texture = new Texture;
770  texture->prepareTexture2D( diff_img.get(), TF_RGBA, true );
771  effect->shader()->gocTextureImageUnit(0)->setTexture( texture.get() );
772  }
773  }
774  }
775  }
776 
777  for(int imesh=0; imesh<(int)mMeshes.size(); ++imesh)
778  {
779  if ( mMeshes[imesh]->facePositionIndex().empty() )
780  {
781  Log::warning("OBJ mesh empty.\n");
782  VL_TRAP()
783  continue;
784  }
785 
786  #ifndef NDEBUG
787  int sum = 0;
788  for(int k=0; k<(int)mMeshes[imesh]->face_type().size(); ++k)
789  sum += mMeshes[imesh]->face_type()[k];
790  VL_CHECK( sum == (int)mMeshes[imesh]->facePositionIndex().size() )
791  #endif
792 
793  ref< ArrayFloat3 > v_coords = new ArrayFloat3;
794  ref< ArrayFloat3 > n_coords = new ArrayFloat3;
795  // we support only 2d textures
796  ref< ArrayFloat2 > t_coords2 = new ArrayFloat2;
797 
798  if ( !mMeshes[imesh]->faceNormalIndex().empty() && mMeshes[imesh]->faceNormalIndex().size() != mMeshes[imesh]->facePositionIndex().size() )
799  {
800  Log::print("OBJ mesh corrupted.\n");
801  continue;
802  }
803 
804  if ( !mMeshes[imesh]->faceTexCoordIndex().empty() && mMeshes[imesh]->faceTexCoordIndex().size() != mMeshes[imesh]->facePositionIndex().size() )
805  {
806  Log::print("OBJ mesh corrupted.\n");
807  continue;
808  }
809 
810  // allocate vertex buffer
811 
812  int tri_verts_count = 0;
813  for(int k=0; k<(int)mMeshes[imesh]->face_type().size(); ++k)
814  tri_verts_count += (mMeshes[imesh]->face_type()[k] - 2) * 3;
815 
816  v_coords->resize( tri_verts_count );
817  if ( !mMeshes[imesh]->faceNormalIndex().empty() )
818  n_coords->resize( tri_verts_count );
819  if ( !mMeshes[imesh]->faceTexCoordIndex().empty() )
820  t_coords2->resize( tri_verts_count );
821 
822  // fill geometry
823 
824  int src_base_idx = 0;
825  int dst_base_idx = 0;
826  for(int iface=0; iface<(int)mMeshes[imesh]->face_type().size(); ++iface)
827  {
828  int type = mMeshes[imesh]->face_type()[iface];
829  for( int ivert=2; ivert < type; ++ivert )
830  {
831  int a = mMeshes[imesh]->facePositionIndex()[src_base_idx+0];
832  int b = mMeshes[imesh]->facePositionIndex()[src_base_idx+ivert-1];
833  int c = mMeshes[imesh]->facePositionIndex()[src_base_idx+ivert];
834 
835  VL_CHECK( a>= 0)
836  VL_CHECK( b>= 0)
837  VL_CHECK( c>= 0)
838  VL_CHECK( a<(int)mCoords.size() )
839  VL_CHECK( b<(int)mCoords.size() )
840  VL_CHECK( c<(int)mCoords.size() )
841 
842  v_coords->at(dst_base_idx+0) = mCoords[a].xyz();
843  v_coords->at(dst_base_idx+1) = mCoords[b].xyz();
844  v_coords->at(dst_base_idx+2) = mCoords[c].xyz();
845 
846  if (!mMeshes[imesh]->faceNormalIndex().empty())
847  {
848  int na = mMeshes[imesh]->faceNormalIndex()[src_base_idx+0];
849  int nb = mMeshes[imesh]->faceNormalIndex()[src_base_idx+ivert-1];
850  int nc = mMeshes[imesh]->faceNormalIndex()[src_base_idx+ivert];
851 
852  VL_CHECK( na>= 0)
853  VL_CHECK( nb>= 0)
854  VL_CHECK( nc>= 0)
855  VL_CHECK( na<(int)mNormals.size() )
856  VL_CHECK( nb<(int)mNormals.size() )
857  VL_CHECK( nc<(int)mNormals.size() )
858 
859  n_coords->at(dst_base_idx+0) = mNormals[na];
860  n_coords->at(dst_base_idx+1) = mNormals[nb];
861  n_coords->at(dst_base_idx+2) = mNormals[nc];
862  }
863 
864  // we consider all the texture coords as 2d
865  if (!mMeshes[imesh]->faceTexCoordIndex().empty())
866  {
867  int na = mMeshes[imesh]->faceTexCoordIndex()[src_base_idx+0];
868  int nb = mMeshes[imesh]->faceTexCoordIndex()[src_base_idx+ivert-1];
869  int nc = mMeshes[imesh]->faceTexCoordIndex()[src_base_idx+ivert];
870 
871  VL_CHECK( na>= 0)
872  VL_CHECK( nb>= 0)
873  VL_CHECK( nc>= 0)
874  VL_CHECK( na<(int)mTexCoords.size() )
875  VL_CHECK( nb<(int)mTexCoords.size() )
876  VL_CHECK( nc<(int)mTexCoords.size() )
877 
878  t_coords2->at(dst_base_idx+0) = mTexCoords[na].st();
879  t_coords2->at(dst_base_idx+1) = mTexCoords[nb].st();
880  t_coords2->at(dst_base_idx+2) = mTexCoords[nc].st();
881  }
882 
883  dst_base_idx+=3;
884  }
885  src_base_idx += type;
886  }
887 
888  // Geometry
889 
890  ref<Geometry> geom = new Geometry;
891  geom->setObjectName(mMeshes[imesh]->objectName().c_str());
892  geom->setVertexArray( v_coords.get() );
893  if ( mMeshes[imesh]->faceNormalIndex().size() )
894  geom->setNormalArray( n_coords.get() );
895  if ( mMeshes[imesh]->faceTexCoordIndex().size() )
896  geom->setTexCoordArray(0, t_coords2.get() );
897  geom->drawCalls().push_back( new DrawArrays(PT_TRIANGLES, 0, tri_verts_count) );
898 
899  // Material/Effect
900 
901  ref<Effect> effect = material_map[ mMeshes[imesh]->material() ];
902  if (!effect)
903  {
904  effect = material_map[ mMeshes[imesh]->material() ] = new Effect;
905  res_db->resources().push_back(effect.get());
906 
907  effect->shader()->enable(EN_DEPTH_TEST);
908  effect->shader()->disable(EN_CULL_FACE);
909  effect->shader()->enable(EN_LIGHTING);
910  effect->shader()->gocLightModel()->setTwoSide(true);
911  }
912  VL_CHECK(effect)
913 
914  // Actor
915  ref<Actor> actor = new Actor(geom.get(), NULL);
916  res_db->resources().push_back(actor);
917  actor->setObjectName(mMeshes[imesh]->objectName().c_str());
918  actor->setEffect(effect.get());
919  }
920 
921  stream->inputFile()->close();
922 
923  return res_db;
924 }
925 //-----------------------------------------------------------------------------
927 {
928  ref<VirtualFile> file = defFileSystem()->locateFile( path );
929  if (file)
930  return loadOBJ( file.get() );
931  else
932  {
933  Log::error( Say("Could not locate '%s'.\n") << path );
934  return NULL;
935  }
936 }
937 //-----------------------------------------------------------------------------
939 {
940  return ObjLoader().loadOBJ(file);
941 }
942 //-----------------------------------------------------------------------------
float ns() const
Ns - specular exponent.
Definition: ioOBJ.hpp:205
Associates a Renderable object to an Effect and Transform.
Definition: Actor.hpp:130
Material * gocMaterial()
Definition: Shader.cpp:119
VLGRAPHICS_EXPORT ref< ResourceDatabase > loadOBJ(const String &path)
Loads a Wavefront OBJ file. See also ObjLoader.
Definition: ioOBJ.cpp:926
void setEffect(Effect *effect)
Binds an Effect to an Actor.
Definition: Actor.hpp:196
VLCORE_EXPORT FileSystem * defFileSystem()
Returns the default FileSystem used by VisualizationLibrary.
Definition: pimpl.cpp:97
TexParameter * getTexParameter()
The TexParameter object associated to a Texture.
Definition: Texture.hpp:281
String & trim()
Equivalent to trim("\n\r\t\v "), that is, removes all the tabs, spaces and new-lines from the beginni...
Definition: String.cpp:343
void prepareTexture2D(int width, int height, ETextureFormat format, bool border=false)
Prepares for creation an empty 2D texture.
Definition: Texture.hpp:361
const std::vector< int > & faceTexCoordIndex() const
Index into ObjLoader::texCoordsArray() vector.
Definition: ioOBJ.hpp:313
Vector3< float > fvec3
A 3 components vector with float precision.
Definition: Vector3.hpp:253
const T_Scalar & b() const
Definition: Vector3.hpp:100
const fvec3 & ka() const
Ka - ambient color.
Definition: ioOBJ.hpp:195
float tr() const
Tr/d - transparency.
Definition: ioOBJ.hpp:203
const T * get() const
Definition: Object.hpp:128
fvec3 mT_UVW
-t u v w
Definition: ioOBJ.hpp:175
An abstract class representing a file.
Definition: VirtualFile.hpp:60
Loads a Wavefront OBJ file.
Definition: ioOBJ.hpp:341
bool mClamp
-clamp on | off
Definition: ioOBJ.hpp:165
If enabled, do depth comparisons and update the depth buffer; Note that even if the depth buffer exis...
bool readLine(std::string &utf8)
Definition: TextStream.hpp:67
Vector4< float > fvec4
A 4 components vector with float precision.
Definition: Vector4.hpp:280
A simple String formatting class.
Definition: Say.hpp:124
const unsigned char * pixels() const
Raw pointer to pixels.
Definition: Image.hpp:170
void setObjectName(const char *name)
The name of the object, by default set to the object&#39;s class name in debug builds.
Definition: Object.hpp:220
static void warning(const String &message)
Use this function to provide information about situations that might lead to errors or loss of data...
Definition: Log.cpp:155
fvec3 mS_UVW
-s u v w
Definition: ioOBJ.hpp:173
void loadObjMaterials(VirtualFile *file, std::vector< ObjMaterial > &materials)
Loads a Wavefront MTL file.
Definition: ioOBJ.cpp:194
The String class implements an advanced UTF16 (Unicode BMP) string manipulation engine.
Definition: String.hpp:62
ref< Image > convertType(EImageType new_type) const
Converts the type() of an image.
Definition: Image.cpp:1303
const fvec3 & kd() const
Kd - diffuse color.
Definition: ioOBJ.hpp:197
If enabled, use the current lighting parameters to compute the vertex color; Otherwise, simply associate the current color with each vertex, see also Material, LightModel, and Light.
const std::vector< int > & faceNormalIndex() const
Index into ObjLoader::normalArray() vector.
Definition: ioOBJ.hpp:311
If enabled, blend the incoming RGBA color values with the values in the color buffers, see also BlendFunc for more information.
void setShininess(float shininess)
Definition: Shader.hpp:797
String mFileName
Texture file name.
Definition: ioOBJ.hpp:157
static void error(const String &message)
Use this function to provide information about run-time errors: file not found, out of memory...
Definition: Log.cpp:165
VirtualFile * inputFile()
void set(EFunction alphafunc, float ref_value)
Definition: Shader.hpp:751
void setVertexArray(ArrayAbstract *data)
Conventional vertex array.
Definition: Geometry.cpp:155
bool mBlendU
-blendu on | off
Definition: ioOBJ.hpp:159
bool mBlendV
-blendv on | off
Definition: ioOBJ.hpp:161
String extractPath() const
If the String contains a file path the function returns the path with trailing slash, without the file name.
Definition: String.cpp:830
void setSpecular(const fvec4 &color)
Definition: Shader.hpp:795
Wraps an OpenGL texture object representing and managing all the supported texture types...
Definition: Texture.hpp:178
virtual ref< VirtualFile > locateFile(const String &full_path, const String &alternate_path=String()) const
Looks for a VirtualFile on the disk and in the currently active FileSystem.
Definition: FileSystem.cpp:61
void resize(size_t dim)
Definition: Array.hpp:233
void setNormalArray(ArrayAbstract *data)
Conventional normal array.
Definition: Geometry.cpp:164
AlphaFunc * gocAlphaFunc()
Definition: Shader.cpp:117
int requiredMemory() const
Returns the number of bytes requested to store the image.
Definition: Image.cpp:532
bool mCC
-cc on | off
Definition: ioOBJ.hpp:163
int toInt(bool hex=false) const
Returns the int number represented by the string. The conversion is done using the standard atoi() fu...
Definition: String.cpp:1352
VLCORE_EXPORT ref< Image > loadImage(VirtualFile *file)
Loads an image from the specified file.
Definition: Image.cpp:1214
String & trim(wchar_t ch)
Removes the specified character ch from the beginning and the end of the String.
Definition: String.cpp:322
const ObjTexture & parseLine(const String &line, const String &file)
Definition: ioOBJ.cpp:80
const String & path() const
Texture file name.
Definition: ioOBJ.hpp:102
virtual void close()=0
Closes the file.
void disable(EEnable capability)
Definition: Shader.hpp:2160
If enabled, performs alpha testing, see also AlphaFunc for more information.
const T_Scalar & r() const
Definition: Vector3.hpp:98
const String & path() const
Returns the path of the file.
Definition: VirtualFile.hpp:98
The Geometry class is a Renderable that implements a polygonal mesh made of polygons, lines and points.
Definition: Geometry.hpp:66
float mMM_Gain
-mm base gain
Definition: ioOBJ.hpp:169
Visualization Library main namespace.
float mTexres_Value
-texres value
Definition: ioOBJ.hpp:177
TextureImageUnit * gocTextureImageUnit(int unit_index)
Definition: Shader.cpp:171
double toDouble() const
Returns the double number represented by the string. The conversion is done using the standard atof()...
Definition: String.cpp:1368
Represents a Wavefront OBJ mesh. See also ObjLoader.
Definition: ioOBJ.hpp:298
The TextStream class can be used to conveniently read or parse utf8-encoded text files.
Definition: TextStream.hpp:46
void setDiffuse(const fvec4 &color)
Definition: Shader.hpp:794
void setMinFilter(ETexParamFilter minfilter)
Definition: Texture.hpp:100
const fvec3 & ke() const
Ke - emissive color.
Definition: ioOBJ.hpp:201
float mBM
-bm mult
Definition: ioOBJ.hpp:181
int height() const
Definition: Image.hpp:209
#define VL_TRAP()
Definition: checks.hpp:70
void setEmission(const fvec4 &color)
Definition: Shader.hpp:796
const std::vector< ref< Object > > & resources() const
Represents a Wavefront OBJ texture. See also ObjMaterial and ObjLoader.
Definition: ioOBJ.hpp:92
fvec3 mO_UVW
-o u v w
Definition: ioOBJ.hpp:171
float toFloat() const
Returns the float number represented by the string. The conversion is done using the standard atof() ...
Definition: String.hpp:348
static void print(const String &message)
Application message for the user.
Definition: Log.cpp:136
int width() const
Definition: Image.hpp:207
bool empty() const
Returns true if length() == 0.
Definition: String.hpp:136
virtual bool open(EOpenMode mode)=0
Opens the file in the specified mode.
ref< ResourceDatabase > loadOBJ(VirtualFile *file)
Loads a Wavefront OBJ file.
Definition: ioOBJ.cpp:303
void split(wchar_t separator, std::vector< String > &fields, bool remove_empty_fields=false) const
Splits a String into a set of fields. The fields are separated by the specified separator and are ret...
Definition: String.cpp:386
LightModel * gocLightModel()
Definition: Shader.cpp:121
void setTexCoordArray(int tex_unit, ArrayAbstract *data)
Conventional texture coords arrays.
Definition: Geometry.cpp:222
bool startsWith(const String &str) const
Returns true if a String starts with the specified String str.
Definition: String.cpp:720
void print()
Prints the content of the material. Used for debugging purposes.
Definition: ioOBJ.cpp:173
const std::vector< int > & face_type() const
Each entry represents a face, the number represents how many vertices the face has.
Definition: ioOBJ.hpp:316
const ObjTexture & map_d() const
map_d - transparency map
Definition: ioOBJ.hpp:219
void setAmbient(const fvec4 &color)
Definition: Shader.hpp:793
#define NULL
Definition: OpenGLDefs.hpp:81
Shader * shader(int lodi=0, int pass=0)
Utility function, same as &#39;lod(lodi)->at(pass);&#39;.
Definition: Effect.hpp:178
bool valid() const
Definition: ioOBJ.hpp:97
Defines the sequence of Shader objects used to render an Actor.
Definition: Effect.hpp:91
T_VectorType & at(size_t i)
Definition: Array.hpp:255
const std::vector< int > & facePositionIndex() const
Index into ObjLoader::vertexArray() vector.
Definition: ioOBJ.hpp:309
const std::string & objectName() const
The name of the object, by default set to the object&#39;s class name.
Definition: Object.hpp:217
char mImfchan
-imfchan r | g | b | m | l | z
Definition: ioOBJ.hpp:179
ref< Image > convertFormat(EImageFormat new_format) const
Converts the format() of an image.
Definition: Image.cpp:1719
float mMM_Base
-mm base gain
Definition: ioOBJ.hpp:167
static std::string trimStdString(const std::string &text)
Remove the spaces before and after an std::string.
Definition: String.cpp:1471
void setTexture(Texture *texture)
The texture sampler by a texture unit.
Definition: Shader.hpp:1765
The ref<> class is used to reference-count an Object.
Definition: Object.hpp:55
const ObjTexture & map_Kd() const
map_Kd - ambient diffuse
Definition: ioOBJ.hpp:211
An array of vl::fvec3.
Definition: Array.hpp:414
std::string toStdString() const
Returns a UTF8 encoded std::string.
Definition: String.cpp:1156
Represents a Wavefront OBJ material as loaded from an MTL file. See also ObjLoader.
Definition: ioOBJ.hpp:187
Wraps the OpenGL function glDrawArrays().
Definition: DrawArrays.hpp:57
void setMagFilter(ETexParamFilter magfilter)
Definition: Shader.cpp:754
An array of vl::fvec2.
Definition: Array.hpp:412
void setTwoSide(bool twoside)
Definition: Shader.hpp:875
const fvec3 & ks() const
Ks - specular color.
Definition: ioOBJ.hpp:199
const T_Scalar & g() const
Definition: Vector3.hpp:99
void setMaterial(ObjMaterial *mat)
The material associated to this mesh.
Definition: ioOBJ.hpp:304
#define VL_CHECK(expr)
Definition: checks.hpp:73
static int bitsPerPixel(EImageType type, EImageFormat format)
Returns the number of bits used to represents one pixel.
Definition: Image.cpp:382
void enable(EEnable capability)
Definition: Shader.hpp:2158
String field(wchar_t separator, int field_index) const
Splits a String into a set of fields based on the specified separator and returns the filed at positi...
Definition: String.cpp:453
The ResourceDatabase class contains and manipulates a set of resources.
Collection< DrawCall > & drawCalls()
Returns the list of DrawCall objects bound to a Geometry.
Definition: Geometry.hpp:102
If enabled, cull polygons based on their winding in window coordinates, see also CullFace.