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.
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"
23 // Allow 'this' in initializer list
24 #pragma warning(disable : 4355)
27 extern const uint8_t kRLETextureData[];
28 extern const size_t kRLETextureDataLength;
32 const float kMinFovY = 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];
67 const uint8_t* const output_end = &g_texture_data[kTextureDataLength];
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.
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];
95 // Move the other decoded bytes (if any) down.
96 decoded[0] = decoded[2];
97 decoded[1] = decoded[3];
99 // Expand the RLE data.
102 assert(output <= output_end);
103 memset(output, value, 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.
120 glGetShaderInfoLog(shader, sizeof(buffer), &length, &buffer[0]);
121 fprintf(stderr, "Shader failed to compile: %s\n", buffer);
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);
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.
140 glGetProgramInfoLog(program, sizeof(buffer), &length, &buffer[0]);
141 fprintf(stderr, "Program failed to link: %s\n", buffer);
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"
154 " gl_FragColor = texture2D(u_texture, v_texcoord);\n"
155 " gl_FragColor += vec4(v_color, 1);\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"
166 " gl_Position = u_mvp * a_position;\n"
167 " v_color = a_color;\n"
168 " v_texcoord = a_texcoord;\n"
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] = {
219 14, 13, 12, 15, 14, 12,
220 18, 17, 16, 19, 18, 16,
221 22, 21, 20, 23, 22, 20,
227 class CubeInstance : public pp::Instance {
229 explicit CubeInstance(PP_Instance instance)
230 : pp::Instance(instance),
231 callback_factory_(this),
245 virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
249 virtual void DidChangeView(const pp::View& view) {
250 int32_t new_width = view.GetRect().width();
251 int32_t new_height = view.GetRect().height();
253 if (context_.is_null()) {
254 if (!InitGL(new_width, new_height)) {
264 // Resize the buffers to the new size of the module.
265 int32_t result = context_.ResizeBuffers(new_width, new_height);
268 "Unable to resize buffers to %d x %d!\n",
276 height_ = new_height;
277 glViewport(0, 0, width_, height_);
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();
287 // An array message sets the current x and y rotation.
288 if (!message.is_array()) {
289 fprintf(stderr, "Expected array message.\n");
293 pp::VarArray array(message);
294 if (array.GetLength() != 2) {
295 fprintf(stderr, "Expected array of length 2.\n");
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();
305 fprintf(stderr, "Expected value to be an int or double.\n");
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();
314 fprintf(stderr, "Expected value to be an int or double.\n");
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");
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
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);
341 glSetCurrentContextPPAPI(context_.pp_resource());
346 frag_shader_ = CompileShader(GL_FRAGMENT_SHADER, kFragShaderSource);
350 vertex_shader_ = CompileShader(GL_VERTEX_SHADER, kVertexShaderSource);
354 program_ = LinkProgram(frag_shader_, vertex_shader_);
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");
366 glGenBuffers(1, &vertex_buffer_);
367 glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
368 glBufferData(GL_ARRAY_BUFFER, sizeof(kCubeVerts), &kCubeVerts[0],
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);
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,
398 x_angle_ = fmod(360.0f + x_angle_ + kXAngleDelta, 360.0f);
399 y_angle_ = fmod(360.0f + y_angle_ + kYAngleDelta, 360.0f);
401 // Send new values to JavaScript.
404 array.Set(0, x_angle_);
405 array.Set(1, y_angle_);
411 glClearColor(0.5, 0.5, 0.5, 1);
413 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
414 glEnable(GL_DEPTH_TEST);
416 //set what program to use
417 glUseProgram(program_);
418 glActiveTexture(GL_TEXTURE0);
419 glBindTexture(GL_TEXTURE_2D, texture_);
420 glUniform1i(texture_loc_, 0);
422 //create our perspective matrix
427 identity_matrix(mvp);
428 const float aspect_ratio = static_cast<float>(width_) / height_;
429 float fovY = kMinFovY / aspect_ratio;
433 glhPerspectivef2(&mvp[0], fovY, aspect_ratio, kZNear, kZFar);
435 translate_matrix(0, 0, kCameraZ, trs);
436 rotate_matrix(x_angle_, y_angle_, 0.0f, rot);
437 multiply_matrix(trs, rot, trs);
438 multiply_matrix(mvp, trs, mvp);
439 glUniformMatrix4fv(mvp_loc_, 1, GL_FALSE, mvp);
441 //define the attributes of the vertex
442 glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
443 glVertexAttribPointer(position_loc_,
448 reinterpret_cast<void*>(offsetof(Vertex, loc)));
449 glEnableVertexAttribArray(position_loc_);
450 glVertexAttribPointer(color_loc_,
455 reinterpret_cast<void*>(offsetof(Vertex, color)));
456 glEnableVertexAttribArray(color_loc_);
457 glVertexAttribPointer(texcoord_loc_,
462 reinterpret_cast<void*>(offsetof(Vertex, tex)));
463 glEnableVertexAttribArray(texcoord_loc_);
465 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_);
466 glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, 0);
469 void MainLoop(int32_t) {
472 context_.SwapBuffers(
473 callback_factory_.NewCallback(&CubeInstance::MainLoop));
476 pp::CompletionCallbackFactory<CubeInstance> callback_factory_;
477 pp::Graphics3D context_;
481 GLuint vertex_shader_;
483 GLuint vertex_buffer_;
484 GLuint index_buffer_;
488 GLuint position_loc_;
489 GLuint texcoord_loc_;
498 class CubeModule : public pp::Module {
500 CubeModule() : pp::Module() {}
501 virtual ~CubeModule() {}
503 virtual pp::Instance* CreateInstance(PP_Instance instance) {
504 return new CubeInstance(instance);
509 Module* CreateModule() { return new CubeModule(); }