1// Copyright (c) 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#include <GLES2/gl2.h>
6#include <math.h>
7#include <stddef.h>
8#include <stdint.h>
9#include <stdio.h>
10#include <string.h>
11
12#include "common/fps.h"
13#include "matrix.h"
14#include "ppapi/cpp/graphics_3d.h"
15#include "ppapi/cpp/instance.h"
16#include "ppapi/cpp/module.h"
17#include "ppapi/cpp/var.h"
18#include "ppapi/cpp/var_array.h"
19#include "ppapi/lib/gl/gles2/gl2ext_ppapi.h"
20#include "ppapi/utility/completion_callback_factory.h"
21
22#ifdef WIN32
23#undef PostMessage
24// Allow 'this' in initializer list
25#pragma warning(disable : 4355)
26#endif
27
28extern const uint8_t kRLETextureData[];
29extern const size_t kRLETextureDataLength;
30
31namespace {
32
33const float kMinFovY = 45.0f;
34const float kZNear = 1.0f;
35const float kZFar = 10.0f;
36const float kCameraZ = -4.0f;
37const float kXAngleDelta = 2.0f;
38const float kYAngleDelta = 0.5f;
39
40const size_t kTextureDataLength = 128 * 128 * 3;  // 128x128, 3 Bytes/pixel.
41
42// The decompressed data is written here.
43uint8_t g_texture_data[kTextureDataLength];
44
45void DecompressTexture() {
46  // The image is first encoded with a very simple RLE scheme:
47  //   <value0> <count0> <value1> <count1> ...
48  // Because a <count> of 0 is useless, we use it to represent 256.
49  //
50  // It is then Base64 encoded to make it use only printable characters (it
51  // stores more easily in a source file that way).
52  //
53  // To decompress, we have to reverse the process.
54  static const uint8_t kBase64Decode[256] = {
55    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
56    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
57    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 62,  0,  0,  0, 63,
58   52, 53, 54, 55, 56, 57, 58, 59, 60, 61,  0,  0,  0,  0,  0,  0,
59    0,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
60   15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,  0,  0,  0,  0,  0,
61    0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
62   41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
63  };
64  const uint8_t* input = &kRLETextureData[0];
65  const uint8_t* const input_end = &kRLETextureData[kRLETextureDataLength];
66  uint8_t* output = &g_texture_data[0];
67#ifndef NDEBUG
68  const uint8_t* const output_end = &g_texture_data[kTextureDataLength];
69#endif
70
71  uint8_t decoded[4];
72  int decoded_count = 0;
73
74  while (input < input_end || decoded_count > 0) {
75    if (decoded_count < 2) {
76      assert(input + 4 <= input_end);
77      // Grab four base-64 encoded (6-bit) bytes.
78      uint32_t data = 0;
79      data |= (kBase64Decode[*input++] << 18);
80      data |= (kBase64Decode[*input++] << 12);
81      data |= (kBase64Decode[*input++] <<  6);
82      data |= (kBase64Decode[*input++]      );
83      // And decode it to 3 (8-bit) bytes.
84      decoded[decoded_count++] = (data >> 16) & 0xff;
85      decoded[decoded_count++] = (data >>  8) & 0xff;
86      decoded[decoded_count++] = (data      ) & 0xff;
87
88      // = is the base64 end marker. Remove decoded bytes if we see any.
89      if (input[-1] == '=') decoded_count--;
90      if (input[-2] == '=') decoded_count--;
91    }
92
93    int value = decoded[0];
94    int count = decoded[1];
95    decoded_count -= 2;
96    // Move the other decoded bytes (if any) down.
97    decoded[0] = decoded[2];
98    decoded[1] = decoded[3];
99
100    // Expand the RLE data.
101    if (count == 0)
102      count = 256;
103    assert(output <= output_end);
104    memset(output, value, count);
105    output += count;
106  }
107  assert(output == output_end);
108}
109
110GLuint CompileShader(GLenum type, const char* data) {
111  GLuint shader = glCreateShader(type);
112  glShaderSource(shader, 1, &data, NULL);
113  glCompileShader(shader);
114
115  GLint compile_status;
116  glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
117  if (compile_status != GL_TRUE) {
118    // Shader failed to compile, let's see what the error is.
119    char buffer[1024];
120    GLsizei length;
121    glGetShaderInfoLog(shader, sizeof(buffer), &length, &buffer[0]);
122    fprintf(stderr, "Shader failed to compile: %s\n", buffer);
123    return 0;
124  }
125
126  return shader;
127}
128
129GLuint LinkProgram(GLuint frag_shader, GLuint vert_shader) {
130  GLuint program = glCreateProgram();
131  glAttachShader(program, frag_shader);
132  glAttachShader(program, vert_shader);
133  glLinkProgram(program);
134
135  GLint link_status;
136  glGetProgramiv(program, GL_LINK_STATUS, &link_status);
137  if (link_status != GL_TRUE) {
138    // Program failed to link, let's see what the error is.
139    char buffer[1024];
140    GLsizei length;
141    glGetProgramInfoLog(program, sizeof(buffer), &length, &buffer[0]);
142    fprintf(stderr, "Program failed to link: %s\n", buffer);
143    return 0;
144  }
145
146  return program;
147}
148
149const char kFragShaderSource[] =
150    "precision mediump float;\n"
151    "varying vec3 v_color;\n"
152    "varying vec2 v_texcoord;\n"
153    "uniform sampler2D u_texture;\n"
154    "void main() {\n"
155    "  gl_FragColor = texture2D(u_texture, v_texcoord);\n"
156    "  gl_FragColor += vec4(v_color, 1);\n"
157    "}\n";
158
159const char kVertexShaderSource[] =
160    "uniform mat4 u_mvp;\n"
161    "attribute vec2 a_texcoord;\n"
162    "attribute vec3 a_color;\n"
163    "attribute vec4 a_position;\n"
164    "varying vec3 v_color;\n"
165    "varying vec2 v_texcoord;\n"
166    "void main() {\n"
167    "  gl_Position = u_mvp * a_position;\n"
168    "  v_color = a_color;\n"
169    "  v_texcoord = a_texcoord;\n"
170    "}\n";
171
172struct Vertex {
173  float loc[3];
174  float color[3];
175  float tex[2];
176};
177
178const Vertex kCubeVerts[24] = {
179  // +Z (red arrow, black tip)
180  {{-1.0, -1.0, +1.0}, {0.0, 0.0, 0.0}, {1.0, 0.0}},
181  {{+1.0, -1.0, +1.0}, {0.0, 0.0, 0.0}, {0.0, 0.0}},
182  {{+1.0, +1.0, +1.0}, {0.5, 0.0, 0.0}, {0.0, 1.0}},
183  {{-1.0, +1.0, +1.0}, {0.5, 0.0, 0.0}, {1.0, 1.0}},
184
185  // +X (green arrow, black tip)
186  {{+1.0, -1.0, -1.0}, {0.0, 0.0, 0.0}, {1.0, 0.0}},
187  {{+1.0, +1.0, -1.0}, {0.0, 0.0, 0.0}, {0.0, 0.0}},
188  {{+1.0, +1.0, +1.0}, {0.0, 0.5, 0.0}, {0.0, 1.0}},
189  {{+1.0, -1.0, +1.0}, {0.0, 0.5, 0.0}, {1.0, 1.0}},
190
191  // +Y (blue arrow, black tip)
192  {{-1.0, +1.0, -1.0}, {0.0, 0.0, 0.0}, {1.0, 0.0}},
193  {{-1.0, +1.0, +1.0}, {0.0, 0.0, 0.0}, {0.0, 0.0}},
194  {{+1.0, +1.0, +1.0}, {0.0, 0.0, 0.5}, {0.0, 1.0}},
195  {{+1.0, +1.0, -1.0}, {0.0, 0.0, 0.5}, {1.0, 1.0}},
196
197  // -Z (red arrow, red tip)
198  {{+1.0, +1.0, -1.0}, {0.0, 0.0, 0.0}, {1.0, 1.0}},
199  {{-1.0, +1.0, -1.0}, {0.0, 0.0, 0.0}, {0.0, 1.0}},
200  {{-1.0, -1.0, -1.0}, {1.0, 0.0, 0.0}, {0.0, 0.0}},
201  {{+1.0, -1.0, -1.0}, {1.0, 0.0, 0.0}, {1.0, 0.0}},
202
203  // -X (green arrow, green tip)
204  {{-1.0, +1.0, +1.0}, {0.0, 0.0, 0.0}, {1.0, 1.0}},
205  {{-1.0, -1.0, +1.0}, {0.0, 0.0, 0.0}, {0.0, 1.0}},
206  {{-1.0, -1.0, -1.0}, {0.0, 1.0, 0.0}, {0.0, 0.0}},
207  {{-1.0, +1.0, -1.0}, {0.0, 1.0, 0.0}, {1.0, 0.0}},
208
209  // -Y (blue arrow, blue tip)
210  {{+1.0, -1.0, +1.0}, {0.0, 0.0, 0.0}, {1.0, 1.0}},
211  {{+1.0, -1.0, -1.0}, {0.0, 0.0, 0.0}, {0.0, 1.0}},
212  {{-1.0, -1.0, -1.0}, {0.0, 0.0, 1.0}, {0.0, 0.0}},
213  {{-1.0, -1.0, +1.0}, {0.0, 0.0, 1.0}, {1.0, 0.0}},
214};
215
216const GLubyte kCubeIndexes[36] = {
217   2,  1,  0,  3,  2,  0,
218   6,  5,  4,  7,  6,  4,
219  10,  9,  8, 11, 10,  8,
220  14, 13, 12, 15, 14, 12,
221  18, 17, 16, 19, 18, 16,
222  22, 21, 20, 23, 22, 20,
223};
224
225}  // namespace
226
227
228class CubeInstance : public pp::Instance {
229 public:
230  explicit CubeInstance(PP_Instance instance)
231      : pp::Instance(instance),
232        callback_factory_(this),
233        width_(0),
234        height_(0),
235        frag_shader_(0),
236        vertex_shader_(0),
237        program_(0),
238        texture_loc_(0),
239        position_loc_(0),
240        color_loc_(0),
241        mvp_loc_(0),
242        x_angle_(0),
243        y_angle_(0),
244        animating_(true) {
245    FpsInit(&fps_state_);
246  }
247
248  virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
249    return true;
250  }
251
252  virtual void DidChangeView(const pp::View& view) {
253    int32_t new_width = view.GetRect().width();
254    int32_t new_height = view.GetRect().height();
255
256    if (context_.is_null()) {
257      if (!InitGL(new_width, new_height)) {
258        // failed.
259        return;
260      }
261
262      InitShaders();
263      InitBuffers();
264      InitTexture();
265      MainLoop(0);
266    } else {
267      // Resize the buffers to the new size of the module.
268      int32_t result = context_.ResizeBuffers(new_width, new_height);
269      if (result < 0) {
270        fprintf(stderr,
271                "Unable to resize buffers to %d x %d!\n",
272                new_width,
273                new_height);
274        return;
275      }
276    }
277
278    width_ = new_width;
279    height_ = new_height;
280    glViewport(0, 0, width_, height_);
281  }
282
283  virtual void HandleMessage(const pp::Var& message) {
284    // A bool message sets whether the cube is animating or not.
285    if (message.is_bool()) {
286      animating_ = message.AsBool();
287      return;
288    }
289
290    // An array message sets the current x and y rotation.
291    if (!message.is_array()) {
292      fprintf(stderr, "Expected array message.\n");
293      return;
294    }
295
296    pp::VarArray array(message);
297    if (array.GetLength() != 2) {
298      fprintf(stderr, "Expected array of length 2.\n");
299      return;
300    }
301
302    pp::Var x_angle_var = array.Get(0);
303    if (x_angle_var.is_int()) {
304      x_angle_ = x_angle_var.AsInt();
305    } else if (x_angle_var.is_double()) {
306      x_angle_ = x_angle_var.AsDouble();
307    } else {
308      fprintf(stderr, "Expected value to be an int or double.\n");
309    }
310
311    pp::Var y_angle_var = array.Get(1);
312    if (y_angle_var.is_int()) {
313      y_angle_ = y_angle_var.AsInt();
314    } else if (y_angle_var.is_double()) {
315      y_angle_ = y_angle_var.AsDouble();
316    } else {
317      fprintf(stderr, "Expected value to be an int or double.\n");
318    }
319  }
320
321 private:
322  bool InitGL(int32_t new_width, int32_t new_height) {
323    if (!glInitializePPAPI(pp::Module::Get()->get_browser_interface())) {
324      fprintf(stderr, "Unable to initialize GL PPAPI!\n");
325      return false;
326    }
327
328    const int32_t attrib_list[] = {
329      PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8,
330      PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 24,
331      PP_GRAPHICS3DATTRIB_WIDTH, new_width,
332      PP_GRAPHICS3DATTRIB_HEIGHT, new_height,
333      PP_GRAPHICS3DATTRIB_NONE
334    };
335
336    context_ = pp::Graphics3D(this, attrib_list);
337    if (!BindGraphics(context_)) {
338      fprintf(stderr, "Unable to bind 3d context!\n");
339      context_ = pp::Graphics3D();
340      glSetCurrentContextPPAPI(0);
341      return false;
342    }
343
344    glSetCurrentContextPPAPI(context_.pp_resource());
345    return true;
346  }
347
348  void InitShaders() {
349    frag_shader_ = CompileShader(GL_FRAGMENT_SHADER, kFragShaderSource);
350    if (!frag_shader_)
351      return;
352
353    vertex_shader_ = CompileShader(GL_VERTEX_SHADER, kVertexShaderSource);
354    if (!vertex_shader_)
355      return;
356
357    program_ = LinkProgram(frag_shader_, vertex_shader_);
358    if (!program_)
359      return;
360
361    texture_loc_ = glGetUniformLocation(program_, "u_texture");
362    position_loc_ = glGetAttribLocation(program_, "a_position");
363    texcoord_loc_ = glGetAttribLocation(program_, "a_texcoord");
364    color_loc_ = glGetAttribLocation(program_, "a_color");
365    mvp_loc_ = glGetUniformLocation(program_, "u_mvp");
366  }
367
368  void InitBuffers() {
369    glGenBuffers(1, &vertex_buffer_);
370    glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
371    glBufferData(GL_ARRAY_BUFFER, sizeof(kCubeVerts), &kCubeVerts[0],
372                 GL_STATIC_DRAW);
373
374    glGenBuffers(1, &index_buffer_);
375    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_);
376    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(kCubeIndexes),
377                 &kCubeIndexes[0], GL_STATIC_DRAW);
378  }
379
380  void InitTexture() {
381    DecompressTexture();
382    glGenTextures(1, &texture_);
383    glBindTexture(GL_TEXTURE_2D, texture_);
384    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
385    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
386    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
387    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
388    glTexImage2D(GL_TEXTURE_2D,
389                 0,
390                 GL_RGB,
391                 128,
392                 128,
393                 0,
394                 GL_RGB,
395                 GL_UNSIGNED_BYTE,
396                 &g_texture_data[0]);
397  }
398
399  void Animate() {
400    if (animating_) {
401      x_angle_ = fmod(360.0f + x_angle_ + kXAngleDelta, 360.0f);
402      y_angle_ = fmod(360.0f + y_angle_ + kYAngleDelta, 360.0f);
403
404      // Send new values to JavaScript.
405      pp::VarArray array;
406      array.SetLength(2);
407      array.Set(0, x_angle_);
408      array.Set(1, y_angle_);
409      PostMessage(array);
410    }
411  }
412
413  void Render() {
414    glClearColor(0.5, 0.5, 0.5, 1);
415    glClearDepthf(1.0f);
416    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
417    glEnable(GL_DEPTH_TEST);
418
419    //set what program to use
420    glUseProgram(program_);
421    glActiveTexture(GL_TEXTURE0);
422    glBindTexture(GL_TEXTURE_2D, texture_);
423    glUniform1i(texture_loc_, 0);
424
425    //create our perspective matrix
426    float mvp[16];
427    float trs[16];
428    float rot[16];
429
430    identity_matrix(mvp);
431    const float aspect_ratio = static_cast<float>(width_) / height_;
432    float fovY = kMinFovY / aspect_ratio;
433    if (fovY < kMinFovY)
434      fovY = kMinFovY;
435
436    glhPerspectivef2(&mvp[0], fovY, aspect_ratio, kZNear, kZFar);
437
438    translate_matrix(0, 0, kCameraZ, trs);
439    rotate_matrix(x_angle_, y_angle_, 0.0f, rot);
440    multiply_matrix(trs, rot, trs);
441    multiply_matrix(mvp, trs, mvp);
442    glUniformMatrix4fv(mvp_loc_, 1, GL_FALSE, mvp);
443
444    //define the attributes of the vertex
445    glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
446    glVertexAttribPointer(position_loc_,
447                          3,
448                          GL_FLOAT,
449                          GL_FALSE,
450                          sizeof(Vertex),
451                          reinterpret_cast<void*>(offsetof(Vertex, loc)));
452    glEnableVertexAttribArray(position_loc_);
453    glVertexAttribPointer(color_loc_,
454                          3,
455                          GL_FLOAT,
456                          GL_FALSE,
457                          sizeof(Vertex),
458                          reinterpret_cast<void*>(offsetof(Vertex, color)));
459    glEnableVertexAttribArray(color_loc_);
460    glVertexAttribPointer(texcoord_loc_,
461                          2,
462                          GL_FLOAT,
463                          GL_FALSE,
464                          sizeof(Vertex),
465                          reinterpret_cast<void*>(offsetof(Vertex, tex)));
466    glEnableVertexAttribArray(texcoord_loc_);
467
468    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_);
469    glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, 0);
470  }
471
472  void MainLoop(int32_t) {
473    Animate();
474    Render();
475    context_.SwapBuffers(
476        callback_factory_.NewCallback(&CubeInstance::MainLoop));
477
478    double fps;
479    if (FpsStep(&fps_state_, &fps))
480      PostMessage(fps);
481  }
482
483  pp::CompletionCallbackFactory<CubeInstance> callback_factory_;
484  pp::Graphics3D context_;
485  int32_t width_;
486  int32_t height_;
487  GLuint frag_shader_;
488  GLuint vertex_shader_;
489  GLuint program_;
490  GLuint vertex_buffer_;
491  GLuint index_buffer_;
492  GLuint texture_;
493
494  GLuint texture_loc_;
495  GLuint position_loc_;
496  GLuint texcoord_loc_;
497  GLuint color_loc_;
498  GLuint mvp_loc_;
499
500  float x_angle_;
501  float y_angle_;
502  bool animating_;
503  FpsState fps_state_;
504};
505
506class CubeModule : public pp::Module {
507 public:
508  CubeModule() : pp::Module() {}
509  virtual ~CubeModule() {}
510
511  virtual pp::Instance* CreateInstance(PP_Instance instance) {
512    return new CubeInstance(instance);
513  }
514};
515
516namespace pp {
517Module* CreateModule() { return new CubeModule(); }
518}  // namespace pp
519