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