Home | History | Annotate | Download | only in sample_app
      1 // Copyright 2013 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 "mojo/examples/sample_app/spinning_cube.h"
     18 
     19 #include <math.h>
     20 #include <stdlib.h>
     21 #include <string.h>
     22 #include <GLES2/gl2.h>
     23 #include <GLES2/gl2ext.h>
     24 
     25 namespace examples {
     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   GLint color_location_;
    317   GLuint vbo_vertices_;
    318   GLuint vbo_indices_;
    319   int num_indices_;
    320   ESMatrix mvp_matrix_;
    321 };
    322 
    323 SpinningCube::GLState::GLState()
    324     : angle_(0) {
    325   OnGLContextLost();
    326 }
    327 
    328 void SpinningCube::GLState::OnGLContextLost() {
    329   program_object_ = 0;
    330   position_location_ = 0;
    331   mvp_location_ = 0;
    332   color_location_ = 0;
    333   vbo_vertices_ = 0;
    334   vbo_indices_ = 0;
    335   num_indices_ = 0;
    336 }
    337 
    338 SpinningCube::SpinningCube()
    339     : initialized_(false),
    340       width_(0),
    341       height_(0),
    342       state_(new GLState()),
    343       fling_multiplier_(1.0f),
    344       direction_(1),
    345       color_() {
    346   state_->angle_ = 45.0f;
    347   set_color(0.0, 1.0, 0.0);
    348 }
    349 
    350 SpinningCube::~SpinningCube() {
    351   if (!initialized_)
    352     return;
    353   if (state_->vbo_vertices_)
    354     glDeleteBuffers(1, &state_->vbo_vertices_);
    355   if (state_->vbo_indices_)
    356     glDeleteBuffers(1, &state_->vbo_indices_);
    357   if (state_->program_object_)
    358     glDeleteProgram(state_->program_object_);
    359 }
    360 
    361 void SpinningCube::Init(uint32_t width, uint32_t height) {
    362   width_ = width;
    363   height_ = height;
    364 
    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       "uniform vec4 u_color;                               \n"
    376       "void main()                                         \n"
    377       "{                                                   \n"
    378       "  gl_FragColor = u_color;                           \n"
    379       "}                                                   \n";
    380 
    381   state_->program_object_ = LoadProgram(
    382       vertext_shader_source, fragment_shader_source);
    383   state_->position_location_ = glGetAttribLocation(
    384       state_->program_object_, "a_position");
    385   state_->mvp_location_ = glGetUniformLocation(
    386       state_->program_object_, "u_mvpMatrix");
    387   state_->color_location_ = glGetUniformLocation(
    388       state_->program_object_, "u_color");
    389   state_->num_indices_ = GenerateCube(
    390       &state_->vbo_vertices_, &state_->vbo_indices_);
    391 
    392   glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    393   initialized_ = true;
    394 }
    395 
    396 void SpinningCube::OnGLContextLost() {
    397   initialized_ = false;
    398   height_ = 0;
    399   width_ = 0;
    400   state_->OnGLContextLost();
    401 }
    402 
    403 void SpinningCube::SetFlingMultiplier(float drag_distance,
    404                                       float drag_time) {
    405   fling_multiplier_ = RotationForDragDistance(drag_distance) /
    406       RotationForTimeDelta(drag_time);
    407 
    408 }
    409 
    410 void SpinningCube::UpdateForTimeDelta(float delta_time) {
    411   state_->angle_ += RotationForTimeDelta(delta_time) * fling_multiplier_;
    412   if (state_->angle_ >= 360.0f)
    413     state_->angle_ -= 360.0f;
    414 
    415   // Arbitrary 50-step linear reduction in spin speed.
    416   if (fling_multiplier_ > 1.0f) {
    417     fling_multiplier_ =
    418         std::max(1.0f, fling_multiplier_ - (fling_multiplier_ - 1.0f) / 50);
    419   }
    420 
    421   Update();
    422 }
    423 
    424 void SpinningCube::UpdateForDragDistance(float distance) {
    425   state_->angle_ += RotationForDragDistance(distance);
    426   if (state_->angle_ >= 360.0f )
    427     state_->angle_ -= 360.0f;
    428 
    429   Update();
    430 }
    431 
    432 void SpinningCube::Draw() {
    433   glViewport(0, 0, width_, height_);
    434   glClear(GL_COLOR_BUFFER_BIT);
    435   glUseProgram(state_->program_object_);
    436   glBindBuffer(GL_ARRAY_BUFFER, state_->vbo_vertices_);
    437   glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, state_->vbo_indices_);
    438   glVertexAttribPointer(state_->position_location_,
    439                            3,
    440                            GL_FLOAT,
    441                            GL_FALSE, 3 * sizeof(GLfloat),
    442                            0);
    443   glEnableVertexAttribArray(state_->position_location_);
    444   glUniformMatrix4fv(state_->mvp_location_,
    445                         1,
    446                         GL_FALSE,
    447                         (GLfloat*) &state_->mvp_matrix_.m[0][0]);
    448   glUniform4f(state_->color_location_, color_[0], color_[1], color_[2], 1.0);
    449   glDrawElements(GL_TRIANGLES,
    450                     state_->num_indices_,
    451                     GL_UNSIGNED_SHORT,
    452                     0);
    453 }
    454 
    455 void SpinningCube::Update() {
    456   float aspect = static_cast<GLfloat>(width_) / static_cast<GLfloat>(height_);
    457 
    458   ESMatrix perspective;
    459   perspective.LoadIdentity();
    460   perspective.Perspective(60.0f, aspect, 1.0f, 20.0f );
    461 
    462   ESMatrix modelview;
    463   modelview.LoadIdentity();
    464   modelview.Translate(0.0, 0.0, -2.0);
    465   modelview.Rotate(state_->angle_ * direction_, 1.0, 0.0, 1.0);
    466 
    467   state_->mvp_matrix_.Multiply(&modelview, &perspective);
    468 }
    469 
    470 }  // namespace examples
    471