00001 #define GL_GLEXT_PROTOTYPES 00002 00003 #include "model.h" 00004 00005 JOEMODEL::JOEMODEL() 00006 { 00007 // Set the pointers to null 00008 pObject = NULL; 00009 loadedfile = false; 00010 // has_paint = false; 00011 00012 radius = 0.0; 00013 radiusxz = 0.0; 00014 00015 int i; 00016 for (i = 0; i < MAX_TEXTURE_UNITS; i++) 00017 { 00018 texturemode[i] = TEXTUREMODE_NOTEX; 00019 tuenable[i] = true; 00020 autounload[i] = true; 00021 } 00022 00023 collidecachepos = 0; 00024 for (i = 0; i < NUM_COLLIDE_CACHE; i++) 00025 collidecache[i] = 0; 00026 } 00027 00028 int JOEMODEL::BinaryRead(void * buffer, unsigned int size, unsigned int count, FILE * f, JOEPACK * pack) 00029 { 00030 if (pack == NULL) 00031 { 00032 return fread(buffer, size, count, f); 00033 } 00034 else 00035 { 00036 return pack->Pack_fread(buffer, size, count); 00037 } 00038 } 00039 00040 bool JOEMODEL::Load(string strFileName, bool autoloadtexture) 00041 { 00042 return this->Load(strFileName, autoloadtexture, NULL); 00043 } 00044 00045 bool JOEMODEL::Load(string strFileName, bool autoloadtexture, JOEPACK * pack) 00046 { 00047 modelpath = strFileName; 00048 00049 if (loadedfile) 00050 { 00051 int i; 00052 00053 for (i = 0; i < MAX_TEXTURE_UNITS; i++) 00054 { 00055 /*if (texturemode[i] != TEXTUREMODE_NOTEX && autounload[i]) 00056 { 00057 glDeleteTextures(1, &textureid[i]); 00058 }*/ 00059 00060 textureid[i].Unload(); 00061 00062 texturemode[i] = TEXTUREMODE_NOTEX; 00063 } 00064 00065 for (i = 0; i < MAX_TEXTURE_UNITS; i++) 00066 { 00067 texturemode[i] = TEXTUREMODE_NOTEX; 00068 tuenable[i] = true; 00069 autounload[i] = true; 00070 } 00071 00072 loadedfile = false; 00073 00074 if (pObject != NULL) 00075 { 00076 for (i = 0; i < pObject->info.num_frames; i++) 00077 { 00078 delete [] pObject->frames[i].faces; 00079 delete [] pObject->frames[i].verts; 00080 delete [] pObject->frames[i].normals; 00081 delete [] pObject->frames[i].texcoords; 00082 } 00083 00084 delete [] pObject->frames; 00085 00086 delete pObject; 00087 } 00088 00089 pObject = NULL; 00090 00091 glDeleteLists(static_list, 1); 00092 static_list = 0; 00093 } 00094 00095 collidecachepos = 0; 00096 00097 char strMessage[255] = {0}; 00098 00099 string filename = strFileName; 00100 00101 FILE * m_FilePointer = NULL; 00102 00103 //open file 00104 bool fileinpack = false; 00105 if (pack == NULL) 00106 m_FilePointer = fopen(filename.c_str(), "rb"); 00107 else 00108 fileinpack = pack->Pack_fopen(filename); 00109 00110 // Make sure we have a valid file pointer (we found the file) 00111 if ((pack == NULL && !m_FilePointer) || (pack != NULL && !fileinpack)) 00112 { 00113 // Display an error message and don't load anything if no file was found 00114 sprintf(strMessage, "Unable to find the file: %s!", strFileName.c_str()); 00115 loadedfile = false; 00116 cerr << strMessage << endl; 00117 return false; 00118 } 00119 00120 if (autoloadtexture) 00121 { 00122 if (utility.FileExists(FileToPNG(filename))) 00123 { 00124 textureid[0].Load(FileToPNG(filename), true); 00125 00126 texturemode[0] = TEXTUREMODE_TEX; 00127 } 00128 else 00129 { 00130 if (utility.FileExists(FileToTexturesizePNG(filename))) 00131 { 00132 textureid[0].Load(FileToTexturesizePNG(filename), true); 00133 texturemode[0] = TEXTUREMODE_TEX; 00134 } 00135 } 00136 } 00137 00138 bool val = LoadFromHandle(m_FilePointer, pack); 00139 00140 // Clean up after everything 00141 CleanUp(m_FilePointer, pack); 00142 00143 return val; 00144 } 00145 00146 bool JOEMODEL::LoadFromHandle(FILE * m_FilePointer, JOEPACK * pack) 00147 { 00148 char strMessage[255] = {0}; 00149 00150 //create a new object 00151 pObject = new JOEObject; 00152 00153 // Read the header data and store it in our variable 00154 BinaryRead(&pObject->info, sizeof(JOEHeader), 1, m_FilePointer, pack); 00155 00156 pObject->info.magic = ENDIAN_SWAP_32(pObject->info.magic); 00157 pObject->info.version = ENDIAN_SWAP_32(pObject->info.version); 00158 pObject->info.num_faces = ENDIAN_SWAP_32(pObject->info.num_faces); 00159 pObject->info.num_frames = ENDIAN_SWAP_32(pObject->info.num_frames); 00160 00161 // Make sure the version is what we expect or else it's a bad egg 00162 if(pObject->info.version != JOE_VERSION) 00163 { 00164 // Display an error message for bad file format, then stop loading 00165 sprintf(strMessage, "Invalid file format (Version is %d not %d): %s!", pObject->info.version, JOE_VERSION, modelpath.c_str()); 00166 loadedfile = false; 00167 cerr << strMessage << endl; 00168 delete [] pObject; 00169 pObject = NULL; 00170 return false; 00171 } 00172 00173 if (pObject->info.num_faces > 32000) 00174 { 00175 cout << modelpath << " has " << pObject->info.num_faces << " faces (max 32768), crashing or other problems may occur." << endl; 00176 } 00177 00178 loadedfile = true; 00179 00180 //cout << pObject->info.version << "," << pObject->info.num_faces << endl; 00181 00182 // Read in the model and animation data 00183 ReadData(m_FilePointer, pack); 00184 00185 //optimize frame zero into a static display list 00186 static_list = glGenLists(1); 00187 glNewList (static_list, GL_COMPILE); 00188 Draw(0,0,0); 00189 glEndList (); 00190 00191 // Return a success 00192 return true; 00193 } 00194 00195 void JOEMODEL::ReadData(FILE *m_FilePointer, JOEPACK * pack) 00196 { 00197 int num_frames = pObject->info.num_frames; 00198 int num_faces = pObject->info.num_faces; 00199 00200 pObject->frames = new JOEFrame [num_frames]; 00201 00202 int i; 00203 for (i = 0; i < num_frames; i++) 00204 //for (i = 0; i < 1; i++) 00205 { 00206 pObject->frames[i].faces = new JOEFace [num_faces]; 00207 00208 BinaryRead(pObject->frames[i].faces, sizeof(JOEFace), num_faces, m_FilePointer, pack); 00209 CorrectEndian(pObject->frames[i].faces, num_faces); 00210 00211 BinaryRead(&pObject->frames[i].num_verts, sizeof(int), 1, m_FilePointer, pack); 00212 pObject->frames[i].num_verts = ENDIAN_SWAP_32(pObject->frames[i].num_verts); 00213 BinaryRead(&pObject->frames[i].num_texcoords, sizeof(int), 1, m_FilePointer, pack); 00214 pObject->frames[i].num_texcoords = ENDIAN_SWAP_32(pObject->frames[i].num_texcoords); 00215 BinaryRead(&pObject->frames[i].num_normals, sizeof(int), 1, m_FilePointer, pack); 00216 pObject->frames[i].num_normals = ENDIAN_SWAP_32(pObject->frames[i].num_normals); 00217 //cout << pObject->frames[i].num_verts << "," << pObject->frames[i].num_texcoords << "," << pObject->frames[i].num_normals << endl; 00218 00219 pObject->frames[i].verts = new JOEVertex [pObject->frames[i].num_verts]; 00220 pObject->frames[i].normals = new JOEVertex [pObject->frames[i].num_normals]; 00221 pObject->frames[i].texcoords = new JOETexCoord [pObject->frames[i].num_texcoords]; 00222 00223 BinaryRead(pObject->frames[i].verts, sizeof(JOEVertex), pObject->frames[i].num_verts, m_FilePointer, pack); 00224 CorrectEndian(pObject->frames[i].verts, pObject->frames[i].num_verts); 00225 BinaryRead(pObject->frames[i].normals, sizeof(JOEVertex), pObject->frames[i].num_normals, m_FilePointer, pack); 00226 CorrectEndian(pObject->frames[i].normals, pObject->frames[i].num_normals); 00227 BinaryRead(pObject->frames[i].texcoords, sizeof(JOETexCoord), pObject->frames[i].num_texcoords, m_FilePointer, pack); 00228 CorrectEndian(pObject->frames[i].texcoords, pObject->frames[i].num_texcoords); 00229 00230 //cout << pObject->frames[i].texcoords[0].u << "," << pObject->frames[i].texcoords[0].v << endl; 00231 } 00232 00233 float maxv[3]; 00234 float minv[3]; 00235 bool havevals[6]; 00236 int n = 0; 00237 for (n = 0; n < 6; n++) 00238 havevals[n] = false; 00239 00240 //go do scaling and flip axes 00241 for (i = 0; i < num_frames; i++) 00242 { 00243 int v; 00244 00245 for (v = 0; v < pObject->frames[i].num_verts; v++) 00246 { 00247 VERTEX temp; 00248 00249 temp.Set(pObject->frames[i].verts[v].vertex); 00250 temp.Scale(MODEL_SCALE); 00251 00252 //cache for bbox stuff 00253 for (n = 0; n < 3; n++) 00254 { 00255 if (temp.v3()[n] > maxv[n] || !havevals[n]) 00256 { 00257 maxv[n] = temp.v3()[n]; 00258 havevals[n] = true; 00259 } 00260 if (temp.v3()[n] < minv[n] || !havevals[n+3]) 00261 { 00262 minv[n] = temp.v3()[n]; 00263 havevals[n+3] = true; 00264 } 00265 } 00266 00267 float r = temp.len(); 00268 float rxz = sqrt(temp.x*temp.x+temp.z*temp.z); 00269 if (r > radius) 00270 radius = r; 00271 if (rxz > radiusxz) 00272 radiusxz = rxz; 00273 pObject->frames[i].verts[v].vertex[0] = temp.y; 00274 pObject->frames[i].verts[v].vertex[1] = temp.z; 00275 pObject->frames[i].verts[v].vertex[2] = -temp.x; 00276 } 00277 } 00278 00279 //make sure vertex ordering is consistent with normals 00280 for (i = 0; i < pObject->info.num_faces; i++) 00281 { 00282 short vi[3]; 00283 VERTEX tri[3]; 00284 VERTEX norms[3]; 00285 for (unsigned int v = 0; v < 3; v++) 00286 { 00287 vi[v] = GetFace(i)[v]; 00288 tri[v].Set(GetVert(vi[v])); 00289 norms[v].Set(GetNorm(GetNormIdx(i)[v])); 00290 } 00291 VERTEX norm; 00292 for (unsigned int v = 0; v < 3; v++) 00293 norm = norm + norms[v]; 00294 norm = norm.normalize(); 00295 VERTEX tnorm = (tri[2] - tri[0]).cross(tri[1] - tri[0]); 00296 if (norm.dot(tnorm) > 0) 00297 { 00298 /*short tvi = pObject->frames[0].faces[i].vertexIndex[1]; 00299 pObject->frames[0].faces[i].vertexIndex[1] = pObject->frames[0].faces[i].vertexIndex[2]; 00300 pObject->frames[0].faces[i].vertexIndex[2] = tvi; 00301 00302 tvi = pObject->frames[0].faces[i].normalIndex[1]; 00303 pObject->frames[0].faces[i].normalIndex[1] = pObject->frames[0].faces[i].normalIndex[2]; 00304 pObject->frames[0].faces[i].normalIndex[2] = tvi; 00305 00306 tvi = pObject->frames[0].faces[i].textureIndex[1]; 00307 pObject->frames[0].faces[i].textureIndex[1] = pObject->frames[0].faces[i].textureIndex[2]; 00308 pObject->frames[0].faces[i].textureIndex[2] = tvi;*/ 00309 00310 /*short tvi = vi[1]; 00311 vi[1] = vi[2]; 00312 vi[2] = tvi;*/ 00313 } 00314 } 00315 00316 VERTEX minvals, maxvals; 00317 minvals.Set(minv); 00318 maxvals.Set(maxv); 00319 bbox.SetFromCorners(minvals, maxvals); 00320 00321 VERTEX center; 00322 center = bbox.GetCenter(); 00323 radius = (minvals - center).len(); 00324 VERTEX mvnoy; 00325 mvnoy = minvals; 00326 mvnoy.y = 0; 00327 center.y = 0; 00328 radiusxz = (mvnoy - center).len(); 00329 00330 //cout << m_Header.numTriangles << endl; 00331 //cout << m_Header.numFrames << endl; 00332 00333 // Here we allocate all of our memory from the header's information 00334 /*m_pSkins = new tMd2Skin [m_Header.numSkins]; 00335 m_pTexCoords = new tMd2TexCoord [m_Header.numTexCoords]; 00336 m_pTriangles = new tMd2Face [m_Header.numTriangles]; 00337 m_pFrames = new tMd2Frame [m_Header.numFrames]; 00338 00339 // Next, we start reading in the data by seeking to our skin names offset 00340 fseek(m_FilePointer, m_Header.offsetSkins, SEEK_SET); 00341 00342 // Depending on the skin count, we read in each skin for this model 00343 fread(m_pSkins, sizeof(tMd2Skin), m_Header.numSkins, m_FilePointer); 00344 00345 // Move the file pointer to the position in the file for texture coordinates 00346 fseek(m_FilePointer, m_Header.offsetTexCoords, SEEK_SET); 00347 00348 // Read in all the texture coordinates in one fell swoop 00349 fread(m_pTexCoords, sizeof(tMd2TexCoord), m_Header.numTexCoords, m_FilePointer); 00350 00351 // Move the file pointer to the triangles/face data offset 00352 fseek(m_FilePointer, m_Header.offsetTriangles, SEEK_SET); 00353 00354 // Read in the face data for each triangle (vertex and texCoord indices) 00355 fread(m_pTriangles, sizeof(tMd2Face), m_Header.numTriangles, m_FilePointer); 00356 00357 // Move the file pointer to the vertices (frames) 00358 fseek(m_FilePointer, m_Header.offsetFrames, SEEK_SET); 00359 00361 00362 // In the last tutorial we just read in one frame of animation here. Now we are going 00363 // to extract every key frame from the .MD2 model. These key frames will be used to 00364 // interpolate between each other to form the somewhat smooth animation. 00365 // The only thing different is that we are putting a for loop in front, then changing 00366 // the '0' for the m_pFrames[0] in the last tutorial, to a 'i'. The loop will 00367 // continue until 'i' has reached the number of key frames for this model. 00368 00369 int i; 00370 for (i=0; i < m_Header.numFrames; i++) 00371 { 00372 // Assign our alias frame to our buffer memory 00373 tMd2AliasFrame *pFrame = (tMd2AliasFrame *) buffer; 00374 00375 // Allocate the memory for the first frame of animation's vertices 00376 m_pFrames[i].pVertices = new tMd2Triangle [m_Header.numVertices]; 00377 00378 //cout << "about to read raw data" << endl; 00379 00380 // Read in the first frame of animation 00381 int numread = fread(pFrame, m_Header.frameSize, 1, m_FilePointer); 00382 00383 //cout << "read raw data: " << m_Header.frameSize << "," << numread << endl; 00384 00385 // Copy the name of the animation to our frames array 00386 strcpy(m_pFrames[i].strName, pFrame->name); 00387 00388 // Store off a vertex array pointer to cut down large lines of code 00389 tMd2Triangle *pVertices = m_pFrames[i].pVertices; 00390 00391 //cout << "reading verts: " << m_Header.numVertices << endl; 00392 00393 int j; 00394 00395 // Go through all of the number of vertices and assign the scale and translations. 00396 // Store the vertices in our current frame's vertex list array, while swapping Y and Z. 00397 // Notice we also negate the Z axis as well to make the swap correctly. 00398 for (j=0; j < m_Header.numVertices; j++) 00399 { 00400 //cout << "verts" << endl; 00401 float temp = pFrame->aliasVertices[j].vertex[0]; 00402 //cout << "read temp: " << temp << endl; 00403 pVertices[j].vertex[0] = temp; 00404 //cout << "assigned temp" << endl; 00405 pVertices[j].vertex[2] = (pFrame->aliasVertices[j].vertex[1]); 00406 pVertices[j].vertex[1] = pFrame->aliasVertices[j].vertex[2]; 00407 //cout << "norms" << endl; 00408 pVertices[j].normal[0] = pFrame->aliasVertices[j].normal[0]; 00409 pVertices[j].normal[2] = (pFrame->aliasVertices[j].normal[1]); 00410 pVertices[j].normal[1] = pFrame->aliasVertices[j].normal[2]; 00411 00412 //cout << pVertices[j].vertex[0] << "," << pVertices[j].vertex[1] << "," << pVertices[j].vertex[2] << endl; 00413 } 00414 //cout << "finished reading verts" << endl; 00415 } 00416 */ 00417 //cout << "done" << endl; 00418 } 00419 00420 void JOEMODEL::CleanUp(FILE *m_FilePointer, JOEPACK * pack) 00421 { 00422 // This just just the regular cleanup or our md2 model class. We can free 00423 // all of this data because we already have it stored in our own structures. 00424 00425 if (pack == NULL) 00426 fclose(m_FilePointer); // Close the current file pointer 00427 else 00428 pack->Pack_fclose(); 00429 } 00430 00431 extern bool verbose_output; 00432 JOEMODEL::~JOEMODEL() 00433 { 00434 if (verbose_output) 00435 cout << "joemodel deinit" << endl; 00436 00437 int i; 00438 00439 // Free the faces, normals, vertices, and texture coordinates. 00440 /* 00441 delete [] pObject[i].pFaces; 00442 delete [] pObject[i].pNormals; 00443 delete [] pObject[i].pVerts; 00444 delete [] pObject[i].pTexVerts;*/ 00445 00446 if (pObject != NULL) 00447 { 00448 for (i = 0; i < pObject->info.num_frames; i++) 00449 { 00450 delete [] pObject->frames[i].faces; 00451 delete [] pObject->frames[i].verts; 00452 delete [] pObject->frames[i].normals; 00453 delete [] pObject->frames[i].texcoords; 00454 } 00455 00456 delete [] pObject->frames; 00457 00458 delete pObject; 00459 } 00460 00461 // int i; 00462 00463 /*glDeleteTextures(1, &texid); 00464 00465 if (has_illum) 00466 glDeleteTextures(1, &illum_tex);*/ 00467 00468 for (i = 0; i < MAX_TEXTURE_UNITS; i++) 00469 { 00470 /*if (texturemode[i] != TEXTUREMODE_NOTEX && autounload[i]) 00471 { 00472 glDeleteTextures(1, &textureid[i]); 00473 }*/ 00474 00475 textureid[i].Unload(); 00476 00477 texturemode[i] = TEXTUREMODE_NOTEX; 00478 } 00479 00480 glDeleteLists(static_list, 1); 00481 } 00482 00488 00489 void JOEMODEL::Draw(int currentFrame, float t) 00490 { 00491 Draw(currentFrame, currentFrame+1, t); 00492 } 00493 00494 void JOEMODEL::NewDraw(int currentFrame, float t, VERTEX lightdir, QUATERNION rotation, int pass) 00495 { 00496 NewDraw(currentFrame, currentFrame+1, t, lightdir, rotation, pass); 00497 } 00498 00499 void JOEMODEL::Draw(int currentFrame, int nextframe, float t) 00500 { 00501 utility.SelectTU(0); 00502 glEnable(GL_TEXTURE_2D); 00503 00504 int num_frames = pObject->info.num_frames; 00505 int num_faces = pObject->info.num_faces; 00506 JOEFrame * pFrame, * pNextFrame; 00507 00508 if (currentFrame >= num_frames) 00509 { 00510 currentFrame = num_frames-1; 00511 } 00512 00513 if (nextframe >= num_frames) 00514 nextframe = num_frames-1; 00515 00516 //cout << currentFrame << "," << nextframe << endl; 00517 00518 pFrame = &pObject->frames[currentFrame]; 00519 pNextFrame = &pObject->frames[nextframe]; 00520 00521 // Start rendering triangles 00522 glBegin(GL_TRIANGLES); 00523 00524 // Go through all of the faces (polygons) of the current frame and draw them 00525 for(int j = 0; j < num_faces; j++) 00526 { 00527 00528 //calculate normals for flat shading 00529 VERTEX a, b, c; 00530 a.Set(pFrame->verts[pFrame->faces[j].vertexIndex[0]].vertex); 00531 b.Set(pFrame->verts[pFrame->faces[j].vertexIndex[1]].vertex); 00532 c.Set(pFrame->verts[pFrame->faces[j].vertexIndex[2]].vertex); 00533 00534 float v0[3], v1[3], v2[3]; 00535 v0[0] = a.x; v0[1] = a.y; v0[2] = a.z; 00536 v1[0] = b.x; v1[1] = b.y; v1[2] = b.z; 00537 v2[0] = c.x; v2[1] = c.y; v2[2] = c.z; 00538 00539 float vb[3], va[3]; 00540 int i; 00541 for (i = 0; i < 3; i++) 00542 { 00543 va[i] = v0[i]-v1[i]; 00544 vb[i] = v2[i]-v1[i]; 00545 } 00546 float norm[3]; 00547 norm[0] = va[1]*vb[2]-vb[1]*va[2]; 00548 norm[1] = va[2]*vb[0]-vb[2]*va[0]; 00549 norm[2] = va[0]*vb[1]-vb[0]*va[1]; 00550 float normmag = sqrt(norm[0]*norm[0]+norm[1]*norm[1]+norm[2]*norm[2]); 00551 for (i = 0; i < 3; i++) 00552 { 00553 norm[i]=norm[i]/normmag; 00554 } 00555 00556 //glNormal3fv(norm); 00557 00558 00559 // Go through each corner of the triangle and draw it. 00560 for(int whichVertex = 0; whichVertex < 3; whichVertex++) 00561 { 00562 00563 // Get the index for each point of the face 00564 int vertIndex = pFrame->faces[j].vertexIndex[whichVertex]; 00565 int nextvertIndex = pNextFrame->faces[j].vertexIndex[whichVertex]; 00566 00567 // Get the index for each texture coordinate for this face 00568 int texIndex = pFrame->faces[j].textureIndex[whichVertex]; 00569 00570 int normIndex = pFrame->faces[j].normalIndex[whichVertex]; 00571 int nextnormIndex = pNextFrame->faces[j].normalIndex[whichVertex]; 00572 00573 // Pass in the texture coordinate for this vertex 00574 //glTexCoord2f(pFrame->texcoords[ texIndex ].u, pFrame->texcoords[ texIndex ].v); 00575 utility.TexCoord2d2f(0, pFrame->texcoords[ texIndex ].u, pFrame->texcoords[ texIndex ].v); 00576 utility.TexCoord2d2f(1, pFrame->texcoords[ texIndex ].u, pFrame->texcoords[ texIndex ].v); 00577 utility.TexCoord2d2f(2, pFrame->texcoords[ texIndex ].u, pFrame->texcoords[ texIndex ].v); 00578 utility.TexCoord2d2f(3, pFrame->texcoords[ texIndex ].u, pFrame->texcoords[ texIndex ].v); 00579 00580 //JGL_2MTEXCOORD(GL_TEXTURE0_ARB, pFrame->texcoords[ texIndex ].u, pFrame->texcoords[ texIndex ].v); 00581 //JGL_2MTEXCOORD(GL_TEXTURE1_ARB, pFrame->texcoords[ texIndex ].u, pFrame->texcoords[ texIndex ].v); 00582 //JGL_2MTEXCOORD(GL_TEXTURE2_ARB, pFrame->texcoords[ texIndex ].u, pFrame->texcoords[ texIndex ].v); 00583 //JGL_2MTEXCOORD(GL_TEXTURE3_ARB, pFrame->texcoords[ texIndex ].u, pFrame->texcoords[ texIndex ].v); 00584 00585 // Now we get to the interpolation part! (*Bites his nails*) 00586 // Below, we first store the vertex we are working on for the current 00587 // frame and the frame we are interpolating too. Next, we use the 00588 // linear interpolation equation to smoothly transition from one 00589 // key frame to the next. 00590 00591 VERTEX vPoint1, vPoint2; 00592 vPoint1.Set(pFrame->normals[ normIndex ].vertex); 00593 vPoint2.Set(pNextFrame->normals[ nextnormIndex ].vertex); 00594 vPoint1 = vPoint1.normalize(); 00595 vPoint2 = vPoint2.normalize(); 00596 //glNormal3f(vPoint1.x + t * (vPoint2.x - vPoint1.x), // Find the interpolated X 00597 // vPoint1.y + t * (vPoint2.y - vPoint1.y), // Find the interpolated Y 00598 // vPoint1.z + t * (vPoint2.z - vPoint1.z));// Find the interpolated Z 00599 00600 VERTEX n; 00601 n.x = vPoint1.x + t * (vPoint2.x - vPoint1.x); 00602 n.y = vPoint1.y + t * (vPoint2.y - vPoint1.y); 00603 n.z = vPoint1.z + t * (vPoint2.z - vPoint1.z); 00604 00605 //n.Set(norm[0], norm[1], norm[2]); 00606 n = n.normalize(); 00607 //n = rotation.RotateVec(n); 00608 00609 //if (pass == 1) 00610 /*if (1) 00611 TexCoordFromNormal(n, lightdir); 00612 else 00613 { 00614 if (pass == 1) 00615 ColorFromNormal(n, lightdir); 00616 else 00617 TexCoordFromNormal(n, lightdir); 00618 }*/ 00619 00620 /*QUATERNION goofyfoot; 00621 goofyfoot.Rotate(-3.141593/2.0, 1,0,0); 00622 n = goofyfoot.ReturnConjugate().RotateVec(n);*/ 00623 //glNormal3fv(n.v3()); 00624 glNormal3f(n.y,-n.x,n.z); 00625 00626 //if (pass == 2) 00627 00628 00629 // Store the current and next frame's vertex 00630 vPoint1.Set(pFrame->verts[ vertIndex ].vertex); 00631 vPoint2.Set(pNextFrame->verts[ nextvertIndex ].vertex); 00632 00633 //CVector3 vNorm1 = pFrame->pNormals[ vertIndex ]; 00634 //CVector3 vNorm2 = pNextFrame->pNormals[ vertIndex ]; 00635 00636 //glNormal3f(vNorm1.x + t * (vNorm2.x - vNorm1.x), // Find the interpolated X 00637 // vNorm1.y + t * (vNorm2.y - vNorm1.y), // Find the interpolated Y 00638 // vNorm1.z + t * (vNorm2.z - vNorm1.z));// Find the interpolated Z 00639 00640 //cout << vNorm1.x << "," << vNorm1.y << "," << vNorm1.z << endl; 00641 00642 //glNormal3f(pFirstFrame->pNormals[vertIndex].x,pFirstFrame->pNormals[vertIndex].y,pFirstFrame->pNormals[vertIndex].z); 00643 00644 // By using the equation: p(t) = p0 + t(p1 - p0), with a time t 00645 // passed in, we create a new vertex that is closer to the next key frame. 00646 VERTEX finv; 00647 finv.x = vPoint1.x + t * (vPoint2.x - vPoint1.x); 00648 finv.y = vPoint1.y + t * (vPoint2.y - vPoint1.y); 00649 finv.z = vPoint1.z + t * (vPoint2.z - vPoint1.z); 00650 00651 //tree shadows 00652 //utility.TexCoord2d2f(3, (finv.x-terrain.GetOffset().x)/terrain.GetSize().x, (finv.y-terrain.GetOffset().z)/terrain.GetSize().z); 00653 //utility.TexCoord2d2f(3, pFrame->texcoords[ texIndex ].u, pFrame->texcoords[ texIndex ].v); 00654 glVertex3f(finv.x, finv.z, finv.y); //required for compatibility with goofy vamos z=up 00655 00656 00657 } 00658 } 00659 00660 // Stop rendering the triangles 00661 glEnd(); 00662 } 00663 00664 void JOEMODEL::NewDraw(int currentFrame, int nextframe, float t, VERTEX lightdir, QUATERNION rotation, int pass) 00665 { 00666 /* int num_frames = pObject->info.num_frames; 00667 int num_faces = pObject->info.num_faces; 00668 JOEFrame * pFrame, * pNextFrame; 00669 00670 if (currentFrame >= num_frames) 00671 { 00672 currentFrame = num_frames-1; 00673 } 00674 00675 if (nextframe >= num_frames) 00676 nextframe = num_frames-1; 00677 00678 //cout << currentFrame << "," << nextframe << endl; 00679 00680 pFrame = &pObject->frames[currentFrame]; 00681 pNextFrame = &pObject->frames[nextframe]; 00682 00683 // Start rendering triangles 00684 glBegin(GL_TRIANGLES); 00685 00686 // Go through all of the faces (polygons) of the current frame and draw them 00687 for(int j = 0; j < num_faces; j++) 00688 { 00689 00690 //calculate normals for flat shading 00691 VERTEX a, b, c; 00692 a.Set(pFrame->verts[pFrame->faces[j].vertexIndex[0]].vertex); 00693 b.Set(pFrame->verts[pFrame->faces[j].vertexIndex[1]].vertex); 00694 c.Set(pFrame->verts[pFrame->faces[j].vertexIndex[2]].vertex); 00695 00696 float v0[3], v1[3], v2[3]; 00697 v0[0] = a.x; v0[1] = a.y; v0[2] = a.z; 00698 v1[0] = b.x; v1[1] = b.y; v1[2] = b.z; 00699 v2[0] = c.x; v2[1] = c.y; v2[2] = c.z; 00700 00701 float vb[3], va[3]; 00702 int i; 00703 for (i = 0; i < 3; i++) 00704 { 00705 va[i] = v0[i]-v1[i]; 00706 vb[i] = v2[i]-v1[i]; 00707 } 00708 float norm[3]; 00709 norm[0] = va[1]*vb[2]-vb[1]*va[2]; 00710 norm[1] = va[2]*vb[0]-vb[2]*va[0]; 00711 norm[2] = va[0]*vb[1]-vb[0]*va[1]; 00712 float normmag = sqrt(norm[0]*norm[0]+norm[1]*norm[1]+norm[2]*norm[2]); 00713 for (i = 0; i < 3; i++) 00714 { 00715 norm[i]=norm[i]/normmag; 00716 } 00717 00718 //glNormal3fv(norm); 00719 00720 00721 // Go through each corner of the triangle and draw it. 00722 for(int whichVertex = 0; whichVertex < 3; whichVertex++) 00723 { 00724 00725 // Get the index for each point of the face 00726 int vertIndex = pFrame->faces[j].vertexIndex[whichVertex]; 00727 int nextvertIndex = pNextFrame->faces[j].vertexIndex[whichVertex]; 00728 00729 // Get the index for each texture coordinate for this face 00730 int texIndex = pFrame->faces[j].textureIndex[whichVertex]; 00731 00732 int normIndex = pFrame->faces[j].normalIndex[whichVertex]; 00733 int nextnormIndex = pNextFrame->faces[j].normalIndex[whichVertex]; 00734 00735 // Pass in the texture coordinate for this vertex 00736 //glTexCoord2f(pFrame->texcoords[ texIndex ].u, pFrame->texcoords[ texIndex ].v); 00737 00738 JGL_2MTEXCOORD(GL_TEXTURE0_ARB, pFrame->texcoords[ texIndex ].u, pFrame->texcoords[ texIndex ].v); 00739 JGL_2MTEXCOORD(GL_TEXTURE1_ARB, pFrame->texcoords[ texIndex ].u, pFrame->texcoords[ texIndex ].v); 00740 JGL_2MTEXCOORD(GL_TEXTURE2_ARB, pFrame->texcoords[ texIndex ].u, pFrame->texcoords[ texIndex ].v); 00741 JGL_2MTEXCOORD(GL_TEXTURE3_ARB, pFrame->texcoords[ texIndex ].u, pFrame->texcoords[ texIndex ].v); 00742 00743 // Now we get to the interpolation part! (*Bites his nails*) 00744 // Below, we first store the vertex we are working on for the current 00745 // frame and the frame we are interpolating too. Next, we use the 00746 // linear interpolation equation to smoothly transition from one 00747 // key frame to the next. 00748 00749 VERTEX vPoint1, vPoint2; 00750 vPoint1.Set(pFrame->normals[ normIndex ].vertex); 00751 vPoint2.Set(pNextFrame->normals[ nextnormIndex ].vertex); 00752 vPoint1 = vPoint1.normalize(); 00753 vPoint2 = vPoint2.normalize(); 00754 //glNormal3f(vPoint1.x + t * (vPoint2.x - vPoint1.x), // Find the interpolated X 00755 // vPoint1.y + t * (vPoint2.y - vPoint1.y), // Find the interpolated Y 00756 // vPoint1.z + t * (vPoint2.z - vPoint1.z));// Find the interpolated Z 00757 00758 VERTEX n; 00759 n.x = vPoint1.x + t * (vPoint2.x - vPoint1.x); 00760 n.y = vPoint1.y + t * (vPoint2.y - vPoint1.y); 00761 n.z = vPoint1.z + t * (vPoint2.z - vPoint1.z); 00762 00763 //n.Set(norm[0], norm[1], norm[2]); 00764 n = n.normalize(); 00765 n = rotation.RotateVec(n); 00766 00767 //if (pass == 1) 00768 if (1) 00769 TexCoordFromNormal(n, lightdir); 00770 else 00771 { 00772 if (pass == 1) 00773 ColorFromNormal(n, lightdir); 00774 else 00775 TexCoordFromNormal(n, lightdir); 00776 } 00777 00778 //glNormal3fv(n.v3()); 00779 00780 //if (pass == 2) 00781 00782 00783 // Store the current and next frame's vertex 00784 vPoint1.Set(pFrame->verts[ vertIndex ].vertex); 00785 vPoint2.Set(pNextFrame->verts[ nextvertIndex ].vertex); 00786 00787 //CVector3 vNorm1 = pFrame->pNormals[ vertIndex ]; 00788 //CVector3 vNorm2 = pNextFrame->pNormals[ vertIndex ]; 00789 00790 //glNormal3f(vNorm1.x + t * (vNorm2.x - vNorm1.x), // Find the interpolated X 00791 // vNorm1.y + t * (vNorm2.y - vNorm1.y), // Find the interpolated Y 00792 // vNorm1.z + t * (vNorm2.z - vNorm1.z));// Find the interpolated Z 00793 00794 //cout << vNorm1.x << "," << vNorm1.y << "," << vNorm1.z << endl; 00795 00796 //glNormal3f(pFirstFrame->pNormals[vertIndex].x,pFirstFrame->pNormals[vertIndex].y,pFirstFrame->pNormals[vertIndex].z); 00797 00798 // By using the equation: p(t) = p0 + t(p1 - p0), with a time t 00799 // passed in, we create a new vertex that is closer to the next key frame. 00800 glVertex3f(vPoint1.x + t * (vPoint2.x - vPoint1.x), // Find the interpolated X 00801 vPoint1.y + t * (vPoint2.y - vPoint1.y), // Find the interpolated Y 00802 vPoint1.z + t * (vPoint2.z - vPoint1.z));// Find the interpolated Z 00803 00804 00805 } 00806 } 00807 00808 // Stop rendering the triangles 00809 glEnd(); 00810 */ 00811 /* 00812 // Now comes the juice of our tutorial. Fear not, this is actually very intuitive 00813 // if you drool over it for a while (stay away from the keyboard though...). 00814 // What's going on here is, we are getting our current animation that we are 00815 // on, finding the current frame of that animation that we are on, then interpolating 00816 // between that frame and the next frame. To make a smooth constant animation when 00817 // we get to the end frame, we interpolate between the last frame of the animation 00818 // and the first frame of the animation. That way, if we are doing the running 00819 // animation let's say, when the last frame of the running animation is hit, we don't 00820 // have a huge jerk when going back to the first frame of that animation. Remember, 00821 // because we have the texture and face information stored in the first frame of our 00822 // animation, we need to reference back to this frame every time when drawing the 00823 // model. The only thing the other frames store is the vertices, but no information 00824 // about them. 00825 00826 // Make sure we have valid objects just in case. (size() is in the vector class) 00827 if(pObject.size() <= 0) return; 00828 00829 // Here we grab the current animation that we are on from our model's animation list 00830 //tAnimationInfo *pAnim = &(pAnimations[0]); 00831 00832 // This gives us the current frame we are on. We mod the current frame plus 00833 // 1 by the current animations end frame to make sure the next frame is valid. 00834 // If the next frame is past our end frame, then we go back to zero. We check this next. 00835 //int nextFrame = (currentFrame + 1) % pAnim->endFrame; 00836 int nextFrame = nextframe; 00837 00838 //cout << currentFrame << "," << nextFrame << endl; 00839 00840 // If the next frame is zero, that means that we need to start the animation over. 00841 // To do this, we set nextFrame to the starting frame of this animation. 00842 //if(nextFrame == 0) 00843 // nextFrame = pAnim->startFrame; 00844 00845 // Get the current key frame we are on 00846 t3DObject *pFrame = &pObject[currentFrame]; 00847 00848 // Get the next key frame we are interpolating too 00849 t3DObject *pNextFrame = &pObject[nextFrame]; 00850 00851 // Get the first key frame so we have an address to the texture and face information 00852 t3DObject *pFirstFrame = &pObject[0]; 00853 00854 // Next, we want to get the current time that we are interpolating by. Remember, 00855 // if t = 0 then we are at the beginning of the animation, where if t = 1 we are at the end. 00856 // Anyhing from 0 to 1 can be thought of as a percentage from 0 to 100 percent complete. 00857 //float t = ReturnCurrentTime(pModel, nextFrame); 00858 00859 // Start rendering lines or triangles, depending on our current rendering mode (Lft Mouse Btn) 00860 glBegin(GL_TRIANGLES); 00861 00862 // Go through all of the faces (polygons) of the current frame and draw them 00863 for(int j = 0; j < pFrame->numOfFaces; j++) 00864 { 00865 //calculate normals for flat shading 00866 CVector3 a = pFrame->pVerts[pFirstFrame->pFaces[j].vertIndex[0]]; 00867 CVector3 b = pFrame->pVerts[pFirstFrame->pFaces[j].vertIndex[1]]; 00868 CVector3 c = pFrame->pVerts[pFirstFrame->pFaces[j].vertIndex[2]]; 00869 00870 float v0[3], v1[3], v2[3]; 00871 v0[0] = a.x; v0[1] = a.y; v0[2] = a.z; 00872 v1[0] = b.x; v1[1] = b.y; v1[2] = b.z; 00873 v2[0] = c.x; v2[1] = c.y; v2[2] = c.z; 00874 00875 float vb[3], va[3]; 00876 int i; 00877 for (i = 0; i < 3; i++) 00878 { 00879 va[i] = v0[i]-v1[i]; 00880 vb[i] = v2[i]-v1[i]; 00881 } 00882 float norm[3]; 00883 norm[0] = va[1]*vb[2]-vb[1]*va[2]; 00884 norm[1] = va[2]*vb[0]-vb[2]*va[0]; 00885 norm[2] = va[0]*vb[1]-vb[0]*va[1]; 00886 float normmag = sqrt(norm[0]*norm[0]+norm[1]*norm[1]+norm[2]*norm[2]); 00887 for (i = 0; i < 3; i++) 00888 { 00889 norm[i]=norm[i]/normmag; 00890 } 00891 00892 //glNormal3fv(norm); 00893 00894 00895 // Go through each corner of the triangle and draw it. 00896 for(int whichVertex = 0; whichVertex < 3; whichVertex++) 00897 { 00898 // Get the index for each point of the face 00899 int vertIndex = pFirstFrame->pFaces[j].vertIndex[whichVertex]; 00900 00901 // Get the index for each texture coordinate for this face 00902 int texIndex = pFirstFrame->pFaces[j].coordIndex[whichVertex]; 00903 00904 //int normIndex = pFirstFrame->pFaces[j].normalIndex[whichVertex]; 00905 00906 // Make sure there was a UVW map applied to the object. Notice that 00907 // we use the first frame to check if we have texture coordinates because 00908 // none of the other frames hold this information, just the first by design. 00909 if(pFirstFrame->pTexVerts) 00910 { 00911 // Pass in the texture coordinate for this vertex 00912 //glTexCoord2f(pFirstFrame->pTexVerts[ texIndex ].x, pFirstFrame->pTexVerts[ texIndex ].y); 00913 00914 JGL_2MTEXCOORD(GL_TEXTURE0_ARB, pFirstFrame->pTexVerts[ texIndex ].x, pFirstFrame->pTexVerts[ texIndex ].y); 00915 JGL_2MTEXCOORD(GL_TEXTURE1_ARB, pFirstFrame->pTexVerts[ texIndex ].x, pFirstFrame->pTexVerts[ texIndex ].y); 00916 JGL_2MTEXCOORD(GL_TEXTURE2_ARB, pFirstFrame->pTexVerts[ texIndex ].x, pFirstFrame->pTexVerts[ texIndex ].y); 00917 JGL_2MTEXCOORD(GL_TEXTURE3_ARB, pFirstFrame->pTexVerts[ texIndex ].x, pFirstFrame->pTexVerts[ texIndex ].y); 00918 } 00919 00920 // Now we get to the interpolation part! (*Bites his nails*) 00921 // Below, we first store the vertex we are working on for the current 00922 // frame and the frame we are interpolating too. Next, we use the 00923 // linear interpolation equation to smoothly transition from one 00924 // key frame to the next. 00925 00926 CVector3 vPoint1 = pFrame->pNormals[ vertIndex ]; 00927 CVector3 vPoint2 = pNextFrame->pNormals[ vertIndex ]; 00928 vPoint1.normalize(); 00929 vPoint2.normalize(); 00930 //glNormal3f(vPoint1.x + t * (vPoint2.x - vPoint1.x), // Find the interpolated X 00931 // vPoint1.y + t * (vPoint2.y - vPoint1.y), // Find the interpolated Y 00932 // vPoint1.z + t * (vPoint2.z - vPoint1.z));// Find the interpolated Z 00933 00934 VERTEX n; 00935 n.x = vPoint1.x + t * (vPoint2.x - vPoint1.x); 00936 n.y = vPoint1.y + t * (vPoint2.y - vPoint1.y); 00937 n.z = vPoint1.z + t * (vPoint2.z - vPoint1.z); 00938 00939 //n.Set(norm[0], norm[1], norm[2]); 00940 n = n.normalize(); 00941 n = rotation.RotateVec(n); 00942 00943 ColorFromNormal(n, lightdir); 00944 00945 // Store the current and next frame's vertex 00946 vPoint1 = pFrame->pVerts[ vertIndex ]; 00947 vPoint2 = pNextFrame->pVerts[ vertIndex ]; 00948 */ 00949 /*CVector3 vNorm1 = pFrame->pNormals[ vertIndex ]; 00950 CVector3 vNorm2 = pNextFrame->pNormals[ vertIndex ]; 00951 00952 glNormal3f(vNorm1.x + t * (vNorm2.x - vNorm1.x), // Find the interpolated X 00953 vNorm1.y + t * (vNorm2.y - vNorm1.y), // Find the interpolated Y 00954 vNorm1.z + t * (vNorm2.z - vNorm1.z));// Find the interpolated Z 00955 00956 cout << vNorm1.x << "," << vNorm1.y << "," << vNorm1.z << endl;*/ 00957 /* 00958 //glNormal3f(pFirstFrame->pNormals[vertIndex].x,pFirstFrame->pNormals[vertIndex].y,pFirstFrame->pNormals[vertIndex].z); 00959 00960 // By using the equation: p(t) = p0 + t(p1 - p0), with a time t 00961 // passed in, we create a new vertex that is closer to the next key frame. 00962 glVertex3f(vPoint1.x + t * (vPoint2.x - vPoint1.x), // Find the interpolated X 00963 vPoint1.y + t * (vPoint2.y - vPoint1.y), // Find the interpolated Y 00964 vPoint1.z + t * (vPoint2.z - vPoint1.z));// Find the interpolated Z 00965 } 00966 } 00967 00968 // Stop rendering the triangles 00969 glEnd();*/ 00970 } 00971 00972 inline void JOEMODEL::ColorFromNormal(VERTEX &norm, VERTEX &ldir) 00973 { 00974 /* VERTEX lpos; 00975 norm.z = 1.0f; 00976 00977 lpos = norm.ScaleR(2*ldir.dot(norm))-ldir; 00978 //glColor3fv(lpos.normalize().v3()); 00979 00980 lpos = lpos.normalize(); 00981 VERTEX tmp; 00982 tmp.Set(0.5,0.5,0.5); 00983 lpos = lpos.ScaleR(0.5)+tmp; 00984 glColor3fv(lpos.v3());*/ 00985 } 00986 00987 inline void JOEMODEL::TexCoordFromNormal(VERTEX &norm, VERTEX &ldir) 00988 { 00989 /* VERTEX lpos; 00990 //norm.z = 1.0f; 00991 00992 //lpos = norm.ScaleR(2*ldir.dot(norm))-ldir; 00993 //R = 2 ( N [dot] V ) V - N 00994 lpos = ldir.ScaleR(2*norm.dot(ldir))-norm; 00995 //R = N - ( 2 * N [dot] V ) V 00996 //lpos = norm - ldir.ScaleR(2*norm.dot(ldir)); 00997 //lpos = norm; 00998 00999 JGL_3MTEXCOORD(GL_TEXTURE0_ARB, lpos.x, lpos.y, lpos.z);*/ 01000 } 01001 01002 void JOEMODEL::DrawStatic() //draw frame 0, optimized for speed 01003 { 01004 int i; 01005 01006 for (i = 0; i < MAX_TEXTURE_UNITS && i < utility.numTUs(); i++) 01007 { 01008 utility.SelectTU(i); 01009 glDisable(GL_TEXTURE_2D); 01010 } 01011 01012 for (i = 0; i < MAX_TEXTURE_UNITS && i < utility.numTUs(); i++) 01013 { 01014 if (texturemode[i] != TEXTUREMODE_NOTEX) 01015 { 01016 utility.SelectTU(i); 01017 //glBindTexture(GL_TEXTURE_2D, textureid[i]); 01018 textureid[i].Activate(); 01019 if (tuenable[i]) 01020 { 01021 glEnable(GL_TEXTURE_2D); 01022 01023 //glColor4f(0,1,0,0.5); 01024 01025 //default texture environment: modulate w/ previous. 01026 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); 01027 01028 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB); 01029 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB,GL_SRC_COLOR); 01030 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE); 01031 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB,GL_SRC_COLOR); 01032 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); 01033 //glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE); 01034 01035 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS_ARB); 01036 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB,GL_SRC_ALPHA); 01037 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, GL_TEXTURE); 01038 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_ARB,GL_SRC_ALPHA); 01039 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE); 01040 //glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); 01041 01042 glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1); 01043 glTexEnvi(GL_TEXTURE_ENV, GL_ALPHA_SCALE, 1); 01044 01045 if (texturemode[i] == TEXTUREMODE_ADD) 01046 { 01047 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); 01048 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB); 01049 glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND1_RGB_ARB,GL_SRC_COLOR); 01050 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); 01051 glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_RGB_ARB,GL_SRC_COLOR); 01052 //if (i > 0) 01053 glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB_ARB, GL_ADD); 01054