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:
OGLES2ChameleonMan()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 ******************************************************************************/
LoadTextures(CPVRTString * const pErrorStr)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 ******************************************************************************/
LoadShaders(CPVRTString * pErrorStr)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 ******************************************************************************/
LoadVbos()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 ******************************************************************************/
InitApplication()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 ******************************************************************************/
QuitApplication()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 ******************************************************************************/
InitView()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 ******************************************************************************/
ReleaseView()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 ******************************************************************************/
RenderScene()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 ******************************************************************************/
DrawSkinnedMesh(int i32NodeIndex)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 ******************************************************************************/
NewDemo()848 PVRShell* NewDemo()
849 {
850 return new OGLES2ChameleonMan();
851 }
852
853 /******************************************************************************
854 End of file (OGLES2ChameleonMan.cpp)
855 ******************************************************************************/
856
857