Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
ModelFileOBJ.cpp
Go to the documentation of this file.
1 /* Project Magic 2.0
2  Destroyer Studios LLC
3  Copyright © 1997-2004. All Rights Reserved.
4 
5  SUBSYSTEM: Magic.exe
6  FILE: ModelFileOBJ.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  File loader for Wavefront/OBJ format models
13 */
14 
15 #include "stdafx.h"
16 #include "Magic.h"
17 #include "MagicDoc.h"
18 #include "ModelFileOBJ.h"
19 
20 #include "Bitmap.h"
21 #include "Polygon.h"
22 #include "List.h"
23 #include "Text.h"
24 
25 // +--------------------------------------------------------------------+
26 
27 ModelFileOBJ::ModelFileOBJ(const char* fname)
28  : ModelFile(fname)
29 {
30 }
31 
33 {
34 }
35 
36 // +--------------------------------------------------------------------+
37 
38 const int MAX_OBJ_FACE_VERTS = 32;
39 
40 struct ObjFace {
44 
45  int nverts;
46 
47  ObjFace() {
48  nverts = 0;
49 
50  for (int i = 0; i < MAX_OBJ_FACE_VERTS; i++)
51  v[i] = n[i] = t[i] = 0;
52  }
53 
54  ObjFace(const ObjFace& f) {
55  nverts = f.nverts;
56 
57  for (int i = 0; i < MAX_OBJ_FACE_VERTS; i++) {
58  v[i] = f.v[i];
59  n[i] = f.n[i];
60  t[i] = f.t[i];
61  }
62  }
63 
64  void ReverseOrder() {
65  int i, tmp[MAX_OBJ_FACE_VERTS];
66 
67  for (i = 0; i < nverts; i++) tmp[i] = v[i];
68  for (i = 0; i < nverts; i++) v[i] = tmp[nverts-1-i];
69 
70  for (i = 0; i < nverts; i++) tmp[i] = n[i];
71  for (i = 0; i < nverts; i++) n[i] = tmp[nverts-1-i];
72 
73  for (i = 0; i < nverts; i++) tmp[i] = t[i];
74  for (i = 0; i < nverts; i++) t[i] = tmp[nverts-1-i];
75  }
76 };
77 
78 static void ParsePoly(const char* line, ObjFace* face)
79 {
80  int v[MAX_OBJ_FACE_VERTS];
81  int n[MAX_OBJ_FACE_VERTS];
82  int t[MAX_OBJ_FACE_VERTS];
83 
84  for (int i = 0; i < MAX_OBJ_FACE_VERTS; i++) {
85  v[i] = n[i] = t[i] = 0;
86  }
87 
88  const char* p = line + 1;
89 
90  while (isspace(*p))
91  p++;
92 
93  int i = 0;
94  while (*p && i < MAX_OBJ_FACE_VERTS) {
95  int factor = 1;
96 
97  if (*p == '-') {
98  factor = -1;
99  p++;
100  }
101 
102  while (isdigit(*p)) {
103  v[i] = v[i]*10 + *p - '0';
104  p++;
105  }
106  v[i] *= factor;
107 
108  if (*p == '/') { p++; // slash one
109 
110  factor = 1;
111 
112  if (*p == '-') {
113  factor = -1;
114  p++;
115  }
116 
117  while (isdigit(*p)) {
118  t[i] = t[i]*10 + *p - '0';
119  p++;
120  }
121  t[i] *= factor;
122 
123  if (*p == '/') { p++; // slash two
124 
125  factor = 1;
126 
127  if (*p == '-') {
128  factor = -1;
129  p++;
130  }
131 
132  while (isdigit(*p)) {
133  n[i] = n[i]*10 + *p - '0';
134  p++;
135  }
136  n[i] *= factor;
137  }}
138 
139  while (isspace(*p)) p++;
140  i++;
141  }
142 
143  face->nverts = i;
144 
145  for (i = 0; i < MAX_OBJ_FACE_VERTS; i++) {
146  face->v[i] = v[i];
147  face->n[i] = n[i];
148  face->t[i] = t[i];
149  }
150 }
151 
152 static int LoadMatls(const char* lpszPathName, Model* m)
153 {
154  int nmatls = 0;
155 
156  FILE* fp = fopen(lpszPathName, "r");
157  if (!fp) {
158  ::MessageBox(0, "Open Failed: could not open file", "ERROR", MB_OK);
159  return 0;
160  }
161 
162  Material* mtl = 0;
163 
164  while (!feof(fp)) {
165  char raw_line[512];
166  char line[512];
167  fgets(raw_line, 512, fp);
168 
169  strcpy(line, Text(raw_line).trim().data());
170 
171  if (strstr(line, "newmtl")) {
172  mtl = new Material;
173  strncpy(mtl->name, line+7, Material::NAMELEN - 1);
174 
175  m->GetMaterials().append(mtl);
176  }
177 
178  else if (line[0] == 'K' && line[1] == 'a') {
179  float r,g,b;
180  sscanf(line, "Ka %f %f %f", &r, &g, &b);
181  mtl->Ka = ColorValue(r,g,b);
182 
183  mtl->ambient_value = 1.0f;
184  mtl->ambient_color = mtl->Ka.ToColor();
185  }
186 
187  else if (line[0] == 'K' && line[1] == 'd') {
188  float r,g,b;
189  sscanf(line, "Kd %f %f %f", &r, &g, &b);
190  mtl->Kd = ColorValue(r,g,b);
191 
192  mtl->diffuse_value = 1.0f;
193  mtl->diffuse_color = mtl->Kd.ToColor();
194  }
195 
196  else if (line[0] == 'K' && line[1] == 's') {
197  float r,g,b;
198  sscanf(line, "Ks %f %f %f", &r, &g, &b);
199  mtl->Ks = ColorValue(r,g,b);
200 
201  mtl->specular_value = 1.0f;
202  mtl->specular_color = mtl->Ks.ToColor();
203  }
204 
205  else if (line[0] == 'N' && line[1] == 's') {
206  float ns;
207  sscanf(line, "Ns %f", &ns);
208  mtl->power = ns;
209  }
210 
211  else if (strstr(line, "map_Kd")) {
212  const char* src = strstr(line, "map_Kd") + 7;
213  while (!isalnum(*src)) src++;
214 
216  }
217  }
218 
219  fclose(fp);
220  return nmatls;
221 }
222 
223 static Material* FindMatl(const char* mtl_name, Model* model)
224 {
225  ListIter<Material> iter = model->GetMaterials();
226  while (++iter) {
227  Material* m = iter.value();
228  if (!strcmp(m->name, mtl_name))
229  return m;
230  }
231 
232  return 0;
233 }
234 
235 // +--------------------------------------------------------------------+
236 
237 static int mcomp(const void* a, const void* b)
238 {
239  Poly* pa = (Poly*) a;
240  Poly* pb = (Poly*) b;
241 
242  if (pa->sortval == pb->sortval)
243  return 0;
244 
245  if (pa->sortval < pb->sortval)
246  return 1;
247 
248  return -1;
249 }
250 
251 bool
252 ModelFileOBJ::Load(Model* m, double scale)
253 {
254  if (m && scale > 0 && strlen(filename) > 0) {
255  ModelFile::Load(m, scale);
256 
257  FILE* fp = fopen(filename, "rb");
258 
259  if (fp == NULL) {
260  ::MessageBox(0, "Wavefront/OBJ Import Failed: Unable to open file", "ERROR", MB_OK);
261  return false;
262  }
263 
264  // ok, now start reading the data:
265  int ntex = 0;
266  int nverts = 0;
267  int npv = 0;
268  int npolys = 0;
269  int vi = 0;
270  int vn = 0;
271  int vt = 0;
272 
273  char root_path[256];
274  ZeroMemory(root_path, 256);
275 
276  if (strrchr(filename, '\\')) {
277  strcpy(root_path, filename);
278  char* p = strrchr(root_path, '\\');
279  if (p)
280  *(p+1) = 0;
281  }
282 
283  else if (strrchr(filename, '/')) {
284  strcpy(root_path, filename);
285  char* p = strrchr(root_path, '/');
286  if (p)
287  *(p+1) = 0;
288  }
289 
290  // count verts and polys:
291  while (!feof(fp)) {
292  char line[256];
293  fgets(line, 255, fp);
294 
295  if (line[0] == 'v') {
296  switch (line[1]) {
297  case ' ': vi++; break;
298  case 'n': vn++; break;
299  case 't': vt++; break;
300  }
301  }
302 
303  else if (line[0] == 'f' && line[1] == ' ') {
304  npolys++;
305 
306  ObjFace f;
307  ParsePoly(line, &f);
308  f.ReverseOrder();
309 
310  npv += f.nverts;
311  }
312 
313  else if (strstr(line, "mtllib")) {
314  const char* libname = strstr(line, "mtllib");
315  libname += 7;
316  while (isspace(*libname))
317  libname++;
318 
319  char libpath[256];
320  strcpy(libpath, root_path);
321  strcat(libpath, libname);
322  int n = strlen(libpath);
323  char* p = &libpath[n-1];
324  while (isspace(*p)) *p-- = 0;
325 
326  int nmatls = LoadMatls(libpath, model);
327  }
328  }
329 
330  nverts = npv;
331  if (vi > nverts) nverts = vi;
332  if (vn > nverts) nverts = vn;
333  if (vt > nverts) nverts = vt;
334 
335  if (nverts > Model::MAX_VERTS || npolys > Model::MAX_POLYS) {
336  ::MessageBox(0, "Wavefront/OBJ Import Failed: that model is just too darn complicated!", "ERROR", MB_OK);
337  return false;
338  }
339 
340  vi = 0;
341  vn = 0;
342  vt = 0;
343 
344  fseek(fp, 0, SEEK_SET);
345 
346  Surface* surface = new Surface;
347  m->GetSurfaces().append(surface);
348 
349  surface->CreateVerts(nverts);
350  surface->CreatePolys(npolys);
351 
352  VertexSet* vset = surface->GetVertexSet();
353  Poly* polys = surface->GetPolys();
354 
355  // read vertex set:
356  Vec3* vloc = new Vec3[nverts];
357  Vec3* vnrm = new Vec3[nverts];
358  float* vtu = new float[nverts];
359  float* vtv = new float[nverts];
360 
361  float radius = 0;
362 
363  while (!feof(fp)) {
364  char line[256];
365  fgets(line, 255, fp);
366 
367  if (line[0] == 'v') {
368  if (line[1] == ' ') {
369  const char* p = line + 2;
370 
371  while (isspace(*p)) p++;
372  sscanf(p, "%f", &vloc[vi].x);
373 
374  while (!isspace(*p)) p++;
375  while (isspace(*p)) p++;
376  sscanf(p, "%f", &vloc[vi].y);
377 
378  while (!isspace(*p)) p++;
379  while (isspace(*p)) p++;
380  sscanf(p, "%f", &vloc[vi].z);
381 
382  float d = vloc[vi].length();
383  if (d > radius)
384  radius = d;
385 
386  vi++;
387  }
388 
389  else if (line[1] == 'n') {
390  const char* p = line + 2;
391 
392  while (isspace(*p)) p++;
393  sscanf(p, "%f", &vnrm[vn].x);
394 
395  while (!isspace(*p)) p++;
396  while (isspace(*p)) p++;
397  sscanf(p, "%f", &vnrm[vn].y);
398 
399  while (!isspace(*p)) p++;
400  while (isspace(*p)) p++;
401  sscanf(p, "%f", &vnrm[vn].z);
402 
403  vn++;
404  }
405 
406  else if (line[1] == 't') {
407  const char* p = line + 2;
408 
409  while (isspace(*p)) p++;
410  sscanf(p, "%f", &vtu[vt]);
411 
412  while (!isspace(*p)) p++;
413  while (isspace(*p)) p++;
414  sscanf(p, "%f", &vtv[vt]);
415 
416  vtv[vt] = 1.0f - vtv[vt];
417 
418  vt++;
419  }
420  }
421  }
422 
423  fseek(fp, 0, SEEK_SET);
424 
425  // read polys:
426  int poly = 0;
427  char line[256];
428  ObjFace face;
429  Material* material = 0;
430  int mtl_index = 0;
431 
432  if (m->NumMaterials())
433  material = m->GetMaterials().first();
434 
435  int current_v = 1;
436  int current_vn = 1;
437  int current_vt = 1;
438  int v = 0; // vset index pointer
439 
440  while (!feof(fp)) {
441  char raw_line[256];
442  fgets(raw_line, 256, fp);
443 
444  strcpy(line, Text(raw_line).trim().data());
445 
446  if (strstr(line, "usemtl")) {
447  material = FindMatl(line + 7, model);
448  ListIter<Material> iter = model->GetMaterials();
449  while (++iter)
450  if (material == iter.value())
451  mtl_index = iter.index();
452  }
453 
454  else if (line[0] == 'v') {
455  if (line[1] == ' ') current_v++;
456  else if (line[1] == 'n') current_vn++;
457  else if (line[1] == 't') current_vt++;
458  }
459 
460  else if (line[0] == 'f') {
461  ParsePoly(line, &face);
462  face.ReverseOrder();
463 
464  for (int n = 0; n < face.nverts; n++) {
465  if (face.v[n] < 0)
466  face.v[n] += current_v;
467 
468  if (face.n[n] < 0)
469  face.n[n] += current_vn;
470 
471  if (face.t[n] < 0)
472  face.t[n] += current_vt;
473  }
474 
475  if (face.nverts > 4) {
476  npolys += face.nverts - 3;
477 
478  for (int tri = 2; tri < face.nverts; tri++) {
479  Poly* p = polys + poly;
480  poly++;
481 
482  p->nverts = 3;
483 
484  vset->loc[v+0] = vloc[face.v[ tri ] -1];
485  vset->loc[v+1] = vloc[face.v[ tri-1 ] -1];
486  vset->loc[v+2] = vloc[face.v[ 0] -1];
487 
488  if (face.n[0] > 0) {
489  vset->nrm[v+0] = vnrm[face.n[ tri ] -1];
490  vset->nrm[v+1] = vnrm[face.n[ tri-1 ] -1];
491  vset->nrm[v+2] = vnrm[face.n[ 0] -1];
492  }
493  else {
494  vset->nrm[v+0] = vloc[v+0]; vset->nrm[v+0].Normalize();
495  vset->nrm[v+1] = vloc[v+1]; vset->nrm[v+1].Normalize();
496  vset->nrm[v+2] = vloc[v+2]; vset->nrm[v+2].Normalize();
497  }
498 
499  if (face.t[0] > 0) {
500  vset->tu[v+0] = vtu[face.t[ tri ] -1];
501  vset->tv[v+0] = vtv[face.t[ tri ] -1];
502  vset->tu[v+1] = vtu[face.t[ tri-1 ] -1];
503  vset->tv[v+1] = vtv[face.t[ tri-1 ] -1];
504  vset->tu[v+2] = vtu[face.t[ 0 ] -1];
505  vset->tv[v+2] = vtv[face.t[ 0 ] -1];
506  }
507 
508  p->verts[0] = v+0;
509  p->verts[1] = v+1;
510  p->verts[2] = v+2;
511 
512  p->material = material;
513  p->sortval = mtl_index;
514  p->vertex_set = vset;
515 
516  p->plane = Plane(vset->loc[p->verts[0]],
517  vset->loc[p->verts[2]],
518  vset->loc[p->verts[1]]);
519 
520  v += p->nverts;
521  }
522  }
523 
524  else if (face.nverts == 3 || face.nverts == 4) {
525  Poly* p = polys + poly;
526  poly++;
527 
528  p->nverts = face.nverts;
529 
530  bool flat = true;
531  int first = v;
532 
533  for (int i = 0; i < p->nverts; i++) {
534  int face_index = i;
535 
536  vset->loc[v] = vloc[face.v[face_index]-1];
537 
538  if (face.n[face_index] > 0)
539  vset->nrm[v] = vnrm[face.n[face_index]-1];
540  else
541  vset->nrm[v] = vset->loc[v];
542 
543  vset->nrm[v].Normalize();
544 
545  if (vset->nrm[v] != vset->nrm[first])
546  flat = false;
547 
548  if (face.t[face_index] > 0) {
549  vset->tu[v] = vtu [face.t[face_index]-1];
550  vset->tv[v] = vtv [face.t[face_index]-1];
551  }
552 
553  p->verts[i] = v++;
554  }
555 
556  p->material = material;
557  p->sortval = mtl_index;
558  p->flatness = flat ? 1.0f : 0.0f;
559 
560  if (p->nverts == 3)
561  surface->AddIndices(3);
562 
563  else if (p->nverts == 4)
564  surface->AddIndices(6);
565 
566  p->vertex_set = vset;
567  p->plane = Plane(vset->loc[p->verts[0]],
568  vset->loc[p->verts[2]],
569  vset->loc[p->verts[1]]);
570  }
571 
572  if (poly >= npolys)
573  break;
574  }
575  }
576 
577  // sort the polys by material index:
578  qsort((void*) polys, npolys, sizeof(Poly), mcomp);
579 
580  // then assign them to cohesive segments:
581  Segment* segment = 0;
582 
583  for (int n = 0; n < npolys; n++) {
584  if (segment && segment->material == polys[n].material) {
585  segment->npolys++;
586  }
587  else {
588  segment = 0;
589  }
590 
591  if (!segment) {
592  segment = new Segment;
593 
594  segment->npolys = 1;
595  segment->polys = &polys[n];
596  segment->material = segment->polys->material;
597 
598  surface->GetSegments().append(segment);
599  }
600  }
601 
602  delete [] vloc;
603  delete [] vnrm;
604  delete [] vtu;
605  delete [] vtv;
606 
607  *pnverts = nverts;
608  *pnpolys = npolys;
609  *pradius = radius;
610 
611  fclose(fp);
612 
613  m->Normalize();
614  return true;
615  }
616 
617  return false;
618 }
619 
620 // +--------------------------------------------------------------------+
621 
622 static bool
623 SaveWaveFrontMatLib(const char* path, const char* root, Model* model)
624 {
625  char filename[256];
626  sprintf(filename, "%s%s.mtl", path, root);
627 
628  FILE* f = fopen(filename, "w");
629  if (f) {
630  fprintf(f, "#\n# %s Material Library exported by Magic 2.0\n#\n\n", root);
631 
632  ListIter<Material> iter = model->GetMaterials();
633  while (++iter) {
634  Material* mtl = iter.value();
635 
636  fprintf(f, "newmtl %s\n", mtl->name);
637  fprintf(f, "Ka %.5f %.5f %.5f\n", mtl->Ka.Red(), mtl->Ka.Green(), mtl->Ka.Blue());
638  fprintf(f, "Kd %.5f %.5f %.5f\n", mtl->Kd.Red(), mtl->Kd.Green(), mtl->Kd.Blue());
639  fprintf(f, "Ks %.5f %.5f %.5f\n", mtl->Ks.Red(), mtl->Ks.Green(), mtl->Ks.Blue());
640  fprintf(f, "Ns %.5f\n", mtl->power);
641  fprintf(f, "illum 2\n");
642  if (mtl->tex_diffuse)
643  fprintf(f, "map_Kd %s\n", mtl->tex_diffuse->GetFilename());
644  fprintf(f, "\n");
645  }
646 
647  fclose(f);
648  return true;
649  }
650 
651  return false;
652 }
653 
654 bool
656 {
657  if (m) {
658  ModelFile::Save(m);
659  char pathname[256];
660  char rootname[256];
661 
662  ZeroMemory(pathname, sizeof(pathname));
663  ZeroMemory(rootname, sizeof(rootname));
664 
665  const char* ext = strstr(filename, ".obj");
666  if (!ext)
667  ext = strstr(filename, ".OBJ");
668 
669  const char* sep = strrchr(filename, '/');
670  if (!sep)
671  sep = strrchr(filename, '\\');
672 
673  const char* src = filename;
674  char* dst = pathname;
675 
676  if (sep) {
677  while (src != sep)
678  *dst++ = *src++;
679  *dst++ = *src++;
680  }
681 
682  if (ext) {
683  dst = rootname;
684  while (src != ext)
685  *dst++ = *src++;
686  }
687  else {
688  strcpy(rootname, src);
689  }
690 
691  strcpy(filename, pathname);
692  strcat(filename, rootname);
693  strcat(filename, ".obj");
694 
695  FILE* f = fopen(filename, "w");
696  if (!f) {
697  ::MessageBox(0, "Export Failed: Magic could not open the file for writing", "ERROR", MB_OK);
698  return false;
699  }
700 
701  fprintf(f, "# Wavefront OBJ exported by Magic 2.0\n\n");
702  fprintf(f, "mtllib %s.mtl\n", rootname);
703 
704  ListIter<Surface> s_iter = m->GetSurfaces();
705 
706  // vertex locations
707  fprintf(f, "\n# VERTEX LOCATIONS: %d\n", m->NumVerts());
708  while (++s_iter) {
709  Surface* s = s_iter.value();
710  VertexSet* vset = s->GetVertexSet();
711 
712  for (int n = 0; n < vset->nverts; n++) {
713  fprintf(f, "v %12.5f %12.5f %12.5f\n",
714  vset->loc[n].x,
715  vset->loc[n].y,
716  vset->loc[n].z);
717  }
718  }
719 
720  s_iter.reset();
721 
722  // vertex normals
723  fprintf(f, "\n# VERTEX NORMALS: %d\n", m->NumVerts());
724  while (++s_iter) {
725  Surface* s = s_iter.value();
726  VertexSet* vset = s->GetVertexSet();
727 
728  for (int n = 0; n < vset->nverts; n++) {
729  fprintf(f, "vn %8.3f %8.3f %8.3f\n",
730  vset->nrm[n].x,
731  vset->nrm[n].y,
732  vset->nrm[n].z);
733  }
734  }
735 
736  s_iter.reset();
737 
738  // texture coordinates
739  fprintf(f, "\n# TEXTURE COORDINATES: %d\n", m->NumVerts());
740  while (++s_iter) {
741  Surface* s = s_iter.value();
742  VertexSet* vset = s->GetVertexSet();
743 
744  for (int n = 0; n < vset->nverts; n++) {
745  fprintf(f, "vt %8.3f %8.3f\n",
746  vset->tu[n], 1 - vset->tv[n]);
747  }
748  }
749 
750  s_iter.reset();
751 
752  // faces
753  Material* current_material = 0;
754 
755  fprintf(f, "\n# FACES: %d\n", m->NumPolys());
756  while (++s_iter) {
757  Surface* s = s_iter.value();
758 
759  for (int n = 0; n < s->NumPolys(); n++) {
760  const Poly* p = s->GetPolys() + n;
761  int nv = p->nverts;
762  Material* mtl = p->material;
763 
764  if (current_material != mtl) {
765  fprintf(f, "\n\nusemtl %s\n", mtl->name);
766  current_material = mtl;
767  }
768 
769  fprintf(f, "\nf ");
770  for (int v = nv-1; v >= 0; v--) {
771  fprintf(f, "%d/%d/%d ",
772  p->verts[v] + 1,
773  p->verts[v] + 1,
774  p->verts[v] + 1);
775  }
776  }
777  }
778 
779  fprintf(f, "\n\n\n# END OF FILE.\n");
780  fclose(f);
781 
782  return SaveWaveFrontMatLib(pathname, rootname, m);
783  }
784 
785  return false;
786 }
787 
788 // +--------------------------------------------------------------------+