2 * Copyright (C) 2006 Bjørn Lindeijer
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 * $Id: OBJModel.cxx,v 1.3 2006/08/12 21:36:09 b_lindeijer Exp $
23 #include "TextureLoader.h"
31 static void stripNewline(char *str
)
33 // Remove newline from the string (either \r,\n or \r\n)
34 int len
= strlen(str
);
35 if (str
[len
- 2] == '\r') str
[len
- 2] = '\0';
36 else str
[len
- 1] = '\0';
39 static string
stripFilename(const string
& path
)
41 // Strip the filename portion from the given path
42 int pos
= path
.rfind("/");
43 return path
.substr(0, pos
+ 1);
46 static GLuint
loadImage(const string
& filename
)
48 // TODO: Support other sizes than just 128x128
49 GLubyte
*imgData
= TextureLoader::loadImage(filename
.c_str(), 128, 128);
52 cerr
<< "Error loading image " << filename
<< std::endl
;
58 glGenTextures(1, &tid
);
59 glBindTexture(GL_TEXTURE_2D
, tid
);
60 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
61 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
63 GL_TEXTURE_2D
, 0, 3, 128, 128,
64 0, GL_RGBA
, GL_UNSIGNED_BYTE
, imgData
);
71 OBJModel::OBJModel(const string
&filename
)
78 // FIXME: Delete created materials
82 void OBJModel::render()
84 // TODO: Could be optimized by using vertex arrays
86 vector
<Face
>::iterator fi
;
87 vector
<FaceVertex
>::iterator vi
;
89 Material
*activeMaterial
= 0;
92 for (fi
= faces
.begin(); fi
!= faces
.end(); fi
++) {
93 // Check if we need to set new material properties
94 if (fi
->material
&& fi
->material
!= activeMaterial
) {
95 glMaterialf(GL_FRONT
,GL_SHININESS
,fi
->material
->shininess
);
96 glMaterialfv(GL_FRONT
,GL_AMBIENT
,fi
->material
->ambient
);
97 glMaterialfv(GL_FRONT
,GL_DIFFUSE
,fi
->material
->diffuse
);
98 glMaterialfv(GL_FRONT
,GL_SPECULAR
,fi
->material
->specular
);
99 if (fi
->material
->textureId
> 0) {
100 glEnable(GL_TEXTURE_2D
);
101 glBindTexture(GL_TEXTURE_2D
, fi
->material
->textureId
);
104 glDisable(GL_TEXTURE_2D
);
106 activeMaterial
= fi
->material
;
109 for (vi
= fi
->vertices
.begin(); vi
!= fi
->vertices
.end(); vi
++) {
117 vertexTextures
[vt
].u
,
118 vertexTextures
[vt
].v
,
119 vertexTextures
[vt
].w
);
131 vertexNormals
[vn
].k
);
146 void OBJModel::load(const string
&filename
)
149 vertexTextures
.clear();
150 vertexNormals
.clear();
151 vertexColors
.clear();
154 FILE* file
= fopen(filename
.c_str(), "r");
158 cerr
<< "Error while opening " << filename
<< "!\n";
162 // By default no material is used
163 Material
*activeMaterial
= 0;
165 while (!feof(file
)) {
167 fgets(str
, 256, file
);
172 case ' ': // Plain vertex ("v x y z w")
175 sscanf(str
, "v %f %f %f %f",
176 &(v
.x
), &(v
.y
), &(v
.z
), &(v
.w
));
177 vertices
.push_back(v
);
178 //cout << "vt " << (v.x * 0.5) << " " << (v.y * 0.5) << " " << (v.z * 0.5) << endl;
181 case 't': // Texture vertex ("vt u v w")
184 sscanf(str
, "vt %f %f %f", &(vt
.u
), &(vt
.v
), &(vt
.w
));
185 vertexTextures
.push_back(vt
);
188 case 'n': // Vertex Normal ("vn i j k")
191 sscanf(str
, "vn %f %f %f", &(vn
.i
), &(vn
.j
), &(vn
.k
));
192 vertexNormals
.push_back(vn
);
195 case 'c': // Vertex Color ("vc r g b a") - not standard OBJ
198 sscanf(str
, "vc %f %f %f %f",
199 &(vc
.r
), &(vc
.g
), &(vc
.b
), &(vc
.a
));
200 vertexColors
.push_back(vc
);
206 case 'f': // Face ("f v/vt/vn v/vt/vn v/vt/vn")
211 f
.material
= activeMaterial
;
213 vstr
= strtok(&str
[2], " "); // skip "f "
214 for (; vstr
; vstr
= strtok(NULL
," ")) {
217 // Check if a//c or a/b/c
218 sscanf(vstr
,"%*d%*c%c", &texlessfaces
);
219 if (texlessfaces
!= '/') {
220 sscanf(vstr
, "%d/%d/%d", &(fv
.v
), &(fv
.vt
), &(fv
.vn
));
222 sscanf(vstr
, "%d//%d", &(fv
.v
), &(fv
.vn
));
225 f
.vertices
.push_back(fv
);
228 // Add the face to the object if there are at least 3 vertices
229 if (f
.vertices
.size() >= 3) {
232 //vector<FaceVertex>::iterator vi;
233 //for (vi = f.vertices.begin(); vi != f.vertices.end(); vi++) {
234 // cout << " " << vi->v << "/" << vi->v << "/" << vi->vn;
240 case 'o': // Object name ("o name")
243 name
= string(&str
[2]);
246 case 'u': // ("usemtl name")
247 if (strncmp(str
, "usemtl ", 7) == 0) {
249 string matName
= string(&str
[7]);
250 if (materials
.count(matName
) > 0) {
251 activeMaterial
= materials
[matName
];
255 case 'm': // ("mtllib file1 file2 ...")
256 if (strncmp(str
, "mtllib ", 7) == 0) {
257 // Read material libraries
258 string path
= stripFilename(filename
);
261 vstr
= strtok(&str
[7], " \r\n"); // skip "mtllib "
262 for (; vstr
; vstr
= strtok(NULL
," \r\n")) {
263 loadMTL(path
+ string(vstr
));
267 case 's': // ("shadow_obj file")
268 if (strncmp(str
, "shadow_obj ", 11) == 0) {
270 shadowObj
= string(&str
[11]);
274 // Nothing recognized, ignore.
279 //cout << "Loaded model " << name << " (" << faces.size() << " faces)\n";
283 void OBJModel::loadMTL(const string
&filename
)
285 FILE* file
= fopen(filename
.c_str(), "r");
287 cerr
<< "Error while opening " << filename
<< "!\n";
294 while (!feof(file
)) {
296 fgets(str
, 256, file
);
299 case 'n': // ("newmtl name")
300 if (strncmp(str
, "newmtl ", 7) == 0) {
301 // TODO: Check if same name already exists
303 string matName
= string(&str
[7]);
304 if (materials
.count(matName
) == 0) {
305 mat
= new Material();
307 materials
[matName
] = mat
;
309 mat
= materials
[matName
];
319 case 'a': // ("Ka r g b")
321 sscanf(str
, "Ka %f %f %f",
322 &mat
->ambient
[0], &mat
->ambient
[1], &mat
->ambient
[2]);
324 case 'd': // ("Kd r g b")
326 sscanf(str
, "Kd %f %f %f",
327 &mat
->diffuse
[0], &mat
->diffuse
[1], &mat
->diffuse
[2]);
329 case 's': // ("Ks r g b")
331 sscanf(str
, "Ks %f %f %f",
332 &mat
->specular
[0],&mat
->specular
[1],&mat
->specular
[2]);
336 case 'i': // ("illum i")
337 // Illumination model (0, 1 or 2)
338 if (strncmp(str
, "illum ", 6)) {
339 sscanf(str
, "illum %d", &mat
->illuminationModel
);
341 else if (strncmp(str
, "d ", 2)) {
342 sscanf(str
, "d %d", &mat
->illuminationModel
);
347 // ("Tr transparency")
348 sscanf(str
, "Tr %f", &mat
->transparency
);
350 mat
->ambient
[3] = mat
->transparency
;
351 mat
->specular
[3] = mat
->transparency
;
352 mat
->diffuse
[3] = mat
->transparency
;
354 case 'd': // ("d transparency")
355 sscanf(str
, "d %f", &mat
->transparency
);
356 mat
->ambient
[3] = mat
->transparency
;
357 mat
->specular
[3] = mat
->transparency
;
358 mat
->diffuse
[3] = mat
->transparency
;
363 sscanf(str
, "Ns %f", &mat
->shininess
);
367 if (strncmp(str
, "map_K", 5) == 0) {
369 case 'd': // ("map_Kd filename") - Diffuse map
371 stripNewline(&str
[7]);
373 stripFilename(filename
) + string(&str
[7]);
374 mat
->textureId
= loadImage(imageFile
);
377 case 'a': // ("map_Ka filename") - Ambient map
379 case 's': // ("map_Ks filename") - Specular map
383 else if (strncmp(str
, "map_Bump ", 9) == 0) {
384 // ("map_Bump filename") - Bump map
386 else if (strncmp(str
, "map_d ", 6) == 0) {
387 // ("map_d filename") - Opacity map
391 // Nothing recognized, ignore.