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.2 2006/08/12 01:26:55 b_lindeijer Exp $
29 static void stripNewline(char *str
)
31 // Remove newline from the string (either \r,\n or \r\n)
32 int len
= strlen(str
);
33 if (str
[len
- 2] == '\r') str
[len
- 2] = '\0';
34 else str
[len
- 1] = '\0';
37 static string
stripFilename(const string
& path
)
39 // Strip the filename portion from the given path
40 int pos
= path
.rfind("/");
41 return path
.substr(0, pos
+ 1);
44 static GLuint
loadImage(const string
& filename
)
47 cerr
<< "Error loading image " << filename
<< std::endl
;
52 glGenTextures(1, &tid
);
53 glBindTexture(GL_TEXTURE_2D
, tid
);
54 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
55 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
60 OBJModel::OBJModel(const string
&filename
)
67 // FIXME: Delete created materials
71 void OBJModel::render()
73 // TODO: Could be optimized by using vertex arrays
75 vector
<Face
>::iterator fi
;
76 vector
<FaceVertex
>::iterator vi
;
78 Material
*activeMaterial
= 0;
81 for (fi
= faces
.begin(); fi
!= faces
.end(); fi
++) {
82 // Check if we need to set new material properties
83 if (fi
->material
&& fi
->material
!= activeMaterial
) {
84 glMaterialf(GL_FRONT
,GL_SHININESS
,fi
->material
->shininess
);
85 glMaterialfv(GL_FRONT
,GL_AMBIENT
,fi
->material
->ambient
);
86 glMaterialfv(GL_FRONT
,GL_DIFFUSE
,fi
->material
->diffuse
);
87 glMaterialfv(GL_FRONT
,GL_SPECULAR
,fi
->material
->specular
);
88 if (fi
->material
->textureId
> 0) {
89 glEnable(GL_TEXTURE_2D
);
90 glBindTexture(GL_TEXTURE_2D
, fi
->material
->textureId
);
93 glDisable(GL_TEXTURE_2D
);
95 activeMaterial
= fi
->material
;
98 for (vi
= fi
->vertices
.begin(); vi
!= fi
->vertices
.end(); vi
++) {
106 vertexTextures
[vt
].u
,
107 vertexTextures
[vt
].v
,
108 vertexTextures
[vt
].w
);
120 vertexNormals
[vn
].k
);
135 void OBJModel::load(const string
&filename
)
138 vertexTextures
.clear();
139 vertexNormals
.clear();
140 vertexColors
.clear();
143 FILE* file
= fopen(filename
.c_str(), "r");
147 cerr
<< "Error while opening " << filename
<< "!\n";
151 // By default no material is used
152 Material
*activeMaterial
= 0;
154 while (!feof(file
)) {
156 fgets(str
, 256, file
);
161 case ' ': // Plain vertex ("v x y z w")
164 sscanf(str
, "v %f %f %f %f",
165 &(v
.x
), &(v
.y
), &(v
.z
), &(v
.w
));
166 vertices
.push_back(v
);
167 //cout << "vt " << (v.x * 0.5) << " " << (v.y * 0.5) << " " << (v.z * 0.5) << endl;
170 case 't': // Texture vertex ("vt u v w")
173 sscanf(str
, "vt %f %f %f", &(vt
.u
), &(vt
.v
), &(vt
.w
));
174 vertexTextures
.push_back(vt
);
177 case 'n': // Vertex Normal ("vn i j k")
180 sscanf(str
, "vn %f %f %f", &(vn
.i
), &(vn
.j
), &(vn
.k
));
181 vertexNormals
.push_back(vn
);
184 case 'c': // Vertex Color ("vc r g b a") - not standard OBJ
187 sscanf(str
, "vc %f %f %f %f",
188 &(vc
.r
), &(vc
.g
), &(vc
.b
), &(vc
.a
));
189 vertexColors
.push_back(vc
);
195 case 'f': // Face ("f v/vt/vn v/vt/vn v/vt/vn")
200 f
.material
= activeMaterial
;
202 vstr
= strtok(&str
[2], " "); // skip "f "
203 for (; vstr
; vstr
= strtok(NULL
," ")) {
206 // Check if a//c or a/b/c
207 sscanf(vstr
,"%*d%*c%c", &texlessfaces
);
208 if (texlessfaces
!= '/') {
209 sscanf(vstr
, "%d/%d/%d", &(fv
.v
), &(fv
.vt
), &(fv
.vn
));
211 sscanf(vstr
, "%d//%d", &(fv
.v
), &(fv
.vn
));
214 f
.vertices
.push_back(fv
);
217 // Add the face to the object if there are at least 3 vertices
218 if (f
.vertices
.size() >= 3) {
221 //vector<FaceVertex>::iterator vi;
222 //for (vi = f.vertices.begin(); vi != f.vertices.end(); vi++) {
223 // cout << " " << vi->v << "/" << vi->v << "/" << vi->vn;
229 case 'o': // Object name ("o name")
232 name
= string(&str
[2]);
235 case 'u': // ("usemtl name")
236 if (strncmp(str
, "usemtl ", 7) == 0) {
238 string matName
= string(&str
[7]);
239 if (materials
.count(matName
) > 0) {
240 activeMaterial
= materials
[matName
];
244 case 'm': // ("mtllib file1 file2 ...")
245 if (strncmp(str
, "mtllib ", 7) == 0) {
246 // Read material libraries
247 string path
= stripFilename(filename
);
250 vstr
= strtok(&str
[7], " \r\n"); // skip "mtllib "
251 for (; vstr
; vstr
= strtok(NULL
," \r\n")) {
252 loadMTL(path
+ string(vstr
));
256 case 's': // ("shadow_obj file")
257 if (strncmp(str
, "shadow_obj ", 11) == 0) {
259 shadowObj
= string(&str
[11]);
263 // Nothing recognized, ignore.
268 cout
<< "Loaded model " << name
<< " (" << faces
.size() << " faces)\n";
272 void OBJModel::loadMTL(const string
&filename
)
274 FILE* file
= fopen(filename
.c_str(), "r");
276 cerr
<< "Error while opening " << filename
<< "!\n";
283 while (!feof(file
)) {
285 fgets(str
, 256, file
);
288 case 'n': // ("newmtl name")
289 if (strncmp(str
, "newmtl ", 7) == 0) {
290 // TODO: Check if same name already exists
292 string matName
= string(&str
[7]);
293 if (materials
.count(matName
) == 0) {
294 mat
= new Material();
296 materials
[matName
] = mat
;
298 mat
= materials
[matName
];
308 case 'a': // ("Ka r g b")
310 sscanf(str
, "Ka %f %f %f",
311 &mat
->ambient
[0], &mat
->ambient
[1], &mat
->ambient
[2]);
313 case 'd': // ("Kd r g b")
315 sscanf(str
, "Kd %f %f %f",
316 &mat
->diffuse
[0], &mat
->diffuse
[1], &mat
->diffuse
[2]);
318 case 's': // ("Ks r g b")
320 sscanf(str
, "Ks %f %f %f",
321 &mat
->specular
[0],&mat
->specular
[1],&mat
->specular
[2]);
325 case 'i': // ("illum i")
326 // Illumination model (0, 1 or 2)
327 if (strncmp(str
, "illum ", 6)) {
328 sscanf(str
, "illum %d", &mat
->illuminationModel
);
330 else if (strncmp(str
, "d ", 2)) {
331 sscanf(str
, "d %d", &mat
->illuminationModel
);
336 // ("Tr transparency")
337 sscanf(str
, "Tr %f", &mat
->transparency
);
339 mat
->ambient
[3] = mat
->transparency
;
340 mat
->specular
[3] = mat
->transparency
;
341 mat
->diffuse
[3] = mat
->transparency
;
343 case 'd': // ("d transparency")
344 sscanf(str
, "d %f", &mat
->transparency
);
345 mat
->ambient
[3] = mat
->transparency
;
346 mat
->specular
[3] = mat
->transparency
;
347 mat
->diffuse
[3] = mat
->transparency
;
352 sscanf(str
, "Ns %f", &mat
->shininess
);
356 if (strncmp(str
, "map_K", 5) == 0) {
358 case 'd': // ("map_Kd filename") - Diffuse map
360 stripNewline(&str
[7]);
362 stripFilename(filename
) + string(&str
[7]);
363 mat
->textureId
= loadImage(imageFile
);
366 case 'a': // ("map_Ka filename") - Ambient map
368 case 's': // ("map_Ks filename") - Specular map
372 else if (strncmp(str
, "map_Bump ", 9) == 0) {
373 // ("map_Bump filename") - Bump map
375 else if (strncmp(str
, "map_d ", 6) == 0) {
376 // ("map_d filename") - Opacity map
380 // Nothing recognized, ignore.