(clock()-tt);
416 | statusBar()->showMessage(tr("Ray Tracing finished in %1 ms with %2 primitives.")
417 | .arg(mLastCostTime).arg(mEngine->getNumOfPrimitives()), TOOLTIP_STRETCH);
418 |
419 | updateInformationBar();
420 |
421 | mImgView->update();
422 | }
423 |
424 | void MainWindow::saveAsImageFile(const QString& fileName)
425 | {
426 | mImage.save(fileName);
427 | }
428 |
429 | void MainWindow::setResolution(int width, int height)
430 | {
431 | mResLabel->setText(tr("%1x%2").arg(width).arg(height));
432 | mImage = mImage.scaled(width, height);
433 | mEngine->setRenderTarget(mImage.width(), mImage.height(), &mImage);
434 | renderObj();
435 | }
436 |
437 | void MainWindow::newFrustumOrLight()
438 | {
439 | renderObj();
440 | }
441 |
442 | void MainWindow::about()
443 | {
444 | QMessageBox::about(this, tr("About Ray Tracer CPU"),
445 | tr("A RayTracer project writed by maxint, in April, 2010.
"
446 | "Email: lnychina@gmail.com
"
447 | "Blog: http://hi.baidu.com/maxint
"));
448 | }
449 |
450 | void MainWindow::rotateBy(double xAngle, double yAngle, double zAngle)
451 | {
452 | //static Mat22d matRot;
453 | //static Vec3 eyePos;
454 |
455 | //xRot += xAngle;
456 | //yRot += yAngle;
457 | //zRot += zAngle;
458 | //xRot %= 360;
459 | //yRot %= 360;
460 | //zRot %= 360;
461 |
462 | //eyePos[0] = mSpinEyeX->value();
463 | //eyePos[1] = mSpinEyeY->value();
464 | //eyePos[2] = mSpinEyeZ->value();
465 | //double len = eyePos.len();
466 | //eyePos[0] = 0;
467 | //eyePos[1] = 0;
468 | //eyePos[2] = len;
469 |
470 | //matRot.rotate(-zRot);
471 | //matRot.map(eyePos[0], eyePos[1], &eyePos[0], &eyePos[1]);
472 | //std::cout << eyePos.len() << std::endl;
473 | //matRot.rotate(yRot);
474 | //matRot.map(eyePos[0], eyePos[2], &eyePos[0], &eyePos[2]);
475 | //std::cout << eyePos.len() << std::endl;
476 | //matRot.rotate(-xRot);
477 | //matRot.map(eyePos[1], eyePos[2], &eyePos[1], &eyePos[2]);
478 | //std::cout << eyePos.len() << std::endl;
479 |
480 | //mSpinEyeX->setValue(eyePos[0]);
481 | //mSpinEyeY->setValue(eyePos[1]);
482 | //mSpinEyeZ->setValue(eyePos[2]);
483 |
484 | newFrustumOrLight();
485 | }
486 |
487 | bool MainWindow::eventFilter(QObject *obj, QEvent *e)
488 | {
489 | if (obj->isWidgetType() &&
490 | obj->objectName() == WIDGET_NAME)
491 | {
492 | QWidget *wid = static_cast(obj);
493 | if (e->type() == QEvent::Paint)
494 | {
495 | QPainter painter(wid);
496 | QRect rect(QPoint(0,0), wid->size());
497 | painter.drawImage(rect, mImage);
498 | e->accept();
499 | return true;
500 | }
501 | }
502 |
503 | return QMainWindow::eventFilter(obj, e);
504 | }
505 |
506 | QString MainWindow::strippedName(const QString& fullFileName)
507 | {
508 | return QFileInfo(fullFileName).fileName();
509 | }
510 |
511 | void MainWindow::updateInformationBar()
512 | {
513 | QString info = tr("");
516 | info += tr("");
517 | info += tr("| Primitives: | %1 |
").arg(mEngine->getNumOfPrimitives());
518 | info += tr("| Resolution: | %1 x %2 |
")
519 | .arg(mImage.width()).arg(mImage.height());
520 | info += tr("| Last cost time: | %1 ms |
").arg(mLastCostTime);
521 | info += tr("| Trace depth: | %1 |
").arg(mEngine->getTraceDepth());
522 | info += tr("| Regular Samples: | %1 |
").arg(mEngine->getRegularSampleSize());
523 | info += tr("
");
524 | mInfoLabel->setText(info);
525 | }
--------------------------------------------------------------------------------
/RayTracerCPU.vcproj:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
15 |
16 |
17 |
18 |
19 |
27 |
30 |
33 |
36 |
39 |
45 |
64 |
67 |
71 |
74 |
88 |
91 |
94 |
97 |
100 |
103 |
106 |
109 |
110 |
118 |
121 |
124 |
127 |
130 |
136 |
155 |
158 |
162 |
165 |
178 |
181 |
184 |
187 |
190 |
193 |
196 |
199 |
200 |
201 |
202 |
203 |
204 |
209 |
212 |
213 |
216 |
217 |
220 |
221 |
224 |
225 |
228 |
229 |
232 |
233 |
236 |
237 |
240 |
241 |
244 |
245 |
248 |
249 |
250 |
255 |
258 |
261 |
268 |
269 |
272 |
279 |
280 |
281 |
284 |
287 |
294 |
295 |
298 |
305 |
306 |
307 |
310 |
313 |
320 |
321 |
324 |
331 |
332 |
333 |
336 |
339 |
346 |
347 |
350 |
357 |
358 |
359 |
362 |
365 |
372 |
373 |
376 |
383 |
384 |
385 |
388 |
391 |
398 |
399 |
402 |
409 |
410 |
411 |
414 |
417 |
424 |
425 |
428 |
435 |
436 |
437 |
440 |
443 |
450 |
451 |
454 |
461 |
462 |
463 |
466 |
469 |
476 |
477 |
480 |
487 |
488 |
489 |
492 |
495 |
502 |
503 |
506 |
513 |
514 |
515 |
518 |
521 |
528 |
529 |
532 |
539 |
540 |
541 |
544 |
547 |
554 |
555 |
558 |
565 |
566 |
567 |
568 |
573 |
576 |
580 |
583 |
584 |
585 |
588 |
592 |
595 |
596 |
597 |
600 |
604 |
607 |
608 |
609 |
612 |
616 |
619 |
620 |
621 |
622 |
628 |
631 |
634 |
641 |
642 |
645 |
652 |
653 |
654 |
657 |
660 |
667 |
668 |
671 |
678 |
679 |
680 |
683 |
686 |
693 |
694 |
697 |
704 |
705 |
706 |
709 |
712 |
719 |
720 |
723 |
730 |
731 |
732 |
735 |
738 |
745 |
746 |
749 |
756 |
757 |
758 |
761 |
764 |
771 |
772 |
775 |
782 |
783 |
784 |
787 |
790 |
797 |
798 |
801 |
808 |
809 |
810 |
811 |
812 |
813 |
817 |
818 |
819 |
--------------------------------------------------------------------------------
/AccessObj.cpp:
--------------------------------------------------------------------------------
1 | #include "AccessObj.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #pragma warning(disable: 4996)
10 |
11 | using namespace std;
12 |
13 | #define objMax(a,b) (((a)>(b))?(a):(b))
14 | #define objMin(a,b) (((a)<(b))?(a):(b))
15 | #define objAbs(x) (((x)>0.f)?(x):(-x))
16 |
17 | #define Tri(x) (m_pModel->pTriangles[(x)])
18 |
19 | namespace trimeshVec
20 | {
21 | //////////////////////////////////////////////////////////////////////
22 | // Construction/Destruction
23 | //////////////////////////////////////////////////////////////////////
24 | CAccessObj::CAccessObj()
25 | {
26 | m_pModel = NULL;
27 | }
28 |
29 | CAccessObj::~CAccessObj()
30 | {
31 | Destory();
32 | }
33 |
34 | //////////////////////////////////////////////////////////////////////
35 | // Equal: compares two vectors and returns true if they are
36 | // equal (within a certain threshold) or false if not. An epsilon
37 | // that works fairly well is 0.000001.
38 | //
39 | // u - array of 3 GLfloats (float u[3])
40 | // v - array of 3 GLfloats (float v[3])
41 | //////////////////////////////////////////////////////////////////////
42 | bool CAccessObj::Equal(CPoint3D * u, CPoint3D * v, float epsilon)
43 | {
44 | if (objAbs(u->x - v->x) < epsilon &&
45 | objAbs(u->y - v->y) < epsilon &&
46 | objAbs(u->z - v->z) < epsilon)
47 | {
48 | return true;
49 | }
50 | return false;
51 | }
52 |
53 |
54 | //////////////////////////////////////////////////////////////////////
55 | // FindGroup: Find a group in the model
56 | //////////////////////////////////////////////////////////////////////
57 | COBJgroup * CAccessObj::FindGroup(char* name)
58 | {
59 | COBJgroup * group;
60 |
61 | assert(m_pModel);
62 |
63 | group = m_pModel->pGroups;
64 | while(group)
65 | {
66 | if (!strcmp(name, group->name))
67 | break;
68 | group = group->next;
69 | }
70 |
71 | return group;
72 | }
73 |
74 | //////////////////////////////////////////////////////////////////////
75 | // AddGroup: Add a group to the model
76 | //////////////////////////////////////////////////////////////////////
77 | COBJgroup * CAccessObj::AddGroup(char* name)
78 | {
79 | COBJgroup* group;
80 |
81 | group = FindGroup(name);
82 | if (!group)
83 | {
84 | group = new COBJgroup;
85 | sprintf_s(group->name, 256, "%s", name);
86 | group->nTriangles = 0;
87 | group->pTriangles = NULL;
88 | group->next = m_pModel->pGroups;
89 | m_pModel->pGroups = group;
90 | m_pModel->nGroups++;
91 | }
92 |
93 | return group;
94 | }
95 |
96 | //////////////////////////////////////////////////////////////////////
97 | // DirName: return the directory given a path
98 | //
99 | // path - filesystem path
100 | //
101 | // NOTE: the return value should be free'd.
102 | //////////////////////////////////////////////////////////////////////
103 | char * CAccessObj::DirName(char* path)
104 | {
105 | static char dir[256];
106 | char *s;
107 |
108 | sprintf_s(dir,256, "%s", path);
109 |
110 | s = strrchr(dir, '\\'); // for windows format
111 | if (s == NULL)
112 | s = strrchr(dir, '/');
113 |
114 | if (s)
115 | s[1] = '\0';
116 | else
117 | dir[0] = '\0';
118 |
119 | return dir;
120 | }
121 |
122 | //////////////////////////////////////////////////////////////////////
123 | // FindGroup: Find a material in the model
124 | //////////////////////////////////////////////////////////////////////
125 | unsigned int CAccessObj::FindMaterial(char* name)
126 | {
127 | unsigned int i;
128 | bool bFound = false;
129 |
130 | // XXX doing a linear search on a string key'd list is pretty lame, but it works and is fast enough for now.
131 | for (i = 0; i < m_pModel->nMaterials; i++)
132 | {
133 | if (!strcmp(m_pModel->pMaterials[i].name, name))
134 | {
135 | bFound = true;
136 | break;
137 | }
138 | }
139 |
140 | // didn't find the name, so print a warning and return the default material (0).
141 | if (!bFound)
142 | {
143 | printf("FindMaterial(): can't find material \"%s\".\n", name);
144 | i = 0;
145 | }
146 |
147 | return i;
148 | }
149 |
150 | //////////////////////////////////////////////////////////////////////
151 | // ReadMTL: read a wavefront material library file
152 | //
153 | // model - properly initialized COBJmodel structure
154 | // name - name of the material library
155 | //////////////////////////////////////////////////////////////////////
156 | void CAccessObj::ReadMTL(char* name)
157 | {
158 | FILE* file;
159 | char dir[256];
160 | char filename[256];
161 | char buf[128];
162 | unsigned int nMaterials, i;
163 |
164 |
165 | char *s;
166 |
167 | sprintf(dir, "%s", DirName(m_pModel->pathname));
168 | strcpy(filename, dir);
169 | strcat(filename, name);
170 |
171 | file = fopen(filename, "r");
172 | if (!file)
173 | {
174 | fprintf(stderr, "ReadMTL() failed: can't open material file \"%s\".\n", filename);
175 | exit(1);
176 | }
177 |
178 | // count the number of materials in the file
179 | nMaterials = 1;
180 | while(fscanf(file, "%s", buf) != EOF)
181 | {
182 | switch(buf[0])
183 | {
184 | case '#': /* comment */
185 | /* eat up rest of line */
186 | fgets(buf, sizeof(buf), file);
187 | break;
188 | case 'n': /* newmtl */
189 | fgets(buf, sizeof(buf), file);
190 | nMaterials++;
191 | sscanf(buf, "%s %s", buf, buf);
192 | break;
193 | default:
194 | /* eat up rest of line */
195 | fgets(buf, sizeof(buf), file);
196 | break;
197 | }
198 | }
199 |
200 | rewind(file);
201 |
202 | m_pModel->pMaterials = new COBJmaterial [nMaterials];
203 | m_pModel->nMaterials = nMaterials;
204 |
205 | // set the default material
206 | for (i = 0; i < nMaterials; i++) {
207 | m_pModel->pMaterials[i].name[0] = '\0';
208 | m_pModel->pMaterials[i].shininess[0] = 32.0f;
209 | m_pModel->pMaterials[i].diffuse[0] = 0.8f;
210 | m_pModel->pMaterials[i].diffuse[1] = 0.8f;
211 | m_pModel->pMaterials[i].diffuse[2] = 0.8f;
212 | m_pModel->pMaterials[i].diffuse[3] = 1.0f;
213 | m_pModel->pMaterials[i].ambient[0] = 0.2f;
214 | m_pModel->pMaterials[i].ambient[1] = 0.2f;
215 | m_pModel->pMaterials[i].ambient[2] = 0.2f;
216 | m_pModel->pMaterials[i].ambient[3] = 1.0f;
217 | m_pModel->pMaterials[i].specular[0] = 0.0f;
218 | m_pModel->pMaterials[i].specular[1] = 0.0f;
219 | m_pModel->pMaterials[i].specular[2] = 0.0f;
220 | m_pModel->pMaterials[i].specular[3] = 1.0f;
221 | m_pModel->pMaterials[i].emissive[0] = 0.0f;
222 | m_pModel->pMaterials[i].emissive[1] = 0.0f;
223 | m_pModel->pMaterials[i].emissive[2] = 0.0f;
224 | m_pModel->pMaterials[i].emissive[3] = 1.0f;
225 | }
226 | sprintf(m_pModel->pMaterials[0].name, "default");
227 |
228 | // now, read in the data
229 | nMaterials = 0;
230 | while(fscanf(file, "%s", buf) != EOF)
231 | {
232 | switch(buf[0])
233 | {
234 | case '#': /* comment */
235 | /* eat up rest of line */
236 | fgets(buf, sizeof(buf), file);
237 | break;
238 | case 'n': /* newmtl */
239 | fgets(buf, sizeof(buf), file);
240 | sscanf(buf, "%s %s", buf, buf);
241 | nMaterials++;
242 | sprintf(m_pModel->pMaterials[nMaterials].name, "%s", buf);
243 | break;
244 | case 'N':
245 | fscanf(file, "%f", &m_pModel->pMaterials[nMaterials].shininess);
246 | /* wavefront shininess is from [0, 1000], so scale for OpenGL */
247 | m_pModel->pMaterials[nMaterials].shininess[0] /= 1000.0f;
248 | m_pModel->pMaterials[nMaterials].shininess[0] *= 128.0f;
249 | break;
250 | case 'K':
251 | switch(buf[1])
252 | {
253 | case 'd':
254 | fscanf(file, "%f %f %f",
255 | &m_pModel->pMaterials[nMaterials].diffuse[0],
256 | &m_pModel->pMaterials[nMaterials].diffuse[1],
257 | &m_pModel->pMaterials[nMaterials].diffuse[2]);
258 | break;
259 | case 's':
260 | fscanf(file, "%f %f %f",
261 | &m_pModel->pMaterials[nMaterials].specular[0],
262 | &m_pModel->pMaterials[nMaterials].specular[1],
263 | &m_pModel->pMaterials[nMaterials].specular[2]);
264 | break;
265 | case 'a':
266 | fscanf(file, "%f %f %f",
267 | &m_pModel->pMaterials[nMaterials].ambient[0],
268 | &m_pModel->pMaterials[nMaterials].ambient[1],
269 | &m_pModel->pMaterials[nMaterials].ambient[2]);
270 | break;
271 | default:
272 | /* eat up rest of line */
273 | fgets(buf, sizeof(buf), file);
274 | break;
275 | }
276 | break;
277 | case 'm':
278 | fscanf(file, "%s", filename);
279 | s = strrchr(filename, ':');
280 | if (!s)
281 | sprintf(m_pModel->pMaterials[nMaterials].sTexture, "%s%s", dir, filename);
282 | else
283 | sprintf(m_pModel->pMaterials[nMaterials].sTexture, "%s", filename);
284 | break;
285 | default:
286 | /* eat up rest of line */
287 | fgets(buf, sizeof(buf), file);
288 | break;
289 | }
290 | }
291 |
292 | fclose (file);
293 | }
294 |
295 | //////////////////////////////////////////////////////////////////////
296 | // WriteMTL: write a wavefront material library file
297 | //
298 | // model - properly initialized COBJmodel structure
299 | // modelpath - pathname of the model being written
300 | // mtllibname - name of the material library to be written
301 | //////////////////////////////////////////////////////////////////////
302 | void CAccessObj::WriteMTL(char* modelpath, char* mtllibname)
303 | {
304 | FILE* file;
305 | char* dir;
306 | char* filename;
307 | COBJmaterial* material;
308 | unsigned int i;
309 |
310 | dir = DirName(modelpath);
311 | filename = new char [(strlen(dir)+strlen(mtllibname))];
312 | strcpy(filename, dir);
313 | strcat(filename, mtllibname);
314 | free(dir);
315 |
316 | /* open the file */
317 | file = fopen(filename, "w");
318 | if (!file)
319 | {
320 | fprintf(stderr, "WriteMTL() failed: can't open file \"%s\".\n", filename);
321 | exit(1);
322 | }
323 | delete [] filename;
324 |
325 | /* spit out a header */
326 | fprintf(file, "# \n");
327 | fprintf(file, "# Wavefront MTL generated by OBJ library\n");
328 | fprintf(file, "# \n");
329 | fprintf(file, "# OBJ library\n");
330 | fprintf(file, "# Nate Robins\n");
331 | fprintf(file, "# ndr@pobox.com\n");
332 | fprintf(file, "# http://www.pobox.com/~ndr\n");
333 | fprintf(file, "# \n\n");
334 |
335 | for (i = 0; i < m_pModel->nMaterials; i++)
336 | {
337 | material = &m_pModel->pMaterials[i];
338 | fprintf(file, "newmtl %s\n", material->name);
339 | fprintf(file, "Ka %f %f %f\n",
340 | material->ambient[0], material->ambient[1], material->ambient[2]);
341 | fprintf(file, "Kd %f %f %f\n",
342 | material->diffuse[0], material->diffuse[1], material->diffuse[2]);
343 | fprintf(file, "Ks %f %f %f\n",
344 | material->specular[0],material->specular[1],material->specular[2]);
345 | fprintf(file, "Ns %f\n", material->shininess[0] / 128.0 * 1000.0);
346 | fprintf(file, "\n");
347 | }
348 | }
349 |
350 | //////////////////////////////////////////////////////////////////////
351 | // FirstPass: first pass at a Wavefront OBJ file that gets all the
352 | // statistics of the model (such as #vertices, #normals, etc)
353 | //
354 | // model - properly initialized COBJmodel structure
355 | // file - (fopen'd) file descriptor
356 | //////////////////////////////////////////////////////////////////////
357 | bool CAccessObj::FirstPass(FILE* file)
358 | {
359 | unsigned int nVertices; /* number of vertices in m_pModel */
360 | unsigned int nNormals; /* number of normals in m_pModel */
361 | unsigned int nTexCoords; /* number of texcoords in m_pModel */
362 | unsigned int nTriangles; /* number of triangles in m_pModel */
363 | COBJgroup* group; /* current group */
364 | unsigned v, n, t;
365 | char buf[129];
366 |
367 | /* make a default group */
368 | group = AddGroup("default");
369 |
370 | nVertices = nNormals = nTexCoords = nTriangles = 0;
371 | while(fscanf(file, "%128s", buf, 129) != EOF)
372 | {
373 | switch(buf[0])
374 | {
375 | case '#': /* comment */
376 | /* eat up rest of line */
377 | fgets(buf, sizeof(buf), file);
378 | break;
379 | case 'v': /* v, vn, vt */
380 | switch(buf[1])
381 | {
382 | case '\0': /* vertex */
383 | /* eat up rest of line */
384 | fgets(buf, sizeof(buf), file);
385 | nVertices++;
386 | break;
387 | case 'n': /* normal */
388 | /* eat up rest of line */
389 | fgets(buf, sizeof(buf), file);
390 | nNormals++;
391 | break;
392 | case 't': /* texcoord */
393 | /* eat up rest of line */
394 | fgets(buf, sizeof(buf), file);
395 | nTexCoords++;
396 | break;
397 | default:
398 | printf("FirstPass(): Unknown token \"%s\".\n", buf);
399 | exit(1);
400 | break;
401 | }
402 | break;
403 |
404 | case 'm':
405 | fgets(buf, sizeof(buf), file);
406 | sscanf(buf, "%s %s", buf, buf);
407 | sprintf(m_pModel->mtllibname, "%s", buf);
408 | ReadMTL(buf);
409 | break;
410 |
411 | case 'u':
412 | /* eat up rest of line */
413 | fgets(buf, sizeof(buf), file);
414 | break;
415 | case 'g': /* group */
416 | /* eat up rest of line */
417 | fgets(buf, sizeof(buf), file);
418 | buf[strlen(buf)-1] = '\0'; /* nuke '\n' */
419 | group = AddGroup(buf);
420 | break;
421 | case 'f': /* face */
422 | v = n = t = 0;
423 | fscanf(file, "%128s", buf, 129);
424 | /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
425 | if (strstr(buf, "//"))
426 | {
427 | /* v//n */
428 | sscanf(buf, "%d//%d", &v, &n);
429 | fscanf(file, "%d//%d", &v, &n);
430 | fscanf(file, "%d//%d", &v, &n);
431 | nTriangles++;
432 | group->nTriangles++;
433 | while(fscanf(file, "%d//%d", &v, &n) > 0)
434 | {
435 | nTriangles++;
436 | group->nTriangles++;
437 | }
438 | }
439 | else if (sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3)
440 | {
441 | /* v/t/n */
442 | fscanf(file, "%d/%d/%d", &v, &t, &n);
443 | fscanf(file, "%d/%d/%d", &v, &t, &n);
444 | nTriangles++;
445 | group->nTriangles++;
446 | while(fscanf(file, "%d/%d/%d", &v, &t, &n) > 0)
447 | {
448 | nTriangles++;
449 | group->nTriangles++;
450 | }
451 | }
452 | else if (sscanf(buf, "%d/%d", &v, &t) == 2)
453 | {
454 | /* v/t */
455 | fscanf(file, "%d/%d", &v, &t);
456 | fscanf(file, "%d/%d", &v, &t);
457 | nTriangles++;
458 | group->nTriangles++;
459 | while(fscanf(file, "%d/%d", &v, &t) > 0)
460 | {
461 | nTriangles++;
462 | group->nTriangles++;
463 | }
464 | }
465 | else
466 | {
467 | /* v */
468 | fscanf(file, "%d", &v);
469 | fscanf(file, "%d", &v);
470 | nTriangles++;
471 | group->nTriangles++;
472 | while(fscanf(file, "%d", &v) > 0)
473 | {
474 | nTriangles++;
475 | group->nTriangles++;
476 | }
477 | }
478 | break;
479 |
480 | default:
481 | /* eat up rest of line */
482 | if (isalpha(buf[0]))
483 | {
484 | fgets(buf, sizeof(buf), file);
485 | }
486 | else
487 | return false;
488 | break;
489 | }
490 | }
491 |
492 | /* set the stats in the m_pModel structure */
493 | m_pModel->nVertices = nVertices;
494 | m_pModel->nNormals = nNormals;
495 | m_pModel->nTexCoords = nTexCoords;
496 | m_pModel->nTriangles = nTriangles;
497 |
498 | /* allocate memory for the triangles in each group */
499 | group = m_pModel->pGroups;
500 | while(group)
501 | {
502 | if (group->nTriangles > 0)
503 | group->pTriangles = new unsigned int [group->nTriangles];
504 | group->nTriangles = 0;
505 | group = group->next;
506 | }
507 | return true;
508 | }
509 |
510 | //////////////////////////////////////////////////////////////////////
511 | // SecondPass: second pass at a Wavefront OBJ file that gets all
512 | // the data.
513 | //
514 | // model - properly initialized COBJmodel structure
515 | // file - (fopen'd) file descriptor
516 | //////////////////////////////////////////////////////////////////////
517 | void CAccessObj::SecondPass(FILE* file)
518 | {
519 | unsigned int nVertices; /* number of vertices in m_pModel */
520 | unsigned int nNormals; /* number of normals in m_pModel */
521 | unsigned int nTexCoords; /* number of texcoords in m_pModel */
522 | unsigned int nTriangles; /* number of triangles in m_pModel */
523 | CPoint3D * vertices; /* array of vertices */
524 | CPoint3D * normals; /* array of normals */
525 | CPoint3D * texcoords; /* array of texture coordinates */
526 | COBJgroup * group; /* current group pointer */
527 | unsigned int material; /* current material */
528 | unsigned int v, n, t;
529 | char buf[128];
530 |
531 | /* set the pointer shortcuts */
532 | vertices = m_pModel->vpVertices;
533 | normals = m_pModel->vpNormals;
534 | texcoords = m_pModel->vpTexCoords;
535 | group = m_pModel->pGroups;
536 |
537 | /* on the second pass through the file, read all the data into the
538 | allocated arrays */
539 | nVertices = nNormals = nTexCoords = 1;
540 | nTriangles = 0;
541 | material = 0;
542 |
543 | int nNormalAdd = 0;
544 | int nNormalCount = 0;
545 |
546 | while(fscanf(file, "%s", buf) != EOF)
547 | {
548 | switch(buf[0])
549 | {
550 | case '#': /* comment */
551 | /* eat up rest of line */
552 | fgets(buf, sizeof(buf), file);
553 | break;
554 | case 'v': /* v, vn, vt */
555 | switch(buf[1])
556 | {
557 | case '\0': /* vertex */
558 | fscanf(file, "%f %f %f",
559 | &vertices[nVertices].x,
560 | &vertices[nVertices].y,
561 | &vertices[nVertices].z);
562 | nVertices++;
563 | break;
564 | case 'n': /* normal */
565 | fscanf(file, "%f %f %f",
566 | &normals[nNormals].x,
567 | &normals[nNormals].y,
568 | &normals[nNormals].z);
569 | nNormals++;
570 | nNormalCount ++;
571 | break;
572 | case 't': /* texcoord */
573 | fscanf(file, "%f %f",
574 | &texcoords[nTexCoords].x,
575 | &texcoords[nTexCoords].y);
576 | nTexCoords++;
577 | break;
578 | }
579 | break;
580 | case 'u':
581 | fgets(buf, sizeof(buf), file);
582 | sscanf(buf, "%s %s", buf, buf);
583 | group->material = material = FindMaterial(buf);
584 |
585 | break;
586 | case 'g': /* group */
587 | /* eat up rest of line */
588 | fgets(buf, sizeof(buf), file);
589 | buf[strlen(buf)-1] = '\0'; /* nuke '\n' */
590 | group = FindGroup(buf);
591 | group->material = material;
592 | nNormalAdd += nNormalCount;
593 | nNormalCount = 0;
594 | break;
595 | case 'f': /* face */
596 | v = n = t = 0;
597 | fscanf(file, "%s", buf);
598 | /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
599 | Tri(nTriangles).mindex = material;
600 | if (strstr(buf, "//"))
601 | {
602 | /* v//n */
603 | sscanf(buf, "%d//%d", &v, &n);
604 | Tri(nTriangles).vindices[0] = v;
605 | Tri(nTriangles).nindices[0] = n;// + nNormalAdd;
606 | fscanf(file, "%d//%d", &v, &n);
607 | Tri(nTriangles).vindices[1] = v;
608 | Tri(nTriangles).nindices[1] = n;// + nNormalAdd;
609 | fscanf(file, "%d//%d", &v, &n);
610 | Tri(nTriangles).vindices[2] = v;
611 | Tri(nTriangles).nindices[2] = n;// + nNormalAdd;
612 | group->pTriangles[group->nTriangles++] = nTriangles;
613 | nTriangles++;
614 | while(fscanf(file, "%d//%d", &v, &n) > 0)
615 | {
616 | Tri(nTriangles).vindices[0] = Tri(nTriangles-1).vindices[0];
617 | Tri(nTriangles).nindices[0] = Tri(nTriangles-1).nindices[0];
618 | Tri(nTriangles).vindices[1] = Tri(nTriangles-1).vindices[2];
619 | Tri(nTriangles).nindices[1] = Tri(nTriangles-1).nindices[2];
620 | Tri(nTriangles).vindices[2] = v;
621 | Tri(nTriangles).nindices[2] = n;// + nNormalAdd;
622 | Tri(nTriangles).mindex = material;
623 | group->pTriangles[group->nTriangles++] = nTriangles;
624 | nTriangles++;
625 | }
626 | }
627 | else if (sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3)
628 | {
629 | /* v/t/n */
630 | Tri(nTriangles).vindices[0] = v;
631 | Tri(nTriangles).tindices[0] = t;
632 | Tri(nTriangles).nindices[0] = n;// + nNormalAdd;
633 | fscanf(file, "%d/%d/%d", &v, &t, &n);
634 | Tri(nTriangles).vindices[1] = v;
635 | Tri(nTriangles).tindices[1] = t;
636 | Tri(nTriangles).nindices[1] = n;// + nNormalAdd;
637 | fscanf(file, "%d/%d/%d", &v, &t, &n);
638 | Tri(nTriangles).vindices[2] = v;
639 | Tri(nTriangles).tindices[2] = t;
640 | Tri(nTriangles).nindices[2] = n;// + nNormalAdd;
641 | group->pTriangles[group->nTriangles++] = nTriangles;
642 | nTriangles++;
643 | while(fscanf(file, "%d/%d/%d", &v, &t, &n) > 0)
644 | {
645 | Tri(nTriangles).vindices[0] = Tri(nTriangles-1).vindices[0];
646 | Tri(nTriangles).tindices[0] = Tri(nTriangles-1).tindices[0];
647 | Tri(nTriangles).nindices[0] = Tri(nTriangles-1).nindices[0];
648 | Tri(nTriangles).vindices[1] = Tri(nTriangles-1).vindices[2];
649 | Tri(nTriangles).tindices[1] = Tri(nTriangles-1).tindices[2];
650 | Tri(nTriangles).nindices[1] = Tri(nTriangles-1).nindices[2];
651 | Tri(nTriangles).vindices[2] = v;
652 | Tri(nTriangles).tindices[2] = t;
653 | Tri(nTriangles).nindices[2] = n;// + nNormalAdd;
654 | Tri(nTriangles).mindex = material;
655 | group->pTriangles[group->nTriangles++] = nTriangles;
656 | nTriangles++;
657 | }
658 | }
659 | else if (sscanf(buf, "%d/%d", &v, &t) == 2)
660 | {
661 | /* v/t */
662 | Tri(nTriangles).vindices[0] = v;
663 | Tri(nTriangles).tindices[0] = t;
664 | fscanf(file, "%d/%d", &v, &t);
665 | Tri(nTriangles).vindices[1] = v;
666 | Tri(nTriangles).tindices[1] = t;
667 | fscanf(file, "%d/%d", &v, &t);
668 | Tri(nTriangles).vindices[2] = v;
669 | Tri(nTriangles).tindices[2] = t;
670 | group->pTriangles[group->nTriangles++] = nTriangles;
671 | nTriangles++;
672 | while(fscanf(file, "%d/%d", &v, &t) > 0)
673 | {
674 | Tri(nTriangles).vindices[0] = Tri(nTriangles-1).vindices[0];
675 | Tri(nTriangles).tindices[0] = Tri(nTriangles-1).tindices[0];
676 | Tri(nTriangles).vindices[1] = Tri(nTriangles-1).vindices[2];
677 | Tri(nTriangles).tindices[1] = Tri(nTriangles-1).tindices[2];
678 | Tri(nTriangles).vindices[2] = v;
679 | Tri(nTriangles).tindices[2] = t;
680 | Tri(nTriangles).mindex = material;
681 | group->pTriangles[group->nTriangles++] = nTriangles;
682 | nTriangles++;
683 | }
684 | }
685 | else
686 | {
687 | /* v */
688 | sscanf(buf, "%d", &v);
689 | Tri(nTriangles).vindices[0] = v;
690 | fscanf(file, "%d", &v);
691 | Tri(nTriangles).vindices[1] = v;
692 | fscanf(file, "%d", &v);
693 | Tri(nTriangles).vindices[2] = v;
694 | group->pTriangles[group->nTriangles++] = nTriangles;
695 | nTriangles++;
696 | while(fscanf(file, "%d", &v) > 0)
697 | {
698 | Tri(nTriangles).vindices[0] = Tri(nTriangles-1).vindices[0];
699 | Tri(nTriangles).vindices[1] = Tri(nTriangles-1).vindices[2];
700 | Tri(nTriangles).vindices[2] = v;
701 | group->pTriangles[group->nTriangles++] = nTriangles;
702 | Tri(nTriangles).mindex = material;
703 | nTriangles++;
704 | }
705 | }
706 | break;
707 |
708 | default:
709 | /* eat up rest of line */
710 | fgets(buf, sizeof(buf), file);
711 | break;
712 | }
713 | }
714 | }
715 |
716 | //////////////////////////////////////////////////////////////////////
717 | // Dimensions: Calculates the dimensions (width, height, depth) of
718 | // a model.
719 | //
720 | // model - initialized COBJmodel structure
721 | // dimensions - array of 3 GLfloats (float dimensions[3])
722 | //////////////////////////////////////////////////////////////////////
723 | void CAccessObj::Dimensions(float* dimensions)
724 | {
725 | unsigned int i;
726 | CPoint3D vMax, vMin;
727 |
728 | assert(m_pModel);
729 | assert(m_pModel->vpVertices);
730 | assert(dimensions);
731 |
732 | /* get the max/mins */
733 | vMax = vMin = m_pModel->vpVertices[0];
734 | for (i = 1; i <= m_pModel->nVertices; i++)
735 | {
736 | if (vMax.x < m_pModel->vpVertices[i].x)
737 | vMax.x = m_pModel->vpVertices[i].x;
738 | if (vMin.x > m_pModel->vpVertices[i].x)
739 | vMin.x = m_pModel->vpVertices[i].x;
740 |
741 | if (vMax.y < m_pModel->vpVertices[i].y)
742 | vMax.y = m_pModel->vpVertices[i].y;
743 | if (vMin.y > m_pModel->vpVertices[i].y)
744 | vMin.y = m_pModel->vpVertices[i].y;
745 |
746 | if (vMax.z < m_pModel->vpVertices[i].z)
747 | vMax.z = m_pModel->vpVertices[i].z;
748 | if (vMin.z > m_pModel->vpVertices[i].z)
749 | vMin.z = m_pModel->vpVertices[i].z;
750 | }
751 |
752 | /* calculate m_pModel width, height, and depth */
753 | dimensions[0] = vMax.x-vMin.x;
754 | dimensions[1] = vMax.y-vMin.y;
755 | dimensions[2] = vMax.z-vMin.z;
756 | }
757 |
758 | //////////////////////////////////////////////////////////////////////
759 | // Scale: Scales a model by a given amount.
760 | //
761 | // model - properly initialized COBJmodel structure
762 | // scale - scalefactor (0.5 = half as large, 2.0 = twice as large)
763 | //////////////////////////////////////////////////////////////////////
764 | void CAccessObj::Scale(float scale)
765 | {
766 | unsigned int i;
767 |
768 | for (i = 1; i <= m_pModel->nVertices; i++)
769 | m_pModel->vpVertices[i] = m_pModel->vpVertices[i] * scale;
770 | }
771 |
772 | //////////////////////////////////////////////////////////////////////
773 | // ReverseWinding: Reverse the polygon winding for all polygons in
774 | // this model. Default winding is counter-clockwise. Also changes
775 | // the direction of the normals.
776 | //
777 | // model - properly initialized COBJmodel structure
778 | //////////////////////////////////////////////////////////////////////
779 | void CAccessObj::ReverseWinding()
780 | {
781 | unsigned int i, swap;
782 |
783 | assert(m_pModel);
784 |
785 | for (i = 0; i < m_pModel->nTriangles; i++)
786 | {
787 | swap = Tri(i).vindices[0];
788 | Tri(i).vindices[0] = Tri(i).vindices[2];
789 | Tri(i).vindices[2] = swap;
790 |
791 | if (m_pModel->nNormals)
792 | {
793 | swap = Tri(i).nindices[0];
794 | Tri(i).nindices[0] = Tri(i).nindices[2];
795 | Tri(i).nindices[2] = swap;
796 | }
797 | }
798 |
799 | /* reverse facet normals */
800 | for (i = 1; i <= m_pModel->nFacetnorms; i++)
801 | m_pModel->vpFacetNorms[i] = m_pModel->vpFacetNorms[i]*(-1);
802 |
803 | /* reverse vertex normals */
804 | for (i = 1; i <= m_pModel->nNormals; i++)
805 | m_pModel->vpNormals[i] = m_pModel->vpNormals[i]*(-1);
806 | }
807 |
808 | //////////////////////////////////////////////////////////////////////
809 | // FacetNormals: Generates facet normals for a model (by taking the
810 | // cross product of the two vectors derived from the sides of each
811 | // triangle). Assumes a counter-clockwise winding.
812 | //
813 | // model - initialized COBJmodel structure
814 | //////////////////////////////////////////////////////////////////////
815 | void CAccessObj::FacetNormals()
816 | {
817 | unsigned int i;
818 | CPoint3D u, v;
819 |
820 | assert(m_pModel);
821 | assert(m_pModel->vpVertices);
822 |
823 | /* clobber any old facetnormals */
824 | SAFE_DELETE_ARRAY(m_pModel->vpFacetNorms);
825 |
826 | /* allocate memory for the new facet normals */
827 | m_pModel->nFacetnorms = m_pModel->nTriangles;
828 | m_pModel->vpFacetNorms = new CPoint3D [m_pModel->nFacetnorms + 1];
829 |
830 | for (i = 0; i < m_pModel->nTriangles; i++)
831 | {
832 | m_pModel->pTriangles[i].findex = i+1;
833 |
834 | u = m_pModel->vpVertices[Tri(i).vindices[1]] - m_pModel->vpVertices[Tri(i).vindices[0]];
835 | v = m_pModel->vpVertices[Tri(i).vindices[2]] - m_pModel->vpVertices[Tri(i).vindices[0]];
836 |
837 | m_pModel->vpFacetNorms[i+1] = u * v;
838 | m_pModel->vpFacetNorms[i+1].unify();
839 | }
840 | }
841 |
842 | //////////////////////////////////////////////////////////////////////
843 | // VertexNormals: Generates smooth vertex normals for a model.
844 | // First builds a list of all the triangles each vertex is in. Then
845 | // loops through each vertex in the the list averaging all the facet
846 | // normals of the triangles each vertex is in. Finally, sets the
847 | // normal idxInTri in the triangle for the vertex to the generated smooth
848 | // normal. If the dot product of a facet normal and the facet normal
849 | // associated with the first triangle in the list of triangles the
850 | // current vertex is in is greater than the cosine of the angle
851 | // parameter to the function, that facet normal is not added into the
852 | // average normal calculation and the corresponding vertex is given
853 | // the facet normal. This tends to preserve hard edges. The angle to
854 | // use depends on the model, but 90 degrees is usually a good start.
855 | //
856 | // model - initialized COBJmodel structure
857 | // angle - maximum angle (in degrees) to smooth across
858 | //////////////////////////////////////////////////////////////////////
859 | void CAccessObj::VertexNormals(float angle)
860 | {
861 | OBJnode* node;
862 | typedef list NodeList;
863 | typedef NodeList::iterator NodeListItor;
864 | typedef vector ListArray;
865 | typedef ListArray::iterator ListArrItor;
866 | ListArray members;
867 | unsigned int nNormals;
868 | CPoint3D t_vAverage;
869 | float dot, cos_angle;
870 | unsigned int i;
871 |
872 | assert(m_pModel);
873 | assert(m_pModel->vpFacetNorms);
874 |
875 | /* calculate the cosine of the angle (in degrees) */
876 | cos_angle = (float)cos(angle * 3.14159265 / 180.0);
877 |
878 | /* nuke any previous normals */
879 | SAFE_DELETE_ARRAY(m_pModel->vpNormals);
880 |
881 | /* allocate space for new normals */
882 | m_pModel->nNormals = m_pModel->nTriangles * 3; /* 3 normals per triangle */
883 | m_pModel->vpNormals = new CPoint3D [m_pModel->nNormals+1];
884 |
885 | /* allocate a structure that will hold a linked list of triangle
886 | indices for each vertex */
887 | members.resize(m_pModel->nVertices + 1);
888 |
889 | /* for every triangle, create a node for each vertex in it */
890 | for (i = 0; i < m_pModel->nTriangles; i++)
891 | {
892 | node = new OBJnode;
893 | node->triIdx = i;
894 | node->idxInTri = 0;
895 | members[Tri(i).vindices[0]].push_back(node);
896 | node = new OBJnode;
897 | node->triIdx = i;
898 | node->idxInTri = 1;
899 | members[Tri(i).vindices[1]].push_back(node);
900 | node = new OBJnode;
901 | node->triIdx = i;
902 | node->idxInTri = 2;
903 | members[Tri(i).vindices[2]].push_back(node);
904 | }
905 |
906 | /* calculate the average normal for each vertex */
907 | nNormals = 1;
908 | for (i = 1; i <= m_pModel->nVertices; i++)
909 | {
910 | if (members[i].empty())
911 | {
912 | continue;
913 | #ifdef _DEBUG
914 | //fprintf(stderr, "VertexNormals(): vertex w/o a triangle\n");
915 | #endif // _DEBUG
916 | }
917 |
918 | // calculate an average normal for this vertex by averaging the
919 | // facet normal of every triangle this vertex is in
920 | NodeListItor itl = members[i].begin();
921 | NodeListItor itl_end = members[i].end();
922 |
923 | for (; itl!=itl_end; ++itl)
924 | {
925 | /* only average if the dot product of the angle between the two
926 | facet normals is greater than the cosine of the threshold
927 | angle -- or, said another way, the angle between the two
928 | facet normals is less than (or equal to) the threshold angle */
929 | node = *itl;
930 | t_vAverage = CPoint3D(0, 0, 0);
931 | NodeListItor itl_t = members[i].begin();
932 | for (; itl_t!=itl_end; ++itl_t)
933 | {
934 | OBJnode* node_t = *itl_t;
935 | dot = m_pModel->vpFacetNorms[Tri(node->triIdx).findex] &
936 | m_pModel->vpFacetNorms[Tri(node_t->triIdx).findex];
937 | if (dot > cos_angle)
938 | {
939 | t_vAverage += m_pModel->vpFacetNorms[Tri(node_t->triIdx).findex];
940 | /* we averaged at least one normal! */
941 | }
942 | }
943 | /* normalize the averaged normal */
944 | t_vAverage.unify();
945 |
946 | /* add the normal to the vertex normals list */
947 | m_pModel->vpNormals[nNormals] = t_vAverage;
948 | Tri(node->triIdx).nindices[node->idxInTri] = nNormals;
949 | ++nNormals;
950 | }
951 | }
952 |
953 | /* free the member information */
954 | ListArrItor ita = members.begin();
955 | ListArrItor ita_end = members.end();
956 | for (; ita!=ita_end; ++ita)
957 | {
958 | NodeListItor itl = ita->begin();
959 | NodeListItor itl_end = ita->end();
960 | for (; itl!=itl_end; ++itl)
961 | {
962 | SAFE_DELETE(*itl);
963 | }
964 | }
965 | }
966 |
967 |
968 |
969 | //////////////////////////////////////////////////////////////////////
970 | // LinearTexture: Generates texture coordinates according to a
971 | // linear projection of the texture map. It generates these by
972 | // linearly mapping the vertices onto a square.
973 | //
974 | // model - pointer to initialized COBJmodel structure
975 | //////////////////////////////////////////////////////////////////////
976 | void CAccessObj::LinearTexture()
977 | {
978 | COBJgroup *group;
979 | float dimensions[3];
980 | float x, y, scalefactor;
981 | unsigned int i;
982 |
983 | assert(m_pModel);
984 |
985 | if (m_pModel->vpTexCoords)
986 | free(m_pModel->vpTexCoords);
987 | m_pModel->nTexCoords = m_pModel->nVertices;
988 | m_pModel->vpTexCoords = new CPoint3D [m_pModel->nTexCoords+1];
989 |
990 | Dimensions(dimensions);
991 | scalefactor = 2.0f / objAbs(objMax(objMax(dimensions[0], dimensions[1]), dimensions[2]));
992 |
993 | /* do the calculations */
994 | for(i = 1; i <= m_pModel->nVertices; i++)
995 | {
996 | x = m_pModel->vpVertices[i].x * scalefactor;
997 | y = m_pModel->vpVertices[i].z * scalefactor;
998 | m_pModel->vpTexCoords[i] = CPoint3D((x + 1.0) / 2.0, (y + 1.0) / 2.0, 0.0);
999 | }
1000 |
1001 | /* go through and put texture coordinate indices in all the triangles */
1002 | group = m_pModel->pGroups;
1003 | while(group)
1004 | {
1005 | for(i = 0; i < group->nTriangles; i++)
1006 | {
1007 | Tri(group->pTriangles[i]).tindices[0] = Tri(group->pTriangles[i]).vindices[0];
1008 | Tri(group->pTriangles[i]).tindices[1] = Tri(group->pTriangles[i]).vindices[1];
1009 | Tri(group->pTriangles[i]).tindices[2] = Tri(group->pTriangles[i]).vindices[2];
1010 | }
1011 | group = group->next;
1012 | }
1013 | }
1014 |
1015 | //////////////////////////////////////////////////////////////////////
1016 | // SpheremapTexture: Generates texture coordinates according to a
1017 | // spherical projection of the texture map. Sometimes referred to as
1018 | // spheremap, or reflection map texture coordinates. It generates
1019 | // these by using the normal to calculate where that vertex would map
1020 | // onto a sphere. Since it is impossible to map something flat
1021 | // perfectly onto something spherical, there is distortion at the
1022 | // poles. This particular implementation causes the poles along the X
1023 | // axis to be distorted.
1024 | //
1025 | // model - pointer to initialized COBJmodel structure
1026 | //////////////////////////////////////////////////////////////////////
1027 | void CAccessObj::SpheremapTexture()
1028 | {
1029 | COBJgroup* group;
1030 | float theta, phi, rho, x, y, z, r;
1031 | unsigned int i;
1032 |
1033 | assert(m_pModel);
1034 | assert(m_pModel->vpNormals);
1035 |
1036 | if (m_pModel->vpTexCoords)
1037 | free(m_pModel->vpTexCoords);
1038 | m_pModel->nTexCoords = m_pModel->nNormals;
1039 | m_pModel->vpTexCoords = new CPoint3D [m_pModel->nTexCoords+1];
1040 |
1041 | for (i = 1; i <= m_pModel->nNormals; i++)
1042 | {
1043 | z = m_pModel->vpNormals[i].x; /* re-arrange for pole distortion */
1044 | y = m_pModel->vpNormals[i].y;
1045 | x = m_pModel->vpNormals[i].z;
1046 | r = (float)sqrt((x * x) + (y * y));
1047 | rho = (float)sqrt((r * r) + (z * z));
1048 |
1049 | if(r == 0.0f)
1050 | {
1051 | theta = 0.0f;
1052 | phi = 0.0f;
1053 | }
1054 | else
1055 | {
1056 | if(z == 0.0f)
1057 | phi = 3.14159265f / 2.0f;
1058 | else phi = (float)acos(z / rho);
1059 |
1060 | if(y == 0.0)
1061 | theta = 3.141592365f / 2.0f;
1062 | else theta = (float)asin(y / r) + (3.14159265f / 2.0f);
1063 | }
1064 |
1065 | m_pModel->vpTexCoords[i] = CPoint3D(theta / 3.14159265f, phi / 3.14159265f, 0.0f);
1066 | }
1067 |
1068 | /* go through and put texcoord indices in all the triangles */
1069 | group = m_pModel->pGroups;
1070 | while(group)
1071 | {
1072 | for (i = 0; i < group->nTriangles; i++)
1073 | {
1074 | Tri(group->pTriangles[i]).tindices[0] = Tri(group->pTriangles[i]).nindices[0];
1075 | Tri(group->pTriangles[i]).tindices[1] = Tri(group->pTriangles[i]).nindices[1];
1076 | Tri(group->pTriangles[i]).tindices[2] = Tri(group->pTriangles[i]).nindices[2];
1077 | }
1078 | group = group->next;
1079 | }
1080 | }
1081 |
1082 | //////////////////////////////////////////////////////////////////////
1083 | // objDelete: Deletes a COBJmodel structure.
1084 | //
1085 | // model - initialized COBJmodel structure
1086 | //////////////////////////////////////////////////////////////////////
1087 | void CAccessObj::Destory()
1088 | {
1089 | delete m_pModel;
1090 | m_pModel = NULL;
1091 | }
1092 |
1093 | //////////////////////////////////////////////////////////////////////
1094 | // objReadOBJ: Reads a model description from a Wavefront .OBJ file.
1095 | // Returns a pointer to the created object which should be freed with
1096 | // objDelete().
1097 | //
1098 | // filename - name of the file containing the Wavefront .OBJ format data.
1099 | //////////////////////////////////////////////////////////////////////
1100 | bool CAccessObj::LoadOBJ(const char* filename)
1101 | {
1102 | FILE* file = NULL;
1103 |
1104 | // open the file
1105 | file = fopen(filename, "r");
1106 | if (!file)
1107 | {
1108 | fprintf(stderr, "objReadOBJ() failed: can't open data file \"%s\".\n",
1109 | filename);
1110 | exit(1);
1111 | }
1112 |
1113 | // save old model for invalid obj file
1114 | COBJmodel *pOldModel = m_pModel;
1115 |
1116 | // allocate a new model
1117 | m_pModel = new COBJmodel;
1118 |
1119 | sprintf(m_pModel->pathname, "%s", filename);
1120 | m_pModel->mtllibname[0] = '\0';
1121 |
1122 | m_pModel->nVertices = 0;
1123 | m_pModel->vpVertices = NULL;
1124 | m_pModel->nNormals = 0;
1125 | m_pModel->vpNormals = NULL;
1126 | m_pModel->nTexCoords = 0;
1127 | m_pModel->vpTexCoords = NULL;
1128 | m_pModel->nFacetnorms = 0;
1129 | m_pModel->vpFacetNorms = NULL;
1130 | m_pModel->nTriangles = 0;
1131 | m_pModel->pTriangles = NULL;
1132 | m_pModel->nMaterials = 0;
1133 | m_pModel->pMaterials = NULL;
1134 | m_pModel->nGroups = 0;
1135 | m_pModel->pGroups = NULL;
1136 | m_pModel->position = CPoint3D (0, 0, 0);
1137 |
1138 | // make a first pass through the file to get a count of the number
1139 | // of vertices, normals, texcoords & triangles
1140 | if (FirstPass(file))
1141 | {
1142 | SAFE_DELETE(pOldModel);
1143 |
1144 | /* allocate memory */
1145 | m_pModel->vpVertices = new CPoint3D [m_pModel->nVertices + 1];
1146 | m_pModel->pTriangles = new COBJtriangle [m_pModel->nTriangles];
1147 | if (m_pModel->nNormals)
1148 | {
1149 | m_pModel->vpNormals = new CPoint3D [m_pModel->nNormals + 1];
1150 | }
1151 | if (m_pModel->nTexCoords)
1152 | {
1153 | m_pModel->vpTexCoords = new CPoint3D [m_pModel->nTexCoords + 1];
1154 | }
1155 |
1156 | /* rewind to beginning of file and read in the data this pass */
1157 | rewind(file);
1158 | SecondPass(file);
1159 |
1160 | // Calc bounding box
1161 | CalcBoundingBox();
1162 | }
1163 | else
1164 | {
1165 | // restore old model
1166 | if (m_pModel != NULL) Destory();
1167 | m_pModel = pOldModel;
1168 | }
1169 |
1170 | /* close the file */
1171 | fclose(file);
1172 |
1173 | return (pOldModel==NULL);
1174 | }
1175 |
1176 |
1177 | //////////////////////////////////////////////////////////////////////
1178 | // WriteOBJ: Writes a model description in Wavefront .OBJ format to
1179 | // a file.
1180 | //
1181 | // model - initialized COBJmodel structure
1182 | // filename - name of the file to write the Wavefront .OBJ format data to
1183 | // mode - a bitwise or of values describing what is written to the file
1184 | // OBJ_NONE - render with only vertices
1185 | // OBJ_FLAT - render with facet normals
1186 | // OBJ_SMOOTH - render with vertex normals
1187 | // OBJ_TEXTURE - render with texture coords
1188 | // OBJ_COLOR - render with colors (color material)
1189 | // OBJ_MATERIAL - render with materials
1190 | // OBJ_COLOR and OBJ_MATERIAL should not both be specified.
1191 | // OBJ_FLAT and OBJ_SMOOTH should not both be specified.
1192 | //////////////////////////////////////////////////////////////////////
1193 | void CAccessObj::WriteOBJ(char* filename, unsigned int mode)
1194 | {
1195 | unsigned int i;
1196 | FILE* file;
1197 | COBJgroup* group;
1198 |
1199 | assert(m_pModel);
1200 |
1201 | /* do a bit of warning */
1202 | if (mode & OBJ_FLAT && !m_pModel->vpFacetNorms)
1203 | {
1204 | printf("WriteOBJ() warning: flat normal output requested "
1205 | "with no facet normals defined.\n");
1206 | mode &= ~OBJ_FLAT;
1207 | }
1208 | if (mode & OBJ_SMOOTH && !m_pModel->vpNormals)
1209 | {
1210 | printf("WriteOBJ() warning: smooth normal output requested "
1211 | "with no normals defined.\n");
1212 | mode &= ~OBJ_SMOOTH;
1213 | }
1214 | if (mode & OBJ_TEXTURE && !m_pModel->vpTexCoords)
1215 | {
1216 | printf("WriteOBJ() warning: texture coordinate output requested "
1217 | "with no texture coordinates defined.\n");
1218 | mode &= ~OBJ_TEXTURE;
1219 | }
1220 | if (mode & OBJ_FLAT && mode & OBJ_SMOOTH)
1221 | {
1222 | printf("WriteOBJ() warning: flat normal output requested "
1223 | "and smooth normal output requested (using smooth).\n");
1224 | mode &= ~OBJ_FLAT;
1225 | }
1226 | if (mode & OBJ_COLOR && !m_pModel->pMaterials)
1227 | {
1228 | printf("WriteOBJ() warning: color output requested "
1229 | "with no colors (materials) defined.\n");
1230 | mode &= ~OBJ_COLOR;
1231 | }
1232 | if (mode & OBJ_MATERIAL && !m_pModel->pMaterials)
1233 | {
1234 | printf("WriteOBJ() warning: material output requested "
1235 | "with no materials defined.\n");
1236 | mode &= ~OBJ_MATERIAL;
1237 | }
1238 | if (mode & OBJ_COLOR && mode & OBJ_MATERIAL)
1239 | {
1240 | printf("WriteOBJ() warning: color and material output requested "
1241 | "outputting only materials.\n");
1242 | mode &= ~OBJ_COLOR;
1243 | }
1244 |
1245 |
1246 | /* open the file */
1247 | file = fopen(filename, "w");
1248 | if (!file)
1249 | {
1250 | fprintf(stderr, "WriteOBJ() failed: can't open file \"%s\" to write.\n",
1251 | filename);
1252 | exit(1);
1253 | }
1254 |
1255 | /* spit out a header */
1256 | fprintf(file, "# \n");
1257 | fprintf(file, "# Wavefront OBJ\n");
1258 | fprintf(file, "# \n");
1259 |
1260 | if (mode & OBJ_MATERIAL && m_pModel->mtllibname)
1261 | {
1262 | fprintf(file, "\nmtllib %s\n\n", m_pModel->mtllibname);
1263 | WriteMTL(filename, m_pModel->mtllibname);
1264 | }
1265 |
1266 | /* spit out the vertices */
1267 | fprintf(file, "\n");
1268 | fprintf(file, "# %d vertices\n", m_pModel->nVertices);
1269 | for (i = 1; i <= m_pModel->nVertices; i++)
1270 | {
1271 | fprintf(file, "v %f %f %f\n",
1272 | m_pModel->vpVertices[i].x,
1273 | m_pModel->vpVertices[i].y,
1274 | m_pModel->vpVertices[i].z);
1275 | }
1276 |
1277 | /* spit out the smooth/flat normals */
1278 | if (mode & OBJ_SMOOTH)
1279 | {
1280 | fprintf(file, "\n");
1281 | fprintf(file, "# %d normals\n", m_pModel->nNormals);
1282 | for (i = 1; i <= m_pModel->nNormals; i++)
1283 | {
1284 | fprintf(file, "vn %f %f %f\n",
1285 | m_pModel->vpNormals[i].x,
1286 | m_pModel->vpNormals[i].y,
1287 | m_pModel->vpNormals[i].z);
1288 | }
1289 | }
1290 | else if (mode & OBJ_FLAT)
1291 | {
1292 | fprintf(file, "\n");
1293 | fprintf(file, "# %d normals\n", m_pModel->nFacetnorms);
1294 | for (i = 1; i <= m_pModel->nNormals; i++)
1295 | {
1296 | fprintf(file, "vn %f %f %f\n",
1297 | m_pModel->vpFacetNorms[i].x,
1298 | m_pModel->vpFacetNorms[i].y,
1299 | m_pModel->vpFacetNorms[i].z);
1300 | }
1301 | }
1302 |
1303 | /* spit out the texture coordinates */
1304 | if (mode & OBJ_TEXTURE)
1305 | {
1306 | fprintf(file, "\n");
1307 | fprintf(file, "# %d texcoords\n", m_pModel->vpTexCoords);
1308 | for (i = 1; i <= m_pModel->nTexCoords; i++)
1309 | {
1310 | fprintf(file, "vt %f %f\n",
1311 | m_pModel->vpTexCoords[i].x,
1312 | m_pModel->vpTexCoords[i].y);
1313 | }
1314 | }
1315 |
1316 | fprintf(file, "\n");
1317 | fprintf(file, "# %d groups\n", m_pModel->nGroups);
1318 | fprintf(file, "# %d faces (triangles)\n", m_pModel->nTriangles);
1319 | fprintf(file, "\n");
1320 |
1321 | group = m_pModel->pGroups;
1322 | while(group)
1323 | {
1324 | fprintf(file, "g %s\n", group->name);
1325 | if (mode & OBJ_MATERIAL)
1326 | fprintf(file, "usemtl %s\n", m_pModel->pMaterials[group->material].name);
1327 | for (i = 0; i < group->nTriangles; i++)
1328 | {
1329 | if (mode & OBJ_SMOOTH && mode & OBJ_TEXTURE)
1330 | {
1331 | fprintf(file, "f %d/%d/%d %d/%d/%d %d/%d/%d\n",
1332 | Tri(group->pTriangles[i]).vindices[0],
1333 | Tri(group->pTriangles[i]).nindices[0],
1334 | Tri(group->pTriangles[i]).tindices[0],
1335 | Tri(group->pTriangles[i]).vindices[1],
1336 | Tri(group->pTriangles[i]).nindices[1],
1337 | Tri(group->pTriangles[i]).tindices[1],
1338 | Tri(group->pTriangles[i]).vindices[2],
1339 | Tri(group->pTriangles[i]).nindices[2],
1340 | Tri(group->pTriangles[i]).tindices[2]);
1341 | }
1342 | else if (mode & OBJ_FLAT && mode & OBJ_TEXTURE)
1343 | {
1344 | fprintf(file, "f %d/%d %d/%d %d/%d\n",
1345 | Tri(group->pTriangles[i]).vindices[0],
1346 | Tri(group->pTriangles[i]).findex,
1347 | Tri(group->pTriangles[i]).vindices[1],
1348 | Tri(group->pTriangles[i]).findex,
1349 | Tri(group->pTriangles[i]).vindices[2],
1350 | Tri(group->pTriangles[i]).findex);
1351 | }
1352 | else if (mode & OBJ_TEXTURE)
1353 | {
1354 | fprintf(file, "f %d/%d %d/%d %d/%d\n",
1355 | Tri(group->pTriangles[i]).vindices[0],
1356 | Tri(group->pTriangles[i]).tindices[0],
1357 | Tri(group->pTriangles[i]).vindices[1],
1358 | Tri(group->pTriangles[i]).tindices[1],
1359 | Tri(group->pTriangles[i]).vindices[2],
1360 | Tri(group->pTriangles[i]).tindices[2]);
1361 | }
1362 | else if (mode & OBJ_SMOOTH)
1363 | {
1364 | fprintf(file, "f %d//%d %d//%d %d//%d\n",
1365 | Tri(group->pTriangles[i]).vindices[0],
1366 | Tri(group->pTriangles[i]).nindices[0],
1367 | Tri(group->pTriangles[i]).vindices[1],
1368 | Tri(group->pTriangles[i]).nindices[1],
1369 | Tri(group->pTriangles[i]).vindices[2],
1370 | Tri(group->pTriangles[i]).nindices[2]);
1371 | }
1372 | else if (mode & OBJ_FLAT)
1373 | {
1374 | fprintf(file, "f %d//%d %d//%d %d//%d\n",
1375 | Tri(group->pTriangles[i]).vindices[0],
1376 | Tri(group->pTriangles[i]).findex,
1377 | Tri(group->pTriangles[i]).vindices[1],
1378 | Tri(group->pTriangles[i]).findex,
1379 | Tri(group->pTriangles[i]).vindices[2],
1380 | Tri(group->pTriangles[i]).findex);
1381 | }
1382 | else
1383 | {
1384 | fprintf(file, "f %d %d %d\n",
1385 | Tri(group->pTriangles[i]).vindices[0],
1386 | Tri(group->pTriangles[i]).vindices[1],
1387 | Tri(group->pTriangles[i]).vindices[2]);
1388 | }
1389 | }
1390 | fprintf(file, "\n");
1391 | group = group->next;
1392 | }
1393 |
1394 | fclose(file);
1395 | }
1396 |
1397 | void CAccessObj::Boundingbox(CPoint3D &vMax, CPoint3D &vMin)
1398 | {
1399 | vMax = m_vMax;
1400 | vMin = m_vMin;
1401 | }
1402 |
1403 | void CAccessObj::CalcBoundingBox()
1404 | {
1405 | unsigned int i;
1406 |
1407 | m_vMax = m_vMin = m_pModel->vpVertices[1];
1408 | for (i = 1; i <= m_pModel->nVertices; i++)
1409 | {
1410 | if (m_vMax.x < m_pModel->vpVertices[i].x)
1411 | m_vMax.x = m_pModel->vpVertices[i].x;
1412 | if (m_vMin.x > m_pModel->vpVertices[i].x)
1413 | m_vMin.x = m_pModel->vpVertices[i].x;
1414 |
1415 | if (m_vMax.y < m_pModel->vpVertices[i].y)
1416 | m_vMax.y = m_pModel->vpVertices[i].y;
1417 | if (m_vMin.y > m_pModel->vpVertices[i].y)
1418 | m_vMin.y = m_pModel->vpVertices[i].y;
1419 |
1420 | if (m_vMax.z < m_pModel->vpVertices[i].z)
1421 | m_vMax.z = m_pModel->vpVertices[i].z;
1422 | if (m_vMin.z > m_pModel->vpVertices[i].z)
1423 | m_vMin.z = m_pModel->vpVertices[i].z;
1424 | }
1425 |
1426 | CPoint3D vCent = (m_vMax + m_vMin)*0.5f;
1427 |
1428 | for (i = 1; i <= m_pModel->nVertices; i++)
1429 | m_pModel->vpVertices[i] = m_pModel->vpVertices[i] - vCent;
1430 |
1431 | m_vMax = m_vMax - vCent;
1432 | m_vMin = m_vMin -vCent;
1433 | }
1434 |
1435 | //////////////////////////////////////////////////////////////////////////
1436 | // Unified the model to center
1437 | //////////////////////////////////////////////////////////////////////////
1438 | void CAccessObj::UnifiedModel()
1439 | {
1440 | if (m_pModel==NULL) return;
1441 |
1442 | //CPoint3D vDiameter = m_vMax - m_vMin;
1443 | //float radius = vDiameter.length() * 0.4f / 1.414f;
1444 | //Scale(1.0f/radius);
1445 | CalcBoundingBox();
1446 | if (m_pModel->nNormals==0)
1447 | {
1448 | FacetNormals();
1449 | VertexNormals(90.f);
1450 | }
1451 | }
1452 |
1453 | } // namespace trimeshVec
--------------------------------------------------------------------------------