1 /*
2  * Copyright 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //--------------------------------------------------------------------------------
18 // MoreTeapotsRenderer.cpp
19 // Render teapots
20 //--------------------------------------------------------------------------------
21 //--------------------------------------------------------------------------------
22 // Include files
23 //--------------------------------------------------------------------------------
24 #include "MoreTeapotsRenderer.h"
25 
26 //--------------------------------------------------------------------------------
27 // Teapot model data
28 //--------------------------------------------------------------------------------
29 #include "teapot.inl"
30 
31 //--------------------------------------------------------------------------------
32 // Ctor
33 //--------------------------------------------------------------------------------
MoreTeapotsRenderer()34 MoreTeapotsRenderer::MoreTeapotsRenderer() :
35                 geometry_instancing_support_( false )
36 {
37 
38 }
39 
40 //--------------------------------------------------------------------------------
41 // Dtor
42 //--------------------------------------------------------------------------------
~MoreTeapotsRenderer()43 MoreTeapotsRenderer::~MoreTeapotsRenderer()
44 {
45     Unload();
46 }
47 
48 //--------------------------------------------------------------------------------
49 // Init
50 //--------------------------------------------------------------------------------
Init(const int32_t numX,const int32_t numY,const int32_t numZ)51 void MoreTeapotsRenderer::Init( const int32_t numX,
52         const int32_t numY,
53         const int32_t numZ )
54 {
55     if( ndk_helper::GLContext::GetInstance()->GetGLVersion() >= 3.0 )
56     {
57         geometry_instancing_support_ = true;
58     }
59     else if( ndk_helper::GLContext::GetInstance()->CheckExtension( "GL_NV_draw_instanced" )
60             && ndk_helper::GLContext::GetInstance()->CheckExtension(
61                     "GL_NV_uniform_buffer_object" ) )
62     {
63         LOGI( "Supported via extension!" );
64         //_bGeometryInstancingSupport = true;
65         //_bARBSupport = true; //Need to patch shaders
66         //Currently this has been disabled
67     }
68 
69     //Settings
70     glFrontFace( GL_CCW );
71 
72     //Create Index buffer
73     num_indices_ = sizeof(teapotIndices) / sizeof(teapotIndices[0]);
74     glGenBuffers( 1, &ibo_ );
75     glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ibo_ );
76     glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof(teapotIndices), teapotIndices, GL_STATIC_DRAW );
77     glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
78 
79     //Create VBO
80     num_vertices_ = sizeof(teapotPositions) / sizeof(teapotPositions[0]) / 3;
81     int32_t iStride = sizeof(TEAPOT_VERTEX);
82     int32_t iIndex = 0;
83     TEAPOT_VERTEX* p = new TEAPOT_VERTEX[num_vertices_];
84     for( int32_t i = 0; i < num_vertices_; ++i )
85     {
86         p[i].pos[0] = teapotPositions[iIndex];
87         p[i].pos[1] = teapotPositions[iIndex + 1];
88         p[i].pos[2] = teapotPositions[iIndex + 2];
89 
90         p[i].normal[0] = teapotNormals[iIndex];
91         p[i].normal[1] = teapotNormals[iIndex + 1];
92         p[i].normal[2] = teapotNormals[iIndex + 2];
93         iIndex += 3;
94     }
95     glGenBuffers( 1, &vbo_ );
96     glBindBuffer( GL_ARRAY_BUFFER, vbo_ );
97     glBufferData( GL_ARRAY_BUFFER, iStride * num_vertices_, p, GL_STATIC_DRAW );
98     glBindBuffer( GL_ARRAY_BUFFER, 0 );
99     delete[] p;
100 
101     //Init Projection matrices
102     teapot_x_ = numX;
103     teapot_y_ = numY;
104     teapot_z_ = numZ;
105     vec_mat_models_.reserve( teapot_x_ * teapot_y_ * teapot_z_ );
106 
107     UpdateViewport();
108 
109     const float total_width = 500.f;
110     float gap_x = total_width / (teapot_x_ - 1);
111     float gap_y = total_width / (teapot_y_ - 1);
112     float gap_z = total_width / (teapot_z_ - 1);
113     float offset_x = -total_width / 2.f;
114     float offset_y = -total_width / 2.f;
115     float offset_z = -total_width / 2.f;
116 
117     for( int32_t iX = 0; iX < teapot_x_; ++iX )
118         for( int32_t iY = 0; iY < teapot_y_; ++iY )
119             for( int32_t iZ = 0; iZ < teapot_z_; ++iZ )
120             {
121                 vec_mat_models_.push_back(
122                         ndk_helper::Mat4::Translation( iX * gap_x + offset_x, iY * gap_y + offset_y,
123                                 iZ * gap_z + offset_z ) );
124                 vec_colors_.push_back(
125                         ndk_helper::Vec3( random() / float( RAND_MAX * 1.1 ),
126                                 random() / float( RAND_MAX * 1.1 ),
127                                 random() / float( RAND_MAX * 1.1 ) ) );
128 
129                 float fX = random() / float( RAND_MAX ) - 0.5f;
130                 float fY = random() / float( RAND_MAX ) - 0.5f;
131                 vec_rotations_.push_back( ndk_helper::Vec2( fX * 0.05f, fY * 0.05f ) );
132                 vec_current_rotations_.push_back( ndk_helper::Vec2( fX * M_PI, fY * M_PI ) );
133             }
134 
135     if( geometry_instancing_support_ )
136     {
137         //
138         //Create parameter dictionary for shader patch
139         std::map<std::string, std::string> param;
140         param[std::string( "%NUM_TEAPOT%" )] = ToString( teapot_x_ * teapot_y_ * teapot_z_ );
141         param[std::string( "%LOCATION_VERTEX%" )] = ToString( ATTRIB_VERTEX );
142         param[std::string( "%LOCATION_NORMAL%" )] = ToString( ATTRIB_NORMAL );
143         if( arb_support_ )
144             param[std::string( "%ARB%" )] = std::string( "ARB" );
145         else
146             param[std::string( "%ARB%" )] = std::string( "" );
147 
148         //Load shader
149         bool b = LoadShadersES3( &shader_param_, "Shaders/VS_ShaderPlainES3.vsh",
150                 "Shaders/ShaderPlainES3.fsh", param );
151         if( b )
152         {
153             //
154             //Create uniform buffer
155             //
156             GLuint bindingPoint = 1;
157             GLuint blockIndex;
158             blockIndex = glGetUniformBlockIndex( shader_param_.program_, "ParamBlock" );
159             glUniformBlockBinding( shader_param_.program_, blockIndex, bindingPoint );
160 
161             //Retrieve array stride value
162             int32_t iNumIndices;
163             glGetActiveUniformBlockiv( shader_param_.program_, blockIndex,
164                     GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &iNumIndices );
165             GLint i[iNumIndices];
166             GLint stride[iNumIndices];
167             glGetActiveUniformBlockiv( shader_param_.program_, blockIndex,
168                     GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, i );
169             glGetActiveUniformsiv( shader_param_.program_, iNumIndices, (GLuint*) i,
170                     GL_UNIFORM_ARRAY_STRIDE, stride );
171 
172             ubo_matrix_stride_ = stride[0] / sizeof(float);
173             ubo_vector_stride_ = stride[2] / sizeof(float);
174 
175             glGenBuffers( 1, &ubo_ );
176             glBindBuffer( GL_UNIFORM_BUFFER, ubo_ );
177             glBindBufferBase( GL_UNIFORM_BUFFER, bindingPoint, ubo_ );
178 
179             //Store color value which wouldn't be updated every frame
180             int32_t iSize = teapot_x_ * teapot_y_ * teapot_z_
181                     * (ubo_matrix_stride_ + ubo_matrix_stride_ + ubo_vector_stride_); //Mat4 + Mat4 + Vec3 + 1 stride
182             float* pBuffer = new float[iSize];
183             float* pColor = pBuffer + teapot_x_ * teapot_y_ * teapot_z_ * ubo_matrix_stride_ * 2;
184             for( int32_t i = 0; i < teapot_x_ * teapot_y_ * teapot_z_; ++i )
185             {
186                 memcpy( pColor, &vec_colors_[i], 3 * sizeof(float) );
187                 pColor += ubo_vector_stride_; //Assuming std140 layout which is 4 DWORD stride for vectors
188             }
189 
190             glBufferData( GL_UNIFORM_BUFFER, iSize * sizeof(float), pBuffer, GL_DYNAMIC_DRAW );
191             delete[] pBuffer;
192         }
193         else
194         {
195             LOGI( "Shader compilation failed!! Falls back to ES2.0 pass" );
196             //This happens some devices.
197             geometry_instancing_support_ = false;
198             //Load shader for GLES2.0
199             LoadShaders( &shader_param_, "Shaders/VS_ShaderPlain.vsh", "Shaders/ShaderPlain.fsh" );
200         }
201     }
202     else
203     {
204         //Load shader for GLES2.0
205         LoadShaders( &shader_param_, "Shaders/VS_ShaderPlain.vsh", "Shaders/ShaderPlain.fsh" );
206     }
207 }
208 
UpdateViewport()209 void MoreTeapotsRenderer::UpdateViewport()
210 {
211     int32_t viewport[4];
212     glGetIntegerv( GL_VIEWPORT, viewport );
213     float fAspect = (float) viewport[2] / (float) viewport[3];
214 
215     const float CAM_NEAR = 5.f;
216     const float CAM_FAR = 10000.f;
217     bool bRotate = false;
218     mat_projection_ = ndk_helper::Mat4::Perspective( fAspect, 1.f, CAM_NEAR, CAM_FAR );
219 }
220 
221 //--------------------------------------------------------------------------------
222 // Unload
223 //--------------------------------------------------------------------------------
Unload()224 void MoreTeapotsRenderer::Unload()
225 {
226     if( vbo_ )
227     {
228         glDeleteBuffers( 1, &vbo_ );
229         vbo_ = 0;
230     }
231     if( ubo_ )
232     {
233         glDeleteBuffers( 1, &ubo_ );
234         ubo_ = 0;
235     }
236     if( ibo_ )
237     {
238         glDeleteBuffers( 1, &ibo_ );
239         ibo_ = 0;
240     }
241     if( shader_param_.program_ )
242     {
243         glDeleteProgram( shader_param_.program_ );
244         shader_param_.program_ = 0;
245     }
246 }
247 
248 //--------------------------------------------------------------------------------
249 // Update
250 //--------------------------------------------------------------------------------
Update(float fTime)251 void MoreTeapotsRenderer::Update( float fTime )
252 {
253     const float CAM_X = 0.f;
254     const float CAM_Y = 0.f;
255     const float CAM_Z = 2000.f;
256 
257     mat_view_ = ndk_helper::Mat4::LookAt( ndk_helper::Vec3( CAM_X, CAM_Y, CAM_Z ),
258             ndk_helper::Vec3( 0.f, 0.f, 0.f ), ndk_helper::Vec3( 0.f, 1.f, 0.f ) );
259 
260     if( camera_ )
261     {
262         camera_->Update();
263         mat_view_ = camera_->GetTransformMatrix() * mat_view_ * camera_->GetRotationMatrix();
264     }
265 }
266 
267 //--------------------------------------------------------------------------------
268 // Render
269 //--------------------------------------------------------------------------------
Render()270 void MoreTeapotsRenderer::Render()
271 {
272     // Bind the VBO
273     glBindBuffer( GL_ARRAY_BUFFER, vbo_ );
274 
275     int32_t iStride = sizeof(TEAPOT_VERTEX);
276     // Pass the vertex data
277     glVertexAttribPointer( ATTRIB_VERTEX, 3, GL_FLOAT, GL_FALSE, iStride, BUFFER_OFFSET( 0 ) );
278     glEnableVertexAttribArray( ATTRIB_VERTEX );
279 
280     glVertexAttribPointer( ATTRIB_NORMAL, 3, GL_FLOAT, GL_FALSE, iStride,
281             BUFFER_OFFSET( 3 * sizeof(GLfloat) ) );
282     glEnableVertexAttribArray( ATTRIB_NORMAL );
283 
284     // Bind the IB
285     glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ibo_ );
286 
287     glUseProgram( shader_param_.program_ );
288 
289     TEAPOT_MATERIALS material = { { 1.0f, 1.0f, 1.0f, 10.f }, { 0.1f, 0.1f, 0.1f }, };
290 
291     //Update uniforms
292     //
293     //using glUniform3fv here was troublesome..
294     //
295     glUniform4f( shader_param_.material_specular_, material.specular_color[0],
296             material.specular_color[1], material.specular_color[2], material.specular_color[3] );
297     glUniform3f( shader_param_.material_ambient_, material.ambient_color[0],
298             material.ambient_color[1], material.ambient_color[2] );
299 
300     glUniform3f( shader_param_.light0_, 100.f, -200.f, -600.f );
301 
302     if( geometry_instancing_support_ )
303     {
304         //
305         //Geometry instancing, new feature in GLES3.0
306         //
307 
308         //Update UBO
309         glBindBuffer( GL_UNIFORM_BUFFER, ubo_ );
310         float* p = (float*) glMapBufferRange( GL_UNIFORM_BUFFER, 0,
311                 teapot_x_ * teapot_y_ * teapot_z_ * (ubo_matrix_stride_ * 2) * sizeof(float),
312                 GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT );
313         float* pMVPMat = p;
314         float* pMVMat = p + teapot_x_ * teapot_y_ * teapot_z_ * ubo_matrix_stride_;
315         for( int32_t i = 0; i < teapot_x_ * teapot_y_ * teapot_z_; ++i )
316         {
317             //Rotation
318             float fX, fY;
319             vec_current_rotations_[i] += vec_rotations_[i];
320             vec_current_rotations_[i].Value( fX, fY );
321             ndk_helper::Mat4 mat_rotation = ndk_helper::Mat4::RotationX( fX )
322                     * ndk_helper::Mat4::RotationY( fY );
323 
324             // Feed Projection and Model View matrices to the shaders
325             ndk_helper::Mat4 mat_v = mat_view_ * vec_mat_models_[i] * mat_rotation;
326             ndk_helper::Mat4 mat_vp = mat_projection_ * mat_v;
327 
328             memcpy( pMVPMat, mat_vp.Ptr(), sizeof(mat_v) );
329             pMVPMat += ubo_matrix_stride_;
330 
331             memcpy( pMVMat, mat_v.Ptr(), sizeof(mat_v) );
332             pMVMat += ubo_matrix_stride_;
333         }
334         glUnmapBuffer( GL_UNIFORM_BUFFER );
335 
336         //Instanced rendering
337         glDrawElementsInstanced( GL_TRIANGLES, num_indices_, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0),
338                 teapot_x_ * teapot_y_ * teapot_z_ );
339 
340     }
341     else
342     {
343         //Regular rendering pass
344         for( int32_t i = 0; i < teapot_x_ * teapot_y_ * teapot_z_; ++i )
345         {
346             //Set diffuse
347             float x, y, z;
348             vec_colors_[i].Value( x, y, z );
349             glUniform4f( shader_param_.material_diffuse_, x, y, z, 1.f );
350 
351             //Rotation
352             vec_current_rotations_[i] += vec_rotations_[i];
353             vec_current_rotations_[i].Value( x, y );
354             ndk_helper::Mat4 mat_rotation = ndk_helper::Mat4::RotationX( x )
355                     * ndk_helper::Mat4::RotationY( y );
356 
357             // Feed Projection and Model View matrices to the shaders
358             ndk_helper::Mat4 mat_v = mat_view_ * vec_mat_models_[i] * mat_rotation;
359             ndk_helper::Mat4 mat_vp = mat_projection_ * mat_v;
360             glUniformMatrix4fv( shader_param_.matrix_projection_, 1, GL_FALSE, mat_vp.Ptr() );
361             glUniformMatrix4fv( shader_param_.matrix_view_, 1, GL_FALSE, mat_v.Ptr() );
362 
363             glDrawElements( GL_TRIANGLES, num_indices_, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0) );
364 
365         }
366     }
367 
368     glBindBuffer( GL_ARRAY_BUFFER, 0 );
369     glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
370 }
371 
372 //--------------------------------------------------------------------------------
373 // LoadShaders
374 //--------------------------------------------------------------------------------
LoadShaders(SHADER_PARAMS * params,const char * strVsh,const char * strFsh)375 bool MoreTeapotsRenderer::LoadShaders( SHADER_PARAMS* params,
376         const char* strVsh,
377         const char* strFsh )
378 {
379     //
380     //Shader load for GLES2
381     //In GLES2.0, shader attribute locations need to be explicitly specified before linking
382     //
383     GLuint program;
384     GLuint vertShader, fragShader;
385     char *vertShaderPathname, *fragShaderPathname;
386 
387     // Create shader program
388     program = glCreateProgram();
389     LOGI( "Created Shader %d", program );
390 
391     // Create and compile vertex shader
392     if( !ndk_helper::shader::CompileShader( &vertShader, GL_VERTEX_SHADER, strVsh ) )
393     {
394         LOGI( "Failed to compile vertex shader" );
395         glDeleteProgram( program );
396         return false;
397     }
398 
399     // Create and compile fragment shader
400     if( !ndk_helper::shader::CompileShader( &fragShader, GL_FRAGMENT_SHADER, strFsh ) )
401     {
402         LOGI( "Failed to compile fragment shader" );
403         glDeleteProgram( program );
404         return false;
405     }
406 
407     // Attach vertex shader to program
408     glAttachShader( program, vertShader );
409 
410     // Attach fragment shader to program
411     glAttachShader( program, fragShader );
412 
413     // Bind attribute locations
414     // this needs to be done prior to linking
415     glBindAttribLocation( program, ATTRIB_VERTEX, "myVertex" );
416     glBindAttribLocation( program, ATTRIB_NORMAL, "myNormal" );
417 
418     // Link program
419     if( !ndk_helper::shader::LinkProgram( program ) )
420     {
421         LOGI( "Failed to link program: %d", program );
422 
423         if( vertShader )
424         {
425             glDeleteShader( vertShader );
426             vertShader = 0;
427         }
428         if( fragShader )
429         {
430             glDeleteShader( fragShader );
431             fragShader = 0;
432         }
433         if( program )
434         {
435             glDeleteProgram( program );
436         }
437         return false;
438     }
439 
440     // Get uniform locations
441     params->matrix_projection_ = glGetUniformLocation( program, "uPMatrix" );
442     params->matrix_view_ = glGetUniformLocation( program, "uMVMatrix" );
443 
444     params->light0_ = glGetUniformLocation( program, "vLight0" );
445     params->material_diffuse_ = glGetUniformLocation( program, "vMaterialDiffuse" );
446     params->material_ambient_ = glGetUniformLocation( program, "vMaterialAmbient" );
447     params->material_specular_ = glGetUniformLocation( program, "vMaterialSpecular" );
448 
449     // Release vertex and fragment shaders
450     if( vertShader )
451         glDeleteShader( vertShader );
452     if( fragShader )
453         glDeleteShader( fragShader );
454 
455     params->program_ = program;
456     return true;
457 }
458 
LoadShadersES3(SHADER_PARAMS * params,const char * strVsh,const char * strFsh,std::map<std::string,std::string> & shaderParams)459 bool MoreTeapotsRenderer::LoadShadersES3( SHADER_PARAMS* params,
460         const char* strVsh,
461         const char* strFsh,
462         std::map<std::string, std::string>&shaderParams )
463 {
464     //
465     //Shader load for GLES3
466     //In GLES3.0, shader attribute index can be described in a shader code directly with layout() attribute
467     //
468     GLuint program;
469     GLuint vertShader, fragShader;
470     char *vertShaderPathname, *fragShaderPathname;
471 
472     // Create shader program
473     program = glCreateProgram();
474     LOGI( "Created Shader %d", program );
475 
476     // Create and compile vertex shader
477     if( !ndk_helper::shader::CompileShader( &vertShader, GL_VERTEX_SHADER, strVsh, shaderParams ) )
478     {
479         LOGI( "Failed to compile vertex shader" );
480         glDeleteProgram( program );
481         return false;
482     }
483 
484     // Create and compile fragment shader
485     if( !ndk_helper::shader::CompileShader( &fragShader, GL_FRAGMENT_SHADER, strFsh,
486             shaderParams ) )
487     {
488         LOGI( "Failed to compile fragment shader" );
489         glDeleteProgram( program );
490         return false;
491     }
492 
493     // Attach vertex shader to program
494     glAttachShader( program, vertShader );
495 
496     // Attach fragment shader to program
497     glAttachShader( program, fragShader );
498 
499     // Link program
500     if( !ndk_helper::shader::LinkProgram( program ) )
501     {
502         LOGI( "Failed to link program: %d", program );
503 
504         if( vertShader )
505         {
506             glDeleteShader( vertShader );
507             vertShader = 0;
508         }
509         if( fragShader )
510         {
511             glDeleteShader( fragShader );
512             fragShader = 0;
513         }
514         if( program )
515         {
516             glDeleteProgram( program );
517         }
518 
519         return false;
520     }
521 
522     // Get uniform locations
523     params->light0_ = glGetUniformLocation( program, "vLight0" );
524     params->material_ambient_ = glGetUniformLocation( program, "vMaterialAmbient" );
525     params->material_specular_ = glGetUniformLocation( program, "vMaterialSpecular" );
526 
527     // Release vertex and fragment shaders
528     if( vertShader )
529         glDeleteShader( vertShader );
530     if( fragShader )
531         glDeleteShader( fragShader );
532 
533     params->program_ = program;
534     return true;
535 }
536 
537 //--------------------------------------------------------------------------------
538 // Bind
539 //--------------------------------------------------------------------------------
Bind(ndk_helper::TapCamera * camera)540 bool MoreTeapotsRenderer::Bind( ndk_helper::TapCamera* camera )
541 {
542     camera_ = camera;
543     return true;
544 }
545 
546 //--------------------------------------------------------------------------------
547 // Helper functions
548 //--------------------------------------------------------------------------------
ToString(const int32_t i)549 std::string MoreTeapotsRenderer::ToString( const int32_t i )
550 {
551     char str[64];
552     snprintf( str, sizeof(str), "%d", i );
553     return std::string( str );
554 }
555 
556