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
34const float g_fCameraNear = 4.0f;
35const float g_fCameraFar  = 2000.0f;
36
37const float g_fDemoFrameRate = 1.0f / 90.0f;
38
39// The camera to use from the pod file
40const int g_ui32Camera = 0;
41
42/******************************************************************************
43 Content file names
44******************************************************************************/
45
46// Source and binary shaders
47const char c_szFragShaderSrcFile[]	= "FragShader.fsh";
48const char c_szFragShaderBinFile[]	= "FragShader.fsc";
49const char c_szVertShaderSrcFile[]	= "VertShader.vsh";
50const char c_szVertShaderBinFile[]	= "VertShader.vsc";
51
52// POD scene files
53const char c_szSceneFile[]			= "DisMapScene.pod";
54const char c_szDisMapFile[]			= "DisMap.pvr";
55
56/*!****************************************************************************
57 Class implementing the PVRShell functions.
58******************************************************************************/
59class 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
96public:
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******************************************************************************/
115OGLES2DisplacementMap::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******************************************************************************/
129bool 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******************************************************************************/
201bool 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******************************************************************************/
248bool 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******************************************************************************/
302bool 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******************************************************************************/
344bool 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******************************************************************************/
363bool 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******************************************************************************/
458bool 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******************************************************************************/
494bool 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******************************************************************************/
616void 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******************************************************************************/
679PVRShell* NewDemo()
680{
681	return new OGLES2DisplacementMap();
682}
683
684/******************************************************************************
685 End of file (OGLES2DisplacementMap.cpp)
686******************************************************************************/
687
688