Home | History | Annotate | Download | only in OGLES2
      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