blob: d6084e38165a978ac442c862f8c5ce519e60107e [file] [log] [blame]
/******************************************************************************
@File OGLES2ChameleonMan.cpp
@Title OGLES2ChameleonMan
@Version
@Copyright Copyright (c) Imagination Technologies Limited.
@Platform Independent
@Description Shows how to perform skinning combined with Dot3 lighting
******************************************************************************/
#include "PVRShell.h"
#include "OGLES2Tools.h"
/******************************************************************************
Constants
******************************************************************************/
// Camera constants used to generate the projection matrix
const float g_fCameraNear = 4.0f;
const float g_fCameraFar = 30000.0f;
const float g_fDemoFrameRate = 0.02f;
/******************************************************************************
shader attributes
******************************************************************************/
// Skinned
// vertex attributes
enum EVertexAttrib {
VERTEX_ARRAY, NORMAL_ARRAY, TANGENT_ARRAY, BINORMAL_ARRAY, TEXCOORD_ARRAY, BONEWEIGHT_ARRAY, BONEINDEX_ARRAY, eNumAttribs };
const char* g_aszAttribNames[] = {
"inVertex", "inNormal", "inTangent", "inBiNormal", "inTexCoord", "inBoneWeight", "inBoneIndex" };
// shader uniforms
enum ESkinnnedUniform {
eViewProj, eLightPos, eBoneCount, eBoneMatrices, eBoneMatricesIT, ebUseDot3, eNumSkinnedUniforms };
const char* g_aszSkinnedUniformNames[] = {
"ViewProjMatrix", "LightPos", "BoneCount", "BoneMatrixArray[0]", "BoneMatrixArrayIT[0]", "bUseDot3" };
// Default
// vertex attributes
enum EDefaultVertexAttrib {
DEFAULT_VERTEX_ARRAY, DEFAULT_TEXCOORD_ARRAY, eNumDefaultAttribs };
const char* g_aszDefaultAttribNames[] = {
"inVertex", "inTexCoord"};
// shader uniforms
enum EDefaultUniform {
eDefaultMVPMatrix, eDefaultUOffset, eNumDefaultUniforms };
const char* g_aszDefaultUniformNames[] = {
"MVPMatrix", "fUOffset" };
/******************************************************************************
Content file names
******************************************************************************/
// Source and binary shaders
const char c_szSkinnedFragShaderSrcFile[] = "SkinnedFragShader.fsh";
const char c_szSkinnedFragShaderBinFile[] = "SkinnedFragShader.fsc";
const char c_szSkinnedVertShaderSrcFile[] = "SkinnedVertShader.vsh";
const char c_szSkinnedVertShaderBinFile[] = "SkinnedVertShader.vsc";
const char c_szDefaultFragShaderSrcFile[] = "DefaultFragShader.fsh";
const char c_szDefaultFragShaderBinFile[] = "DefaultFragShader.fsc";
const char c_szDefaultVertShaderSrcFile[] = "DefaultVertShader.vsh";
const char c_szDefaultVertShaderBinFile[] = "DefaultVertShader.vsc";
// Base Textures
const char c_szFinalChameleonManHeadBodyTexFile[] = "FinalChameleonManHeadBody.pvr";
const char c_szFinalChameleonManLegsTexFile[] = "FinalChameleonManLegs.pvr";
const char c_szLampTexFile[] = "lamp.pvr";
const char c_szChameleonBeltTexFile[] = "ChameleonBelt.pvr";
const char c_szSkylineTexFile[] = "skyline.pvr";
const char c_szWallDiffuseBakedTexFile[] = "Wall_diffuse_baked.pvr";
// Tangent Space BumpMap Textures
const char c_szTang_space_BodyMapTexFile[] = "Tang_space_BodyMap.pvr";
const char c_szTang_space_LegsMapTexFile[] = "Tang_space_LegsMap.pvr";
const char c_szTang_space_BeltMapTexFile[] = "Tang_space_BeltMap.pvr";
// POD scene files
const char c_szSceneFile[] = "ChameleonScene.pod";
/****************************************************************************
** Enums **
****************************************************************************/
enum EMeshes
{
eBody,
eLegs,
eBelt,
eWall,
eBackground,
eLights
};
/****************************************************************************
** Structures
****************************************************************************/
/*!****************************************************************************
Class implementing the PVRShell functions.
******************************************************************************/
class OGLES2ChameleonMan : public PVRShell
{
// Print3D class used to display text
CPVRTPrint3D m_Print3D;
// 3D Model
CPVRTModelPOD m_Scene;
// Model transformation variables
float m_fWallPos;
float m_fBackgroundPos;
float m_fLightPos;
// OpenGL handles for shaders and VBOs
GLuint m_uiSkinnedVertShader;
GLuint m_uiDefaultVertShader;
GLuint m_uiSkinnedFragShader;
GLuint m_uiDefaultFragShader;
GLuint* m_puiVbo;
GLuint* m_puiIndexVbo;
// Texture IDs
GLuint m_ui32TexHeadBody;
GLuint m_ui32TexLegs;
GLuint m_ui32TexBeltNormalMap;
GLuint m_ui32TexHeadNormalMap;
GLuint m_ui32TexLegsNormalMap;
GLuint m_ui32TexSkyLine;
GLuint m_ui32TexWall;
GLuint m_ui32TexLamp;
GLuint m_ui32TexBelt;
// Group shader programs and their uniform locations together
struct
{
GLuint uiId;
GLuint auiLoc[eNumSkinnedUniforms];
}
m_SkinnedShaderProgram;
struct
{
GLuint uiId;
GLuint auiLoc[eNumDefaultUniforms];
}
m_DefaultShaderProgram;
bool m_bEnableDOT3;
// Variables to handle the animation in a time-based manner
unsigned long m_iTimePrev;
float m_fFrame;
public:
OGLES2ChameleonMan() : m_fWallPos(0),
m_fBackgroundPos(0),
m_fLightPos(0),
m_puiVbo(0),
m_puiIndexVbo(0),
m_bEnableDOT3(true),
m_iTimePrev(0),
m_fFrame(0)
{
}
virtual bool InitApplication();
virtual bool InitView();
virtual bool ReleaseView();
virtual bool QuitApplication();
virtual bool RenderScene();
bool LoadTextures(CPVRTString* pErrorStr);
bool LoadShaders(CPVRTString* pErrorStr);
void LoadVbos();
void DrawSkinnedMesh(int i32NodeIndex);
};
/*!****************************************************************************
@Function LoadTextures
@Output pErrorStr A string describing the error on failure
@Return bool true if no error occured
@Description Loads the textures required for this training course
******************************************************************************/
bool OGLES2ChameleonMan::LoadTextures(CPVRTString* const pErrorStr)
{
// Load Textures
if(PVRTTextureLoadFromPVR(c_szFinalChameleonManHeadBodyTexFile, &m_ui32TexHeadBody) != PVR_SUCCESS)
{
*pErrorStr = CPVRTString("ERROR: Failed to load texture for Upper Body.\n");
return false;
}
if(PVRTTextureLoadFromPVR(c_szFinalChameleonManLegsTexFile, &m_ui32TexLegs) != PVR_SUCCESS)
{
*pErrorStr = CPVRTString("ERROR: Failed to load texture for Legs.\n");
return false;
}
if(PVRTTextureLoadFromPVR(c_szTang_space_BodyMapTexFile, &m_ui32TexHeadNormalMap) != PVR_SUCCESS)
{
*pErrorStr = CPVRTString("ERROR: Failed to load normalmap texture for Upper Body.\n");
return false;
}
if(PVRTTextureLoadFromPVR(c_szTang_space_LegsMapTexFile, &m_ui32TexLegsNormalMap) != PVR_SUCCESS)
{
*pErrorStr = CPVRTString("ERROR: Failed to load normalmap texture for Legs.\n");
return false;
}
if(PVRTTextureLoadFromPVR(c_szTang_space_BeltMapTexFile, &m_ui32TexBeltNormalMap) != PVR_SUCCESS)
{
*pErrorStr = CPVRTString("ERROR: Failed to load normalmap texture for Belt.\n");
return false;
}
if(PVRTTextureLoadFromPVR(c_szSkylineTexFile, &m_ui32TexSkyLine) != PVR_SUCCESS)
{
*pErrorStr = CPVRTString("ERROR: Failed to load texture for SkyLine.\n");
return false;
}
if(PVRTTextureLoadFromPVR(c_szWallDiffuseBakedTexFile, &m_ui32TexWall) != PVR_SUCCESS)
{
*pErrorStr = CPVRTString("ERROR: Failed to load texture for Wall.\n");
return false;
}
if(PVRTTextureLoadFromPVR(c_szLampTexFile, &m_ui32TexLamp) != PVR_SUCCESS)
{
*pErrorStr = CPVRTString("ERROR: Failed to load texture for Lamps.\n");
return false;
}
if(PVRTTextureLoadFromPVR(c_szChameleonBeltTexFile, &m_ui32TexBelt) != PVR_SUCCESS)
{
*pErrorStr = CPVRTString("ERROR: Failed to load texture for Belt.\n");
return false;
}
return true;
}
/*!****************************************************************************
@Function LoadShaders
@Output pErrorStr A string describing the error on failure
@Return bool true if no error occured
@Description Loads and compiles the shaders and links the shader programs
required for this training course
******************************************************************************/
bool OGLES2ChameleonMan::LoadShaders(CPVRTString* pErrorStr)
{
int i;
/*
Load and compile the shaders from files.
Binary shaders are tried first, source shaders
are used as fallback.
*/
// Create the skinned program
if(PVRTShaderLoadFromFile(
c_szSkinnedVertShaderBinFile, c_szSkinnedVertShaderSrcFile, GL_VERTEX_SHADER, GL_SGX_BINARY_IMG, &m_uiSkinnedVertShader, pErrorStr) != PVR_SUCCESS)
{
return false;
}
if(PVRTShaderLoadFromFile(
c_szSkinnedFragShaderBinFile, c_szSkinnedFragShaderSrcFile, GL_FRAGMENT_SHADER, GL_SGX_BINARY_IMG, &m_uiSkinnedFragShader, pErrorStr) != PVR_SUCCESS)
{
return false;
}
if(PVRTCreateProgram(&m_SkinnedShaderProgram.uiId, m_uiSkinnedVertShader, m_uiSkinnedFragShader, g_aszAttribNames, eNumAttribs, pErrorStr) != PVR_SUCCESS)
{
PVRShellSet(prefExitMessage, pErrorStr->c_str());
return false;
}
// Store the location of uniforms for later use
for(i = 0; i < eNumSkinnedUniforms; ++i)
{
m_SkinnedShaderProgram.auiLoc[i] = glGetUniformLocation(m_SkinnedShaderProgram.uiId, g_aszSkinnedUniformNames[i]);
}
glUniform1i(m_SkinnedShaderProgram.auiLoc[ebUseDot3], m_bEnableDOT3);
// Set the sampler2D uniforms to corresponding texture units
glUniform1i(glGetUniformLocation(m_SkinnedShaderProgram.uiId, "sTexture"), 0);
glUniform1i(glGetUniformLocation(m_SkinnedShaderProgram.uiId, "sNormalMap"), 1);
// Create the non-skinned program
if(PVRTShaderLoadFromFile(
c_szDefaultVertShaderBinFile, c_szDefaultVertShaderSrcFile, GL_VERTEX_SHADER, GL_SGX_BINARY_IMG, &m_uiDefaultVertShader, pErrorStr) != PVR_SUCCESS)
{
return false;
}
if(PVRTShaderLoadFromFile(
c_szDefaultFragShaderBinFile, c_szDefaultFragShaderSrcFile, GL_FRAGMENT_SHADER, GL_SGX_BINARY_IMG, &m_uiDefaultFragShader, pErrorStr) != PVR_SUCCESS)
{
return false;
}
if(PVRTCreateProgram(&m_DefaultShaderProgram.uiId, m_uiDefaultVertShader, m_uiDefaultFragShader, g_aszDefaultAttribNames, eNumDefaultAttribs, pErrorStr) != PVR_SUCCESS)
{
PVRShellSet(prefExitMessage, pErrorStr->c_str());
return false;
}
// Store the location of uniforms for later use
for(i = 0; i < eNumDefaultUniforms; ++i)
{
m_DefaultShaderProgram.auiLoc[i] = glGetUniformLocation(m_DefaultShaderProgram.uiId, g_aszDefaultUniformNames[i]);
}
// Set the sampler2D uniforms to corresponding texture units
glUniform1i(glGetUniformLocation(m_DefaultShaderProgram.uiId, "sTexture"), 0);
return true;
}
/*!****************************************************************************
@Function LoadVbos
@Description Loads the mesh data required for this training course into
vertex buffer objects
******************************************************************************/
void OGLES2ChameleonMan::LoadVbos()
{
if (!m_puiVbo) m_puiVbo = new GLuint[m_Scene.nNumMesh];
if (!m_puiIndexVbo) m_puiIndexVbo = new GLuint[m_Scene.nNumMesh];
/*
Load vertex data of all meshes in the scene into VBOs
The meshes have been exported with the "Interleave Vectors" option,
so all data is interleaved in the buffer at pMesh->pInterleaved.
Interleaving data improves the memory access pattern and cache efficiency,
thus it can be read faster by the hardware.
*/
glGenBuffers(m_Scene.nNumMesh, m_puiVbo);
for (unsigned int i = 0; i < m_Scene.nNumMesh; ++i)
{
// Load vertex data into buffer object
SPODMesh& Mesh = m_Scene.pMesh[i];
unsigned int uiSize = Mesh.nNumVertex * Mesh.sVertex.nStride;
glBindBuffer(GL_ARRAY_BUFFER, m_puiVbo[i]);
glBufferData(GL_ARRAY_BUFFER, uiSize, Mesh.pInterleaved, GL_STATIC_DRAW);
// Load index data into buffer object if available
m_puiIndexVbo[i] = 0;
if (Mesh.sFaces.pData)
{
glGenBuffers(1, &m_puiIndexVbo[i]);
uiSize = PVRTModelPODCountIndices(Mesh) * sizeof(GLshort);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_puiIndexVbo[i]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, uiSize, Mesh.sFaces.pData, GL_STATIC_DRAW);
}
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
/*!****************************************************************************
@Function InitApplication
@Return bool true if no error occured
@Description Code in InitApplication() will be called by PVRShell once per
run, before the rendering context is created.
Used to initialize variables that are not dependant on it
(e.g. external modules, loading meshes, etc.)
If the rendering context is lost, InitApplication() will
not be called again.
******************************************************************************/
bool OGLES2ChameleonMan::InitApplication()
{
// Get and set the read path for content files
CPVRTResourceFile::SetReadPath((char*)PVRShellGet(prefReadPath));
// Get and set the load/release functions for loading external files.
// In the majority of cases the PVRShell will return NULL function pointers implying that
// nothing special is required to load external files.
CPVRTResourceFile::SetLoadReleaseFunctions(PVRShellGet(prefLoadFileFunc), PVRShellGet(prefReleaseFileFunc));
// Load the scene
if (m_Scene.ReadFromFile(c_szSceneFile) != PVR_SUCCESS)
{
PVRShellSet(prefExitMessage, "ERROR: Couldn't load the .pod file\n");
return false;
}
// The cameras are stored in the file. We check it contains at least one.
if (m_Scene.nNumCamera == 0)
{
PVRShellSet(prefExitMessage, "ERROR: The scene does not contain a camera\n");
return false;
}
// Check the scene contains at least one light
if (m_Scene.nNumLight == 0)
{
PVRShellSet(prefExitMessage, "ERROR: The scene does not contain a light\n");
return false;
}
return true;
}
/*!****************************************************************************
@Function QuitApplication
@Return bool true if no error occured
@Description Code in QuitApplication() will be called by PVRShell once per
run, just before exiting the program.
If the rendering context is lost, QuitApplication() will
not be called.
******************************************************************************/
bool OGLES2ChameleonMan::QuitApplication()
{
// Free the memory allocated for the scene
m_Scene.Destroy();
delete [] m_puiVbo;
delete [] m_puiIndexVbo;
return true;
}
/*!****************************************************************************
@Function InitView
@Return bool true if no error occured
@Description Code in InitView() will be called by PVRShell upon
initialization or after a change in the rendering context.
Used to initialize variables that are dependant on the rendering
context (e.g. textures, vertex buffers, etc.)
******************************************************************************/
bool OGLES2ChameleonMan::InitView()
{
CPVRTString ErrorStr;
/*
Initialize VBO data
*/
LoadVbos();
/*
Load textures
*/
if (!LoadTextures(&ErrorStr))
{
PVRShellSet(prefExitMessage, ErrorStr.c_str());
return false;
}
/*
Load and compile the shaders & link programs
*/
if (!LoadShaders(&ErrorStr))
{
PVRShellSet(prefExitMessage, ErrorStr.c_str());
return false;
}
/*
Initialize Print3D
*/
// Is the screen rotated?
bool bRotate = PVRShellGet(prefIsRotated) && PVRShellGet(prefFullScreen);
if(m_Print3D.SetTextures(0,PVRShellGet(prefWidth),PVRShellGet(prefHeight), bRotate) != PVR_SUCCESS)
{
PVRShellSet(prefExitMessage, "ERROR: Cannot initialise Print3D\n");
return false;
}
/*
Set OpenGL ES render states needed for this training course
*/
// Enable backface culling and depth test
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
// Use black as our clear colour
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// Initialise variables used for the animation
m_iTimePrev = PVRShellGetTime();
return true;
}
/*!****************************************************************************
@Function ReleaseView
@Return bool true if no error occured
@Description Code in ReleaseView() will be called by PVRShell when the
application quits or before a change in the rendering context.
******************************************************************************/
bool OGLES2ChameleonMan::ReleaseView()
{
// Delete textures
glDeleteTextures(1, &m_ui32TexLegs);
glDeleteTextures(1, &m_ui32TexBeltNormalMap);
glDeleteTextures(1, &m_ui32TexHeadNormalMap);
glDeleteTextures(1, &m_ui32TexLegsNormalMap);
glDeleteTextures(1, &m_ui32TexSkyLine);
glDeleteTextures(1, &m_ui32TexWall);
glDeleteTextures(1, &m_ui32TexLamp);
glDeleteTextures(1, &m_ui32TexBelt);
// Delete program and shader objects
glDeleteProgram(m_SkinnedShaderProgram.uiId);
glDeleteProgram(m_DefaultShaderProgram.uiId);
glDeleteShader(m_uiSkinnedVertShader);
glDeleteShader(m_uiDefaultVertShader);
glDeleteShader(m_uiSkinnedFragShader);
glDeleteShader(m_uiDefaultFragShader);
// Delete buffer objects
glDeleteBuffers(m_Scene.nNumMesh, m_puiVbo);
glDeleteBuffers(m_Scene.nNumMesh, m_puiIndexVbo);
// Release Print3D Textures
m_Print3D.ReleaseTextures();
return true;
}
/*!****************************************************************************
@Function RenderScene
@Return bool true if no error occured
@Description Main rendering loop function of the program. The shell will
call this function every frame.
eglSwapBuffers() will be performed by PVRShell automatically.
PVRShell will also manage important OS events.
Will also manage relevent OS events. The user has access to
these events through an abstraction layer provided by PVRShell.
******************************************************************************/
bool OGLES2ChameleonMan::RenderScene()
{
// Clear the color and depth buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Use shader program
glUseProgram(m_SkinnedShaderProgram.uiId);
if(PVRShellIsKeyPressed(PVRShellKeyNameACTION1))
{
m_bEnableDOT3 = !m_bEnableDOT3;
glUniform1i(m_SkinnedShaderProgram.auiLoc[ebUseDot3], m_bEnableDOT3);
}
/*
Calculates the frame number to animate in a time-based manner.
Uses the shell function PVRShellGetTime() to get the time in milliseconds.
*/
unsigned long iTime = PVRShellGetTime();
if(iTime > m_iTimePrev)
{
float fDelta = (float) (iTime - m_iTimePrev);
m_fFrame += fDelta * g_fDemoFrameRate;
// Increment the counters to make sure our animation works
m_fLightPos += fDelta * 0.0034f;
m_fWallPos += fDelta * 0.00027f;
m_fBackgroundPos += fDelta * -0.000027f;
// Wrap the Animation back to the Start
if(m_fLightPos >= PVRT_TWO_PI)
m_fLightPos -= PVRT_TWO_PI;
if(m_fWallPos >= PVRT_TWO_PI)
m_fWallPos -= PVRT_TWO_PI;
if(m_fBackgroundPos <= 0)
m_fBackgroundPos += 1.0f;
if(m_fFrame > m_Scene.nNumFrame - 1)
m_fFrame = 0;
}
m_iTimePrev = iTime;
// Set the scene animation to the current frame
m_Scene.SetFrame(m_fFrame);
// Set up camera
PVRTVec3 vFrom, vTo, vUp(0.0f, 1.0f, 0.0f);
PVRTMat4 mView, mProjection;
PVRTVec3 LightPos;
float fFOV;
int i;
bool bRotate = PVRShellGet(prefIsRotated) && PVRShellGet(prefFullScreen);
// Get the camera position, target and field of view (fov)
if(m_Scene.pCamera[0].nIdxTarget != -1) // Does the camera have a target?
fFOV = m_Scene.GetCameraPos( vFrom, vTo, 0); // vTo is taken from the target node
else
fFOV = m_Scene.GetCamera( vFrom, vTo, vUp, 0); // vTo is calculated from the rotation
fFOV *= bRotate ? (float)PVRShellGet(prefWidth)/(float)PVRShellGet(prefHeight) : (float)PVRShellGet(prefHeight)/(float)PVRShellGet(prefWidth);
/*
We can build the model view matrix from the camera position, target and an up vector.
For this we use PVRTMat4::LookAtRH().
*/
mView = PVRTMat4::LookAtRH(vFrom, vTo, vUp);
// Calculate the projection matrix
mProjection = PVRTMat4::PerspectiveFovRH(fFOV, (float)PVRShellGet(prefWidth)/(float)PVRShellGet(prefHeight), g_fCameraNear, g_fCameraFar, PVRTMat4::OGL, bRotate);
// Update Light Position and related VGP Program constant
LightPos.x = 200.0f;
LightPos.y = 350.0f;
LightPos.z = 200.0f * PVRTABS(sin((PVRT_PI / 4.0f) + m_fLightPos));
glUniform3fv(m_SkinnedShaderProgram.auiLoc[eLightPos], 1, LightPos.ptr());
// Set up the View * Projection Matrix
PVRTMat4 mViewProjection;
mViewProjection = mProjection * mView;
glUniformMatrix4fv(m_SkinnedShaderProgram.auiLoc[eViewProj], 1, GL_FALSE, mViewProjection.ptr());
// Enable the vertex attribute arrays
for(i = 0; i < eNumAttribs; ++i) glEnableVertexAttribArray(i);
// Draw skinned meshes
for(unsigned int i32NodeIndex = 0; i32NodeIndex < 3; ++i32NodeIndex)
{
// Bind correct texture
switch(i32NodeIndex)
{
case eBody:
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, m_ui32TexHeadNormalMap);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_ui32TexHeadBody);
break;
case eLegs:
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, m_ui32TexLegsNormalMap);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_ui32TexLegs);
break;
default:
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, m_ui32TexBeltNormalMap);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_ui32TexBelt);
break;
}
DrawSkinnedMesh(i32NodeIndex);
}
// Safely disable the vertex attribute arrays
for(i = 0; i < eNumAttribs; ++i) glDisableVertexAttribArray(i);
// Draw non-skinned meshes
glUseProgram(m_DefaultShaderProgram.uiId);
// Enable the vertex attribute arrays
for(i = 0; i < eNumDefaultAttribs; ++i) glEnableVertexAttribArray(i);
for(unsigned int i32NodeIndex = 3; i32NodeIndex < m_Scene.nNumMeshNode; ++i32NodeIndex)
{
SPODNode& Node = m_Scene.pNode[i32NodeIndex];
SPODMesh& Mesh = m_Scene.pMesh[Node.nIdx];
// bind the VBO for the mesh
glBindBuffer(GL_ARRAY_BUFFER, m_puiVbo[Node.nIdx]);
// bind the index buffer, won't hurt if the handle is 0
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_puiIndexVbo[Node.nIdx]);
// Get the node model matrix
PVRTMat4 mWorld;
mWorld = m_Scene.GetWorldMatrix(Node);
// Setup the appropriate texture and transformation (if needed)
switch(i32NodeIndex)
{
case eWall:
glBindTexture(GL_TEXTURE_2D, m_ui32TexWall);
// Rotate the wall mesh which is circular
mWorld *= PVRTMat4::RotationY(m_fWallPos);
glUniform1f(m_DefaultShaderProgram.auiLoc[eDefaultUOffset], 0);
break;
case eBackground:
glBindTexture(GL_TEXTURE_2D, m_ui32TexSkyLine);
glUniform1f(m_DefaultShaderProgram.auiLoc[eDefaultUOffset], m_fBackgroundPos);
break;
case eLights:
{
glBindTexture(GL_TEXTURE_2D, m_ui32TexLamp);
PVRTMat4 mWallWorld = m_Scene.GetWorldMatrix(m_Scene.pNode[eWall]);
mWorld = mWallWorld * PVRTMat4::RotationY(m_fWallPos) * mWallWorld.inverse() * mWorld;
glUniform1f(m_DefaultShaderProgram.auiLoc[eDefaultUOffset], 0);
}
break;
default:
break;
};
// Set up shader uniforms
PVRTMat4 mModelViewProj;
mModelViewProj = mViewProjection * mWorld;
glUniformMatrix4fv(m_DefaultShaderProgram.auiLoc[eDefaultMVPMatrix], 1, GL_FALSE, mModelViewProj.ptr());
// Set the vertex attribute offsets
glVertexAttribPointer(DEFAULT_VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, Mesh.sVertex.nStride, Mesh.sVertex.pData);
glVertexAttribPointer(DEFAULT_TEXCOORD_ARRAY, 2, GL_FLOAT, GL_FALSE, Mesh.psUVW[0].nStride, Mesh.psUVW[0].pData);
// Indexed Triangle list
glDrawElements(GL_TRIANGLES, Mesh.nNumFaces*3, GL_UNSIGNED_SHORT, 0);
}
// Safely disable the vertex attribute arrays
for(i = 0; i < eNumAttribs; ++i) glDisableVertexAttribArray(i);
// unbind the VBOs
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
// Display the demo name using the tools. For a detailed explanation, see the training course IntroducingPVRTools
const char * pDescription;
if(m_bEnableDOT3)
pDescription = "Skinning with DOT3 Per Pixel Lighting";
else
pDescription = "Skinning with Vertex Lighting";
m_Print3D.DisplayDefaultTitle("Chameleon Man", pDescription, ePVRTPrint3DSDKLogo);
m_Print3D.Flush();
return true;
}
/*!****************************************************************************
@Function DrawSkinnedMesh
@Input i32NodeIndex Node index of the mesh to draw
@Description Draws a SPODMesh after the model view matrix has been set and
the meterial prepared.
******************************************************************************/
void OGLES2ChameleonMan::DrawSkinnedMesh(int i32NodeIndex)
{
SPODNode& Node = m_Scene.pNode[i32NodeIndex];
SPODMesh& Mesh = m_Scene.pMesh[Node.nIdx];
// bind the VBO for the mesh
glBindBuffer(GL_ARRAY_BUFFER, m_puiVbo[Node.nIdx]);
// bind the index buffer, won't hurt if the handle is 0
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_puiIndexVbo[Node.nIdx]);
// Set the vertex attribute offsets
glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, Mesh.sVertex.nStride, Mesh.sVertex.pData);
glVertexAttribPointer(NORMAL_ARRAY, 3, GL_FLOAT, GL_FALSE, Mesh.sNormals.nStride, Mesh.sNormals.pData);
glVertexAttribPointer(TANGENT_ARRAY, 3, GL_FLOAT, GL_FALSE, Mesh.sTangents.nStride, Mesh.sTangents.pData);
glVertexAttribPointer(BINORMAL_ARRAY, 3, GL_FLOAT, GL_FALSE, Mesh.sBinormals.nStride, Mesh.sBinormals.pData);
glVertexAttribPointer(TEXCOORD_ARRAY, 2, GL_FLOAT, GL_FALSE, Mesh.psUVW[0].nStride, Mesh.psUVW[0].pData);
glVertexAttribPointer(BONEINDEX_ARRAY, Mesh.sBoneIdx.n, GL_UNSIGNED_BYTE, GL_FALSE, Mesh.sBoneIdx.nStride, Mesh.sBoneIdx.pData);
glVertexAttribPointer(BONEWEIGHT_ARRAY, Mesh.sBoneWeight.n, GL_UNSIGNED_BYTE, GL_TRUE, Mesh.sBoneWeight.nStride, Mesh.sBoneWeight.pData);
for(int i32Batch = 0; i32Batch < Mesh.sBoneBatches.nBatchCnt; ++i32Batch)
{
/*
If the current mesh has bone index and weight data then we need to
set up some additional variables in the shaders.
*/
// Set the number of bones that will influence each vertex in the mesh
glUniform1i(m_SkinnedShaderProgram.auiLoc[eBoneCount], Mesh.sBoneIdx.n);
// Go through the bones for the current bone batch
PVRTMat4 amBoneWorld[8];
PVRTMat3 afBoneWorldIT[8], mBoneIT;
int i32Count = Mesh.sBoneBatches.pnBatchBoneCnt[i32Batch];
for(int i = 0; i < i32Count; ++i)
{
// Get the Node of the bone
int i32NodeID = Mesh.sBoneBatches.pnBatches[i32Batch * Mesh.sBoneBatches.nBatchBoneMax + i];
// Get the World transformation matrix for this bone and combine it with our app defined
// transformation matrix
amBoneWorld[i] = m_Scene.GetBoneWorldMatrix(Node, m_Scene.pNode[i32NodeID]);
// Calculate the inverse transpose of the 3x3 rotation/scale part for correct lighting
afBoneWorldIT[i] = PVRTMat3(amBoneWorld[i]).inverse().transpose();
}
glUniformMatrix4fv(m_SkinnedShaderProgram.auiLoc[eBoneMatrices], i32Count, GL_FALSE, amBoneWorld[0].ptr());
glUniformMatrix3fv(m_SkinnedShaderProgram.auiLoc[eBoneMatricesIT], i32Count, GL_FALSE, afBoneWorldIT[0].ptr());
/*
As we are using bone batching we don't want to draw all the faces contained within pMesh, we only want
to draw the ones that are in the current batch. To do this we pass to the drawMesh function the offset
to the start of the current batch of triangles (Mesh.sBoneBatches.pnBatchOffset[i32Batch]) and the
total number of triangles to draw (i32Tris)
*/
int i32Tris;
if(i32Batch+1 < Mesh.sBoneBatches.nBatchCnt)
i32Tris = Mesh.sBoneBatches.pnBatchOffset[i32Batch+1] - Mesh.sBoneBatches.pnBatchOffset[i32Batch];
else
i32Tris = Mesh.nNumFaces - Mesh.sBoneBatches.pnBatchOffset[i32Batch];
// Draw the mesh
size_t offset = sizeof(GLushort) * 3 * Mesh.sBoneBatches.pnBatchOffset[i32Batch];
glDrawElements(GL_TRIANGLES, i32Tris * 3, GL_UNSIGNED_SHORT, (void*) offset);
}
}
/*!****************************************************************************
@Function NewDemo
@Return PVRShell* The demo supplied by the user
@Description This function must be implemented by the user of the shell.
The user should return its PVRShell object defining the
behaviour of the application.
******************************************************************************/
PVRShell* NewDemo()
{
return new OGLES2ChameleonMan();
}
/******************************************************************************
End of file (OGLES2ChameleonMan.cpp)
******************************************************************************/