Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / native_client_sdk / src / examples / api / graphics_3d / graphics_3d.cc
blob0777cb889f6e8826c498a0ff02b1d07f87f62b5f
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.
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>
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"
21 #ifdef WIN32
22 #undef PostMessage
23 // Allow 'this' in initializer list
24 #pragma warning(disable : 4355)
25 #endif
27 extern const uint8_t kRLETextureData[];
28 extern const size_t kRLETextureDataLength;
30 namespace {
32 const float kFovY = 45.0f;
33 const float kZNear = 1.0f;
34 const float kZFar = 10.0f;
35 const float kCameraZ = -4.0f;
36 const float kXAngleDelta = 2.0f;
37 const float kYAngleDelta = 0.5f;
39 const size_t kTextureDataLength = 128 * 128 * 3; // 128x128, 3 Bytes/pixel.
41 // The decompressed data is written here.
42 uint8_t g_texture_data[kTextureDataLength];
44 void 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.
49 // It is then Base64 encoded to make it use only printable characters (it
50 // stores more easily in a source file that way).
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,
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
70 uint8_t decoded[4];
71 int decoded_count = 0;
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;
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--;
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];
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;
106 assert(output == output_end);
109 GLuint CompileShader(GLenum type, const char* data) {
110 GLuint shader = glCreateShader(type);
111 glShaderSource(shader, 1, &data, NULL);
112 glCompileShader(shader);
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;
125 return shader;
128 GLuint LinkProgram(GLuint frag_shader, GLuint vert_shader) {
129 GLuint program = glCreateProgram();
130 glAttachShader(program, frag_shader);
131 glAttachShader(program, vert_shader);
132 glLinkProgram(program);
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;
145 return program;
148 const 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";
158 const 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";
171 struct Vertex {
172 float loc[3];
173 float color[3];
174 float tex[2];
177 const 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}},
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}},
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}},
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}},
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}},
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}},
215 const 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,
224 } // namespace
227 class 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) {}
245 virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
246 return true;
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();
256 if (context_.is_null()) {
257 if (!InitGL(new_width, new_height)) {
258 // failed.
259 return;
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;
278 width_ = new_width;
279 height_ = new_height;
280 glViewport(0, 0, width_, height_);
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;
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;
296 pp::VarArray array(message);
297 if (array.GetLength() != 2) {
298 fprintf(stderr, "Expected array of length 2.\n");
299 return;
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");
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");
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;
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
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;
344 glSetCurrentContextPPAPI(context_.pp_resource());
345 return true;
348 void InitShaders() {
349 frag_shader_ = CompileShader(GL_FRAGMENT_SHADER, kFragShaderSource);
350 if (!frag_shader_)
351 return;
353 vertex_shader_ = CompileShader(GL_VERTEX_SHADER, kVertexShaderSource);
354 if (!vertex_shader_)
355 return;
357 program_ = LinkProgram(frag_shader_, vertex_shader_);
358 if (!program_)
359 return;
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");
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);
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);
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,
390 GL_RGB,
391 128,
392 128,
394 GL_RGB,
395 GL_UNSIGNED_BYTE,
396 &g_texture_data[0]);
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);
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);
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);
419 //set what program to use
420 glUseProgram(program_);
421 glActiveTexture(GL_TEXTURE0);
422 glBindTexture(GL_TEXTURE_2D, texture_);
423 glUniform1i(texture_loc_, 0);
425 //create our perspective matrix
426 float mvp[16];
427 float trs[16];
428 float rot[16];
430 identity_matrix(mvp);
431 const float aspect_ratio = static_cast<float>(width_) / height_;
432 glhPerspectivef2(&mvp[0], kFovY, aspect_ratio, kZNear, kZFar);
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);
440 //define the attributes of the vertex
441 glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
442 glVertexAttribPointer(position_loc_,
444 GL_FLOAT,
445 GL_FALSE,
446 sizeof(Vertex),
447 reinterpret_cast<void*>(offsetof(Vertex, loc)));
448 glEnableVertexAttribArray(position_loc_);
449 glVertexAttribPointer(color_loc_,
451 GL_FLOAT,
452 GL_FALSE,
453 sizeof(Vertex),
454 reinterpret_cast<void*>(offsetof(Vertex, color)));
455 glEnableVertexAttribArray(color_loc_);
456 glVertexAttribPointer(texcoord_loc_,
458 GL_FLOAT,
459 GL_FALSE,
460 sizeof(Vertex),
461 reinterpret_cast<void*>(offsetof(Vertex, tex)));
462 glEnableVertexAttribArray(texcoord_loc_);
464 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_);
465 glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, 0);
468 void MainLoop(int32_t) {
469 Animate();
470 Render();
471 context_.SwapBuffers(
472 callback_factory_.NewCallback(&Graphics3DInstance::MainLoop));
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_;
486 GLuint texture_loc_;
487 GLuint position_loc_;
488 GLuint texcoord_loc_;
489 GLuint color_loc_;
490 GLuint mvp_loc_;
492 float x_angle_;
493 float y_angle_;
494 bool animating_;
497 class Graphics3DModule : public pp::Module {
498 public:
499 Graphics3DModule() : pp::Module() {}
500 virtual ~Graphics3DModule() {}
502 virtual pp::Instance* CreateInstance(PP_Instance instance) {
503 return new Graphics3DInstance(instance);
507 namespace pp {
508 Module* CreateModule() { return new Graphics3DModule(); }
509 } // namespace pp