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