1 // Copyright (c) 2012 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 //----------------------------------------------------------------------------- 6 // The spinning Cube 7 //----------------------------------------------------------------------------- 8 9 #define _USE_MATH_DEFINES 1 10 #include <limits.h> 11 #include <math.h> 12 #include <stdarg.h> 13 #include <stddef.h> 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 18 #include "ppapi/c/pp_completion_callback.h" 19 #include "ppapi/c/pp_errors.h" 20 #include "ppapi/c/pp_graphics_3d.h" 21 #include "ppapi/c/pp_module.h" 22 #include "ppapi/c/pp_stdint.h" 23 #include "ppapi/c/pp_var.h" 24 #include "ppapi/c/ppb.h" 25 #include "ppapi/c/ppb_core.h" 26 #include "ppapi/c/ppb_graphics_3d.h" 27 #include "ppapi/c/ppb_instance.h" 28 #include "ppapi/c/ppb_messaging.h" 29 #include "ppapi/c/ppb_opengles2.h" 30 #include "ppapi/c/ppb_url_loader.h" 31 #include "ppapi/c/ppb_url_request_info.h" 32 #include "ppapi/c/ppb_var.h" 33 #include "ppapi/c/ppp.h" 34 #include "ppapi/c/ppp_instance.h" 35 #include "ppapi/c/ppp_messaging.h" 36 37 #include "ppapi/c/ppp_graphics_3d.h" 38 #include "ppapi/lib/gl/gles2/gl2ext_ppapi.h" 39 40 #include <GLES2/gl2.h> 41 #include "matrix.h" 42 43 static PPB_Messaging* ppb_messaging_interface = NULL; 44 static PPB_Var* ppb_var_interface = NULL; 45 static PPB_Core* ppb_core_interface = NULL; 46 static PPB_Graphics3D* ppb_g3d_interface = NULL; 47 static PPB_Instance* ppb_instance_interface = NULL; 48 static PPB_URLRequestInfo* ppb_urlrequestinfo_interface = NULL; 49 static PPB_URLLoader* ppb_urlloader_interface = NULL; 50 51 static PP_Instance g_instance; 52 static PP_Resource g_context; 53 54 GLuint g_positionLoc; 55 GLuint g_texCoordLoc; 56 GLuint g_colorLoc; 57 GLuint g_MVPLoc; 58 GLuint g_vboID; 59 GLuint g_ibID; 60 GLubyte g_Indices[36]; 61 62 GLuint g_programObj; 63 GLuint g_vertexShader; 64 GLuint g_fragmentShader; 65 66 GLuint g_textureLoc = 0; 67 GLuint g_textureID = 0; 68 69 float g_fSpinX = 0.0f; 70 float g_fSpinY = 0.0f; 71 72 //----------------------------------------------------------------------------- 73 // Rendering Assets 74 //----------------------------------------------------------------------------- 75 struct Vertex { 76 float tu, tv; 77 float color[3]; 78 float loc[3]; 79 }; 80 81 Vertex* g_quadVertices = NULL; 82 const char* g_TextureData = NULL; 83 const char* g_VShaderData = NULL; 84 const char* g_FShaderData = NULL; 85 int g_LoadCnt = 0; 86 87 //----------------------------------------------------------------------------- 88 // PROTOTYPES 89 //----------------------------------------------------------------------------- 90 void PostMessage(const char* fmt, ...); 91 char* LoadFile(const char* fileName); 92 93 void BuildQuad(Vertex* verts, int axis[3], float depth, float color[3]); 94 Vertex* BuildCube(void); 95 96 void InitGL(void); 97 void InitProgram(void); 98 void Render(void); 99 100 static struct PP_Var CStrToVar(const char* str) { 101 if (ppb_var_interface != NULL) { 102 return ppb_var_interface->VarFromUtf8(str, strlen(str)); 103 } 104 return PP_MakeUndefined(); 105 } 106 107 void PostMessage(const char* fmt, ...) { 108 va_list args; 109 va_start(args, fmt); 110 111 char msg[4096]; 112 vsnprintf(msg, sizeof(msg), fmt, args); 113 114 if (ppb_messaging_interface) 115 ppb_messaging_interface->PostMessage(g_instance, CStrToVar(msg)); 116 117 va_end(args); 118 } 119 120 void MainLoop(void* foo, int bar) { 121 if (g_LoadCnt == 3) { 122 InitProgram(); 123 g_LoadCnt++; 124 } 125 if (g_LoadCnt > 3) { 126 Render(); 127 PP_CompletionCallback cc = PP_MakeCompletionCallback(MainLoop, 0); 128 ppb_g3d_interface->SwapBuffers(g_context, cc); 129 } else { 130 PP_CompletionCallback cc = PP_MakeCompletionCallback(MainLoop, 0); 131 ppb_core_interface->CallOnMainThread(0, cc, 0); 132 } 133 } 134 135 void InitGL(void) { 136 int32_t attribs[] = { 137 PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8, 138 PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 24, 139 PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 8, 140 PP_GRAPHICS3DATTRIB_SAMPLES, 0, 141 PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0, 142 PP_GRAPHICS3DATTRIB_WIDTH, 640, 143 PP_GRAPHICS3DATTRIB_HEIGHT, 480, 144 PP_GRAPHICS3DATTRIB_NONE 145 }; 146 147 g_context = ppb_g3d_interface->Create(g_instance, 0, attribs); 148 int32_t success = ppb_instance_interface->BindGraphics(g_instance, g_context); 149 if (success == PP_FALSE) { 150 glSetCurrentContextPPAPI(0); 151 printf("Failed to set context.\n"); 152 return; 153 } 154 glSetCurrentContextPPAPI(g_context); 155 156 glViewport(0, 0, 640, 480); 157 glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 158 } 159 160 GLuint compileShader(GLenum type, const char* data) { 161 const char* shaderStrings[1]; 162 shaderStrings[0] = data; 163 164 GLuint shader = glCreateShader(type); 165 glShaderSource(shader, 1, shaderStrings, NULL); 166 glCompileShader(shader); 167 return shader; 168 } 169 170 void InitProgram(void) { 171 glSetCurrentContextPPAPI(g_context); 172 173 g_vertexShader = compileShader(GL_VERTEX_SHADER, g_VShaderData); 174 g_fragmentShader = compileShader(GL_FRAGMENT_SHADER, g_FShaderData); 175 176 g_programObj = glCreateProgram(); 177 glAttachShader(g_programObj, g_vertexShader); 178 glAttachShader(g_programObj, g_fragmentShader); 179 glLinkProgram(g_programObj); 180 181 glGenBuffers(1, &g_vboID); 182 glBindBuffer(GL_ARRAY_BUFFER, g_vboID); 183 glBufferData(GL_ARRAY_BUFFER, 184 24 * sizeof(Vertex), 185 (void*)&g_quadVertices[0], 186 GL_STATIC_DRAW); 187 188 glGenBuffers(1, &g_ibID); 189 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ibID); 190 glBufferData(GL_ELEMENT_ARRAY_BUFFER, 191 36 * sizeof(char), 192 (void*)&g_Indices[0], 193 GL_STATIC_DRAW); 194 195 // 196 // Create a texture to test out our fragment shader... 197 // 198 glGenTextures(1, &g_textureID); 199 glBindTexture(GL_TEXTURE_2D, g_textureID); 200 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 201 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 202 glTexImage2D(GL_TEXTURE_2D, 203 0, 204 GL_RGB, 205 128, 206 128, 207 0, 208 GL_RGB, 209 GL_UNSIGNED_BYTE, 210 g_TextureData); 211 212 // 213 // Locate some parameters by name so we can set them later... 214 // 215 g_textureLoc = glGetUniformLocation(g_programObj, "arrowTexture"); 216 g_positionLoc = glGetAttribLocation(g_programObj, "a_position"); 217 g_texCoordLoc = glGetAttribLocation(g_programObj, "a_texCoord"); 218 g_colorLoc = glGetAttribLocation(g_programObj, "a_color"); 219 g_MVPLoc = glGetUniformLocation(g_programObj, "a_MVP"); 220 } 221 222 void BuildQuad(Vertex* verts, int axis[3], float depth, float color[3]) { 223 static float X[4] = { -1.0f, 1.0f, 1.0f, -1.0f }; 224 static float Y[4] = { -1.0f, -1.0f, 1.0f, 1.0f }; 225 226 for (int i = 0; i < 4; i++) { 227 verts[i].tu = (1.0 - X[i]) / 2.0f; 228 verts[i].tv = (Y[i] + 1.0f) / -2.0f * depth; 229 verts[i].loc[axis[0]] = X[i] * depth; 230 verts[i].loc[axis[1]] = Y[i] * depth; 231 verts[i].loc[axis[2]] = depth; 232 for (int j = 0; j < 3; j++) 233 verts[i].color[j] = color[j] * (Y[i] + 1.0f) / 2.0f; 234 } 235 } 236 237 Vertex* BuildCube() { 238 Vertex* verts = new Vertex[24]; 239 for (int i = 0; i < 3; i++) { 240 int Faxis[3]; 241 int Baxis[3]; 242 float Fcolor[3]; 243 float Bcolor[3]; 244 for (int j = 0; j < 3; j++) { 245 Faxis[j] = (j + i) % 3; 246 Baxis[j] = (j + i) % 3; 247 } 248 memset(Fcolor, 0, sizeof(float) * 3); 249 memset(Bcolor, 0, sizeof(float) * 3); 250 Fcolor[i] = 0.5f; 251 Bcolor[i] = 1.0f; 252 BuildQuad(&verts[0 + i * 4], Faxis, 1.0f, Fcolor); 253 BuildQuad(&verts[12 + i * 4], Baxis, -1.0f, Bcolor); 254 } 255 256 for (int i = 0; i < 6; i++) { 257 g_Indices[i * 6 + 0] = 2 + i * 4; 258 g_Indices[i * 6 + 1] = 1 + i * 4; 259 g_Indices[i * 6 + 2] = 0 + i * 4; 260 g_Indices[i * 6 + 3] = 3 + i * 4; 261 g_Indices[i * 6 + 4] = 2 + i * 4; 262 g_Indices[i * 6 + 5] = 0 + i * 4; 263 } 264 return verts; 265 } 266 267 void Render(void) { 268 static float xRot = 0.0; 269 static float yRot = 0.0; 270 271 xRot += 2.0f; 272 yRot += 0.5f; 273 if (xRot >= 360.0f) 274 xRot = 0.0; 275 if (yRot >= 360.0f) 276 yRot = 0.0; 277 278 glClearColor(0.5, 0.5, 0.5, 1); 279 glClearDepthf(1.0); 280 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 281 glEnable(GL_DEPTH_TEST); 282 283 //set what program to use 284 glUseProgram(g_programObj); 285 glActiveTexture(GL_TEXTURE0); 286 glBindTexture(GL_TEXTURE_2D, g_textureID); 287 glUniform1i(g_textureLoc, 0); 288 289 //create our perspective matrix 290 float mpv[16]; 291 float trs[16]; 292 float rot[16]; 293 294 identity_matrix(mpv); 295 glhPerspectivef2(&mpv[0], 45.0f, 640.0f / 480.0f, 1, 10); 296 297 translate_matrix(0, 0, -4.0, trs); 298 rotate_matrix(xRot, yRot, 0.0f, rot); 299 multiply_matrix(trs, rot, trs); 300 multiply_matrix(mpv, trs, mpv); 301 glUniformMatrix4fv(g_MVPLoc, 1, GL_FALSE, (GLfloat*)mpv); 302 303 //define the attributes of the vertex 304 glBindBuffer(GL_ARRAY_BUFFER, g_vboID); 305 glVertexAttribPointer(g_positionLoc, 306 3, 307 GL_FLOAT, 308 GL_FALSE, 309 sizeof(Vertex), 310 (void*)offsetof(Vertex, loc)); 311 glEnableVertexAttribArray(g_positionLoc); 312 glVertexAttribPointer(g_texCoordLoc, 313 2, 314 GL_FLOAT, 315 GL_FALSE, 316 sizeof(Vertex), 317 (void*)offsetof(Vertex, tu)); 318 glEnableVertexAttribArray(g_texCoordLoc); 319 glVertexAttribPointer(g_colorLoc, 320 3, 321 GL_FLOAT, 322 GL_FALSE, 323 sizeof(Vertex), 324 (void*)offsetof(Vertex, color)); 325 glEnableVertexAttribArray(g_colorLoc); 326 327 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ibID); 328 glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, 0); 329 } 330 331 typedef void (*OpenCB)(void* dataPtr); 332 struct OpenRequest { 333 PP_Resource loader_; 334 PP_Resource request_; 335 char* buf_; 336 void* data_; 337 int64_t size_; 338 int64_t avail_; 339 OpenCB cb_; 340 }; 341 342 void FreeRequest(OpenRequest* req) { 343 if (req) { 344 ppb_core_interface->ReleaseResource(req->request_); 345 ppb_core_interface->ReleaseResource(req->loader_); 346 free(req); 347 } 348 } 349 350 static void URLPartialRead(void* user_data, int mode) { 351 OpenRequest* req = (OpenRequest*)user_data; 352 int64_t total; 353 int32_t cnt; 354 355 if (mode < 0) { 356 free(req->buf_); 357 req->cb_(NULL); 358 FreeRequest(req); 359 return; 360 } 361 362 req->avail_ += mode; 363 total = req->size_ - req->avail_; 364 365 cnt = (total > LONG_MAX) ? LONG_MAX : (int32_t) total; 366 // If we still have more to do, re-issue the read. 367 if (cnt > 0) { 368 int32_t bytes = ppb_urlloader_interface->ReadResponseBody( 369 req->loader_, 370 (void*)&req->buf_[req->avail_], 371 cnt, 372 PP_MakeCompletionCallback(URLPartialRead, req)); 373 374 // If the reissue completes immediately, then process it. 375 if (bytes != PP_OK_COMPLETIONPENDING) { 376 URLPartialRead(user_data, bytes); 377 } 378 return; 379 } 380 381 // Nothing left, so signal complete. 382 req->cb_(req); 383 FreeRequest(req); 384 printf("Loaded\n"); 385 } 386 387 static void URLOpened(void* user_data, int mode) { 388 OpenRequest* req = (OpenRequest*)user_data; 389 390 int64_t cur, total; 391 int32_t cnt; 392 ppb_urlloader_interface->GetDownloadProgress(req->loader_, &cur, &total); 393 394 // If we can't preallocate the buffer because the size is unknown, then 395 // fail the load. 396 if (total == -1) { 397 req->cb_(NULL); 398 FreeRequest(req); 399 return; 400 } 401 402 // Otherwise allocate a buffer with enough space for a terminating 403 // NULL in case we need one. 404 cnt = (total > LONG_MAX) ? LONG_MAX : (int32_t) total; 405 req->buf_ = (char*)malloc(cnt + 1); 406 req->buf_[cnt] = 0; 407 req->size_ = cnt; 408 int32_t bytes = ppb_urlloader_interface->ReadResponseBody( 409 req->loader_, 410 req->buf_, 411 cnt, 412 PP_MakeCompletionCallback(URLPartialRead, req)); 413 414 // Usually we are pending. 415 if (bytes == PP_OK_COMPLETIONPENDING) 416 return; 417 418 // But if we did complete the read, then dispatch the handler. 419 URLPartialRead(req, bytes); 420 } 421 422 void LoadURL(PP_Instance inst, const char* url, OpenCB cb, void* data) { 423 OpenRequest* req = (OpenRequest*)malloc(sizeof(OpenRequest)); 424 memset(req, 0, sizeof(OpenRequest)); 425 426 req->loader_ = ppb_urlloader_interface->Create(inst); 427 req->request_ = ppb_urlrequestinfo_interface->Create(inst); 428 req->cb_ = cb; 429 req->data_ = data; 430 431 if (!req->loader_ || !req->request_) { 432 cb(NULL); 433 FreeRequest(req); 434 return; 435 } 436 437 ppb_urlrequestinfo_interface->SetProperty( 438 req->request_, PP_URLREQUESTPROPERTY_URL, CStrToVar(url)); 439 ppb_urlrequestinfo_interface->SetProperty( 440 req->request_, PP_URLREQUESTPROPERTY_METHOD, CStrToVar("GET")); 441 ppb_urlrequestinfo_interface->SetProperty( 442 req->request_, 443 PP_URLREQUESTPROPERTY_RECORDDOWNLOADPROGRESS, 444 PP_MakeBool(PP_TRUE)); 445 446 int val = ppb_urlloader_interface->Open( 447 req->loader_, req->request_, PP_MakeCompletionCallback(URLOpened, req)); 448 449 if (val != PP_OK_COMPLETIONPENDING) { 450 cb(NULL); 451 free(req); 452 } 453 } 454 455 void Loaded(void* data) { 456 OpenRequest* req = (OpenRequest*)data; 457 if (req && req->buf_) { 458 char** pptr = (char**)req->data_; 459 *pptr = req->buf_; 460 g_LoadCnt++; 461 return; 462 } 463 PostMessage("Failed to load asset.\n"); 464 } 465 466 /** 467 * Called when the NaCl module is instantiated on the web page. The identifier 468 * of the new instance will be passed in as the first argument (this value is 469 * generated by the browser and is an opaque handle). This is called for each 470 * instantiation of the NaCl module, which is each time the <embed> tag for 471 * this module is encountered. 472 * 473 * If this function reports a failure (by returning @a PP_FALSE), the NaCl 474 * module will be deleted and DidDestroy will be called. 475 * @param[in] instance The identifier of the new instance representing this 476 * NaCl module. 477 * @param[in] argc The number of arguments contained in @a argn and @a argv. 478 * @param[in] argn An array of argument names. These argument names are 479 * supplied in the <embed> tag, for example: 480 * <embed id="nacl_module" dimensions="2"> 481 * will produce two arguments, one named "id" and one named "dimensions". 482 * @param[in] argv An array of argument values. These are the values of the 483 * arguments listed in the <embed> tag. In the above example, there will 484 * be two elements in this array, "nacl_module" and "2". The indices of 485 * these values match the indices of the corresponding names in @a argn. 486 * @return @a PP_TRUE on success. 487 */ 488 static PP_Bool Instance_DidCreate(PP_Instance instance, 489 uint32_t argc, 490 const char* argn[], 491 const char* argv[]) { 492 g_instance = instance; 493 LoadURL(instance, "hello.raw", Loaded, &g_TextureData); 494 LoadURL(instance, "vertex_shader_es2.vert", Loaded, &g_VShaderData); 495 LoadURL(instance, "fragment_shader_es2.frag", Loaded, &g_FShaderData); 496 g_quadVertices = BuildCube(); 497 return PP_TRUE; 498 } 499 500 /** 501 * Called when the NaCl module is destroyed. This will always be called, 502 * even if DidCreate returned failure. This routine should deallocate any data 503 * associated with the instance. 504 * @param[in] instance The identifier of the instance representing this NaCl 505 * module. 506 */ 507 static void Instance_DidDestroy(PP_Instance instance) { 508 delete[] g_TextureData; 509 delete[] g_VShaderData; 510 delete[] g_FShaderData; 511 delete[] g_quadVertices; 512 } 513 514 /** 515 * Called when the position, the size, or the clip rect of the element in the 516 * browser that corresponds to this NaCl module has changed. 517 * @param[in] instance The identifier of the instance representing this NaCl 518 * module. 519 * @param[in] position The location on the page of this NaCl module. This is 520 * relative to the top left corner of the viewport, which changes as the 521 * page is scrolled. 522 * @param[in] clip The visible region of the NaCl module. This is relative to 523 * the top left of the plugin's coordinate system (not the page). If the 524 * plugin is invisible, @a clip will be (0, 0, 0, 0). 525 */ 526 static void Instance_DidChangeView(PP_Instance instance, 527 PP_Resource view_resource) { 528 if (g_context == 0) { 529 InitGL(); 530 MainLoop(NULL, 0); 531 } 532 } 533 534 /** 535 * Notification that the given NaCl module has gained or lost focus. 536 * Having focus means that keyboard events will be sent to the NaCl module 537 * represented by @a instance. A NaCl module's default condition is that it 538 * will not have focus. 539 * 540 * Note: clicks on NaCl modules will give focus only if you handle the 541 * click event. You signal if you handled it by returning @a true from 542 * HandleInputEvent. Otherwise the browser will bubble the event and give 543 * focus to the element on the page that actually did end up consuming it. 544 * If you're not getting focus, check to make sure you're returning true from 545 * the mouse click in HandleInputEvent. 546 * @param[in] instance The identifier of the instance representing this NaCl 547 * module. 548 * @param[in] has_focus Indicates whether this NaCl module gained or lost 549 * event focus. 550 */ 551 static void Instance_DidChangeFocus(PP_Instance instance, PP_Bool has_focus) {} 552 553 /** 554 * Handler that gets called after a full-frame module is instantiated based on 555 * registered MIME types. This function is not called on NaCl modules. This 556 * function is essentially a place-holder for the required function pointer in 557 * the PPP_Instance structure. 558 * @param[in] instance The identifier of the instance representing this NaCl 559 * module. 560 * @param[in] url_loader A PP_Resource an open PPB_URLLoader instance. 561 * @return PP_FALSE. 562 */ 563 static PP_Bool Instance_HandleDocumentLoad(PP_Instance instance, 564 PP_Resource url_loader) { 565 /* NaCl modules do not need to handle the document load function. */ 566 return PP_FALSE; 567 } 568 569 /** 570 * Entry points for the module. 571 * Initialize needed interfaces: PPB_Core, PPB_Messaging and PPB_Var. 572 * @param[in] a_module_id module ID 573 * @param[in] get_browser pointer to PPB_GetInterface 574 * @return PP_OK on success, any other value on failure. 575 */ 576 PP_EXPORT int32_t PPP_InitializeModule(PP_Module a_module_id, 577 PPB_GetInterface get_browser) { 578 ppb_core_interface = (PPB_Core*)(get_browser(PPB_CORE_INTERFACE)); 579 ppb_instance_interface = (PPB_Instance*)get_browser(PPB_INSTANCE_INTERFACE); 580 ppb_messaging_interface = 581 (PPB_Messaging*)(get_browser(PPB_MESSAGING_INTERFACE)); 582 ppb_var_interface = (PPB_Var*)(get_browser(PPB_VAR_INTERFACE)); 583 ppb_urlloader_interface = 584 (PPB_URLLoader*)(get_browser(PPB_URLLOADER_INTERFACE)); 585 ppb_urlrequestinfo_interface = 586 (PPB_URLRequestInfo*)(get_browser(PPB_URLREQUESTINFO_INTERFACE)); 587 ppb_g3d_interface = (PPB_Graphics3D*)get_browser(PPB_GRAPHICS_3D_INTERFACE); 588 if (!glInitializePPAPI(get_browser)) 589 return PP_ERROR_FAILED; 590 return PP_OK; 591 } 592 593 /** 594 * Returns an interface pointer for the interface of the given name, or NULL 595 * if the interface is not supported. 596 * @param[in] interface_name name of the interface 597 * @return pointer to the interface 598 */ 599 PP_EXPORT const void* PPP_GetInterface(const char* interface_name) { 600 if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0) { 601 static PPP_Instance instance_interface = { 602 &Instance_DidCreate, 603 &Instance_DidDestroy, 604 &Instance_DidChangeView, 605 &Instance_DidChangeFocus, 606 &Instance_HandleDocumentLoad, 607 }; 608 return &instance_interface; 609 } 610 return NULL; 611 } 612 613 /** 614 * Called before the plugin module is unloaded. 615 */ 616 PP_EXPORT void PPP_ShutdownModule() {} 617