123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601 |
- /******************************************************************************
- @File OGLES2LevelOfDetail.cpp
- @Title Level of detail
- @Version
- @Copyright Copyright (C) Imagination Technologies Limited.
- @Platform Independent
- @Description Demonstrates the use of different shader detail levels
- ******************************************************************************/
- #include <math.h>
- #include "PVRShell.h"
- #include "OGLES2Tools.h"
- /******************************************************************************
- Constants
- ******************************************************************************/
- // Camera constants used to generate the projection matrix
- const float CAM_NEAR = 75.0f;
- const float CAM_FAR = 3000.0f;
- /******************************************************************************
- shader attributes
- ******************************************************************************/
- // vertex attributes
- enum EVertexAttrib {
- VERTEX_ARRAY, NORMAL_ARRAY, TEXCOORD_ARRAY, TANGENT_ARRAY, eNumAttribs };
- const char* g_aszAttribNames[] = {
- "inVertex", "inNormal", "inTexCoord", "inTangent" };
- // shader uniforms
- enum EUniform {
- eMVPMatrix, eModelWorld, eEyePosModel, eHighDetail, eNumUniforms };
- const char* g_aszUniformNames[] = {
- "MVPMatrix", "ModelWorld", "EyePosModel", "bHighDetail" };
- /******************************************************************************
- Content file names
- ******************************************************************************/
- // Source and binary shaders
- const char c_szFragShaderSrcFile[] = "FragShader.fsh";
- const char c_szFragShaderBinFile[] = "FragShader.fsc";
- const char c_szVertShaderSrcFile[] = "VertShader.vsh";
- const char c_szVertShaderBinFile[] = "VertShader.vsc";
- // PVR texture files
- const char c_szReflectTexFile[] = "Reflection.pvr";
- const char c_szNormalTexFile[] = "NormalMap.pvr";
- // POD scene files
- const char c_szSceneFile[] = "Mask.pod";
- const char* g_Detail[] = {"Detail: High", "Detail: Low"};
- /*!****************************************************************************
- Class implementing the PVRShell functions.
- ******************************************************************************/
- class OGLES2LevelOfDetail : public PVRShell
- {
- // Print3D class used to display text
- CPVRTPrint3D m_Print3D;
- // 3D Model
- CPVRTModelPOD m_Scene;
- // Projection and view matrices
- PVRTMat4 m_mProjection, m_mView;
- // OpenGL handles for shaders, textures and VBOs
- GLuint m_uiVertShader;
- GLuint m_uiFragShader;
- GLuint m_uiReflectTex;
- GLuint m_uiNormalTex;
- GLuint* m_puiVbo;
- GLuint* m_puiIndexVbo;
- // Group shader programs and their uniform locations together
- struct
- {
- GLuint uiId;
- GLuint auiLoc[eNumUniforms];
- }
- m_ShaderProgram;
- // Rotation angle for the model
- float m_fAngleY;
- // Z position of the model
- float m_fPositionZ;
- // Level of detail flag
- bool m_bHighDetail;
- // The mesh bounding box
- PVRTVec4 m_avBoundingBox[8];
- public:
- 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 ProjectVertex(PVRTVec4& Vector, PVRTMat4& Matrix, float& fX, float& fY);
- void DrawMesh(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 OGLES2LevelOfDetail::LoadTextures(CPVRTString* const pErrorStr)
- {
- if(PVRTTextureLoadFromPVR(c_szReflectTexFile, &m_uiReflectTex) != PVR_SUCCESS)
- {
- *pErrorStr = "ERROR: Failed to load texture.";
- return false;
- }
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- if(PVRTTextureLoadFromPVR(c_szNormalTexFile, &m_uiNormalTex) != PVR_SUCCESS)
- {
- *pErrorStr = "ERROR: Failed to load texture.";
- return false;
- }
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- 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 OGLES2LevelOfDetail::LoadShaders(CPVRTString* pErrorStr)
- {
- /*
- Load and compile the shaders from files.
- Binary shaders are tried first, source shaders
- are used as fallback.
- */
- if (PVRTShaderLoadFromFile(
- c_szVertShaderBinFile, c_szVertShaderSrcFile, GL_VERTEX_SHADER, GL_SGX_BINARY_IMG, &m_uiVertShader, pErrorStr) != PVR_SUCCESS)
- {
- return false;
- }
- if (PVRTShaderLoadFromFile(
- c_szFragShaderBinFile, c_szFragShaderSrcFile, GL_FRAGMENT_SHADER, GL_SGX_BINARY_IMG, &m_uiFragShader, pErrorStr) != PVR_SUCCESS)
- {
- return false;
- }
- /*
- Set up and link the shader program
- */
- if (PVRTCreateProgram(&m_ShaderProgram.uiId, m_uiVertShader, m_uiFragShader, g_aszAttribNames, eNumAttribs, pErrorStr) != PVR_SUCCESS)
- {
- PVRShellSet(prefExitMessage, pErrorStr->c_str());
- return false;
- }
- // Store the location of uniforms for later use
- for (int i = 0; i < eNumUniforms; ++i)
- {
- m_ShaderProgram.auiLoc[i] = glGetUniformLocation(m_ShaderProgram.uiId, g_aszUniformNames[i]);
- }
- return true;
- }
- /*!****************************************************************************
- @Function LoadVbos
- @Description Loads the mesh data required for this training course into
- vertex buffer objects
- ******************************************************************************/
- void OGLES2LevelOfDetail::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);
- }
- if (i == 0)
- {
- PVRTVec3 vBoundingBoxMin, vBoundingBoxMax;
- // calculate bounding box for mesh 0
- float* pfData = (float*)Mesh.pInterleaved;
- vBoundingBoxMin.x = vBoundingBoxMax.x = pfData[0];
- vBoundingBoxMin.y = vBoundingBoxMax.y = pfData[1];
- vBoundingBoxMin.z = vBoundingBoxMax.z = pfData[2];
- for(unsigned int i = 1; i < Mesh.nNumVertex; ++i)
- {
- pfData = (float*)(((char*)pfData) + Mesh.sVertex.nStride);
- vBoundingBoxMin.x = PVRT_MIN(vBoundingBoxMin.x, pfData[0]);
- vBoundingBoxMin.y = PVRT_MIN(vBoundingBoxMin.y, pfData[1]);
- vBoundingBoxMin.z = PVRT_MIN(vBoundingBoxMin.z, pfData[2]);
- vBoundingBoxMax.x = PVRT_MAX(vBoundingBoxMax.x, pfData[0]);
- vBoundingBoxMax.y = PVRT_MAX(vBoundingBoxMax.y, pfData[1]);
- vBoundingBoxMax.z = PVRT_MAX(vBoundingBoxMax.z, pfData[2]);
- }
- for (int i = 0; i < 8; ++i)
- {
- m_avBoundingBox[i].x = (i & 1) ? vBoundingBoxMin.x : vBoundingBoxMax.x;
- m_avBoundingBox[i].y = (i & 2) ? vBoundingBoxMin.y : vBoundingBoxMax.y;
- m_avBoundingBox[i].z = (i & 4) ? vBoundingBoxMin.z : vBoundingBoxMax.z;
- m_avBoundingBox[i].w = 1.0f;
- }
- }
- }
- 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 OGLES2LevelOfDetail::InitApplication()
- {
- m_puiVbo = 0;
- m_puiIndexVbo = 0;
- // Get and set the read path for content files
- CPVRTResourceFile::SetReadPath((char*)PVRShellGet(prefReadPath));
- // Load the scene
- if (m_Scene.ReadFromFile(c_szSceneFile) != PVR_SUCCESS)
- {
- PVRShellSet(prefExitMessage, "ERROR: Couldn't load the .pod file\n");
- return false;
- }
- m_fAngleY = 0.0f;
- m_fPositionZ = 0.0f;
- m_bHighDetail = true;
- 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.x
- ******************************************************************************/
- bool OGLES2LevelOfDetail::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 OGLES2LevelOfDetail::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;
- }
- // Set the sampler2D uniforms to corresponding texture units
- glUniform1i(glGetUniformLocation(m_ShaderProgram.uiId, "sReflectTex"), 0);
- glUniform1i(glGetUniformLocation(m_ShaderProgram.uiId, "sNormalMap"), 1);
- // Is the screen rotated?
- bool bRotate = PVRShellGet(prefIsRotated) && PVRShellGet(prefFullScreen);
- /*
- Initialize Print3D
- */
- if(m_Print3D.SetTextures(0,PVRShellGet(prefWidth),PVRShellGet(prefHeight), bRotate) != PVR_SUCCESS)
- {
- PVRShellSet(prefExitMessage, "ERROR: Cannot initialise Print3D\n");
- return false;
- }
- /*
- Calculate the projection and view matrices
- */
- m_mProjection = PVRTMat4::PerspectiveFovRH(PVRT_PI/6, (float)PVRShellGet(prefWidth)/(float)PVRShellGet(prefHeight), CAM_NEAR, CAM_FAR, PVRTMat4::OGL, bRotate);
- m_mView = PVRTMat4::LookAtRH(PVRTVec3(0, 0, 150), PVRTVec3(0, 0, 0), PVRTVec3(0, 1, 0));
- /*
- 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 a nice bright blue as clear colour
- glClearColor(0.6f, 0.8f, 1.0f, 1.0f);
- 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 OGLES2LevelOfDetail::ReleaseView()
- {
- // Delete textures
- glDeleteTextures(1, &m_uiReflectTex);
- glDeleteTextures(1, &m_uiNormalTex);
- // Delete program and shader objects
- glDeleteProgram(m_ShaderProgram.uiId);
- glDeleteShader(m_uiVertShader);
- glDeleteShader(m_uiFragShader);
- // 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 OGLES2LevelOfDetail::RenderScene()
- {
- // Clear the color and depth buffer
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- // Use shader program
- glUseProgram(m_ShaderProgram.uiId);
- // Bind textures
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, m_uiReflectTex);
- glActiveTexture(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE_2D, m_uiNormalTex);
- // Rotate and Translate the model matrix
- PVRTMat4 mModel, mRotY, mTrans;
- float fDistance = 1400.0f * cos(m_fPositionZ) - 1350.0f;
-
- mTrans = PVRTMat4::Translation(0.0, 0.0, fDistance);
- mRotY = PVRTMat4::RotationY(m_fAngleY);
- mModel = mTrans * mRotY;
- m_fAngleY += PVRT_PI / 210;
- m_fPositionZ += 2 * PVRT_PI * 0.0008f;
- // Set model view projection matrix
- PVRTMat4 mModelView, mMVP;
- mModelView = m_mView * mModel;
- mMVP = m_mProjection * mModelView;
- glUniformMatrix4fv(m_ShaderProgram.auiLoc[eMVPMatrix], 1, GL_FALSE, mMVP.ptr());
- // Set model matrix
- PVRTMat3 Model3x3 = PVRTMat3(mModel);
- glUniformMatrix3fv(m_ShaderProgram.auiLoc[eModelWorld], 1, GL_FALSE, Model3x3.ptr());
- // Set eye position in model space
- PVRTVec4 vEyePosModel;
- vEyePosModel = mModelView.inverse() * PVRTVec4(0, 0, 0, 1);
- glUniform3fv(m_ShaderProgram.auiLoc[eEyePosModel], 1, &vEyePosModel.x);
- // Calculate the square of the pixel area that the mesh takes up on screen
- // This is done by projecting the vertices of the bounding box to screen space
- // then taking the axis aligned 2D bounding box of the projected vertices.
- // This is a very conservative estimate
- float fMinX, fMaxX, fMinY, fMaxY, fX, fY;
- ProjectVertex(m_avBoundingBox[0], mMVP, fX, fY);
- fMinX = fMaxX = fX;
- fMinY = fMaxY = fY;
- for (int i = 1; i < 8; ++i)
- {
- ProjectVertex(m_avBoundingBox[i], mMVP, fX, fY);
- fMinX = PVRT_MIN(fMinX, fX);
- fMinY = PVRT_MIN(fMinY, fY);
- fMaxX = PVRT_MAX(fMaxX, fX);
- fMaxY = PVRT_MAX(fMaxY, fY);
- }
- // Choose high detail if the mesh bounding box covers more than 2% of the screen
- m_bHighDetail = ((fMaxX - fMinX) * (fMaxY - fMinY) > 0.02);
- glUniform1i(m_ShaderProgram.auiLoc[eHighDetail], m_bHighDetail);
- /*
- Now that the uniforms are set, call another function to actually draw the mesh.
- */
- DrawMesh(m_bHighDetail ? 0 : 1);
- // Displays the demo name using the tools. For a detailed explanation, see the training course IntroducingPVRTools
- m_Print3D.DisplayDefaultTitle("Level of detail", (m_bHighDetail) ? "Detail: high" : "Detail: low", ePVRTPrint3DLogoIMG);
- m_Print3D.Flush();
- return true;
- }
- void OGLES2LevelOfDetail::ProjectVertex(PVRTVec4& Vector, PVRTMat4& Matrix, float& fX, float& fY)
- {
- PVRTVec4 vProj;
- vProj = Matrix * Vector;
- float fRcpW = 1 / vProj.w;
- fX = vProj.x * fRcpW;
- fY = vProj.y * fRcpW;
- }
- /*!****************************************************************************
- @Function DrawMesh
- @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 OGLES2LevelOfDetail::DrawMesh(int i32NodeIndex)
- {
- int i32MeshIndex = m_Scene.pNode[i32NodeIndex].nIdx;
- SPODMesh* pMesh = &m_Scene.pMesh[i32MeshIndex];
- // bind the VBO for the mesh
- glBindBuffer(GL_ARRAY_BUFFER, m_puiVbo[i32MeshIndex]);
- // bind the index buffer, won't hurt if the handle is 0
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_puiIndexVbo[i32MeshIndex]);
- // Enable the vertex attribute arrays
- for (int i = 0; i < eNumAttribs; ++i) glEnableVertexAttribArray(i);
- // Set the vertex attribute offsets
- glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, pMesh->sVertex.nStride, pMesh->sVertex.pData);
- glVertexAttribPointer(NORMAL_ARRAY, 3, GL_FLOAT, GL_FALSE, pMesh->sNormals.nStride, pMesh->sNormals.pData);
- glVertexAttribPointer(TEXCOORD_ARRAY, 2, GL_FLOAT, GL_FALSE, pMesh->psUVW[0].nStride, pMesh->psUVW[0].pData);
- glVertexAttribPointer(TANGENT_ARRAY, 3, GL_FLOAT, GL_FALSE, pMesh->sTangents.nStride, pMesh->sTangents.pData);
- /*
- The geometry can be exported in 4 ways:
- - Indexed Triangle list
- - Non-Indexed Triangle list
- - Indexed Triangle strips
- - Non-Indexed Triangle strips
- */
- if(pMesh->nNumStrips == 0)
- {
- if(m_puiIndexVbo[i32MeshIndex])
- {
- // Indexed Triangle list
- glDrawElements(GL_TRIANGLES, pMesh->nNumFaces*3, GL_UNSIGNED_SHORT, 0);
- }
- else
- {
- // Non-Indexed Triangle list
- glDrawArrays(GL_TRIANGLES, 0, pMesh->nNumFaces*3);
- }
- }
- else
- {
- for(int i = 0; i < (int)pMesh->nNumStrips; ++i)
- {
- int offset = 0;
- if(m_puiIndexVbo[i32MeshIndex])
- {
- // Indexed Triangle strips
- glDrawElements(GL_TRIANGLE_STRIP, pMesh->pnStripLength[i]+2, GL_UNSIGNED_SHORT, &((GLshort*)0)[offset]);
- }
- else
- {
- // Non-Indexed Triangle strips
- glDrawArrays(GL_TRIANGLE_STRIP, offset, pMesh->pnStripLength[i]+2);
- }
- offset += pMesh->pnStripLength[i]+2;
- }
- }
- // Safely disable the vertex attribute arrays
- for (int i = 0; i < eNumAttribs; ++i) glDisableVertexAttribArray(i);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- }
- /*!****************************************************************************
- @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 OGLES2LevelOfDetail();
- }
- /******************************************************************************
- End of file (OGLES2LevelOfDetail.cpp)
- ******************************************************************************/
|