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    // Pepper specifies dimensions in DIPs (device-independent pixels). To
251    // generate a context that is at device-pixel resolution on HiDPI devices,
252    // scale the dimensions by view.GetDeviceScale().
253    int32_t new_width = view.GetRect().width() * view.GetDeviceScale();
254    int32_t new_height = view.GetRect().height() * view.GetDeviceScale();
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    glhPerspectivef2(&mvp[0], kFovY, aspect_ratio, kZNear, kZFar);
433
434    translate_matrix(0, 0, kCameraZ, trs);
435    rotate_matrix(x_angle_, y_angle_, 0.0f, rot);
436    multiply_matrix(trs, rot, trs);
437    multiply_matrix(mvp, trs, mvp);
438    glUniformMatrix4fv(mvp_loc_, 1, GL_FALSE, mvp);
439
440    //define the attributes of the vertex
441    glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
442    glVertexAttribPointer(position_loc_,
443                          3,
444                          GL_FLOAT,
445                          GL_FALSE,
446                          sizeof(Vertex),
447                          reinterpret_cast<void*>(offsetof(Vertex, loc)));
448    glEnableVertexAttribArray(position_loc_);
449    glVertexAttribPointer(color_loc_,
450                          3,
451                          GL_FLOAT,
452                          GL_FALSE,
453                          sizeof(Vertex),
454                          reinterpret_cast<void*>(offsetof(Vertex, color)));
455    glEnableVertexAttribArray(color_loc_);
456    glVertexAttribPointer(texcoord_loc_,
457                          2,
458                          GL_FLOAT,
459                          GL_FALSE,
460                          sizeof(Vertex),
461                          reinterpret_cast<void*>(offsetof(Vertex, tex)));
462    glEnableVertexAttribArray(texcoord_loc_);
463
464    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_);
465    glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, 0);
466  }
467
468  void MainLoop(int32_t) {
469    Animate();
470    Render();
471    context_.SwapBuffers(
472        callback_factory_.NewCallback(&Graphics3DInstance::MainLoop));
473  }
474
475  pp::CompletionCallbackFactory<Graphics3DInstance> callback_factory_;
476  pp::Graphics3D context_;
477  int32_t width_;
478  int32_t height_;
479  GLuint frag_shader_;
480  GLuint vertex_shader_;
481  GLuint program_;
482  GLuint vertex_buffer_;
483  GLuint index_buffer_;
484  GLuint texture_;
485
486  GLuint texture_loc_;
487  GLuint position_loc_;
488  GLuint texcoord_loc_;
489  GLuint color_loc_;
490  GLuint mvp_loc_;
491
492  float x_angle_;
493  float y_angle_;
494  bool animating_;
495};
496
497class Graphics3DModule : public pp::Module {
498 public:
499  Graphics3DModule() : pp::Module() {}
500  virtual ~Graphics3DModule() {}
501
502  virtual pp::Instance* CreateInstance(PP_Instance instance) {
503    return new Graphics3DInstance(instance);
504  }
505};
506
507namespace pp {
508Module* CreateModule() { return new Graphics3DModule(); }
509}  // namespace pp
510