Home | History | Annotate | Download | only in OGLES2
      1 /******************************************************************************
      2 
      3  @File         OGLES2DisplacementMap.cpp
      4 
      5  @Title        Displacement Map
      6 
      7  @Version
      8 
      9  @Copyright    Copyright (c) Imagination Technologies Limited.
     10 
     11  @Platform     Independent
     12 
     13  @Description  Shows how to displace geometry in the vertex shader using a
     14                texture.
     15 
     16 ******************************************************************************/
     17 #include <string.h>
     18 
     19 #include "PVRShell.h"
     20 #include "OGLES2Tools.h"
     21 
     22 /******************************************************************************
     23  Defines
     24 ******************************************************************************/
     25 // Index to bind the attributes to vertex shaders
     26 #define VERTEX_ARRAY	0
     27 #define NORMAL_ARRAY	1
     28 #define TEXCOORD_ARRAY	2
     29 
     30 /******************************************************************************
     31  Consts
     32 ******************************************************************************/
     33 // Camera constants. Used for making the projection matrix
     34 const float g_fCameraNear = 4.0f;
     35 const float g_fCameraFar  = 2000.0f;
     36 
     37 const float g_fDemoFrameRate = 1.0f / 90.0f;
     38 
     39 // The camera to use from the pod file
     40 const int g_ui32Camera = 0;
     41 
     42 /******************************************************************************
     43  Content file names
     44 ******************************************************************************/
     45 
     46 // Source and binary shaders
     47 const char c_szFragShaderSrcFile[]	= "FragShader.fsh";
     48 const char c_szFragShaderBinFile[]	= "FragShader.fsc";
     49 const char c_szVertShaderSrcFile[]	= "VertShader.vsh";
     50 const char c_szVertShaderBinFile[]	= "VertShader.vsc";
     51 
     52 // POD scene files
     53 const char c_szSceneFile[]			= "DisMapScene.pod";
     54 const char c_szDisMapFile[]			= "DisMap.pvr";
     55 
     56 /*!****************************************************************************
     57  Class implementing the PVRShell functions.
     58 ******************************************************************************/
     59 class OGLES2DisplacementMap : public PVRShell
     60 {
     61 	// Print3D class used to display text
     62 	CPVRTPrint3D	m_Print3D;
     63 
     64 	// 3D Model
     65 	CPVRTModelPOD	m_Scene;
     66 
     67 	// OpenGL handles for shaders, textures and VBOs
     68 	GLuint m_uiVertShader;
     69 	GLuint m_uiFragShader;
     70 	GLuint* m_puiVbo;
     71 	GLuint* m_puiIndexVbo;
     72 	GLuint* m_puiTextureIDs;
     73 	GLuint  m_uiDisMapID;
     74 
     75 	// Group shader programs and their uniform locations together
     76 	struct
     77 	{
     78 		GLuint uiId;
     79 		GLuint uiMVPMatrixLoc;
     80 		GLuint uiLightDirLoc;
     81 		GLuint uiTexture;
     82 		GLuint uiDisMap;
     83 		GLuint uiDisplacementFactor;
     84 	}
     85 	m_ShaderProgram;
     86 
     87 	// Variables to handle the animation in a time-based manner
     88 	unsigned long		m_ulTimePrev;
     89 
     90 	// App variables
     91 	PVRTVec4		m_LightDir;
     92 	PVRTMat4		m_View, m_Projection;
     93 	float			m_DisplacementFactor;
     94 	bool			m_bGrow;
     95 
     96 public:
     97 	virtual bool InitApplication();
     98 	virtual bool InitView();
     99 	virtual bool ReleaseView();
    100 	virtual bool QuitApplication();
    101 	virtual bool RenderScene();
    102 
    103 	OGLES2DisplacementMap();
    104 	bool LoadTextures(CPVRTString* pErrorStr);
    105 	bool LoadShaders(CPVRTString* pErrorStr);
    106 	bool LoadVbos(CPVRTString* pErrorStr);
    107 
    108 	void DrawMesh(int i32NodeIndex);
    109 };
    110 
    111 /*!****************************************************************************
    112  @Function		OGLES2DisplacementMap
    113  @Description	Constructor
    114 ******************************************************************************/
    115 OGLES2DisplacementMap::OGLES2DisplacementMap() :    m_puiVbo(0),
    116 													m_puiIndexVbo(0),
    117 													m_puiTextureIDs(0),
    118 													m_ulTimePrev(0),
    119 													m_DisplacementFactor(0),
    120 													m_bGrow(false)
    121 {
    122 }
    123 
    124 /*!****************************************************************************
    125  @Function		LoadTextures
    126  @Return		bool			true if no error occurred
    127  @Description	Loads the textures required for this training course
    128 ******************************************************************************/
    129 bool OGLES2DisplacementMap::LoadTextures(CPVRTString* pErrorStr)
    130 {
    131 	/*
    132 		Load the textures.
    133 		For a more detailed explanation, see Texturing and IntroducingPVRTools
    134 	*/
    135 
    136 	/*
    137 		Initialises an array to lookup the textures
    138 		for each material in the scene.
    139 	*/
    140 	m_puiTextureIDs = new GLuint[m_Scene.nNumMaterial];
    141 
    142 	if(!m_puiTextureIDs)
    143 	{
    144 		*pErrorStr = "ERROR: Insufficient memory.";
    145 		return false;
    146 	}
    147 
    148 	for(int i = 0; i < (int) m_Scene.nNumMaterial; ++i)
    149 	{
    150 		m_puiTextureIDs[i] = 0;
    151 		SPODMaterial* pMaterial = &m_Scene.pMaterial[i];
    152 
    153 		if(pMaterial->nIdxTexDiffuse != -1)
    154 		{
    155 			/*
    156 				Using the tools function PVRTTextureLoadFromPVR load the textures required by the pod file.
    157 
    158 				Note: This function only loads .pvr files. You can set the textures in 3D Studio Max to .pvr
    159 				files using the PVRTexTool plug-in for max. Alternatively, the pod material properties can be
    160 				modified in PVRShaman.
    161 			*/
    162 
    163 			CPVRTString sTextureName = m_Scene.pTexture[pMaterial->nIdxTexDiffuse].pszName;
    164 
    165 			if(PVRTTextureLoadFromPVR(sTextureName.c_str(), &m_puiTextureIDs[i]) != PVR_SUCCESS)
    166 			{
    167 				*pErrorStr = "ERROR: Failed to load " + sTextureName + ".";
    168 
    169 				// Check to see if we're trying to load .pvr or not
    170 				CPVRTString sFileExtension = PVRTStringGetFileExtension(sTextureName);
    171 
    172 				if(sFileExtension.toLower() == "pvr")
    173 					*pErrorStr += "Note: Can only load pvr files.";
    174 
    175 				return false;
    176 			}
    177 		}
    178 	}
    179 
    180 	// Load the texture used for the displacement map
    181 	if(PVRTTextureLoadFromPVR(c_szDisMapFile, &m_uiDisMapID) != PVR_SUCCESS)
    182 	{
    183 		*pErrorStr = "ERROR: Failed to load " + CPVRTString(c_szDisMapFile) + ".";
    184 		return false;
    185 	}
    186 
    187 	// Define the wrapping to use for the displacement map
    188 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    189 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    190 
    191 	return true;
    192 }
    193 
    194 /*!****************************************************************************
    195  @Function		LoadShaders
    196  @Output		pErrorStr		A string describing the error on failure
    197  @Return		bool			true if no error occurred
    198  @Description	Loads and compiles the shaders and links the shader programs
    199 				required for this training course
    200 ******************************************************************************/
    201 bool OGLES2DisplacementMap::LoadShaders(CPVRTString* pErrorStr)
    202 {
    203 	/*
    204 		Load and compile the shaders from files.
    205 		Binary shaders are tried first, source shaders
    206 		are used as fallback.
    207 	*/
    208 	if(PVRTShaderLoadFromFile(
    209 			c_szVertShaderBinFile, c_szVertShaderSrcFile, GL_VERTEX_SHADER, GL_SGX_BINARY_IMG, &m_uiVertShader, pErrorStr) != PVR_SUCCESS)
    210 	{
    211 		return false;
    212 	}
    213 
    214 	if (PVRTShaderLoadFromFile(
    215 			c_szFragShaderBinFile, c_szFragShaderSrcFile, GL_FRAGMENT_SHADER, GL_SGX_BINARY_IMG, &m_uiFragShader, pErrorStr) != PVR_SUCCESS)
    216 	{
    217 		return false;
    218 	}
    219 
    220 	/*
    221 		Set up and link the shader program
    222 	*/
    223 	const char* aszAttribs[] = { "inVertex", "inNormal", "inTexCoord" };
    224 
    225 	if(PVRTCreateProgram(
    226 			&m_ShaderProgram.uiId, m_uiVertShader, m_uiFragShader, aszAttribs, 3, pErrorStr) != PVR_SUCCESS)
    227 	{
    228 		PVRShellSet(prefExitMessage, pErrorStr->c_str());
    229 		return false;
    230 	}
    231 
    232 	// Store the location of uniforms for later use
    233 	m_ShaderProgram.uiMVPMatrixLoc	= glGetUniformLocation(m_ShaderProgram.uiId, "MVPMatrix");
    234 	m_ShaderProgram.uiLightDirLoc	= glGetUniformLocation(m_ShaderProgram.uiId, "LightDirection");
    235 	m_ShaderProgram.uiDisplacementFactor = glGetUniformLocation(m_ShaderProgram.uiId, "DisplacementFactor");
    236 
    237 	m_ShaderProgram.uiTexture = glGetUniformLocation(m_ShaderProgram.uiId, "sTexture");
    238 	m_ShaderProgram.uiDisMap  = glGetUniformLocation(m_ShaderProgram.uiId, "sDisMap");
    239 
    240 	return true;
    241 }
    242 
    243 /*!****************************************************************************
    244  @Function		LoadVbos
    245  @Description	Loads the mesh data required for this training course into
    246 				vertex buffer objects
    247 ******************************************************************************/
    248 bool OGLES2DisplacementMap::LoadVbos(CPVRTString* pErrorStr)
    249 {
    250 	if(!m_Scene.pMesh[0].pInterleaved)
    251 	{
    252 		*pErrorStr = "ERROR: IntroducingPOD requires the pod data to be interleaved. Please re-export with the interleaved option enabled.";
    253 		return false;
    254 	}
    255 
    256 	if (!m_puiVbo)      m_puiVbo = new GLuint[m_Scene.nNumMesh];
    257 	if (!m_puiIndexVbo) m_puiIndexVbo = new GLuint[m_Scene.nNumMesh];
    258 
    259 	/*
    260 		Load vertex data of all meshes in the scene into VBOs
    261 
    262 		The meshes have been exported with the "Interleave Vectors" option,
    263 		so all data is interleaved in the buffer at pMesh->pInterleaved.
    264 		Interleaving data improves the memory access pattern and cache efficiency,
    265 		thus it can be read faster by the hardware.
    266 	*/
    267 	glGenBuffers(m_Scene.nNumMesh, m_puiVbo);
    268 	for (unsigned int i = 0; i < m_Scene.nNumMesh; ++i)
    269 	{
    270 		// Load vertex data into buffer object
    271 		SPODMesh& Mesh = m_Scene.pMesh[i];
    272 		unsigned int uiSize = Mesh.nNumVertex * Mesh.sVertex.nStride;
    273 		glBindBuffer(GL_ARRAY_BUFFER, m_puiVbo[i]);
    274 		glBufferData(GL_ARRAY_BUFFER, uiSize, Mesh.pInterleaved, GL_STATIC_DRAW);
    275 
    276 		// Load index data into buffer object if available
    277 		m_puiIndexVbo[i] = 0;
    278 		if (Mesh.sFaces.pData)
    279 		{
    280 			glGenBuffers(1, &m_puiIndexVbo[i]);
    281 			uiSize = PVRTModelPODCountIndices(Mesh) * sizeof(GLshort);
    282 			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_puiIndexVbo[i]);
    283 			glBufferData(GL_ELEMENT_ARRAY_BUFFER, uiSize, Mesh.sFaces.pData, GL_STATIC_DRAW);
    284 		}
    285 	}
    286 	glBindBuffer(GL_ARRAY_BUFFER, 0);
    287 	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    288 
    289 	return true;
    290 }
    291 
    292 /*!****************************************************************************
    293  @Function		InitApplication
    294  @Return		bool		true if no error occurred
    295  @Description	Code in InitApplication() will be called by PVRShell once per
    296 				run, before the rendering context is created.
    297 				Used to initialize variables that are not dependent on it
    298 				(e.g. external modules, loading meshes, etc.)
    299 				If the rendering context is lost, InitApplication() will
    300 				not be called again.
    301 ******************************************************************************/
    302 bool OGLES2DisplacementMap::InitApplication()
    303 {
    304 	// Get and set the read path for content files
    305 	CPVRTResourceFile::SetReadPath((char*)PVRShellGet(prefReadPath));
    306 
    307 	// Get and set the load/release functions for loading external files.
    308 	// In the majority of cases the PVRShell will return NULL function pointers implying that
    309 	// nothing special is required to load external files.
    310 	CPVRTResourceFile::SetLoadReleaseFunctions(PVRShellGet(prefLoadFileFunc), PVRShellGet(prefReleaseFileFunc));
    311 
    312 	// Load the scene
    313 	if(m_Scene.ReadFromFile(c_szSceneFile) != PVR_SUCCESS)
    314 	{
    315 		PVRShellSet(prefExitMessage, "ERROR: Couldn't load the .pod file\n");
    316 		return false;
    317 	}
    318 
    319 	// The cameras are stored in the file. We check it contains at least one.
    320 	if(m_Scene.nNumCamera == 0)
    321 	{
    322 		PVRShellSet(prefExitMessage, "ERROR: The scene does not contain a camera. Please add one and re-export.\n");
    323 		return false;
    324 	}
    325 
    326 	// We also check that the scene contains at least one light
    327 	if(m_Scene.nNumLight == 0)
    328 	{
    329 		PVRShellSet(prefExitMessage, "ERROR: The scene does not contain a light. Please add one and re-export.\n");
    330 		return false;
    331 	}
    332 
    333 	return true;
    334 }
    335 
    336 /*!****************************************************************************
    337  @Function		QuitApplication
    338  @Return		bool		true if no error occurred
    339  @Description	Code in QuitApplication() will be called by PVRShell once per
    340 				run, just before exiting the program.
    341 				If the rendering context is lost, QuitApplication() will
    342 				not be called.
    343 ******************************************************************************/
    344 bool OGLES2DisplacementMap::QuitApplication()
    345 {
    346 	// Free the memory allocated for the scene
    347 	m_Scene.Destroy();
    348 
    349 	delete[] m_puiVbo;
    350 	delete[] m_puiIndexVbo;
    351 
    352     return true;
    353 }
    354 
    355 /*!****************************************************************************
    356  @Function		InitView
    357  @Return		bool		true if no error occurred
    358  @Description	Code in InitView() will be called by PVRShell upon
    359 				initialization or after a change in the rendering context.
    360 				Used to initialize variables that are dependent on the rendering
    361 				context (e.g. textures, vertex buffers, etc.)
    362 ******************************************************************************/
    363 bool OGLES2DisplacementMap::InitView()
    364 {
    365 	CPVRTString ErrorStr;
    366 
    367 	/*
    368 		Initialize VBO data
    369 	*/
    370 	if(!LoadVbos(&ErrorStr))
    371 	{
    372 		PVRShellSet(prefExitMessage, ErrorStr.c_str());
    373 		return false;
    374 	}
    375 
    376 	/*
    377 		Load textures
    378 	*/
    379 	if(!LoadTextures(&ErrorStr))
    380 	{
    381 		PVRShellSet(prefExitMessage, ErrorStr.c_str());
    382 		return false;
    383 	}
    384 
    385 	/*
    386 		Load and compile the shaders & link programs
    387 	*/
    388 	if(!LoadShaders(&ErrorStr))
    389 	{
    390 		PVRShellSet(prefExitMessage, ErrorStr.c_str());
    391 		return false;
    392 	}
    393 
    394 	/*
    395 		Initialize Print3D
    396 	*/
    397 	bool bRotate = PVRShellGet(prefIsRotated) && PVRShellGet(prefFullScreen);
    398 
    399 	if(m_Print3D.SetTextures(0,PVRShellGet(prefWidth),PVRShellGet(prefHeight), bRotate) != PVR_SUCCESS)
    400 	{
    401 		PVRShellSet(prefExitMessage, "ERROR: Cannot initialise Print3D\n");
    402 		return false;
    403 	}
    404 
    405 	/*
    406 		Set OpenGL ES render states needed for this training course
    407 	*/
    408 	// Enable backface culling and depth test
    409 	glCullFace(GL_BACK);
    410 	glEnable(GL_CULL_FACE);
    411 
    412 	glEnable(GL_DEPTH_TEST);
    413 
    414 	// Use a nice bright blue as clear colour
    415 	glClearColor(0.6f, 0.8f, 1.0f, 1.0f);
    416 
    417 	//Get the direction of the first light from the scene.
    418 	m_LightDir = m_Scene.GetLightDirection(0);
    419 
    420 	// For direction vectors, w should be 0
    421 	m_LightDir.w = 0.0f;
    422 
    423 
    424 	//	Set up the view and projection matrices from the camera
    425 	PVRTVec3	vFrom, vTo(0.0f), vUp(0.0f, 1.0f, 0.0f);
    426 	float fFOV;
    427 
    428 	// Setup the camera
    429 
    430 	// Camera nodes are after the mesh and light nodes in the array
    431 	int i32CamID = m_Scene.pNode[m_Scene.nNumMeshNode + m_Scene.nNumLight + g_ui32Camera].nIdx;
    432 
    433 	// Get the camera position, target and field of view (fov)
    434 	if(m_Scene.pCamera[i32CamID].nIdxTarget != -1) // Does the camera have a target?
    435 		fFOV = m_Scene.GetCameraPos( vFrom, vTo, g_ui32Camera); // vTo is taken from the target node
    436 	else
    437 		fFOV = m_Scene.GetCamera( vFrom, vTo, vUp, g_ui32Camera); // vTo is calculated from the rotation
    438 
    439 	// We can build the model view matrix from the camera position, target and an up vector.
    440 	// For this we usePVRTMat4LookAtRH()
    441 	m_View = PVRTMat4::LookAtRH(vFrom, vTo, vUp);
    442 
    443 	// Calculate the projection matrix
    444 	m_Projection = PVRTMat4::PerspectiveFovRH(fFOV, (float)PVRShellGet(prefWidth)/(float)PVRShellGet(prefHeight), g_fCameraNear, g_fCameraFar, PVRTMat4::OGL, bRotate);
    445 
    446 	// Initialize variables used for the animation
    447 	m_ulTimePrev = PVRShellGetTime();
    448 
    449 	return true;
    450 }
    451 
    452 /*!****************************************************************************
    453  @Function		ReleaseView
    454  @Return		bool		true if no error occurred
    455  @Description	Code in ReleaseView() will be called by PVRShell when the
    456 				application quits or before a change in the rendering context.
    457 ******************************************************************************/
    458 bool OGLES2DisplacementMap::ReleaseView()
    459 {
    460 	// Deletes the textures
    461 	glDeleteTextures(m_Scene.nNumMaterial, &m_puiTextureIDs[0]);
    462 	glDeleteTextures(1, &m_uiDisMapID);
    463 
    464 	// Frees the texture lookup array
    465 	delete[] m_puiTextureIDs;
    466 	m_puiTextureIDs = 0;
    467 
    468 	// Delete program and shader objects
    469 	glDeleteProgram(m_ShaderProgram.uiId);
    470 
    471 	glDeleteShader(m_uiVertShader);
    472 	glDeleteShader(m_uiFragShader);
    473 
    474 	// Delete buffer objects
    475 	glDeleteBuffers(m_Scene.nNumMesh, m_puiVbo);
    476 	glDeleteBuffers(m_Scene.nNumMesh, m_puiIndexVbo);
    477 
    478 	// Release Print3D Textures
    479 	m_Print3D.ReleaseTextures();
    480 
    481 	return true;
    482 }
    483 
    484 /*!****************************************************************************
    485  @Function		RenderScene
    486  @Return		bool		true if no error occurred
    487  @Description	Main rendering loop function of the program. The shell will
    488 				call this function every frame.
    489 				eglSwapBuffers() will be performed by PVRShell automatically.
    490 				PVRShell will also manage important OS events.
    491 				Will also manage relevant OS events. The user has access to
    492 				these events through an abstraction layer provided by PVRShell.
    493 ******************************************************************************/
    494 bool OGLES2DisplacementMap::RenderScene()
    495 {
    496 	// Clear the color and depth buffer
    497 	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    498 
    499 
    500 	//	Calculates the the time since the last frame
    501 	unsigned long ulTime = PVRShellGetTime();
    502 	unsigned long ulDeltaTime = ulTime - m_ulTimePrev;
    503 	m_ulTimePrev = ulTime;
    504 
    505 	// Use shader program
    506 	glUseProgram(m_ShaderProgram.uiId);
    507 
    508 	// Enable 2D texturing for the first texture.
    509 	glActiveTexture(GL_TEXTURE0);
    510 
    511 	// Set the sampler2D variable to the first texture unit
    512 	glUniform1i(m_ShaderProgram.uiTexture, 0);
    513 
    514 	// Enable 2D texturing for the second texture.
    515 	glActiveTexture(GL_TEXTURE1);
    516 
    517 	// Set the displacement map variable to the second texture unit
    518 	glUniform1i(m_ShaderProgram.uiDisMap, 1);
    519 
    520 	// Calculate and set the displacement factor
    521 	if(m_bGrow)
    522 	{
    523 		m_DisplacementFactor += (float)ulDeltaTime * g_fDemoFrameRate;
    524 
    525 		if(m_DisplacementFactor > 25.0f)
    526 		{
    527 			m_bGrow = false;
    528 			m_DisplacementFactor = 25.0f;
    529 		}
    530 	}
    531 	else
    532 	{
    533 		m_DisplacementFactor -= (float)ulDeltaTime * g_fDemoFrameRate;
    534 
    535 		if(m_DisplacementFactor < 0.0f)
    536 		{
    537 			m_bGrow = true;
    538 			m_DisplacementFactor = 0.0f;
    539 		}
    540 	}
    541 
    542 	glUniform1f(m_ShaderProgram.uiDisplacementFactor, m_DisplacementFactor);
    543 
    544 	// Bind the displacement map texture
    545 	glBindTexture(GL_TEXTURE_2D, m_uiDisMapID);
    546 
    547 	// Now the displacement map texture is bound set the active texture to texture 0
    548 	glActiveTexture(GL_TEXTURE0);
    549 
    550 	// Draw the scene
    551 
    552 	// Enable the vertex attribute arrays
    553 	glEnableVertexAttribArray(VERTEX_ARRAY);
    554 	glEnableVertexAttribArray(NORMAL_ARRAY);
    555 	glEnableVertexAttribArray(TEXCOORD_ARRAY);
    556 
    557 	for(unsigned int i = 0; i < m_Scene.nNumMeshNode; ++i)
    558 	{
    559 		SPODNode& Node = m_Scene.pNode[i];
    560 
    561 		// Get the node model matrix
    562 		PVRTMat4 mWorld;
    563 		mWorld = m_Scene.GetWorldMatrix(Node);
    564 
    565 		// Pass the model-view-projection matrix (MVP) to the shader to transform the vertices
    566 		PVRTMat4 mModelView, mMVP;
    567 		mModelView = m_View * mWorld;
    568 		mMVP = m_Projection * mModelView;
    569 		glUniformMatrix4fv(m_ShaderProgram.uiMVPMatrixLoc, 1, GL_FALSE, mMVP.f);
    570 
    571 		// Pass the light direction in model space to the shader
    572 		PVRTVec4 vLightDir;
    573 		vLightDir = mWorld.inverse() * m_LightDir;
    574 
    575 		PVRTVec3 vLightDirModel = *(PVRTVec3*) vLightDir.ptr();
    576 		vLightDirModel.normalize();
    577 
    578 		glUniform3fv(m_ShaderProgram.uiLightDirLoc, 1, &vLightDirModel.x);
    579 
    580 		// Load the correct texture for the mesh using our texture lookup table
    581 		GLuint uiTex = 0;
    582 
    583 		if(Node.nIdxMaterial != -1)
    584 			uiTex = m_puiTextureIDs[Node.nIdxMaterial];
    585 
    586 		glBindTexture(GL_TEXTURE_2D, uiTex);
    587 
    588 		/*
    589 			Now that the model-view matrix is set and the materials ready,
    590 			call another function to actually draw the mesh.
    591 		*/
    592 		DrawMesh(i);
    593 	}
    594 
    595 	// Safely disable the vertex attribute arrays
    596 	glDisableVertexAttribArray(VERTEX_ARRAY);
    597 	glDisableVertexAttribArray(NORMAL_ARRAY);
    598 	glDisableVertexAttribArray(TEXCOORD_ARRAY);
    599 
    600 	glBindBuffer(GL_ARRAY_BUFFER, 0);
    601 	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    602 
    603 	// Display the demo name using the tools. For a detailed explanation, see the training course IntroducingPVRTools
    604 	m_Print3D.DisplayDefaultTitle("DisplacementMapping", "", ePVRTPrint3DSDKLogo);
    605 	m_Print3D.Flush();
    606 
    607 	return true;
    608 }
    609 
    610 /*!****************************************************************************
    611  @Function		DrawMesh
    612  @Input			i32NodeIndex		Node index of the mesh to draw
    613  @Description	Draws a SPODMesh after the model view matrix has been set and
    614 				the material prepared.
    615 ******************************************************************************/
    616 void OGLES2DisplacementMap::DrawMesh(int i32NodeIndex)
    617 {
    618 	int i32MeshIndex = m_Scene.pNode[i32NodeIndex].nIdx;
    619 	SPODMesh* pMesh = &m_Scene.pMesh[i32MeshIndex];
    620 
    621 	// bind the VBO for the mesh
    622 	glBindBuffer(GL_ARRAY_BUFFER, m_puiVbo[i32MeshIndex]);
    623 	// bind the index buffer, won't hurt if the handle is 0
    624 	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_puiIndexVbo[i32MeshIndex]);
    625 
    626 	// Set the vertex attribute offsets
    627 	glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, pMesh->sVertex.nStride, pMesh->sVertex.pData);
    628 	glVertexAttribPointer(NORMAL_ARRAY, 3, GL_FLOAT, GL_FALSE, pMesh->sNormals.nStride, pMesh->sNormals.pData);
    629 	glVertexAttribPointer(TEXCOORD_ARRAY, 2, GL_FLOAT, GL_FALSE, pMesh->psUVW[0].nStride, pMesh->psUVW[0].pData);
    630 
    631 	/*
    632 		The geometry can be exported in 4 ways:
    633 		- Indexed Triangle list
    634 		- Non-Indexed Triangle list
    635 		- Indexed Triangle strips
    636 		- Non-Indexed Triangle strips
    637 	*/
    638 	if(pMesh->nNumStrips == 0)
    639 	{
    640 		if(m_puiIndexVbo[i32MeshIndex])
    641 		{
    642 			// Indexed Triangle list
    643 			glDrawElements(GL_TRIANGLES, pMesh->nNumFaces*3, GL_UNSIGNED_SHORT, 0);
    644 		}
    645 		else
    646 		{
    647 			// Non-Indexed Triangle list
    648 			glDrawArrays(GL_TRIANGLES, 0, pMesh->nNumFaces*3);
    649 		}
    650 	}
    651 	else
    652 	{
    653 		int offset = 0;
    654 
    655 		for(int i = 0; i < (int)pMesh->nNumStrips; ++i)
    656 		{
    657 			if(m_puiIndexVbo[i32MeshIndex])
    658 			{
    659 				// Indexed Triangle strips
    660 				glDrawElements(GL_TRIANGLE_STRIP, pMesh->pnStripLength[i]+2, GL_UNSIGNED_SHORT, &((GLshort*)0)[offset]);
    661 			}
    662 			else
    663 			{
    664 				// Non-Indexed Triangle strips
    665 				glDrawArrays(GL_TRIANGLE_STRIP, offset, pMesh->pnStripLength[i]+2);
    666 			}
    667 			offset += pMesh->pnStripLength[i]+2;
    668 		}
    669 	}
    670 }
    671 
    672 /*!****************************************************************************
    673  @Function		NewDemo
    674  @Return		PVRShell*		The demo supplied by the user
    675  @Description	This function must be implemented by the user of the shell.
    676 				The user should return its PVRShell object defining the
    677 				behaviour of the application.
    678 ******************************************************************************/
    679 PVRShell* NewDemo()
    680 {
    681 	return new OGLES2DisplacementMap();
    682 }
    683 
    684 /******************************************************************************
    685  End of file (OGLES2DisplacementMap.cpp)
    686 ******************************************************************************/
    687 
    688