1 /****************************************************************************** 2 3 @File OGLES2ChameleonMan.cpp 4 5 @Title OGLES2ChameleonMan 6 7 @Version 8 9 @Copyright Copyright (c) Imagination Technologies Limited. 10 11 @Platform Independent 12 13 @Description Shows how to perform skinning combined with Dot3 lighting 14 15 ******************************************************************************/ 16 #include "PVRShell.h" 17 #include "OGLES2Tools.h" 18 19 /****************************************************************************** 20 Constants 21 ******************************************************************************/ 22 23 // Camera constants used to generate the projection matrix 24 const float g_fCameraNear = 4.0f; 25 const float g_fCameraFar = 30000.0f; 26 27 const float g_fDemoFrameRate = 0.02f; 28 29 /****************************************************************************** 30 shader attributes 31 ******************************************************************************/ 32 33 // Skinned 34 35 // vertex attributes 36 enum EVertexAttrib { 37 VERTEX_ARRAY, NORMAL_ARRAY, TANGENT_ARRAY, BINORMAL_ARRAY, TEXCOORD_ARRAY, BONEWEIGHT_ARRAY, BONEINDEX_ARRAY, eNumAttribs }; 38 const char* g_aszAttribNames[] = { 39 "inVertex", "inNormal", "inTangent", "inBiNormal", "inTexCoord", "inBoneWeight", "inBoneIndex" }; 40 41 // shader uniforms 42 enum ESkinnnedUniform { 43 eViewProj, eLightPos, eBoneCount, eBoneMatrices, eBoneMatricesIT, ebUseDot3, eNumSkinnedUniforms }; 44 const char* g_aszSkinnedUniformNames[] = { 45 "ViewProjMatrix", "LightPos", "BoneCount", "BoneMatrixArray[0]", "BoneMatrixArrayIT[0]", "bUseDot3" }; 46 47 // Default 48 49 // vertex attributes 50 enum EDefaultVertexAttrib { 51 DEFAULT_VERTEX_ARRAY, DEFAULT_TEXCOORD_ARRAY, eNumDefaultAttribs }; 52 const char* g_aszDefaultAttribNames[] = { 53 "inVertex", "inTexCoord"}; 54 55 // shader uniforms 56 enum EDefaultUniform { 57 eDefaultMVPMatrix, eDefaultUOffset, eNumDefaultUniforms }; 58 const char* g_aszDefaultUniformNames[] = { 59 "MVPMatrix", "fUOffset" }; 60 61 /****************************************************************************** 62 Content file names 63 ******************************************************************************/ 64 65 // Source and binary shaders 66 const char c_szSkinnedFragShaderSrcFile[] = "SkinnedFragShader.fsh"; 67 const char c_szSkinnedFragShaderBinFile[] = "SkinnedFragShader.fsc"; 68 const char c_szSkinnedVertShaderSrcFile[] = "SkinnedVertShader.vsh"; 69 const char c_szSkinnedVertShaderBinFile[] = "SkinnedVertShader.vsc"; 70 const char c_szDefaultFragShaderSrcFile[] = "DefaultFragShader.fsh"; 71 const char c_szDefaultFragShaderBinFile[] = "DefaultFragShader.fsc"; 72 const char c_szDefaultVertShaderSrcFile[] = "DefaultVertShader.vsh"; 73 const char c_szDefaultVertShaderBinFile[] = "DefaultVertShader.vsc"; 74 75 // Base Textures 76 const char c_szFinalChameleonManHeadBodyTexFile[] = "FinalChameleonManHeadBody.pvr"; 77 const char c_szFinalChameleonManLegsTexFile[] = "FinalChameleonManLegs.pvr"; 78 const char c_szLampTexFile[] = "lamp.pvr"; 79 const char c_szChameleonBeltTexFile[] = "ChameleonBelt.pvr"; 80 81 const char c_szSkylineTexFile[] = "skyline.pvr"; 82 const char c_szWallDiffuseBakedTexFile[] = "Wall_diffuse_baked.pvr"; 83 84 // Tangent Space BumpMap Textures 85 const char c_szTang_space_BodyMapTexFile[] = "Tang_space_BodyMap.pvr"; 86 const char c_szTang_space_LegsMapTexFile[] = "Tang_space_LegsMap.pvr"; 87 const char c_szTang_space_BeltMapTexFile[] = "Tang_space_BeltMap.pvr"; 88 89 // POD scene files 90 const char c_szSceneFile[] = "ChameleonScene.pod"; 91 92 /**************************************************************************** 93 ** Enums ** 94 ****************************************************************************/ 95 enum EMeshes 96 { 97 eBody, 98 eLegs, 99 eBelt, 100 eWall, 101 eBackground, 102 eLights 103 }; 104 105 /**************************************************************************** 106 ** Structures 107 ****************************************************************************/ 108 109 /*!**************************************************************************** 110 Class implementing the PVRShell functions. 111 ******************************************************************************/ 112 class OGLES2ChameleonMan : public PVRShell 113 { 114 // Print3D class used to display text 115 CPVRTPrint3D m_Print3D; 116 117 // 3D Model 118 CPVRTModelPOD m_Scene; 119 120 // Model transformation variables 121 float m_fWallPos; 122 float m_fBackgroundPos; 123 float m_fLightPos; 124 125 // OpenGL handles for shaders and VBOs 126 GLuint m_uiSkinnedVertShader; 127 GLuint m_uiDefaultVertShader; 128 GLuint m_uiSkinnedFragShader; 129 GLuint m_uiDefaultFragShader; 130 GLuint* m_puiVbo; 131 GLuint* m_puiIndexVbo; 132 133 // Texture IDs 134 GLuint m_ui32TexHeadBody; 135 GLuint m_ui32TexLegs; 136 GLuint m_ui32TexBeltNormalMap; 137 GLuint m_ui32TexHeadNormalMap; 138 GLuint m_ui32TexLegsNormalMap; 139 GLuint m_ui32TexSkyLine; 140 GLuint m_ui32TexWall; 141 GLuint m_ui32TexLamp; 142 GLuint m_ui32TexBelt; 143 144 // Group shader programs and their uniform locations together 145 struct 146 { 147 GLuint uiId; 148 GLuint auiLoc[eNumSkinnedUniforms]; 149 } 150 m_SkinnedShaderProgram; 151 152 struct 153 { 154 GLuint uiId; 155 GLuint auiLoc[eNumDefaultUniforms]; 156 } 157 m_DefaultShaderProgram; 158 159 bool m_bEnableDOT3; 160 161 // Variables to handle the animation in a time-based manner 162 unsigned long m_iTimePrev; 163 float m_fFrame; 164 165 public: 166 OGLES2ChameleonMan() : m_fWallPos(0), 167 m_fBackgroundPos(0), 168 m_fLightPos(0), 169 m_puiVbo(0), 170 m_puiIndexVbo(0), 171 m_bEnableDOT3(true), 172 m_iTimePrev(0), 173 m_fFrame(0) 174 { 175 } 176 177 virtual bool InitApplication(); 178 virtual bool InitView(); 179 virtual bool ReleaseView(); 180 virtual bool QuitApplication(); 181 virtual bool RenderScene(); 182 183 bool LoadTextures(CPVRTString* pErrorStr); 184 bool LoadShaders(CPVRTString* pErrorStr); 185 void LoadVbos(); 186 187 void DrawSkinnedMesh(int i32NodeIndex); 188 }; 189 190 /*!**************************************************************************** 191 @Function LoadTextures 192 @Output pErrorStr A string describing the error on failure 193 @Return bool true if no error occured 194 @Description Loads the textures required for this training course 195 ******************************************************************************/ 196 bool OGLES2ChameleonMan::LoadTextures(CPVRTString* const pErrorStr) 197 { 198 // Load Textures 199 if(PVRTTextureLoadFromPVR(c_szFinalChameleonManHeadBodyTexFile, &m_ui32TexHeadBody) != PVR_SUCCESS) 200 { 201 *pErrorStr = CPVRTString("ERROR: Failed to load texture for Upper Body.\n"); 202 return false; 203 } 204 205 if(PVRTTextureLoadFromPVR(c_szFinalChameleonManLegsTexFile, &m_ui32TexLegs) != PVR_SUCCESS) 206 { 207 *pErrorStr = CPVRTString("ERROR: Failed to load texture for Legs.\n"); 208 return false; 209 } 210 211 if(PVRTTextureLoadFromPVR(c_szTang_space_BodyMapTexFile, &m_ui32TexHeadNormalMap) != PVR_SUCCESS) 212 { 213 *pErrorStr = CPVRTString("ERROR: Failed to load normalmap texture for Upper Body.\n"); 214 return false; 215 } 216 217 if(PVRTTextureLoadFromPVR(c_szTang_space_LegsMapTexFile, &m_ui32TexLegsNormalMap) != PVR_SUCCESS) 218 { 219 *pErrorStr = CPVRTString("ERROR: Failed to load normalmap texture for Legs.\n"); 220 return false; 221 } 222 223 if(PVRTTextureLoadFromPVR(c_szTang_space_BeltMapTexFile, &m_ui32TexBeltNormalMap) != PVR_SUCCESS) 224 { 225 *pErrorStr = CPVRTString("ERROR: Failed to load normalmap texture for Belt.\n"); 226 return false; 227 } 228 229 if(PVRTTextureLoadFromPVR(c_szSkylineTexFile, &m_ui32TexSkyLine) != PVR_SUCCESS) 230 { 231 *pErrorStr = CPVRTString("ERROR: Failed to load texture for SkyLine.\n"); 232 return false; 233 } 234 235 if(PVRTTextureLoadFromPVR(c_szWallDiffuseBakedTexFile, &m_ui32TexWall) != PVR_SUCCESS) 236 { 237 *pErrorStr = CPVRTString("ERROR: Failed to load texture for Wall.\n"); 238 return false; 239 } 240 241 if(PVRTTextureLoadFromPVR(c_szLampTexFile, &m_ui32TexLamp) != PVR_SUCCESS) 242 { 243 *pErrorStr = CPVRTString("ERROR: Failed to load texture for Lamps.\n"); 244 return false; 245 } 246 247 if(PVRTTextureLoadFromPVR(c_szChameleonBeltTexFile, &m_ui32TexBelt) != PVR_SUCCESS) 248 { 249 *pErrorStr = CPVRTString("ERROR: Failed to load texture for Belt.\n"); 250 return false; 251 } 252 253 return true; 254 } 255 256 /*!**************************************************************************** 257 @Function LoadShaders 258 @Output pErrorStr A string describing the error on failure 259 @Return bool true if no error occured 260 @Description Loads and compiles the shaders and links the shader programs 261 required for this training course 262 ******************************************************************************/ 263 bool OGLES2ChameleonMan::LoadShaders(CPVRTString* pErrorStr) 264 { 265 int i; 266 267 /* 268 Load and compile the shaders from files. 269 Binary shaders are tried first, source shaders 270 are used as fallback. 271 */ 272 273 274 // Create the skinned program 275 if(PVRTShaderLoadFromFile( 276 c_szSkinnedVertShaderBinFile, c_szSkinnedVertShaderSrcFile, GL_VERTEX_SHADER, GL_SGX_BINARY_IMG, &m_uiSkinnedVertShader, pErrorStr) != PVR_SUCCESS) 277 { 278 return false; 279 } 280 281 if(PVRTShaderLoadFromFile( 282 c_szSkinnedFragShaderBinFile, c_szSkinnedFragShaderSrcFile, GL_FRAGMENT_SHADER, GL_SGX_BINARY_IMG, &m_uiSkinnedFragShader, pErrorStr) != PVR_SUCCESS) 283 { 284 return false; 285 } 286 287 if(PVRTCreateProgram(&m_SkinnedShaderProgram.uiId, m_uiSkinnedVertShader, m_uiSkinnedFragShader, g_aszAttribNames, eNumAttribs, pErrorStr) != PVR_SUCCESS) 288 { 289 PVRShellSet(prefExitMessage, pErrorStr->c_str()); 290 return false; 291 } 292 293 // Store the location of uniforms for later use 294 for(i = 0; i < eNumSkinnedUniforms; ++i) 295 { 296 m_SkinnedShaderProgram.auiLoc[i] = glGetUniformLocation(m_SkinnedShaderProgram.uiId, g_aszSkinnedUniformNames[i]); 297 } 298 299 glUniform1i(m_SkinnedShaderProgram.auiLoc[ebUseDot3], m_bEnableDOT3); 300 301 // Set the sampler2D uniforms to corresponding texture units 302 glUniform1i(glGetUniformLocation(m_SkinnedShaderProgram.uiId, "sTexture"), 0); 303 glUniform1i(glGetUniformLocation(m_SkinnedShaderProgram.uiId, "sNormalMap"), 1); 304 305 // Create the non-skinned program 306 if(PVRTShaderLoadFromFile( 307 c_szDefaultVertShaderBinFile, c_szDefaultVertShaderSrcFile, GL_VERTEX_SHADER, GL_SGX_BINARY_IMG, &m_uiDefaultVertShader, pErrorStr) != PVR_SUCCESS) 308 { 309 return false; 310 } 311 312 if(PVRTShaderLoadFromFile( 313 c_szDefaultFragShaderBinFile, c_szDefaultFragShaderSrcFile, GL_FRAGMENT_SHADER, GL_SGX_BINARY_IMG, &m_uiDefaultFragShader, pErrorStr) != PVR_SUCCESS) 314 { 315 return false; 316 } 317 318 if(PVRTCreateProgram(&m_DefaultShaderProgram.uiId, m_uiDefaultVertShader, m_uiDefaultFragShader, g_aszDefaultAttribNames, eNumDefaultAttribs, pErrorStr) != PVR_SUCCESS) 319 { 320 PVRShellSet(prefExitMessage, pErrorStr->c_str()); 321 return false; 322 } 323 324 // Store the location of uniforms for later use 325 for(i = 0; i < eNumDefaultUniforms; ++i) 326 { 327 m_DefaultShaderProgram.auiLoc[i] = glGetUniformLocation(m_DefaultShaderProgram.uiId, g_aszDefaultUniformNames[i]); 328 } 329 330 // Set the sampler2D uniforms to corresponding texture units 331 glUniform1i(glGetUniformLocation(m_DefaultShaderProgram.uiId, "sTexture"), 0); 332 333 return true; 334 } 335 336 /*!**************************************************************************** 337 @Function LoadVbos 338 @Description Loads the mesh data required for this training course into 339 vertex buffer objects 340 ******************************************************************************/ 341 void OGLES2ChameleonMan::LoadVbos() 342 { 343 if (!m_puiVbo) m_puiVbo = new GLuint[m_Scene.nNumMesh]; 344 if (!m_puiIndexVbo) m_puiIndexVbo = new GLuint[m_Scene.nNumMesh]; 345 346 /* 347 Load vertex data of all meshes in the scene into VBOs 348 349 The meshes have been exported with the "Interleave Vectors" option, 350 so all data is interleaved in the buffer at pMesh->pInterleaved. 351 Interleaving data improves the memory access pattern and cache efficiency, 352 thus it can be read faster by the hardware. 353 */ 354 355 glGenBuffers(m_Scene.nNumMesh, m_puiVbo); 356 357 for (unsigned int i = 0; i < m_Scene.nNumMesh; ++i) 358 { 359 // Load vertex data into buffer object 360 SPODMesh& Mesh = m_Scene.pMesh[i]; 361 unsigned int uiSize = Mesh.nNumVertex * Mesh.sVertex.nStride; 362 363 glBindBuffer(GL_ARRAY_BUFFER, m_puiVbo[i]); 364 glBufferData(GL_ARRAY_BUFFER, uiSize, Mesh.pInterleaved, GL_STATIC_DRAW); 365 366 // Load index data into buffer object if available 367 m_puiIndexVbo[i] = 0; 368 369 if (Mesh.sFaces.pData) 370 { 371 glGenBuffers(1, &m_puiIndexVbo[i]); 372 uiSize = PVRTModelPODCountIndices(Mesh) * sizeof(GLshort); 373 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_puiIndexVbo[i]); 374 glBufferData(GL_ELEMENT_ARRAY_BUFFER, uiSize, Mesh.sFaces.pData, GL_STATIC_DRAW); 375 } 376 } 377 378 glBindBuffer(GL_ARRAY_BUFFER, 0); 379 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 380 } 381 382 /*!**************************************************************************** 383 @Function InitApplication 384 @Return bool true if no error occured 385 @Description Code in InitApplication() will be called by PVRShell once per 386 run, before the rendering context is created. 387 Used to initialize variables that are not dependant on it 388 (e.g. external modules, loading meshes, etc.) 389 If the rendering context is lost, InitApplication() will 390 not be called again. 391 ******************************************************************************/ 392 bool OGLES2ChameleonMan::InitApplication() 393 { 394 // Get and set the read path for content files 395 CPVRTResourceFile::SetReadPath((char*)PVRShellGet(prefReadPath)); 396 397 // Get and set the load/release functions for loading external files. 398 // In the majority of cases the PVRShell will return NULL function pointers implying that 399 // nothing special is required to load external files. 400 CPVRTResourceFile::SetLoadReleaseFunctions(PVRShellGet(prefLoadFileFunc), PVRShellGet(prefReleaseFileFunc)); 401 402 // Load the scene 403 if (m_Scene.ReadFromFile(c_szSceneFile) != PVR_SUCCESS) 404 { 405 PVRShellSet(prefExitMessage, "ERROR: Couldn't load the .pod file\n"); 406 return false; 407 } 408 409 // The cameras are stored in the file. We check it contains at least one. 410 if (m_Scene.nNumCamera == 0) 411 { 412 PVRShellSet(prefExitMessage, "ERROR: The scene does not contain a camera\n"); 413 return false; 414 } 415 416 // Check the scene contains at least one light 417 if (m_Scene.nNumLight == 0) 418 { 419 PVRShellSet(prefExitMessage, "ERROR: The scene does not contain a light\n"); 420 return false; 421 } 422 return true; 423 } 424 425 /*!**************************************************************************** 426 @Function QuitApplication 427 @Return bool true if no error occured 428 @Description Code in QuitApplication() will be called by PVRShell once per 429 run, just before exiting the program. 430 If the rendering context is lost, QuitApplication() will 431 not be called. 432 ******************************************************************************/ 433 bool OGLES2ChameleonMan::QuitApplication() 434 { 435 // Free the memory allocated for the scene 436 m_Scene.Destroy(); 437 438 delete [] m_puiVbo; 439 delete [] m_puiIndexVbo; 440 441 return true; 442 } 443 444 /*!**************************************************************************** 445 @Function InitView 446 @Return bool true if no error occured 447 @Description Code in InitView() will be called by PVRShell upon 448 initialization or after a change in the rendering context. 449 Used to initialize variables that are dependant on the rendering 450 context (e.g. textures, vertex buffers, etc.) 451 ******************************************************************************/ 452 bool OGLES2ChameleonMan::InitView() 453 { 454 CPVRTString ErrorStr; 455 456 /* 457 Initialize VBO data 458 */ 459 LoadVbos(); 460 461 /* 462 Load textures 463 */ 464 if (!LoadTextures(&ErrorStr)) 465 { 466 PVRShellSet(prefExitMessage, ErrorStr.c_str()); 467 return false; 468 } 469 470 /* 471 Load and compile the shaders & link programs 472 */ 473 if (!LoadShaders(&ErrorStr)) 474 { 475 PVRShellSet(prefExitMessage, ErrorStr.c_str()); 476 return false; 477 } 478 479 /* 480 Initialize Print3D 481 */ 482 483 // Is the screen rotated? 484 bool bRotate = PVRShellGet(prefIsRotated) && PVRShellGet(prefFullScreen); 485 486 if(m_Print3D.SetTextures(0,PVRShellGet(prefWidth),PVRShellGet(prefHeight), bRotate) != PVR_SUCCESS) 487 { 488 PVRShellSet(prefExitMessage, "ERROR: Cannot initialise Print3D\n"); 489 return false; 490 } 491 492 /* 493 Set OpenGL ES render states needed for this training course 494 */ 495 // Enable backface culling and depth test 496 glCullFace(GL_BACK); 497 glEnable(GL_CULL_FACE); 498 499 glEnable(GL_DEPTH_TEST); 500 501 // Use black as our clear colour 502 glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 503 504 // Initialise variables used for the animation 505 m_iTimePrev = PVRShellGetTime(); 506 507 return true; 508 } 509 510 /*!**************************************************************************** 511 @Function ReleaseView 512 @Return bool true if no error occured 513 @Description Code in ReleaseView() will be called by PVRShell when the 514 application quits or before a change in the rendering context. 515 ******************************************************************************/ 516 bool OGLES2ChameleonMan::ReleaseView() 517 { 518 // Delete textures 519 glDeleteTextures(1, &m_ui32TexLegs); 520 glDeleteTextures(1, &m_ui32TexBeltNormalMap); 521 glDeleteTextures(1, &m_ui32TexHeadNormalMap); 522 glDeleteTextures(1, &m_ui32TexLegsNormalMap); 523 glDeleteTextures(1, &m_ui32TexSkyLine); 524 glDeleteTextures(1, &m_ui32TexWall); 525 glDeleteTextures(1, &m_ui32TexLamp); 526 glDeleteTextures(1, &m_ui32TexBelt); 527 528 // Delete program and shader objects 529 glDeleteProgram(m_SkinnedShaderProgram.uiId); 530 glDeleteProgram(m_DefaultShaderProgram.uiId); 531 532 glDeleteShader(m_uiSkinnedVertShader); 533 glDeleteShader(m_uiDefaultVertShader); 534 glDeleteShader(m_uiSkinnedFragShader); 535 glDeleteShader(m_uiDefaultFragShader); 536 537 // Delete buffer objects 538 glDeleteBuffers(m_Scene.nNumMesh, m_puiVbo); 539 glDeleteBuffers(m_Scene.nNumMesh, m_puiIndexVbo); 540 541 // Release Print3D Textures 542 m_Print3D.ReleaseTextures(); 543 544 return true; 545 } 546 547 /*!**************************************************************************** 548 @Function RenderScene 549 @Return bool true if no error occured 550 @Description Main rendering loop function of the program. The shell will 551 call this function every frame. 552 eglSwapBuffers() will be performed by PVRShell automatically. 553 PVRShell will also manage important OS events. 554 Will also manage relevent OS events. The user has access to 555 these events through an abstraction layer provided by PVRShell. 556 ******************************************************************************/ 557 bool OGLES2ChameleonMan::RenderScene() 558 { 559 // Clear the color and depth buffer 560 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 561 562 // Use shader program 563 glUseProgram(m_SkinnedShaderProgram.uiId); 564 565 if(PVRShellIsKeyPressed(PVRShellKeyNameACTION1)) 566 { 567 m_bEnableDOT3 = !m_bEnableDOT3; 568 glUniform1i(m_SkinnedShaderProgram.auiLoc[ebUseDot3], m_bEnableDOT3); 569 } 570 571 /* 572 Calculates the frame number to animate in a time-based manner. 573 Uses the shell function PVRShellGetTime() to get the time in milliseconds. 574 */ 575 unsigned long iTime = PVRShellGetTime(); 576 577 if(iTime > m_iTimePrev) 578 { 579 float fDelta = (float) (iTime - m_iTimePrev); 580 m_fFrame += fDelta * g_fDemoFrameRate; 581 582 // Increment the counters to make sure our animation works 583 m_fLightPos += fDelta * 0.0034f; 584 m_fWallPos += fDelta * 0.00027f; 585 m_fBackgroundPos += fDelta * -0.000027f; 586 587 // Wrap the Animation back to the Start 588 if(m_fLightPos >= PVRT_TWO_PI) 589 m_fLightPos -= PVRT_TWO_PI; 590 591 if(m_fWallPos >= PVRT_TWO_PI) 592 m_fWallPos -= PVRT_TWO_PI; 593 594 if(m_fBackgroundPos <= 0) 595 m_fBackgroundPos += 1.0f; 596 597 if(m_fFrame > m_Scene.nNumFrame - 1) 598 m_fFrame = 0; 599 } 600 601 m_iTimePrev = iTime; 602 603 // Set the scene animation to the current frame 604 m_Scene.SetFrame(m_fFrame); 605 606 // Set up camera 607 PVRTVec3 vFrom, vTo, vUp(0.0f, 1.0f, 0.0f); 608 PVRTMat4 mView, mProjection; 609 PVRTVec3 LightPos; 610 float fFOV; 611 int i; 612 613 bool bRotate = PVRShellGet(prefIsRotated) && PVRShellGet(prefFullScreen); 614 615 // Get the camera position, target and field of view (fov) 616 if(m_Scene.pCamera[0].nIdxTarget != -1) // Does the camera have a target? 617 fFOV = m_Scene.GetCameraPos( vFrom, vTo, 0); // vTo is taken from the target node 618 else 619 fFOV = m_Scene.GetCamera( vFrom, vTo, vUp, 0); // vTo is calculated from the rotation 620 621 fFOV *= bRotate ? (float)PVRShellGet(prefWidth)/(float)PVRShellGet(prefHeight) : (float)PVRShellGet(prefHeight)/(float)PVRShellGet(prefWidth); 622 623 /* 624 We can build the model view matrix from the camera position, target and an up vector. 625 For this we use PVRTMat4::LookAtRH(). 626 */ 627 mView = PVRTMat4::LookAtRH(vFrom, vTo, vUp); 628 629 // Calculate the projection matrix 630 mProjection = PVRTMat4::PerspectiveFovRH(fFOV, (float)PVRShellGet(prefWidth)/(float)PVRShellGet(prefHeight), g_fCameraNear, g_fCameraFar, PVRTMat4::OGL, bRotate); 631 632 // Update Light Position and related VGP Program constant 633 LightPos.x = 200.0f; 634 LightPos.y = 350.0f; 635 LightPos.z = 200.0f * PVRTABS(sin((PVRT_PI / 4.0f) + m_fLightPos)); 636 637 glUniform3fv(m_SkinnedShaderProgram.auiLoc[eLightPos], 1, LightPos.ptr()); 638 639 // Set up the View * Projection Matrix 640 PVRTMat4 mViewProjection; 641 642 mViewProjection = mProjection * mView; 643 glUniformMatrix4fv(m_SkinnedShaderProgram.auiLoc[eViewProj], 1, GL_FALSE, mViewProjection.ptr()); 644 645 // Enable the vertex attribute arrays 646 for(i = 0; i < eNumAttribs; ++i) glEnableVertexAttribArray(i); 647 648 // Draw skinned meshes 649 for(unsigned int i32NodeIndex = 0; i32NodeIndex < 3; ++i32NodeIndex) 650 { 651 // Bind correct texture 652 switch(i32NodeIndex) 653 { 654 case eBody: 655 glActiveTexture(GL_TEXTURE1); 656 glBindTexture(GL_TEXTURE_2D, m_ui32TexHeadNormalMap); 657 glActiveTexture(GL_TEXTURE0); 658 glBindTexture(GL_TEXTURE_2D, m_ui32TexHeadBody); 659 break; 660 case eLegs: 661 glActiveTexture(GL_TEXTURE1); 662 glBindTexture(GL_TEXTURE_2D, m_ui32TexLegsNormalMap); 663 glActiveTexture(GL_TEXTURE0); 664 glBindTexture(GL_TEXTURE_2D, m_ui32TexLegs); 665 break; 666 default: 667 glActiveTexture(GL_TEXTURE1); 668 glBindTexture(GL_TEXTURE_2D, m_ui32TexBeltNormalMap); 669 glActiveTexture(GL_TEXTURE0); 670 glBindTexture(GL_TEXTURE_2D, m_ui32TexBelt); 671 break; 672 } 673 674 DrawSkinnedMesh(i32NodeIndex); 675 } 676 677 // Safely disable the vertex attribute arrays 678 for(i = 0; i < eNumAttribs; ++i) glDisableVertexAttribArray(i); 679 680 // Draw non-skinned meshes 681 glUseProgram(m_DefaultShaderProgram.uiId); 682 683 // Enable the vertex attribute arrays 684 for(i = 0; i < eNumDefaultAttribs; ++i) glEnableVertexAttribArray(i); 685 686 for(unsigned int i32NodeIndex = 3; i32NodeIndex < m_Scene.nNumMeshNode; ++i32NodeIndex) 687 { 688 SPODNode& Node = m_Scene.pNode[i32NodeIndex]; 689 SPODMesh& Mesh = m_Scene.pMesh[Node.nIdx]; 690 691 // bind the VBO for the mesh 692 glBindBuffer(GL_ARRAY_BUFFER, m_puiVbo[Node.nIdx]); 693 694 // bind the index buffer, won't hurt if the handle is 0 695 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_puiIndexVbo[Node.nIdx]); 696 697 // Get the node model matrix 698 PVRTMat4 mWorld; 699 mWorld = m_Scene.GetWorldMatrix(Node); 700 701 // Setup the appropriate texture and transformation (if needed) 702 switch(i32NodeIndex) 703 { 704 case eWall: 705 glBindTexture(GL_TEXTURE_2D, m_ui32TexWall); 706 707 // Rotate the wall mesh which is circular 708 mWorld *= PVRTMat4::RotationY(m_fWallPos); 709 710 glUniform1f(m_DefaultShaderProgram.auiLoc[eDefaultUOffset], 0); 711 712 break; 713 case eBackground: 714 glBindTexture(GL_TEXTURE_2D, m_ui32TexSkyLine); 715 716 glUniform1f(m_DefaultShaderProgram.auiLoc[eDefaultUOffset], m_fBackgroundPos); 717 break; 718 case eLights: 719 { 720 glBindTexture(GL_TEXTURE_2D, m_ui32TexLamp); 721 722 PVRTMat4 mWallWorld = m_Scene.GetWorldMatrix(m_Scene.pNode[eWall]); 723 mWorld = mWallWorld * PVRTMat4::RotationY(m_fWallPos) * mWallWorld.inverse() * mWorld; 724 725 glUniform1f(m_DefaultShaderProgram.auiLoc[eDefaultUOffset], 0); 726 } 727 break; 728 default: 729 break; 730 }; 731 732 // Set up shader uniforms 733 PVRTMat4 mModelViewProj; 734 mModelViewProj = mViewProjection * mWorld; 735 glUniformMatrix4fv(m_DefaultShaderProgram.auiLoc[eDefaultMVPMatrix], 1, GL_FALSE, mModelViewProj.ptr()); 736 737 // Set the vertex attribute offsets 738 glVertexAttribPointer(DEFAULT_VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, Mesh.sVertex.nStride, Mesh.sVertex.pData); 739 glVertexAttribPointer(DEFAULT_TEXCOORD_ARRAY, 2, GL_FLOAT, GL_FALSE, Mesh.psUVW[0].nStride, Mesh.psUVW[0].pData); 740 741 // Indexed Triangle list 742 glDrawElements(GL_TRIANGLES, Mesh.nNumFaces*3, GL_UNSIGNED_SHORT, 0); 743 } 744 745 // Safely disable the vertex attribute arrays 746 for(i = 0; i < eNumAttribs; ++i) glDisableVertexAttribArray(i); 747 748 // unbind the VBOs 749 glBindBuffer(GL_ARRAY_BUFFER, 0); 750 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 751 752 // Display the demo name using the tools. For a detailed explanation, see the training course IntroducingPVRTools 753 const char * pDescription; 754 755 if(m_bEnableDOT3) 756 pDescription = "Skinning with DOT3 Per Pixel Lighting"; 757 else 758 pDescription = "Skinning with Vertex Lighting"; 759 760 m_Print3D.DisplayDefaultTitle("Chameleon Man", pDescription, ePVRTPrint3DSDKLogo); 761 m_Print3D.Flush(); 762 763 return true; 764 } 765 766 /*!**************************************************************************** 767 @Function DrawSkinnedMesh 768 @Input i32NodeIndex Node index of the mesh to draw 769 @Description Draws a SPODMesh after the model view matrix has been set and 770 the meterial prepared. 771 ******************************************************************************/ 772 void OGLES2ChameleonMan::DrawSkinnedMesh(int i32NodeIndex) 773 { 774 SPODNode& Node = m_Scene.pNode[i32NodeIndex]; 775 SPODMesh& Mesh = m_Scene.pMesh[Node.nIdx]; 776 777 // bind the VBO for the mesh 778 glBindBuffer(GL_ARRAY_BUFFER, m_puiVbo[Node.nIdx]); 779 // bind the index buffer, won't hurt if the handle is 0 780 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_puiIndexVbo[Node.nIdx]); 781 782 // Set the vertex attribute offsets 783 glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, Mesh.sVertex.nStride, Mesh.sVertex.pData); 784 glVertexAttribPointer(NORMAL_ARRAY, 3, GL_FLOAT, GL_FALSE, Mesh.sNormals.nStride, Mesh.sNormals.pData); 785 glVertexAttribPointer(TANGENT_ARRAY, 3, GL_FLOAT, GL_FALSE, Mesh.sTangents.nStride, Mesh.sTangents.pData); 786 glVertexAttribPointer(BINORMAL_ARRAY, 3, GL_FLOAT, GL_FALSE, Mesh.sBinormals.nStride, Mesh.sBinormals.pData); 787 glVertexAttribPointer(TEXCOORD_ARRAY, 2, GL_FLOAT, GL_FALSE, Mesh.psUVW[0].nStride, Mesh.psUVW[0].pData); 788 glVertexAttribPointer(BONEINDEX_ARRAY, Mesh.sBoneIdx.n, GL_UNSIGNED_BYTE, GL_FALSE, Mesh.sBoneIdx.nStride, Mesh.sBoneIdx.pData); 789 glVertexAttribPointer(BONEWEIGHT_ARRAY, Mesh.sBoneWeight.n, GL_UNSIGNED_BYTE, GL_TRUE, Mesh.sBoneWeight.nStride, Mesh.sBoneWeight.pData); 790 791 for(int i32Batch = 0; i32Batch < Mesh.sBoneBatches.nBatchCnt; ++i32Batch) 792 { 793 /* 794 If the current mesh has bone index and weight data then we need to 795 set up some additional variables in the shaders. 796 */ 797 798 // Set the number of bones that will influence each vertex in the mesh 799 glUniform1i(m_SkinnedShaderProgram.auiLoc[eBoneCount], Mesh.sBoneIdx.n); 800 801 // Go through the bones for the current bone batch 802 PVRTMat4 amBoneWorld[8]; 803 PVRTMat3 afBoneWorldIT[8], mBoneIT; 804 805 int i32Count = Mesh.sBoneBatches.pnBatchBoneCnt[i32Batch]; 806 807 for(int i = 0; i < i32Count; ++i) 808 { 809 // Get the Node of the bone 810 int i32NodeID = Mesh.sBoneBatches.pnBatches[i32Batch * Mesh.sBoneBatches.nBatchBoneMax + i]; 811 812 // Get the World transformation matrix for this bone and combine it with our app defined 813 // transformation matrix 814 amBoneWorld[i] = m_Scene.GetBoneWorldMatrix(Node, m_Scene.pNode[i32NodeID]); 815 816 // Calculate the inverse transpose of the 3x3 rotation/scale part for correct lighting 817 afBoneWorldIT[i] = PVRTMat3(amBoneWorld[i]).inverse().transpose(); 818 } 819 820 glUniformMatrix4fv(m_SkinnedShaderProgram.auiLoc[eBoneMatrices], i32Count, GL_FALSE, amBoneWorld[0].ptr()); 821 glUniformMatrix3fv(m_SkinnedShaderProgram.auiLoc[eBoneMatricesIT], i32Count, GL_FALSE, afBoneWorldIT[0].ptr()); 822 823 /* 824 As we are using bone batching we don't want to draw all the faces contained within pMesh, we only want 825 to draw the ones that are in the current batch. To do this we pass to the drawMesh function the offset 826 to the start of the current batch of triangles (Mesh.sBoneBatches.pnBatchOffset[i32Batch]) and the 827 total number of triangles to draw (i32Tris) 828 */ 829 int i32Tris; 830 if(i32Batch+1 < Mesh.sBoneBatches.nBatchCnt) 831 i32Tris = Mesh.sBoneBatches.pnBatchOffset[i32Batch+1] - Mesh.sBoneBatches.pnBatchOffset[i32Batch]; 832 else 833 i32Tris = Mesh.nNumFaces - Mesh.sBoneBatches.pnBatchOffset[i32Batch]; 834 835 // Draw the mesh 836 size_t offset = sizeof(GLushort) * 3 * Mesh.sBoneBatches.pnBatchOffset[i32Batch]; 837 glDrawElements(GL_TRIANGLES, i32Tris * 3, GL_UNSIGNED_SHORT, (void*) offset); 838 } 839 } 840 841 /*!**************************************************************************** 842 @Function NewDemo 843 @Return PVRShell* The demo supplied by the user 844 @Description This function must be implemented by the user of the shell. 845 The user should return its PVRShell object defining the 846 behaviour of the application. 847 ******************************************************************************/ 848 PVRShell* NewDemo() 849 { 850 return new OGLES2ChameleonMan(); 851 } 852 853 /****************************************************************************** 854 End of file (OGLES2ChameleonMan.cpp) 855 ******************************************************************************/ 856 857