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