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