1 /***************************************************************************
2 modelloader.cpp - description
3 -------------------
4 begin : may 22th, 2004
5 copyright : (C) 2004-2008 by Duong Khang NGUYEN
6 email : neoneurone @ gmail com
7
8 $Id: modelloader.cpp 375 2008-10-28 14:47:15Z neoneurone $
9 ***************************************************************************/
10
11 /***************************************************************************
12 * *
13 * This program is free software; you can redistribute it and/or modify *
14 * it under the terms of the GNU General Public License as published by *
15 * the Free Software Foundation; either version 2 of the License, or *
16 * any later version. *
17 * *
18 ***************************************************************************/
19
20 #include "modelloader.h"
21 #include "model.h"
22 #include "ac3dmodel.h" // For AC3D structure manipulation
23 #include "ac3dobject.h" // For normal calculation
24 #include "texture.h" // for texture manipulation
25 #include "star.h" // Triangulation algorithms
26
27 #include <vector>
28 #include <fstream>
29 #include <cstring>
30
31 #define OC_AC3D_MAX_LINE_LENGTH 1024
32
33 using std::vector;
34 using std::ifstream;
35
36 using namespace AC3D;
37
38
39 // Local module static variables
40 static float locAccu[3]; ///< Accumulate the locations command
41 static bool bNeedAlpha; ///< Alpha processing state
42
43 // Debug variables
44 // unsigned int nbPoly;
45 // unsigned int nbVertex;
46
47
48 /*=====================================================================*/
49 Model* const
Load(const string & rcsFileName)50 ModelLoader::Load(
51 const string & rcsFileName )
52 {
53 OPENCITY_DEBUG( rcsFileName.c_str() );
54
55 // File extension checking
56 if (rcsFileName.rfind(".ac") != rcsFileName.npos) {
57 return ModelLoader::LoadAC3D(rcsFileName);
58 }
59 else {
60 OPENCITY_ERROR( "Unable to load model file: " << rcsFileName );
61 return NULL;
62 }
63
64 // Otherwise, return NULL
65 assert(0);
66 return NULL;
67 }
68
69
70 /*=====================================================================*/
71 Model* const
LoadAC3D(const string & rcsFileName)72 ModelLoader::LoadAC3D( const string & rcsFileName )
73 {
74 AC3DModel ac3dmodel( rcsFileName );
75 vector<AC3DMaterial> vMaterial;
76 GLuint list = 0, listTwoSide = 0, listAlpha = 0;
77 string strPath = "", strFile = "";
78
79 if (!ac3dmodel.IsGood())
80 return NULL;
81
82 // Don't go further if there is no object to parse
83 const AC3DObject* const pObject = ac3dmodel.GetPObject();
84 if (pObject == NULL)
85 return NULL;
86
87 // Get the path
88 if (rcsFileName.rfind( '/' ) != rcsFileName.npos ) {
89 strPath = rcsFileName.substr( 0, rcsFileName.rfind('/') );
90 }
91 else {
92 strPath = ".";
93 }
94 //debug cout << "path: " << strPath << endl;
95
96 vMaterial = ac3dmodel.GetVMaterial();
97
98 // Initialize the model view matrix
99 glMatrixMode( GL_MODELVIEW );
100 glLoadIdentity();
101
102 // Initialize the "loc" command accumulation variable
103 locAccu[0] = .0;
104 locAccu[1] = .0;
105 locAccu[2] = .0;
106
107 // Initialize the alpha state
108 bNeedAlpha = false;
109
110 // Debug: count the number of polys and vertex
111 // nbPoly = 0;
112 // nbVertex = 0;
113
114 // Load all the texture used by the model
115 Texture oModelTexture;
116 _AC3DTextureToGL( pObject, strFile );
117 if (strFile != "") {
118 oModelTexture = Texture( strPath + "/" + strFile );
119 }
120 GLuint uiName = oModelTexture.GetName();
121
122
123 /*=====================================================================*/
124 // Recursively load all the objects into the _opaque_ one side display list
125 list = glGenLists( 1 );
126 glNewList( list, GL_COMPILE );
127 glPushAttrib( GL_ENABLE_BIT );
128 glEnable( GL_CULL_FACE );
129
130 // Enable the texture target and bind the _first_ texture only
131 if ( glIsTexture(uiName) == GL_TRUE ) {
132 glEnable( GL_TEXTURE_2D );
133 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
134 glBindTexture( GL_TEXTURE_2D, uiName );
135 }
136 else {
137 glDisable( GL_TEXTURE_2D );
138 }
139
140 // Load all the vertex
141 glBegin( GL_TRIANGLES );
142 _AC3DVertexToGL( strPath, vMaterial, pObject, false, false );
143 glEnd();
144
145 // Restore OpenGL attributes
146 glPopAttrib();
147 glEndList();
148
149
150 /*=====================================================================*/
151 // Recursively load all the objects into the _opaque_ two side display list
152 listTwoSide = glGenLists( 1 );
153 glNewList( listTwoSide, GL_COMPILE );
154 glPushAttrib( GL_ENABLE_BIT );
155
156 // Enable the texture target and bind the _first_ texture only
157 if ( glIsTexture(uiName) == GL_TRUE ) {
158 glEnable( GL_TEXTURE_2D );
159 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
160 glBindTexture( GL_TEXTURE_2D, uiName );
161 }
162 else {
163 glDisable( GL_TEXTURE_2D );
164 }
165
166 // Load all the vertex
167 glBegin( GL_TRIANGLES );
168 _AC3DVertexToGL( strPath, vMaterial, pObject, false, true );
169 glEnd();
170
171 // Restore OpenGL attributes
172 glPopAttrib();
173 glEndList();
174
175
176 /*=====================================================================*/
177 if (bNeedAlpha) {
178 // Recursively load all the objects into the _alpha_ display list
179 listAlpha = glGenLists( 1 );
180 glNewList( listAlpha, GL_COMPILE );
181 glPushAttrib( GL_ENABLE_BIT );
182
183 glEnable( GL_BLEND );
184 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
185 glEnable(GL_ALPHA_TEST);
186 glAlphaFunc(GL_GREATER, 0.2);
187 // Enable the texture target and bind the _first_ texture only
188 if ( glIsTexture(uiName) == GL_TRUE ) {
189 glEnable( GL_TEXTURE_2D );
190 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
191 glBindTexture( GL_TEXTURE_2D, uiName );
192 }
193 else {
194 glDisable( GL_TEXTURE_2D );
195 }
196
197 // Load all the vertex
198 glBegin( GL_TRIANGLES );
199 _AC3DVertexToGL( strPath, vMaterial, pObject, true );
200 glEnd();
201
202 // Restore all enabled bits
203 glPopAttrib();
204 glEndList();
205 } // if (bNeedAlpha)
206
207
208 // Debug: print out the number of polys
209 // cout << "Number of polygons: " << nbPoly
210 // << " / vertex: " << nbVertex << endl;
211
212 Model* pModel = NULL;
213 if (glIsTexture(uiName)) {
214 pModel = new Model( list, listTwoSide, listAlpha, strPath + "/" + strFile );
215 }
216 else {
217 pModel = new Model( list, listTwoSide, listAlpha );
218 }
219 assert( pModel != NULL );
220
221 return pModel;
222 }
223
224
225 /*=====================================================================*/
226 Vertex
GetNormal(Vertex & vO,Vertex & vA,Vertex & vB)227 ModelLoader::GetNormal(
228 Vertex & vO,
229 Vertex & vA,
230 Vertex & vB )
231 {
232 static Vertex a, b, c;
233
234 /// This is the secret formula: c = a^b ;)
235 // cx = ay * bz - by * az;
236 // cy = bx * az - ax * bz;
237 // cz = ax * by - bx * ay;
238
239 // Calculate the relative coordinates of A and B to O
240 a.x = vA.x - vO.x;
241 a.y = vA.y - vO.y;
242 a.z = vA.z - vO.z;
243
244 b.x = vB.x - vO.x;
245 b.y = vB.y - vO.y;
246 b.z = vB.z - vO.z;
247
248 // Now, calculate the normal
249 c.x = a.y * b.z - b.y * a.z;
250 c.y = b.x * a.z - a.x * b.z;
251 c.z = a.x * b.y - b.x * a.y;
252
253 return c;
254 }
255
256
257 /*=====================================================================*/
258 /* PRIVATE METHODS */
259 /*=====================================================================*/
260 void
_AC3DTextureToGL(const AC3DObject * const pObject,string & strTextureFile)261 ModelLoader::_AC3DTextureToGL
262 (
263 const AC3DObject* const pObject,
264 string& strTextureFile
265 )
266 {
267 vector<AC3DObject*>::size_type posObj, sizeObj;
268 vector<AC3DObject*> vpObj;
269
270
271 assert( pObject != NULL );
272
273 // Get the texture
274 if ( pObject->GetTextureFile() != "" ) {
275 if (strTextureFile == "") {
276 strTextureFile = pObject->GetTextureFile();
277 }
278 else {
279 if (strTextureFile != pObject->GetTextureFile()) {
280 OPENCITY_FATAL( "The model tries to use multiple textures" );
281 assert( 0 );
282 }
283 }
284 }
285
286 // Parse all the child objects and retrieve the texture
287 vpObj = pObject->GetVPObject();
288 sizeObj = vpObj.size();
289 for (posObj = 0; posObj < sizeObj; posObj++) {
290 _AC3DTextureToGL( vpObj[posObj], strTextureFile );
291 }
292 }
293
294
295 /*=====================================================================*/
296 // TRIANGLES version, inline triangulation
297 void
_AC3DVertexToGL(const string & strPath,const vector<AC3DMaterial> & vMaterial,const AC3DObject * const pObject,const bool bProcessTranslucent,const bool bProcessTwoSide)298 ModelLoader::_AC3DVertexToGL
299 (
300 const string& strPath,
301 const vector<AC3DMaterial>& vMaterial,
302 const AC3DObject* const pObject,
303 const bool bProcessTranslucent,
304 const bool bProcessTwoSide
305 )
306 {
307 const float* loc;
308 AC3DMaterial mat;
309 vector<Vertex> vVertex;
310
311 vector<AC3DSurface*>::size_type pos, size;
312 vector<AC3DSurface*> vpSurface;
313
314 vector<AC3DObject*>::size_type posObj, sizeObj;
315 vector<AC3DObject*> vpObj;
316
317 vector<Ref>::size_type posRef, sizeRef;
318 vector<Ref> vRef;
319 Ref r1, r2, r3;
320
321 // Used for normal calculation
322 Vertex v1, v2, v3, normal;
323
324 assert( pObject != NULL );
325 loc = pObject->GetLoc();
326 locAccu[0] += loc[0];
327 locAccu[1] += loc[1];
328 locAccu[2] += loc[2];
329
330 // Does this object need alpha processing ?
331 if (pObject->IsTranslucent()) {
332 bNeedAlpha = true;
333 }
334
335 // Process only objects that we are asked to do
336 if (bProcessTranslucent xor pObject->IsTranslucent())
337 goto process_child_objects;
338
339 vVertex = pObject->GetVVertex();
340 vpSurface = pObject->GetVPSurface();
341
342 size = vpSurface.size();
343 for (pos = 0; pos < size; pos++) {
344 // Process only the polygons with the requested number of sides
345 if (!bProcessTranslucent and (bProcessTwoSide xor vpSurface[pos]->IsTwoSide())) {
346 continue;
347 }
348
349 // Debug ++nbPoly;
350 // Get the material of the current surface
351 mat = vMaterial[ vpSurface[pos]->GetMat() ];
352 // Set the color of the surface with COLOR_MATERIAL enabled
353 glColor3f( mat.rgb.fR, mat.rgb.fG, mat.rgb.fB );
354 // glColor4f( 1, 1, 1, 1 );
355
356 vRef = vpSurface[pos]->GetVRef();
357 sizeRef = vRef.size();
358
359 assert( sizeRef >= 3 ); // We need at least one triangle
360 for (posRef = 1; posRef < sizeRef-1; posRef++) {
361 // Fist vertex
362 r1 = vRef[0];
363 v1.x = vVertex[r1.uiVertIndex].x + locAccu[0];
364 v1.y = vVertex[r1.uiVertIndex].y + locAccu[1];
365 v1.z = vVertex[r1.uiVertIndex].z + locAccu[2];
366
367 // Second vertex
368 r2 = vRef[posRef];
369 v2.x = vVertex[r2.uiVertIndex].x + locAccu[0];
370 v2.y = vVertex[r2.uiVertIndex].y + locAccu[1];
371 v2.z = vVertex[r2.uiVertIndex].z + locAccu[2];
372
373 // Third vertex
374 r3 = vRef[posRef+1];
375 v3.x = vVertex[r3.uiVertIndex].x + locAccu[0];
376 v3.y = vVertex[r3.uiVertIndex].y + locAccu[1];
377 v3.z = vVertex[r3.uiVertIndex].z + locAccu[2];
378
379 // Now issue the OpenGL commands
380 normal = GetNormal( v1, v2, v3 );
381 glNormal3f( normal.x, normal.y, normal.z );
382
383 glTexCoord2f( r1.fTexS, r1.fTexT );
384 glVertex3f( v1.x, v1.y, v1.z );
385
386 glTexCoord2f( r2.fTexS, r2.fTexT );
387 glVertex3f( v2.x, v2.y, v2.z );
388
389 glTexCoord2f( r3.fTexS, r3.fTexT );
390 glVertex3f( v3.x, v3.y, v3.z );
391 }
392 } // For each surface
393
394 process_child_objects:
395
396 // Parse all the child objects
397 vpObj = pObject->GetVPObject();
398 sizeObj = vpObj.size();
399 for (posObj = 0; posObj < sizeObj; posObj++) {
400 _AC3DVertexToGL( strPath, vMaterial, vpObj[posObj], bProcessTranslucent, bProcessTwoSide );
401 }
402
403 locAccu[0] -= loc[0];
404 locAccu[1] -= loc[1];
405 locAccu[2] -= loc[2];
406 }
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427