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