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.1 2006/07/19 01:56:21 lorien420 Exp $
30 static void stripNewline(char *str
)
32 // Remove newline from the string (either \r,\n or \r\n)
33 int len
= strlen(str
);
34 if (str
[len
- 2] == '\r') str
[len
- 2] = '\0';
35 else str
[len
- 1] = '\0';
38 static string
stripFilename(const string
& path
)
40 // Strip the filename portion from the given path
41 int pos
= path
.rfind("/");
42 return path
.substr(0, pos
+ 1);
45 static GLuint
loadImage(const string
& filename
)
48 cerr
<< "Error loading image " << filename
<< std::endl
;
53 glGenTextures(1, &tid
);
54 glBindTexture(GL_TEXTURE_2D
, tid
);
55 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
56 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
61 OBJModel::OBJModel(const string
&filename
)
68 // FIXME: Delete created materials
72 void OBJModel::render()
74 // TODO: Could be optimized by using vertex arrays
76 vector
<Face
>::iterator fi
;
77 vector
<FaceVertex
>::iterator vi
;
79 Material
*activeMaterial
= 0;
82 for (fi
= faces
.begin(); fi
!= faces
.end(); fi
++) {
83 // Check if we need to set new material properties
84 if (fi
->material
&& fi
->material
!= activeMaterial
) {
85 glMaterialf(GL_FRONT
,GL_SHININESS
,fi
->material
->shininess
);
86 glMaterialfv(GL_FRONT
,GL_AMBIENT
,fi
->material
->ambient
);
87 glMaterialfv(GL_FRONT
,GL_DIFFUSE
,fi
->material
->diffuse
);
88 glMaterialfv(GL_FRONT
,GL_SPECULAR
,fi
->material
->specular
);
89 if (fi
->material
->textureId
> 0) {
90 glEnable(GL_TEXTURE_2D
);
91 glBindTexture(GL_TEXTURE_2D
, fi
->material
->textureId
);
94 glDisable(GL_TEXTURE_2D
);
96 activeMaterial
= fi
->material
;
99 for (vi
= fi
->vertices
.begin(); vi
!= fi
->vertices
.end(); vi
++) {
107 vertexTextures
[vt
].u
,
108 vertexTextures
[vt
].v
,
109 vertexTextures
[vt
].w
);
121 vertexNormals
[vn
].k
);
136 void OBJModel::load(const string
&filename
)
139 vertexTextures
.clear();
140 vertexNormals
.clear();
141 vertexColors
.clear();
144 FILE* file
= fopen(filename
.c_str(), "r");
148 cerr
<< "Error while opening " << filename
<< "!\n";
152 // By default no material is used
153 Material
*activeMaterial
= 0;
155 while (!feof(file
)) {
157 fgets(str
, 256, file
);
162 case ' ': // Plain vertex ("v x y z w")
165 sscanf(str
, "v %f %f %f %f",
166 &(v
.x
), &(v
.y
), &(v
.z
), &(v
.w
));
167 vertices
.push_back(v
);
168 //cout << "vt " << (v.x * 0.5) << " " << (v.y * 0.5) << " " << (v.z * 0.5) << endl;
171 case 't': // Texture vertex ("vt u v w")
174 sscanf(str
, "vt %f %f %f", &(vt
.u
), &(vt
.v
), &(vt
.w
));
175 vertexTextures
.push_back(vt
);
178 case 'n': // Vertex Normal ("vn i j k")
181 sscanf(str
, "vn %f %f %f", &(vn
.i
), &(vn
.j
), &(vn
.k
));
182 vertexNormals
.push_back(vn
);
185 case 'c': // Vertex Color ("vc r g b a") - not standard OBJ
188 sscanf(str
, "vc %f %f %f %f",
189 &(vc
.r
), &(vc
.g
), &(vc
.b
), &(vc
.a
));
190 vertexColors
.push_back(vc
);
196 case 'f': // Face ("f v/vt/vn v/vt/vn v/vt/vn")
201 f
.material
= activeMaterial
;
203 vstr
= strtok(&str
[2], " "); // skip "f "
204 for (; vstr
; vstr
= strtok(NULL
," ")) {
207 // Check if a//c or a/b/c
208 sscanf(vstr
,"%*d%*c%c", &texlessfaces
);
209 if (texlessfaces
!= '/') {
210 sscanf(vstr
, "%d/%d/%d", &(fv
.v
), &(fv
.vt
), &(fv
.vn
));
212 sscanf(vstr
, "%d//%d", &(fv
.v
), &(fv
.vn
));
215 f
.vertices
.push_back(fv
);
218 // Add the face to the object if there are at least 3 vertices
219 if (f
.vertices
.size() >= 3) {
222 //vector<FaceVertex>::iterator vi;
223 //for (vi = f.vertices.begin(); vi != f.vertices.end(); vi++) {
224 // cout << " " << vi->v << "/" << vi->v << "/" << vi->vn;
230 case 'o': // Object name ("o name")
233 name
= string(&str
[2]);
236 case 'u': // ("usemtl name")
237 if (strncmp(str
, "usemtl ", 7) == 0) {
239 string matName
= string(&str
[7]);
240 if (materials
.count(matName
) > 0) {
241 activeMaterial
= materials
[matName
];
245 case 'm': // ("mtllib file1 file2 ...")
246 if (strncmp(str
, "mtllib ", 7) == 0) {
247 // Read material libraries
248 string path
= stripFilename(filename
);
251 vstr
= strtok(&str
[7], " \r\n"); // skip "mtllib "
252 for (; vstr
; vstr
= strtok(NULL
," \r\n")) {
253 loadMTL(path
+ string(vstr
));
257 case 's': // ("shadow_obj file")
258 if (strncmp(str
, "shadow_obj ", 11) == 0) {
260 shadowObj
= string(&str
[11]);
264 // Nothing recognized, ignore.
269 cout
<< "Loaded model " << name
<< " (" << faces
.size() << " faces)\n";
273 void OBJModel::loadMTL(const string
&filename
)
275 FILE* file
= fopen(filename
.c_str(), "r");
277 cerr
<< "Error while opening " << filename
<< "!\n";
284 while (!feof(file
)) {
286 fgets(str
, 256, file
);
289 case 'n': // ("newmtl name")
290 if (strncmp(str
, "newmtl ", 7) == 0) {
291 // TODO: Check if same name already exists
293 string matName
= string(&str
[7]);
294 if (materials
.count(matName
) == 0) {
295 mat
= new Material();
297 materials
[matName
] = mat
;
299 mat
= materials
[matName
];
309 case 'a': // ("Ka r g b")
311 sscanf(str
, "Ka %f %f %f",
312 &mat
->ambient
[0], &mat
->ambient
[1], &mat
->ambient
[2]);
314 case 'd': // ("Kd r g b")
316 sscanf(str
, "Kd %f %f %f",
317 &mat
->diffuse
[0], &mat
->diffuse
[1], &mat
->diffuse
[2]);
319 case 's': // ("Ks r g b")
321 sscanf(str
, "Ks %f %f %f",
322 &mat
->specular
[0],&mat
->specular
[1],&mat
->specular
[2]);
326 case 'i': // ("illum i")
327 // Illumination model (0, 1 or 2)
328 if (strncmp(str
, "illum ", 6)) {
329 sscanf(str
, "illum %d", &mat
->illuminationModel
);
331 else if (strncmp(str
, "d ", 2)) {
332 sscanf(str
, "d %d", &mat
->illuminationModel
);
337 // ("Tr transparency")
338 sscanf(str
, "Tr %f", &mat
->transparency
);
340 mat
->ambient
[3] = mat
->transparency
;
341 mat
->specular
[3] = mat
->transparency
;
342 mat
->diffuse
[3] = mat
->transparency
;
344 case 'd': // ("d transparency")
345 sscanf(str
, "d %f", &mat
->transparency
);
346 mat
->ambient
[3] = mat
->transparency
;
347 mat
->specular
[3] = mat
->transparency
;
348 mat
->diffuse
[3] = mat
->transparency
;
353 sscanf(str
, "Ns %f", &mat
->shininess
);
357 if (strncmp(str
, "map_K", 5) == 0) {
359 case 'd': // ("map_Kd filename") - Diffuse map
361 stripNewline(&str
[7]);
363 stripFilename(filename
) + string(&str
[7]);
364 mat
->textureId
= loadImage(imageFile
);
367 case 'a': // ("map_Ka filename") - Ambient map
369 case 's': // ("map_Ks filename") - Specular map
373 else if (strncmp(str
, "map_Bump ", 9) == 0) {
374 // ("map_Bump filename") - Bump map
376 else if (strncmp(str
, "map_d ", 6) == 0) {
377 // ("map_d filename") - Opacity map
381 // Nothing recognized, ignore.