/* * Copyright 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //-------------------------------------------------------------------------------- // MoreTeapotsRenderer.cpp // Render teapots //-------------------------------------------------------------------------------- //-------------------------------------------------------------------------------- // Include files //-------------------------------------------------------------------------------- #include "MoreTeapotsRenderer.h" //-------------------------------------------------------------------------------- // Teapot model data //-------------------------------------------------------------------------------- #include "teapot.inl" //-------------------------------------------------------------------------------- // Ctor //-------------------------------------------------------------------------------- MoreTeapotsRenderer::MoreTeapotsRenderer() : geometry_instancing_support_( false ) { } //-------------------------------------------------------------------------------- // Dtor //-------------------------------------------------------------------------------- MoreTeapotsRenderer::~MoreTeapotsRenderer() { Unload(); } //-------------------------------------------------------------------------------- // Init //-------------------------------------------------------------------------------- void MoreTeapotsRenderer::Init( const int32_t numX, const int32_t numY, const int32_t numZ ) { if( ndk_helper::GLContext::GetInstance()->GetGLVersion() >= 3.0 ) { geometry_instancing_support_ = true; } else if( ndk_helper::GLContext::GetInstance()->CheckExtension( "GL_NV_draw_instanced" ) && ndk_helper::GLContext::GetInstance()->CheckExtension( "GL_NV_uniform_buffer_object" ) ) { LOGI( "Supported via extension!" ); //_bGeometryInstancingSupport = true; //_bARBSupport = true; //Need to patch shaders //Currently this has been disabled } //Settings glFrontFace( GL_CCW ); //Create Index buffer num_indices_ = sizeof(teapotIndices) / sizeof(teapotIndices[0]); glGenBuffers( 1, &ibo_ ); glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ibo_ ); glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof(teapotIndices), teapotIndices, GL_STATIC_DRAW ); glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 ); //Create VBO num_vertices_ = sizeof(teapotPositions) / sizeof(teapotPositions[0]) / 3; int32_t iStride = sizeof(TEAPOT_VERTEX); int32_t iIndex = 0; TEAPOT_VERTEX* p = new TEAPOT_VERTEX[num_vertices_]; for( int32_t i = 0; i < num_vertices_; ++i ) { p[i].pos[0] = teapotPositions[iIndex]; p[i].pos[1] = teapotPositions[iIndex + 1]; p[i].pos[2] = teapotPositions[iIndex + 2]; p[i].normal[0] = teapotNormals[iIndex]; p[i].normal[1] = teapotNormals[iIndex + 1]; p[i].normal[2] = teapotNormals[iIndex + 2]; iIndex += 3; } glGenBuffers( 1, &vbo_ ); glBindBuffer( GL_ARRAY_BUFFER, vbo_ ); glBufferData( GL_ARRAY_BUFFER, iStride * num_vertices_, p, GL_STATIC_DRAW ); glBindBuffer( GL_ARRAY_BUFFER, 0 ); delete[] p; //Init Projection matrices teapot_x_ = numX; teapot_y_ = numY; teapot_z_ = numZ; vec_mat_models_.reserve( teapot_x_ * teapot_y_ * teapot_z_ ); UpdateViewport(); const float total_width = 500.f; float gap_x = total_width / (teapot_x_ - 1); float gap_y = total_width / (teapot_y_ - 1); float gap_z = total_width / (teapot_z_ - 1); float offset_x = -total_width / 2.f; float offset_y = -total_width / 2.f; float offset_z = -total_width / 2.f; for( int32_t iX = 0; iX < teapot_x_; ++iX ) for( int32_t iY = 0; iY < teapot_y_; ++iY ) for( int32_t iZ = 0; iZ < teapot_z_; ++iZ ) { vec_mat_models_.push_back( ndk_helper::Mat4::Translation( iX * gap_x + offset_x, iY * gap_y + offset_y, iZ * gap_z + offset_z ) ); vec_colors_.push_back( ndk_helper::Vec3( random() / float( RAND_MAX * 1.1 ), random() / float( RAND_MAX * 1.1 ), random() / float( RAND_MAX * 1.1 ) ) ); float fX = random() / float( RAND_MAX ) - 0.5f; float fY = random() / float( RAND_MAX ) - 0.5f; vec_rotations_.push_back( ndk_helper::Vec2( fX * 0.05f, fY * 0.05f ) ); vec_current_rotations_.push_back( ndk_helper::Vec2( fX * M_PI, fY * M_PI ) ); } if( geometry_instancing_support_ ) { // //Create parameter dictionary for shader patch std::map param; param[std::string( "%NUM_TEAPOT%" )] = ToString( teapot_x_ * teapot_y_ * teapot_z_ ); param[std::string( "%LOCATION_VERTEX%" )] = ToString( ATTRIB_VERTEX ); param[std::string( "%LOCATION_NORMAL%" )] = ToString( ATTRIB_NORMAL ); if( arb_support_ ) param[std::string( "%ARB%" )] = std::string( "ARB" ); else param[std::string( "%ARB%" )] = std::string( "" ); //Load shader bool b = LoadShadersES3( &shader_param_, "Shaders/VS_ShaderPlainES3.vsh", "Shaders/ShaderPlainES3.fsh", param ); if( b ) { // //Create uniform buffer // GLuint bindingPoint = 1; GLuint blockIndex; blockIndex = glGetUniformBlockIndex( shader_param_.program_, "ParamBlock" ); glUniformBlockBinding( shader_param_.program_, blockIndex, bindingPoint ); //Retrieve array stride value int32_t iNumIndices; glGetActiveUniformBlockiv( shader_param_.program_, blockIndex, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &iNumIndices ); GLint i[iNumIndices]; GLint stride[iNumIndices]; glGetActiveUniformBlockiv( shader_param_.program_, blockIndex, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, i ); glGetActiveUniformsiv( shader_param_.program_, iNumIndices, (GLuint*) i, GL_UNIFORM_ARRAY_STRIDE, stride ); ubo_matrix_stride_ = stride[0] / sizeof(float); ubo_vector_stride_ = stride[2] / sizeof(float); glGenBuffers( 1, &ubo_ ); glBindBuffer( GL_UNIFORM_BUFFER, ubo_ ); glBindBufferBase( GL_UNIFORM_BUFFER, bindingPoint, ubo_ ); //Store color value which wouldn't be updated every frame int32_t iSize = teapot_x_ * teapot_y_ * teapot_z_ * (ubo_matrix_stride_ + ubo_matrix_stride_ + ubo_vector_stride_); //Mat4 + Mat4 + Vec3 + 1 stride float* pBuffer = new float[iSize]; float* pColor = pBuffer + teapot_x_ * teapot_y_ * teapot_z_ * ubo_matrix_stride_ * 2; for( int32_t i = 0; i < teapot_x_ * teapot_y_ * teapot_z_; ++i ) { memcpy( pColor, &vec_colors_[i], 3 * sizeof(float) ); pColor += ubo_vector_stride_; //Assuming std140 layout which is 4 DWORD stride for vectors } glBufferData( GL_UNIFORM_BUFFER, iSize * sizeof(float), pBuffer, GL_DYNAMIC_DRAW ); delete[] pBuffer; } else { LOGI( "Shader compilation failed!! Falls back to ES2.0 pass" ); //This happens some devices. geometry_instancing_support_ = false; //Load shader for GLES2.0 LoadShaders( &shader_param_, "Shaders/VS_ShaderPlain.vsh", "Shaders/ShaderPlain.fsh" ); } } else { //Load shader for GLES2.0 LoadShaders( &shader_param_, "Shaders/VS_ShaderPlain.vsh", "Shaders/ShaderPlain.fsh" ); } } void MoreTeapotsRenderer::UpdateViewport() { int32_t viewport[4]; glGetIntegerv( GL_VIEWPORT, viewport ); float fAspect = (float) viewport[2] / (float) viewport[3]; const float CAM_NEAR = 5.f; const float CAM_FAR = 10000.f; bool bRotate = false; mat_projection_ = ndk_helper::Mat4::Perspective( fAspect, 1.f, CAM_NEAR, CAM_FAR ); } //-------------------------------------------------------------------------------- // Unload //-------------------------------------------------------------------------------- void MoreTeapotsRenderer::Unload() { if( vbo_ ) { glDeleteBuffers( 1, &vbo_ ); vbo_ = 0; } if( ubo_ ) { glDeleteBuffers( 1, &ubo_ ); ubo_ = 0; } if( ibo_ ) { glDeleteBuffers( 1, &ibo_ ); ibo_ = 0; } if( shader_param_.program_ ) { glDeleteProgram( shader_param_.program_ ); shader_param_.program_ = 0; } } //-------------------------------------------------------------------------------- // Update //-------------------------------------------------------------------------------- void MoreTeapotsRenderer::Update( float fTime ) { const float CAM_X = 0.f; const float CAM_Y = 0.f; const float CAM_Z = 2000.f; mat_view_ = ndk_helper::Mat4::LookAt( ndk_helper::Vec3( CAM_X, CAM_Y, CAM_Z ), ndk_helper::Vec3( 0.f, 0.f, 0.f ), ndk_helper::Vec3( 0.f, 1.f, 0.f ) ); if( camera_ ) { camera_->Update(); mat_view_ = camera_->GetTransformMatrix() * mat_view_ * camera_->GetRotationMatrix(); } } //-------------------------------------------------------------------------------- // Render //-------------------------------------------------------------------------------- void MoreTeapotsRenderer::Render() { // Bind the VBO glBindBuffer( GL_ARRAY_BUFFER, vbo_ ); int32_t iStride = sizeof(TEAPOT_VERTEX); // Pass the vertex data glVertexAttribPointer( ATTRIB_VERTEX, 3, GL_FLOAT, GL_FALSE, iStride, BUFFER_OFFSET( 0 ) ); glEnableVertexAttribArray( ATTRIB_VERTEX ); glVertexAttribPointer( ATTRIB_NORMAL, 3, GL_FLOAT, GL_FALSE, iStride, BUFFER_OFFSET( 3 * sizeof(GLfloat) ) ); glEnableVertexAttribArray( ATTRIB_NORMAL ); // Bind the IB glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ibo_ ); glUseProgram( shader_param_.program_ ); TEAPOT_MATERIALS material = { { 1.0f, 1.0f, 1.0f, 10.f }, { 0.1f, 0.1f, 0.1f }, }; //Update uniforms // //using glUniform3fv here was troublesome.. // glUniform4f( shader_param_.material_specular_, material.specular_color[0], material.specular_color[1], material.specular_color[2], material.specular_color[3] ); glUniform3f( shader_param_.material_ambient_, material.ambient_color[0], material.ambient_color[1], material.ambient_color[2] ); glUniform3f( shader_param_.light0_, 100.f, -200.f, -600.f ); if( geometry_instancing_support_ ) { // //Geometry instancing, new feature in GLES3.0 // //Update UBO glBindBuffer( GL_UNIFORM_BUFFER, ubo_ ); float* p = (float*) glMapBufferRange( GL_UNIFORM_BUFFER, 0, teapot_x_ * teapot_y_ * teapot_z_ * (ubo_matrix_stride_ * 2) * sizeof(float), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT ); float* pMVPMat = p; float* pMVMat = p + teapot_x_ * teapot_y_ * teapot_z_ * ubo_matrix_stride_; for( int32_t i = 0; i < teapot_x_ * teapot_y_ * teapot_z_; ++i ) { //Rotation float fX, fY; vec_current_rotations_[i] += vec_rotations_[i]; vec_current_rotations_[i].Value( fX, fY ); ndk_helper::Mat4 mat_rotation = ndk_helper::Mat4::RotationX( fX ) * ndk_helper::Mat4::RotationY( fY ); // Feed Projection and Model View matrices to the shaders ndk_helper::Mat4 mat_v = mat_view_ * vec_mat_models_[i] * mat_rotation; ndk_helper::Mat4 mat_vp = mat_projection_ * mat_v; memcpy( pMVPMat, mat_vp.Ptr(), sizeof(mat_v) ); pMVPMat += ubo_matrix_stride_; memcpy( pMVMat, mat_v.Ptr(), sizeof(mat_v) ); pMVMat += ubo_matrix_stride_; } glUnmapBuffer( GL_UNIFORM_BUFFER ); //Instanced rendering glDrawElementsInstanced( GL_TRIANGLES, num_indices_, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0), teapot_x_ * teapot_y_ * teapot_z_ ); } else { //Regular rendering pass for( int32_t i = 0; i < teapot_x_ * teapot_y_ * teapot_z_; ++i ) { //Set diffuse float x, y, z; vec_colors_[i].Value( x, y, z ); glUniform4f( shader_param_.material_diffuse_, x, y, z, 1.f ); //Rotation vec_current_rotations_[i] += vec_rotations_[i]; vec_current_rotations_[i].Value( x, y ); ndk_helper::Mat4 mat_rotation = ndk_helper::Mat4::RotationX( x ) * ndk_helper::Mat4::RotationY( y ); // Feed Projection and Model View matrices to the shaders ndk_helper::Mat4 mat_v = mat_view_ * vec_mat_models_[i] * mat_rotation; ndk_helper::Mat4 mat_vp = mat_projection_ * mat_v; glUniformMatrix4fv( shader_param_.matrix_projection_, 1, GL_FALSE, mat_vp.Ptr() ); glUniformMatrix4fv( shader_param_.matrix_view_, 1, GL_FALSE, mat_v.Ptr() ); glDrawElements( GL_TRIANGLES, num_indices_, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0) ); } } glBindBuffer( GL_ARRAY_BUFFER, 0 ); glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 ); } //-------------------------------------------------------------------------------- // LoadShaders //-------------------------------------------------------------------------------- bool MoreTeapotsRenderer::LoadShaders( SHADER_PARAMS* params, const char* strVsh, const char* strFsh ) { // //Shader load for GLES2 //In GLES2.0, shader attribute locations need to be explicitly specified before linking // GLuint program; GLuint vertShader, fragShader; char *vertShaderPathname, *fragShaderPathname; // Create shader program program = glCreateProgram(); LOGI( "Created Shader %d", program ); // Create and compile vertex shader if( !ndk_helper::shader::CompileShader( &vertShader, GL_VERTEX_SHADER, strVsh ) ) { LOGI( "Failed to compile vertex shader" ); glDeleteProgram( program ); return false; } // Create and compile fragment shader if( !ndk_helper::shader::CompileShader( &fragShader, GL_FRAGMENT_SHADER, strFsh ) ) { LOGI( "Failed to compile fragment shader" ); glDeleteProgram( program ); return false; } // Attach vertex shader to program glAttachShader( program, vertShader ); // Attach fragment shader to program glAttachShader( program, fragShader ); // Bind attribute locations // this needs to be done prior to linking glBindAttribLocation( program, ATTRIB_VERTEX, "myVertex" ); glBindAttribLocation( program, ATTRIB_NORMAL, "myNormal" ); // Link program if( !ndk_helper::shader::LinkProgram( program ) ) { LOGI( "Failed to link program: %d", program ); if( vertShader ) { glDeleteShader( vertShader ); vertShader = 0; } if( fragShader ) { glDeleteShader( fragShader ); fragShader = 0; } if( program ) { glDeleteProgram( program ); } return false; } // Get uniform locations params->matrix_projection_ = glGetUniformLocation( program, "uPMatrix" ); params->matrix_view_ = glGetUniformLocation( program, "uMVMatrix" ); params->light0_ = glGetUniformLocation( program, "vLight0" ); params->material_diffuse_ = glGetUniformLocation( program, "vMaterialDiffuse" ); params->material_ambient_ = glGetUniformLocation( program, "vMaterialAmbient" ); params->material_specular_ = glGetUniformLocation( program, "vMaterialSpecular" ); // Release vertex and fragment shaders if( vertShader ) glDeleteShader( vertShader ); if( fragShader ) glDeleteShader( fragShader ); params->program_ = program; return true; } bool MoreTeapotsRenderer::LoadShadersES3( SHADER_PARAMS* params, const char* strVsh, const char* strFsh, std::map&shaderParams ) { // //Shader load for GLES3 //In GLES3.0, shader attribute index can be described in a shader code directly with layout() attribute // GLuint program; GLuint vertShader, fragShader; char *vertShaderPathname, *fragShaderPathname; // Create shader program program = glCreateProgram(); LOGI( "Created Shader %d", program ); // Create and compile vertex shader if( !ndk_helper::shader::CompileShader( &vertShader, GL_VERTEX_SHADER, strVsh, shaderParams ) ) { LOGI( "Failed to compile vertex shader" ); glDeleteProgram( program ); return false; } // Create and compile fragment shader if( !ndk_helper::shader::CompileShader( &fragShader, GL_FRAGMENT_SHADER, strFsh, shaderParams ) ) { LOGI( "Failed to compile fragment shader" ); glDeleteProgram( program ); return false; } // Attach vertex shader to program glAttachShader( program, vertShader ); // Attach fragment shader to program glAttachShader( program, fragShader ); // Link program if( !ndk_helper::shader::LinkProgram( program ) ) { LOGI( "Failed to link program: %d", program ); if( vertShader ) { glDeleteShader( vertShader ); vertShader = 0; } if( fragShader ) { glDeleteShader( fragShader ); fragShader = 0; } if( program ) { glDeleteProgram( program ); } return false; } // Get uniform locations params->light0_ = glGetUniformLocation( program, "vLight0" ); params->material_ambient_ = glGetUniformLocation( program, "vMaterialAmbient" ); params->material_specular_ = glGetUniformLocation( program, "vMaterialSpecular" ); // Release vertex and fragment shaders if( vertShader ) glDeleteShader( vertShader ); if( fragShader ) glDeleteShader( fragShader ); params->program_ = program; return true; } //-------------------------------------------------------------------------------- // Bind //-------------------------------------------------------------------------------- bool MoreTeapotsRenderer::Bind( ndk_helper::TapCamera* camera ) { camera_ = camera; return true; } //-------------------------------------------------------------------------------- // Helper functions //-------------------------------------------------------------------------------- std::string MoreTeapotsRenderer::ToString( const int32_t i ) { char str[64]; snprintf( str, sizeof(str), "%d", i ); return std::string( str ); }