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 "common/fps.h" 13#include "matrix.h" 14#include "ppapi/cpp/graphics_3d.h" 15#include "ppapi/cpp/instance.h" 16#include "ppapi/cpp/module.h" 17#include "ppapi/cpp/var.h" 18#include "ppapi/cpp/var_array.h" 19#include "ppapi/lib/gl/gles2/gl2ext_ppapi.h" 20#include "ppapi/utility/completion_callback_factory.h" 21 22#ifdef WIN32 23#undef PostMessage 24// Allow 'this' in initializer list 25#pragma warning(disable : 4355) 26#endif 27 28extern const uint8_t kRLETextureData[]; 29extern const size_t kRLETextureDataLength; 30 31namespace { 32 33const float kMinFovY = 45.0f; 34const float kZNear = 1.0f; 35const float kZFar = 10.0f; 36const float kCameraZ = -4.0f; 37const float kXAngleDelta = 2.0f; 38const float kYAngleDelta = 0.5f; 39 40const size_t kTextureDataLength = 128 * 128 * 3; // 128x128, 3 Bytes/pixel. 41 42// The decompressed data is written here. 43uint8_t g_texture_data[kTextureDataLength]; 44 45void DecompressTexture() { 46 // The image is first encoded with a very simple RLE scheme: 47 // <value0> <count0> <value1> <count1> ... 48 // Because a <count> of 0 is useless, we use it to represent 256. 49 // 50 // It is then Base64 encoded to make it use only printable characters (it 51 // stores more easily in a source file that way). 52 // 53 // To decompress, we have to reverse the process. 54 static const uint8_t kBase64Decode[256] = { 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, 0, 0, 0, 0, 0, 57 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, 58 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 59 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 60 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 61 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 62 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 63 }; 64 const uint8_t* input = &kRLETextureData[0]; 65 const uint8_t* const input_end = &kRLETextureData[kRLETextureDataLength]; 66 uint8_t* output = &g_texture_data[0]; 67#ifndef NDEBUG 68 const uint8_t* const output_end = &g_texture_data[kTextureDataLength]; 69#endif 70 71 uint8_t decoded[4]; 72 int decoded_count = 0; 73 74 while (input < input_end || decoded_count > 0) { 75 if (decoded_count < 2) { 76 assert(input + 4 <= input_end); 77 // Grab four base-64 encoded (6-bit) bytes. 78 uint32_t data = 0; 79 data |= (kBase64Decode[*input++] << 18); 80 data |= (kBase64Decode[*input++] << 12); 81 data |= (kBase64Decode[*input++] << 6); 82 data |= (kBase64Decode[*input++] ); 83 // And decode it to 3 (8-bit) bytes. 84 decoded[decoded_count++] = (data >> 16) & 0xff; 85 decoded[decoded_count++] = (data >> 8) & 0xff; 86 decoded[decoded_count++] = (data ) & 0xff; 87 88 // = is the base64 end marker. Remove decoded bytes if we see any. 89 if (input[-1] == '=') decoded_count--; 90 if (input[-2] == '=') decoded_count--; 91 } 92 93 int value = decoded[0]; 94 int count = decoded[1]; 95 decoded_count -= 2; 96 // Move the other decoded bytes (if any) down. 97 decoded[0] = decoded[2]; 98 decoded[1] = decoded[3]; 99 100 // Expand the RLE data. 101 if (count == 0) 102 count = 256; 103 assert(output <= output_end); 104 memset(output, value, count); 105 output += count; 106 } 107 assert(output == output_end); 108} 109 110GLuint CompileShader(GLenum type, const char* data) { 111 GLuint shader = glCreateShader(type); 112 glShaderSource(shader, 1, &data, NULL); 113 glCompileShader(shader); 114 115 GLint compile_status; 116 glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status); 117 if (compile_status != GL_TRUE) { 118 // Shader failed to compile, let's see what the error is. 119 char buffer[1024]; 120 GLsizei length; 121 glGetShaderInfoLog(shader, sizeof(buffer), &length, &buffer[0]); 122 fprintf(stderr, "Shader failed to compile: %s\n", buffer); 123 return 0; 124 } 125 126 return shader; 127} 128 129GLuint LinkProgram(GLuint frag_shader, GLuint vert_shader) { 130 GLuint program = glCreateProgram(); 131 glAttachShader(program, frag_shader); 132 glAttachShader(program, vert_shader); 133 glLinkProgram(program); 134 135 GLint link_status; 136 glGetProgramiv(program, GL_LINK_STATUS, &link_status); 137 if (link_status != GL_TRUE) { 138 // Program failed to link, let's see what the error is. 139 char buffer[1024]; 140 GLsizei length; 141 glGetProgramInfoLog(program, sizeof(buffer), &length, &buffer[0]); 142 fprintf(stderr, "Program failed to link: %s\n", buffer); 143 return 0; 144 } 145 146 return program; 147} 148 149const char kFragShaderSource[] = 150 "precision mediump float;\n" 151 "varying vec3 v_color;\n" 152 "varying vec2 v_texcoord;\n" 153 "uniform sampler2D u_texture;\n" 154 "void main() {\n" 155 " gl_FragColor = texture2D(u_texture, v_texcoord);\n" 156 " gl_FragColor += vec4(v_color, 1);\n" 157 "}\n"; 158 159const char kVertexShaderSource[] = 160 "uniform mat4 u_mvp;\n" 161 "attribute vec2 a_texcoord;\n" 162 "attribute vec3 a_color;\n" 163 "attribute vec4 a_position;\n" 164 "varying vec3 v_color;\n" 165 "varying vec2 v_texcoord;\n" 166 "void main() {\n" 167 " gl_Position = u_mvp * a_position;\n" 168 " v_color = a_color;\n" 169 " v_texcoord = a_texcoord;\n" 170 "}\n"; 171 172struct Vertex { 173 float loc[3]; 174 float color[3]; 175 float tex[2]; 176}; 177 178const Vertex kCubeVerts[24] = { 179 // +Z (red arrow, black tip) 180 {{-1.0, -1.0, +1.0}, {0.0, 0.0, 0.0}, {1.0, 0.0}}, 181 {{+1.0, -1.0, +1.0}, {0.0, 0.0, 0.0}, {0.0, 0.0}}, 182 {{+1.0, +1.0, +1.0}, {0.5, 0.0, 0.0}, {0.0, 1.0}}, 183 {{-1.0, +1.0, +1.0}, {0.5, 0.0, 0.0}, {1.0, 1.0}}, 184 185 // +X (green arrow, black tip) 186 {{+1.0, -1.0, -1.0}, {0.0, 0.0, 0.0}, {1.0, 0.0}}, 187 {{+1.0, +1.0, -1.0}, {0.0, 0.0, 0.0}, {0.0, 0.0}}, 188 {{+1.0, +1.0, +1.0}, {0.0, 0.5, 0.0}, {0.0, 1.0}}, 189 {{+1.0, -1.0, +1.0}, {0.0, 0.5, 0.0}, {1.0, 1.0}}, 190 191 // +Y (blue arrow, black tip) 192 {{-1.0, +1.0, -1.0}, {0.0, 0.0, 0.0}, {1.0, 0.0}}, 193 {{-1.0, +1.0, +1.0}, {0.0, 0.0, 0.0}, {0.0, 0.0}}, 194 {{+1.0, +1.0, +1.0}, {0.0, 0.0, 0.5}, {0.0, 1.0}}, 195 {{+1.0, +1.0, -1.0}, {0.0, 0.0, 0.5}, {1.0, 1.0}}, 196 197 // -Z (red arrow, red tip) 198 {{+1.0, +1.0, -1.0}, {0.0, 0.0, 0.0}, {1.0, 1.0}}, 199 {{-1.0, +1.0, -1.0}, {0.0, 0.0, 0.0}, {0.0, 1.0}}, 200 {{-1.0, -1.0, -1.0}, {1.0, 0.0, 0.0}, {0.0, 0.0}}, 201 {{+1.0, -1.0, -1.0}, {1.0, 0.0, 0.0}, {1.0, 0.0}}, 202 203 // -X (green arrow, green tip) 204 {{-1.0, +1.0, +1.0}, {0.0, 0.0, 0.0}, {1.0, 1.0}}, 205 {{-1.0, -1.0, +1.0}, {0.0, 0.0, 0.0}, {0.0, 1.0}}, 206 {{-1.0, -1.0, -1.0}, {0.0, 1.0, 0.0}, {0.0, 0.0}}, 207 {{-1.0, +1.0, -1.0}, {0.0, 1.0, 0.0}, {1.0, 0.0}}, 208 209 // -Y (blue arrow, blue tip) 210 {{+1.0, -1.0, +1.0}, {0.0, 0.0, 0.0}, {1.0, 1.0}}, 211 {{+1.0, -1.0, -1.0}, {0.0, 0.0, 0.0}, {0.0, 1.0}}, 212 {{-1.0, -1.0, -1.0}, {0.0, 0.0, 1.0}, {0.0, 0.0}}, 213 {{-1.0, -1.0, +1.0}, {0.0, 0.0, 1.0}, {1.0, 0.0}}, 214}; 215 216const GLubyte kCubeIndexes[36] = { 217 2, 1, 0, 3, 2, 0, 218 6, 5, 4, 7, 6, 4, 219 10, 9, 8, 11, 10, 8, 220 14, 13, 12, 15, 14, 12, 221 18, 17, 16, 19, 18, 16, 222 22, 21, 20, 23, 22, 20, 223}; 224 225} // namespace 226 227 228class CubeInstance : public pp::Instance { 229 public: 230 explicit CubeInstance(PP_Instance instance) 231 : pp::Instance(instance), 232 callback_factory_(this), 233 width_(0), 234 height_(0), 235 frag_shader_(0), 236 vertex_shader_(0), 237 program_(0), 238 texture_loc_(0), 239 position_loc_(0), 240 color_loc_(0), 241 mvp_loc_(0), 242 x_angle_(0), 243 y_angle_(0), 244 animating_(true) { 245 FpsInit(&fps_state_); 246 } 247 248 virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) { 249 return true; 250 } 251 252 virtual void DidChangeView(const pp::View& view) { 253 int32_t new_width = view.GetRect().width(); 254 int32_t new_height = view.GetRect().height(); 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 float fovY = kMinFovY / aspect_ratio; 433 if (fovY < kMinFovY) 434 fovY = kMinFovY; 435 436 glhPerspectivef2(&mvp[0], fovY, aspect_ratio, kZNear, kZFar); 437 438 translate_matrix(0, 0, kCameraZ, trs); 439 rotate_matrix(x_angle_, y_angle_, 0.0f, rot); 440 multiply_matrix(trs, rot, trs); 441 multiply_matrix(mvp, trs, mvp); 442 glUniformMatrix4fv(mvp_loc_, 1, GL_FALSE, mvp); 443 444 //define the attributes of the vertex 445 glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_); 446 glVertexAttribPointer(position_loc_, 447 3, 448 GL_FLOAT, 449 GL_FALSE, 450 sizeof(Vertex), 451 reinterpret_cast<void*>(offsetof(Vertex, loc))); 452 glEnableVertexAttribArray(position_loc_); 453 glVertexAttribPointer(color_loc_, 454 3, 455 GL_FLOAT, 456 GL_FALSE, 457 sizeof(Vertex), 458 reinterpret_cast<void*>(offsetof(Vertex, color))); 459 glEnableVertexAttribArray(color_loc_); 460 glVertexAttribPointer(texcoord_loc_, 461 2, 462 GL_FLOAT, 463 GL_FALSE, 464 sizeof(Vertex), 465 reinterpret_cast<void*>(offsetof(Vertex, tex))); 466 glEnableVertexAttribArray(texcoord_loc_); 467 468 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_); 469 glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, 0); 470 } 471 472 void MainLoop(int32_t) { 473 Animate(); 474 Render(); 475 context_.SwapBuffers( 476 callback_factory_.NewCallback(&CubeInstance::MainLoop)); 477 478 double fps; 479 if (FpsStep(&fps_state_, &fps)) 480 PostMessage(fps); 481 } 482 483 pp::CompletionCallbackFactory<CubeInstance> callback_factory_; 484 pp::Graphics3D context_; 485 int32_t width_; 486 int32_t height_; 487 GLuint frag_shader_; 488 GLuint vertex_shader_; 489 GLuint program_; 490 GLuint vertex_buffer_; 491 GLuint index_buffer_; 492 GLuint texture_; 493 494 GLuint texture_loc_; 495 GLuint position_loc_; 496 GLuint texcoord_loc_; 497 GLuint color_loc_; 498 GLuint mvp_loc_; 499 500 float x_angle_; 501 float y_angle_; 502 bool animating_; 503 FpsState fps_state_; 504}; 505 506class CubeModule : public pp::Module { 507 public: 508 CubeModule() : pp::Module() {} 509 virtual ~CubeModule() {} 510 511 virtual pp::Instance* CreateInstance(PP_Instance instance) { 512 return new CubeInstance(instance); 513 } 514}; 515 516namespace pp { 517Module* CreateModule() { return new CubeModule(); } 518} // namespace pp 519