Home | History | Annotate | Download | only in gles2_spinning_cube
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // This example program is based on Simple_VertexShader.c from:
      6 
      7 //
      8 // Book:      OpenGL(R) ES 2.0 Programming Guide
      9 // Authors:   Aaftab Munshi, Dan Ginsburg, Dave Shreiner
     10 // ISBN-10:   0321502795
     11 // ISBN-13:   9780321502797
     12 // Publisher: Addison-Wesley Professional
     13 // URLs:      http://safari.informit.com/9780321563835
     14 //            http://www.opengles-book.com
     15 //
     16 
     17 #include "ppapi/examples/gles2_spinning_cube/spinning_cube.h"
     18 
     19 #include <math.h>
     20 #include <stdlib.h>
     21 #include <string.h>
     22 
     23 #include <algorithm>
     24 
     25 #include "ppapi/lib/gl/include/GLES2/gl2.h"
     26 
     27 namespace {
     28 
     29 const float kPi = 3.14159265359f;
     30 
     31 int GenerateCube(GLuint *vbo_vertices,
     32                  GLuint *vbo_indices) {
     33   const int num_indices = 36;
     34 
     35   const GLfloat cube_vertices[] = {
     36     -0.5f, -0.5f, -0.5f,
     37     -0.5f, -0.5f,  0.5f,
     38     0.5f, -0.5f,  0.5f,
     39     0.5f, -0.5f, -0.5f,
     40     -0.5f,  0.5f, -0.5f,
     41     -0.5f,  0.5f,  0.5f,
     42     0.5f,  0.5f,  0.5f,
     43     0.5f,  0.5f, -0.5f,
     44     -0.5f, -0.5f, -0.5f,
     45     -0.5f,  0.5f, -0.5f,
     46     0.5f,  0.5f, -0.5f,
     47     0.5f, -0.5f, -0.5f,
     48     -0.5f, -0.5f, 0.5f,
     49     -0.5f,  0.5f, 0.5f,
     50     0.5f,  0.5f, 0.5f,
     51     0.5f, -0.5f, 0.5f,
     52     -0.5f, -0.5f, -0.5f,
     53     -0.5f, -0.5f,  0.5f,
     54     -0.5f,  0.5f,  0.5f,
     55     -0.5f,  0.5f, -0.5f,
     56     0.5f, -0.5f, -0.5f,
     57     0.5f, -0.5f,  0.5f,
     58     0.5f,  0.5f,  0.5f,
     59     0.5f,  0.5f, -0.5f,
     60   };
     61 
     62   const GLushort cube_indices[] = {
     63     0, 2, 1,
     64     0, 3, 2,
     65     4, 5, 6,
     66     4, 6, 7,
     67     8, 9, 10,
     68     8, 10, 11,
     69     12, 15, 14,
     70     12, 14, 13,
     71     16, 17, 18,
     72     16, 18, 19,
     73     20, 23, 22,
     74     20, 22, 21
     75   };
     76 
     77   if (vbo_vertices) {
     78     glGenBuffers(1, vbo_vertices);
     79     glBindBuffer(GL_ARRAY_BUFFER, *vbo_vertices);
     80     glBufferData(GL_ARRAY_BUFFER,
     81                  sizeof(cube_vertices),
     82                  cube_vertices,
     83                  GL_STATIC_DRAW);
     84     glBindBuffer(GL_ARRAY_BUFFER, 0);
     85   }
     86 
     87   if (vbo_indices) {
     88     glGenBuffers(1, vbo_indices);
     89     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, *vbo_indices);
     90     glBufferData(GL_ELEMENT_ARRAY_BUFFER,
     91                  sizeof(cube_indices),
     92                  cube_indices,
     93                  GL_STATIC_DRAW);
     94     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
     95   }
     96 
     97   return num_indices;
     98 }
     99 
    100 GLuint LoadShader(GLenum type,
    101                   const char* shader_source) {
    102   GLuint shader = glCreateShader(type);
    103   glShaderSource(shader, 1, &shader_source, NULL);
    104   glCompileShader(shader);
    105 
    106   GLint compiled = 0;
    107   glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
    108 
    109   if (!compiled) {
    110     glDeleteShader(shader);
    111     return 0;
    112   }
    113 
    114   return shader;
    115 }
    116 
    117 GLuint LoadProgram(const char* vertext_shader_source,
    118                    const char* fragment_shader_source) {
    119   GLuint vertex_shader = LoadShader(GL_VERTEX_SHADER,
    120                                     vertext_shader_source);
    121   if (!vertex_shader)
    122     return 0;
    123 
    124   GLuint fragment_shader = LoadShader(GL_FRAGMENT_SHADER,
    125                                       fragment_shader_source);
    126   if (!fragment_shader) {
    127     glDeleteShader(vertex_shader);
    128     return 0;
    129   }
    130 
    131   GLuint program_object = glCreateProgram();
    132   glAttachShader(program_object, vertex_shader);
    133   glAttachShader(program_object, fragment_shader);
    134 
    135   glLinkProgram(program_object);
    136 
    137   glDeleteShader(vertex_shader);
    138   glDeleteShader(fragment_shader);
    139 
    140   GLint linked = 0;
    141   glGetProgramiv(program_object, GL_LINK_STATUS, &linked);
    142 
    143   if (!linked) {
    144     glDeleteProgram(program_object);
    145     return 0;
    146   }
    147 
    148   return program_object;
    149 }
    150 
    151 class ESMatrix {
    152  public:
    153   GLfloat m[4][4];
    154 
    155   ESMatrix() {
    156     LoadZero();
    157   }
    158 
    159   void LoadZero() {
    160     memset(this, 0x0, sizeof(ESMatrix));
    161   }
    162 
    163   void LoadIdentity() {
    164     LoadZero();
    165     m[0][0] = 1.0f;
    166     m[1][1] = 1.0f;
    167     m[2][2] = 1.0f;
    168     m[3][3] = 1.0f;
    169   }
    170 
    171   void Multiply(ESMatrix* a, ESMatrix* b) {
    172     ESMatrix result;
    173     for (int i = 0; i < 4; ++i) {
    174       result.m[i][0] = (a->m[i][0] * b->m[0][0]) +
    175                        (a->m[i][1] * b->m[1][0]) +
    176                        (a->m[i][2] * b->m[2][0]) +
    177                        (a->m[i][3] * b->m[3][0]);
    178 
    179       result.m[i][1] = (a->m[i][0] * b->m[0][1]) +
    180                        (a->m[i][1] * b->m[1][1]) +
    181                        (a->m[i][2] * b->m[2][1]) +
    182                        (a->m[i][3] * b->m[3][1]);
    183 
    184       result.m[i][2] = (a->m[i][0] * b->m[0][2]) +
    185                        (a->m[i][1] * b->m[1][2]) +
    186                        (a->m[i][2] * b->m[2][2]) +
    187                        (a->m[i][3] * b->m[3][2]);
    188 
    189       result.m[i][3] = (a->m[i][0] * b->m[0][3]) +
    190                        (a->m[i][1] * b->m[1][3]) +
    191                        (a->m[i][2] * b->m[2][3]) +
    192                        (a->m[i][3] * b->m[3][3]);
    193     }
    194     *this = result;
    195   }
    196 
    197   void Frustum(float left,
    198                float right,
    199                float bottom,
    200                float top,
    201                float near_z,
    202                float far_z) {
    203     float delta_x = right - left;
    204     float delta_y = top - bottom;
    205     float delta_z = far_z - near_z;
    206 
    207     if ((near_z <= 0.0f) ||
    208         (far_z <= 0.0f) ||
    209         (delta_z <= 0.0f) ||
    210         (delta_y <= 0.0f) ||
    211         (delta_y <= 0.0f))
    212       return;
    213 
    214     ESMatrix frust;
    215     frust.m[0][0] = 2.0f * near_z / delta_x;
    216     frust.m[0][1] = frust.m[0][2] = frust.m[0][3] = 0.0f;
    217 
    218     frust.m[1][1] = 2.0f * near_z / delta_y;
    219     frust.m[1][0] = frust.m[1][2] = frust.m[1][3] = 0.0f;
    220 
    221     frust.m[2][0] = (right + left) / delta_x;
    222     frust.m[2][1] = (top + bottom) / delta_y;
    223     frust.m[2][2] = -(near_z + far_z) / delta_z;
    224     frust.m[2][3] = -1.0f;
    225 
    226     frust.m[3][2] = -2.0f * near_z * far_z / delta_z;
    227     frust.m[3][0] = frust.m[3][1] = frust.m[3][3] = 0.0f;
    228 
    229     Multiply(&frust, this);
    230   }
    231 
    232   void Perspective(float fov_y, float aspect, float near_z, float far_z) {
    233     GLfloat frustum_h = tanf(fov_y / 360.0f * kPi) * near_z;
    234     GLfloat frustum_w = frustum_h * aspect;
    235     Frustum(-frustum_w, frustum_w, -frustum_h, frustum_h, near_z, far_z);
    236   }
    237 
    238   void Translate(GLfloat tx, GLfloat ty, GLfloat tz) {
    239     m[3][0] += m[0][0] * tx + m[1][0] * ty + m[2][0] * tz;
    240     m[3][1] += m[0][1] * tx + m[1][1] * ty + m[2][1] * tz;
    241     m[3][2] += m[0][2] * tx + m[1][2] * ty + m[2][2] * tz;
    242     m[3][3] += m[0][3] * tx + m[1][3] * ty + m[2][3] * tz;
    243   }
    244 
    245   void Rotate(GLfloat angle, GLfloat x, GLfloat y, GLfloat z) {
    246     GLfloat mag = sqrtf(x * x + y * y + z * z);
    247 
    248     GLfloat sin_angle = sinf(angle * kPi / 180.0f);
    249     GLfloat cos_angle = cosf(angle * kPi / 180.0f);
    250     if (mag > 0.0f) {
    251       GLfloat xx, yy, zz, xy, yz, zx, xs, ys, zs;
    252       GLfloat one_minus_cos;
    253       ESMatrix rotation;
    254 
    255       x /= mag;
    256       y /= mag;
    257       z /= mag;
    258 
    259       xx = x * x;
    260       yy = y * y;
    261       zz = z * z;
    262       xy = x * y;
    263       yz = y * z;
    264       zx = z * x;
    265       xs = x * sin_angle;
    266       ys = y * sin_angle;
    267       zs = z * sin_angle;
    268       one_minus_cos = 1.0f - cos_angle;
    269 
    270       rotation.m[0][0] = (one_minus_cos * xx) + cos_angle;
    271       rotation.m[0][1] = (one_minus_cos * xy) - zs;
    272       rotation.m[0][2] = (one_minus_cos * zx) + ys;
    273       rotation.m[0][3] = 0.0F;
    274 
    275       rotation.m[1][0] = (one_minus_cos * xy) + zs;
    276       rotation.m[1][1] = (one_minus_cos * yy) + cos_angle;
    277       rotation.m[1][2] = (one_minus_cos * yz) - xs;
    278       rotation.m[1][3] = 0.0F;
    279 
    280       rotation.m[2][0] = (one_minus_cos * zx) - ys;
    281       rotation.m[2][1] = (one_minus_cos * yz) + xs;
    282       rotation.m[2][2] = (one_minus_cos * zz) + cos_angle;
    283       rotation.m[2][3] = 0.0F;
    284 
    285       rotation.m[3][0] = 0.0F;
    286       rotation.m[3][1] = 0.0F;
    287       rotation.m[3][2] = 0.0F;
    288       rotation.m[3][3] = 1.0F;
    289 
    290       Multiply(&rotation, this);
    291     }
    292   }
    293 };
    294 
    295 float RotationForTimeDelta(float delta_time) {
    296   return delta_time * 40.0f;
    297 }
    298 
    299 float RotationForDragDistance(float drag_distance) {
    300   return drag_distance / 5; // Arbitrary damping.
    301 }
    302 
    303 }  // namespace
    304 
    305 class SpinningCube::GLState {
    306  public:
    307   GLState();
    308 
    309   void OnGLContextLost();
    310 
    311   GLfloat angle_;  // Survives losing the GL context.
    312 
    313   GLuint program_object_;
    314   GLint position_location_;
    315   GLint mvp_location_;
    316   GLuint vbo_vertices_;
    317   GLuint vbo_indices_;
    318   int num_indices_;
    319   ESMatrix mvp_matrix_;
    320 };
    321 
    322 SpinningCube::GLState::GLState()
    323     : angle_(0) {
    324   OnGLContextLost();
    325 }
    326 
    327 void SpinningCube::GLState::OnGLContextLost() {
    328   program_object_ = 0;
    329   position_location_ = 0;
    330   mvp_location_ = 0;
    331   vbo_vertices_ = 0;
    332   vbo_indices_ = 0;
    333   num_indices_ = 0;
    334 }
    335 
    336 SpinningCube::SpinningCube()
    337     : initialized_(false),
    338       width_(0),
    339       height_(0),
    340       state_(new GLState()),
    341       fling_multiplier_(1.0f),
    342       direction_(1) {
    343   state_->angle_ = 45.0f;
    344 }
    345 
    346 SpinningCube::~SpinningCube() {
    347   if (!initialized_)
    348     return;
    349   if (state_->vbo_vertices_)
    350     glDeleteBuffers(1, &state_->vbo_vertices_);
    351   if (state_->vbo_indices_)
    352     glDeleteBuffers(1, &state_->vbo_indices_);
    353   if (state_->program_object_)
    354     glDeleteProgram(state_->program_object_);
    355 
    356   delete state_;
    357 }
    358 
    359 void SpinningCube::Init(uint32_t width, uint32_t height) {
    360   width_ = width;
    361   height_ = height;
    362 
    363   if (!initialized_) {
    364     initialized_ = true;
    365     const char vertext_shader_source[] =
    366         "uniform mat4 u_mvpMatrix;                   \n"
    367         "attribute vec4 a_position;                  \n"
    368         "void main()                                 \n"
    369         "{                                           \n"
    370         "   gl_Position = u_mvpMatrix * a_position;  \n"
    371         "}                                           \n";
    372 
    373     const char fragment_shader_source[] =
    374         "precision mediump float;                            \n"
    375         "void main()                                         \n"
    376         "{                                                   \n"
    377         "  gl_FragColor = vec4( 0.0, 0.0, 1.0, 1.0 );        \n"
    378         "}                                                   \n";
    379 
    380     state_->program_object_ = LoadProgram(
    381         vertext_shader_source, fragment_shader_source);
    382     state_->position_location_ = glGetAttribLocation(
    383         state_->program_object_, "a_position");
    384     state_->mvp_location_ = glGetUniformLocation(
    385         state_->program_object_, "u_mvpMatrix");
    386     state_->num_indices_ = GenerateCube(
    387         &state_->vbo_vertices_, &state_->vbo_indices_);
    388 
    389     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    390   }
    391 }
    392 
    393 void SpinningCube::OnGLContextLost() {
    394   // TODO(yzshen): Is it correct that in this case we don't need to do cleanup
    395   // for program and buffers?
    396   initialized_ = false;
    397   height_ = 0;
    398   width_ = 0;
    399   state_->OnGLContextLost();
    400 }
    401 
    402 void SpinningCube::SetFlingMultiplier(float drag_distance,
    403                                       float drag_time) {
    404   fling_multiplier_ = RotationForDragDistance(drag_distance) /
    405       RotationForTimeDelta(drag_time);
    406 
    407 }
    408 
    409 void SpinningCube::UpdateForTimeDelta(float delta_time) {
    410   state_->angle_ += RotationForTimeDelta(delta_time) * fling_multiplier_;
    411   if (state_->angle_ >= 360.0f)
    412     state_->angle_ -= 360.0f;
    413 
    414   // Arbitrary 50-step linear reduction in spin speed.
    415   if (fling_multiplier_ > 1.0f) {
    416     fling_multiplier_ =
    417         std::max(1.0f, fling_multiplier_ - (fling_multiplier_ - 1.0f) / 50);
    418   }
    419 
    420   Update();
    421 }
    422 
    423 void SpinningCube::UpdateForDragDistance(float distance) {
    424   state_->angle_ += RotationForDragDistance(distance);
    425   if (state_->angle_ >= 360.0f )
    426     state_->angle_ -= 360.0f;
    427 
    428   Update();
    429 }
    430 
    431 void SpinningCube::Draw() {
    432   glViewport(0, 0, width_, height_);
    433   glClear(GL_COLOR_BUFFER_BIT);
    434   glUseProgram(state_->program_object_);
    435   glBindBuffer(GL_ARRAY_BUFFER, state_->vbo_vertices_);
    436   glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, state_->vbo_indices_);
    437   glVertexAttribPointer(state_->position_location_,
    438                            3,
    439                            GL_FLOAT,
    440                            GL_FALSE, 3 * sizeof(GLfloat),
    441                            0);
    442   glEnableVertexAttribArray(state_->position_location_);
    443   glUniformMatrix4fv(state_->mvp_location_,
    444                         1,
    445                         GL_FALSE,
    446                         (GLfloat*) &state_->mvp_matrix_.m[0][0]);
    447   glDrawElements(GL_TRIANGLES,
    448                     state_->num_indices_,
    449                     GL_UNSIGNED_SHORT,
    450                     0);
    451 }
    452 
    453 void SpinningCube::Update() {
    454   float aspect = static_cast<GLfloat>(width_) / static_cast<GLfloat>(height_);
    455 
    456   ESMatrix perspective;
    457   perspective.LoadIdentity();
    458   perspective.Perspective(60.0f, aspect, 1.0f, 20.0f );
    459 
    460   ESMatrix modelview;
    461   modelview.LoadIdentity();
    462   modelview.Translate(0.0, 0.0, -2.0);
    463   modelview.Rotate(state_->angle_ * direction_, 1.0, 0.0, 1.0);
    464 
    465   state_->mvp_matrix_.Multiply(&modelview, &perspective);
    466 }
    467