Object loading patch
[crack-attack.git] / src / OBJModel.cxx
blobdcd7c8017e9e821bf79a23f72fd0d5b804276bf1
1 /*
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 $
21 #include <GL/gl.h>
22 #include <iostream>
23 #include <vector>
24 #include <list>
26 #include "OBJModel.h"
28 using namespace std;
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)
47 if (true) {
48 cerr << "Error loading image " << filename << std::endl;
49 return 0;
50 } else {
51 GLuint tid;
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);
57 return tid;
61 OBJModel::OBJModel(const string &filename)
63 load(filename);
66 OBJModel::~OBJModel()
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;
78 int v, vt, vn, vc;
79 Material *activeMaterial = 0;
81 // Draw all faces
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);
93 else {
94 glDisable(GL_TEXTURE_2D);
96 activeMaterial = fi->material;
98 glBegin(GL_POLYGON);
99 for (vi = fi->vertices.begin(); vi != fi->vertices.end(); vi++) {
100 v = vi->v - 1;
101 vt = vi->vt - 1;
102 vn = vi->vn - 1;
103 vc = vi->vc - 1;
105 if (vt >= 0) {
106 glTexCoord3f(
107 vertexTextures[vt].u,
108 vertexTextures[vt].v,
109 vertexTextures[vt].w);
111 if (vc >= 0) {
112 glColor3f(
113 vertexColors[vc].r,
114 vertexColors[vc].g,
115 vertexColors[vc].b);
118 glNormal3f(
119 vertexNormals[vn].i,
120 vertexNormals[vn].j,
121 vertexNormals[vn].k);
123 glVertex4f(
124 vertices[v].x,
125 vertices[v].y,
126 vertices[v].z,
127 vertices[v].w);
129 glEnd();
132 //SGNode::render();
136 void OBJModel::load(const string &filename)
138 vertices.clear();
139 vertexTextures.clear();
140 vertexNormals.clear();
141 vertexColors.clear();
142 faces.clear();
144 FILE* file = fopen(filename.c_str(), "r");
145 char str[256];
147 if (!file) {
148 cerr << "Error while opening " << filename << "!\n";
149 return;
152 // By default no material is used
153 Material *activeMaterial = 0;
155 while (!feof(file)) {
156 str[0] = '\0';
157 fgets(str, 256, file);
159 switch (str[0]) {
160 case 'v':
161 switch (str[1]) {
162 case ' ': // Plain vertex ("v x y z w")
164 Vertex v;
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;
170 break;
171 case 't': // Texture vertex ("vt u v w")
173 VertexTexture vt;
174 sscanf(str, "vt %f %f %f", &(vt.u), &(vt.v), &(vt.w));
175 vertexTextures.push_back(vt);
177 break;
178 case 'n': // Vertex Normal ("vn i j k")
180 VertexNormal vn;
181 sscanf(str, "vn %f %f %f", &(vn.i), &(vn.j), &(vn.k));
182 vertexNormals.push_back(vn);
184 break;
185 case 'c': // Vertex Color ("vc r g b a") - not standard OBJ
187 VertexColor vc;
188 sscanf(str, "vc %f %f %f %f",
189 &(vc.r), &(vc.g), &(vc.b), &(vc.a));
190 vertexColors.push_back(vc);
192 break;
193 default: break;
195 break;
196 case 'f': // Face ("f v/vt/vn v/vt/vn v/vt/vn")
198 char* vstr;
199 char texlessfaces;
200 Face f;
201 f.material = activeMaterial;
203 vstr = strtok(&str[2], " "); // skip "f "
204 for (; vstr; vstr = strtok(NULL," ")) {
205 FaceVertex fv;
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));
211 } else {
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) {
220 faces.push_back(f);
221 //cout << "f";
222 //vector<FaceVertex>::iterator vi;
223 //for (vi = f.vertices.begin(); vi != f.vertices.end(); vi++) {
224 // cout << " " << vi->v << "/" << vi->v << "/" << vi->vn;
226 //cout << endl;
229 break;
230 case 'o': // Object name ("o name")
232 stripNewline(str);
233 name = string(&str[2]);
235 break;
236 case 'u': // ("usemtl name")
237 if (strncmp(str, "usemtl ", 7) == 0) {
238 stripNewline(str);
239 string matName = string(&str[7]);
240 if (materials.count(matName) > 0) {
241 activeMaterial = materials[matName];
244 break;
245 case 'm': // ("mtllib file1 file2 ...")
246 if (strncmp(str, "mtllib ", 7) == 0) {
247 // Read material libraries
248 string path = stripFilename(filename);
249 char* vstr;
251 vstr = strtok(&str[7], " \r\n"); // skip "mtllib "
252 for (; vstr; vstr = strtok(NULL," \r\n")) {
253 loadMTL(path + string(vstr));
256 break;
257 case 's': // ("shadow_obj file")
258 if (strncmp(str, "shadow_obj ", 11) == 0) {
259 stripNewline(str);
260 shadowObj = string(&str[11]);
262 break;
263 default:
264 // Nothing recognized, ignore.
265 break;
269 cout << "Loaded model " << name << " (" << faces.size() << " faces)\n";
273 void OBJModel::loadMTL(const string &filename)
275 FILE* file = fopen(filename.c_str(), "r");
276 if (!file) {
277 cerr << "Error while opening " << filename << "!\n";
278 return;
281 char str[256];
282 Material* mat = 0;
284 while (!feof(file)) {
285 str[0] = '\0';
286 fgets(str, 256, file);
288 switch (str[0]) {
289 case 'n': // ("newmtl name")
290 if (strncmp(str, "newmtl ", 7) == 0) {
291 // TODO: Check if same name already exists
292 stripNewline(str);
293 string matName = string(&str[7]);
294 if (materials.count(matName) == 0) {
295 mat = new Material();
296 mat->name = matName;
297 materials[matName] = mat;
298 } else {
299 mat = materials[matName];
304 if (!mat) continue;
306 switch (str[0]) {
307 case 'K':
308 switch (str[1]) {
309 case 'a': // ("Ka r g b")
310 // Ambient
311 sscanf(str, "Ka %f %f %f",
312 &mat->ambient[0], &mat->ambient[1], &mat->ambient[2]);
313 break;
314 case 'd': // ("Kd r g b")
315 // Diffuse
316 sscanf(str, "Kd %f %f %f",
317 &mat->diffuse[0], &mat->diffuse[1], &mat->diffuse[2]);
318 break;
319 case 's': // ("Ks r g b")
320 // Specular
321 sscanf(str, "Ks %f %f %f",
322 &mat->specular[0],&mat->specular[1],&mat->specular[2]);
323 break;
324 default: break;
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);
334 break;
335 case 'T':
336 if (str[1] == 'r') {
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;
343 break;
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;
349 break;
350 case 'N':
351 if (str[1] == 's') {
352 // ("Ns shininess")
353 sscanf(str, "Ns %f", &mat->shininess);
355 break;
356 case 'm':
357 if (strncmp(str, "map_K", 5) == 0) {
358 switch (str[5]) {
359 case 'd': // ("map_Kd filename") - Diffuse map
361 stripNewline(&str[7]);
362 string imageFile =
363 stripFilename(filename) + string(&str[7]);
364 mat->textureId = loadImage(imageFile);
365 break;
367 case 'a': // ("map_Ka filename") - Ambient map
368 break;
369 case 's': // ("map_Ks filename") - Specular map
370 break;
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
379 break;
380 default:
381 // Nothing recognized, ignore.
382 break;