Home | History | Annotate | Download | only in earth
      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 <assert.h>
      6 #include <math.h>
      7 #include <ppapi/c/ppb_input_event.h>
      8 #include <ppapi/cpp/input_event.h>
      9 #include <ppapi/cpp/var.h>
     10 #include <ppapi/cpp/var_array.h>
     11 #include <ppapi/cpp/var_array_buffer.h>
     12 #include <ppapi/cpp/var_dictionary.h>
     13 #include <pthread.h>
     14 #include <stdio.h>
     15 #include <stdlib.h>
     16 #include <string.h>
     17 #include <sys/time.h>
     18 #include <unistd.h>
     19 
     20 #include <algorithm>
     21 #include <string>
     22 
     23 #include "ppapi_simple/ps.h"
     24 #include "ppapi_simple/ps_context_2d.h"
     25 #include "ppapi_simple/ps_event.h"
     26 #include "ppapi_simple/ps_interface.h"
     27 #include "ppapi_simple/ps_main.h"
     28 #include "sdk_util/macros.h"
     29 #include "sdk_util/thread_pool.h"
     30 
     31 using namespace sdk_util;  // For sdk_util::ThreadPool
     32 
     33 // Global properties used to setup Earth demo.
     34 namespace {
     35 const float kHugeZ = 1.0e38f;
     36 const float kPI = M_PI;
     37 const float kTwoPI = kPI * 2.0f;
     38 const float kOneOverPI = 1.0f / kPI;
     39 const float kOneOver2PI = 1.0f / kTwoPI;
     40 const float kOneOver255 = 1.0f / 255.0f;
     41 const int kArcCosineTableSize = 4096;
     42 const int kFramesToBenchmark = 100;
     43 const float kZoomMin = 1.0f;
     44 const float kZoomMax = 50.0f;
     45 const float kWheelSpeed = 2.0f;
     46 const float kLightMin = 0.0f;
     47 const float kLightMax = 2.0f;
     48 const int kFrameTimeBufferSize = 512;
     49 
     50 // Timer helper for benchmarking.  Returns seconds elapsed since program start,
     51 // as a double.
     52 timeval start_tv;
     53 int start_tv_retv = gettimeofday(&start_tv, NULL);
     54 
     55 inline double getseconds() {
     56   const double usec_to_sec = 0.000001;
     57   timeval tv;
     58   if ((0 == start_tv_retv) && (0 == gettimeofday(&tv, NULL)))
     59     return (tv.tv_sec - start_tv.tv_sec) + tv.tv_usec * usec_to_sec;
     60   return 0.0;
     61 }
     62 
     63 // RGBA helper functions, used for extracting color from RGBA source image.
     64 inline float ExtractR(uint32_t c) {
     65   return static_cast<float>(c & 0xFF) * kOneOver255;
     66 }
     67 
     68 inline float ExtractG(uint32_t c) {
     69   return static_cast<float>((c & 0xFF00) >> 8) * kOneOver255;
     70 }
     71 
     72 inline float ExtractB(uint32_t c) {
     73   return static_cast<float>((c & 0xFF0000) >> 16) * kOneOver255;
     74 }
     75 
     76 // BGRA helper function, for constructing a pixel for a BGRA buffer.
     77 inline uint32_t MakeBGRA(uint32_t b, uint32_t g, uint32_t r, uint32_t a) {
     78   return (((a) << 24) | ((r) << 16) | ((g) << 8) | (b));
     79 }
     80 
     81 // simple container for earth texture
     82 struct Texture {
     83   int width, height;
     84   uint32_t* pixels;
     85   Texture(int w, int h) : width(w), height(h) {
     86     pixels = new uint32_t[w * h];
     87     memset(pixels, 0, sizeof(uint32_t) * w * h);
     88   }
     89   explicit Texture(int w, int h, uint32_t* p) : width(w), height(h) {
     90     pixels = new uint32_t[w * h];
     91     memcpy(pixels, p, sizeof(uint32_t) * w * h);
     92   }
     93   ~Texture() { delete[] pixels; }
     94 
     95   DISALLOW_COPY_AND_ASSIGN(Texture);
     96 };
     97 
     98 
     99 
    100 struct ArcCosine {
    101   // slightly larger table so we can interpolate beyond table size
    102   float table[kArcCosineTableSize + 2];
    103   float TableLerp(float x);
    104   ArcCosine();
    105 };
    106 
    107 ArcCosine::ArcCosine() {
    108   // build a slightly larger table to allow for numeric imprecision
    109   for (int i = 0; i < (kArcCosineTableSize + 2); ++i) {
    110     float f = static_cast<float>(i) / kArcCosineTableSize;
    111     f = f * 2.0f - 1.0f;
    112     table[i] = acos(f);
    113   }
    114 }
    115 
    116 // looks up acos(f) using a table and lerping between entries
    117 // (it is expected that input f is between -1 and 1)
    118 float ArcCosine::TableLerp(float f) {
    119   float x = (f + 1.0f) * 0.5f;
    120   x = x * kArcCosineTableSize;
    121   int ix = static_cast<int>(x);
    122   float fx = static_cast<float>(ix);
    123   float dx = x - fx;
    124   float af = table[ix];
    125   float af2 = table[ix + 1];
    126   return af + (af2 - af) * dx;
    127 }
    128 
    129 // Helper functions for quick but approximate sqrt.
    130 union Convert {
    131   float f;
    132   int i;
    133   Convert(int x) { i = x; }
    134   Convert(float x) { f = x; }
    135   int AsInt() { return i; }
    136   float AsFloat() { return f; }
    137 };
    138 
    139 inline const int AsInteger(const float f) {
    140   Convert u(f);
    141   return u.AsInt();
    142 }
    143 
    144 inline const float AsFloat(const int i) {
    145   Convert u(i);
    146   return u.AsFloat();
    147 }
    148 
    149 const long int kOneAsInteger = AsInteger(1.0f);
    150 const float kScaleUp = float(0x00800000);
    151 const float kScaleDown = 1.0f / kScaleUp;
    152 
    153 inline float inline_quick_sqrt(float x) {
    154   int i;
    155   i = (AsInteger(x) >> 1) + (kOneAsInteger >> 1);
    156   return AsFloat(i);
    157 }
    158 
    159 inline float inline_sqrt(float x) {
    160   float y;
    161   y = inline_quick_sqrt(x);
    162   y = (y * y + x) / (2.0f * y);
    163   y = (y * y + x) / (2.0f * y);
    164   return y;
    165 }
    166 
    167 // takes a -0..1+ color, clamps it to 0..1 and maps it to 0..255 integer
    168 inline uint32_t Clamp255(float x) {
    169   if (x < 0.0f) {
    170     x = 0.0f;
    171   } else if (x > 1.0f) {
    172     x = 1.0f;
    173   }
    174   return static_cast<uint32_t>(x * 255.0f);
    175 }
    176 }  // namespace
    177 
    178 
    179 // The main object that runs the Earth demo.
    180 class Planet {
    181  public:
    182   Planet();
    183   virtual ~Planet();
    184   // Runs a tick of the simulations, update 2D output.
    185   void Update();
    186   // Handle event from user, or message from JS.
    187   void HandleEvent(PSEvent* ps_event);
    188 
    189  private:
    190   // Methods prefixed with 'w' are run on worker threads.
    191   uint32_t* wGetAddr(int x, int y);
    192   void wRenderPixelSpan(int x0, int x1, int y);
    193   void wMakeRect(int r, int *x, int *y, int *w, int *h);
    194   void wRenderRect(int x0, int y0, int x1, int y1);
    195   void wRenderRegion(int region);
    196   static void wRenderRegionEntry(int region, void *thiz);
    197 
    198   // These methods are only called by the main thread.
    199   void CacheCalcs();
    200   void SetPlanetXYZR(float x, float y, float z, float r);
    201   void SetPlanetPole(float x, float y, float z);
    202   void SetPlanetEquator(float x, float y, float z);
    203   void SetPlanetSpin(float x, float y);
    204   void SetEyeXYZ(float x, float y, float z);
    205   void SetLightXYZ(float x, float y, float z);
    206   void SetAmbientRGB(float r, float g, float b);
    207   void SetDiffuseRGB(float r, float g, float b);
    208   void SetZoom(float zoom);
    209   void SetLight(float zoom);
    210   void SetTexture(const std::string& name, int width, int height,
    211       uint32_t* pixels);
    212 
    213   void Reset();
    214   void RequestTextures();
    215   void UpdateSim();
    216   void Render();
    217   void Draw();
    218   void StartBenchmark();
    219   void EndBenchmark();
    220   // Post a small key-value message to update JS.
    221   void PostUpdateMessage(const char* message_name, double value);
    222 
    223   // User Interface settings.  These settings are controlled via html
    224   // controls or via user input.
    225   float ui_light_;
    226   float ui_zoom_;
    227   float ui_spin_x_;
    228   float ui_spin_y_;
    229 
    230   // Various settings for position & orientation of planet.  Do not change
    231   // these variables, instead use SetPlanet*() functions.
    232   float planet_radius_;
    233   float planet_spin_x_;
    234   float planet_spin_y_;
    235   float planet_x_, planet_y_, planet_z_;
    236   float planet_pole_x_, planet_pole_y_, planet_pole_z_;
    237   float planet_equator_x_, planet_equator_y_, planet_equator_z_;
    238 
    239   // Observer's eye.  Do not change these variables, instead use SetEyeXYZ().
    240   float eye_x_, eye_y_, eye_z_;
    241 
    242   // Light position, ambient and diffuse settings.  Do not change these
    243   // variables, instead use SetLightXYZ(), SetAmbientRGB() and SetDiffuseRGB().
    244   float light_x_, light_y_, light_z_;
    245   float diffuse_r_, diffuse_g_, diffuse_b_;
    246   float ambient_r_, ambient_g_, ambient_b_;
    247 
    248   // Cached calculations.  Do not change these variables - they are updated by
    249   // CacheCalcs() function.
    250   float planet_xyz_;
    251   float planet_pole_x_equator_x_;
    252   float planet_pole_x_equator_y_;
    253   float planet_pole_x_equator_z_;
    254   float planet_radius2_;
    255   float planet_one_over_radius_;
    256   float eye_xyz_;
    257 
    258   // Source texture (earth map).
    259   Texture* base_tex_;
    260   Texture* night_tex_;
    261   int width_for_tex_;
    262   int height_for_tex_;
    263 
    264   // Quick ArcCos helper.
    265   ArcCosine acos_;
    266 
    267   // Misc.
    268   PSContext2D_t* ps_context_;
    269   int num_threads_;
    270   ThreadPool* workers_;
    271   bool benchmarking_;
    272   int benchmark_frame_counter_;
    273   double benchmark_start_time_;
    274   double benchmark_end_time_;
    275 };
    276 
    277 
    278 void Planet::RequestTextures() {
    279   // Request a set of images from JS.  After images are loaded by JS, a
    280   // message from JS -> NaCl will arrive containing the pixel data.  See
    281   // HandleMessage() method in this file.
    282   pp::VarDictionary message;
    283   message.Set("message", "request_textures");
    284   pp::VarArray names;
    285   names.Set(0, "earth.jpg");
    286   names.Set(1, "earthnight.jpg");
    287   message.Set("names", names);
    288   PSInterfaceMessaging()->PostMessage(PSGetInstanceId(), message.pp_var());
    289 }
    290 
    291 void Planet::Reset() {
    292   // Reset has to first fill in all variables with valid floats, so
    293   // CacheCalcs() doesn't potentially propagate NaNs when calling Set*()
    294   // functions further below.
    295   planet_radius_ = 1.0f;
    296   planet_spin_x_ = 0.0f;
    297   planet_spin_y_ = 0.0f;
    298   planet_x_ = 0.0f;
    299   planet_y_ = 0.0f;
    300   planet_z_ = 0.0f;
    301   planet_pole_x_ = 0.0f;
    302   planet_pole_y_ = 0.0f;
    303   planet_pole_z_ = 0.0f;
    304   planet_equator_x_ = 0.0f;
    305   planet_equator_y_ = 0.0f;
    306   planet_equator_z_ = 0.0f;
    307   eye_x_ = 0.0f;
    308   eye_y_ = 0.0f;
    309   eye_z_ = 0.0f;
    310   light_x_ = 0.0f;
    311   light_y_ = 0.0f;
    312   light_z_ = 0.0f;
    313   diffuse_r_ = 0.0f;
    314   diffuse_g_ = 0.0f;
    315   diffuse_b_ = 0.0f;
    316   ambient_r_ = 0.0f;
    317   ambient_g_ = 0.0f;
    318   ambient_b_ = 0.0f;
    319   planet_xyz_ = 0.0f;
    320   planet_pole_x_equator_x_ = 0.0f;
    321   planet_pole_x_equator_y_ = 0.0f;
    322   planet_pole_x_equator_z_ = 0.0f;
    323   planet_radius2_ = 0.0f;
    324   planet_one_over_radius_ = 0.0f;
    325   eye_xyz_ = 0.0f;
    326   ui_zoom_ = 14.0f;
    327   ui_light_ = 1.0f;
    328   ui_spin_x_ = 0.01f;
    329   ui_spin_y_ = 0.0f;
    330 
    331   // Set up reasonable default values.
    332   SetPlanetXYZR(0.0f, 0.0f, 48.0f, 4.0f);
    333   SetEyeXYZ(0.0f, 0.0f, -ui_zoom_);
    334   SetLightXYZ(-60.0f, -30.0f, 0.0f);
    335   SetAmbientRGB(0.05f, 0.05f, 0.05f);
    336   SetDiffuseRGB(0.8f, 0.8f, 0.8f);
    337   SetPlanetPole(0.0f, 1.0f, 0.0f);
    338   SetPlanetEquator(1.0f, 0.0f, 0.0f);
    339   SetPlanetSpin(kPI / 2.0f, kPI / 2.0f);
    340   SetZoom(ui_zoom_);
    341   SetLight(ui_light_);
    342 
    343   // Send UI values to JS to reset html sliders.
    344   PostUpdateMessage("set_zoom", ui_zoom_);
    345   PostUpdateMessage("set_light", ui_light_);
    346 }
    347 
    348 
    349 Planet::Planet() : base_tex_(NULL), night_tex_(NULL), num_threads_(0),
    350     benchmarking_(false), benchmark_frame_counter_(0) {
    351 
    352   Reset();
    353   RequestTextures();
    354   // By default, render from the dispatch thread.
    355   workers_ = new ThreadPool(num_threads_);
    356   PSEventSetFilter(PSE_ALL);
    357   ps_context_ = PSContext2DAllocate(PP_IMAGEDATAFORMAT_BGRA_PREMUL);
    358 }
    359 
    360 Planet::~Planet() {
    361   delete workers_;
    362   PSContext2DFree(ps_context_);
    363 }
    364 
    365 // Given a region r, derive a rectangle.
    366 // This rectangle shouldn't overlap with work being done by other workers.
    367 // If multithreading, this function is only called by the worker threads.
    368 void Planet::wMakeRect(int r, int *x, int *y, int *w, int *h) {
    369   *x = 0;
    370   *w = ps_context_->width;
    371   *y = r;
    372   *h = 1;
    373 }
    374 
    375 
    376 inline uint32_t* Planet::wGetAddr(int x, int y) {
    377   return ps_context_->data + x + y * ps_context_->stride / sizeof(uint32_t);
    378 }
    379 
    380 // This is the meat of the ray tracer.  Given a pixel span (x0, x1) on
    381 // scanline y, shoot rays into the scene and render what they hit.  Use
    382 // scanline coherence to do a few optimizations
    383 void Planet::wRenderPixelSpan(int x0, int x1, int y) {
    384   if (!base_tex_ || !night_tex_)
    385     return;
    386   const int kColorBlack = MakeBGRA(0, 0, 0, 0xFF);
    387   float width = ps_context_->width;
    388   float height = ps_context_->height;
    389   float min_dim = width < height ? width : height;
    390   float offset_x = width < height ? 0 : (width - min_dim) * 0.5f;
    391   float offset_y = width < height ? (height - min_dim) * 0.5f : 0;
    392   float y0 = eye_y_;
    393   float z0 = eye_z_;
    394   float y1 = (static_cast<float>(y - offset_y) / min_dim) * 2.0f - 1.0f;
    395   float z1 = 0.0f;
    396   float dy = (y1 - y0);
    397   float dz = (z1 - z0);
    398   float dy_dy_dz_dz = dy * dy + dz * dz;
    399   float two_dy_y0_y_two_dz_z0_z = 2.0f * dy * (y0 - planet_y_) +
    400                                   2.0f * dz * (z0 - planet_z_);
    401   float planet_xyz_eye_xyz = planet_xyz_ + eye_xyz_;
    402   float y_y0_z_z0 = planet_y_ * y0 + planet_z_ * z0;
    403   float oowidth = 1.0f / min_dim;
    404   uint32_t* pixels = this->wGetAddr(x0, y);
    405   for (int x = x0; x <= x1; ++x) {
    406     // scan normalized screen -1..1
    407     float x1 = (static_cast<float>(x - offset_x) * oowidth) * 2.0f - 1.0f;
    408     // eye
    409     float x0 = eye_x_;
    410     // delta from screen to eye
    411     float dx = (x1 - x0);
    412     // build a, b, c
    413     float a = dx * dx + dy_dy_dz_dz;
    414     float b = 2.0f * dx * (x0 - planet_x_) + two_dy_y0_y_two_dz_z0_z;
    415     float c = planet_xyz_eye_xyz +
    416               -2.0f * (planet_x_ * x0 + y_y0_z_z0) - (planet_radius2_);
    417     // calculate discriminant
    418     float disc = b * b - 4.0f * a * c;
    419 
    420     // Did ray hit the sphere?
    421     if (disc < 0.0f) {
    422       *pixels = kColorBlack;
    423       ++pixels;
    424       continue;
    425     }
    426 
    427     // calc parametric t value
    428     float t = (-b - inline_sqrt(disc)) / (2.0f * a);
    429     float px = x0 + t * dx;
    430     float py = y0 + t * dy;
    431     float pz = z0 + t * dz;
    432     float nx = (px - planet_x_) * planet_one_over_radius_;
    433     float ny = (py - planet_y_) * planet_one_over_radius_;
    434     float nz = (pz - planet_z_) * planet_one_over_radius_;
    435 
    436     // Misc raytrace calculations.
    437     float Lx = (light_x_ - px);
    438     float Ly = (light_y_ - py);
    439     float Lz = (light_z_ - pz);
    440     float Lq = 1.0f / inline_quick_sqrt(Lx * Lx + Ly * Ly + Lz * Lz);
    441     Lx *= Lq;
    442     Ly *= Lq;
    443     Lz *= Lq;
    444     float d = (Lx * nx + Ly * ny + Lz * nz);
    445     float pr = (diffuse_r_ * d) + ambient_r_;
    446     float pg = (diffuse_g_ * d) + ambient_g_;
    447     float pb = (diffuse_b_ * d) + ambient_b_;
    448     float ds = -(nx * planet_pole_x_ +
    449                  ny * planet_pole_y_ +
    450                  nz * planet_pole_z_);
    451     float ang = acos_.TableLerp(ds);
    452     float v = ang * kOneOverPI;
    453     float dp = planet_equator_x_ * nx +
    454                planet_equator_y_ * ny +
    455                planet_equator_z_ * nz;
    456     float w = dp / sin(ang);
    457     if (w > 1.0f) w = 1.0f;
    458     if (w < -1.0f) w = -1.0f;
    459     float th = acos_.TableLerp(w) * kOneOver2PI;
    460     float dps = planet_pole_x_equator_x_ * nx +
    461                 planet_pole_x_equator_y_ * ny +
    462                 planet_pole_x_equator_z_ * nz;
    463     float u;
    464     if (dps < 0.0f)
    465       u = th;
    466     else
    467       u = 1.0f - th;
    468 
    469     // Look up daylight texel.
    470     int tx = static_cast<int>(u * base_tex_->width);
    471     int ty = static_cast<int>(v * base_tex_->height);
    472     int offset = tx + ty * base_tex_->width;
    473     uint32_t base_texel = base_tex_->pixels[offset];
    474     float tr = ExtractR(base_texel);
    475     float tg = ExtractG(base_texel);
    476     float tb = ExtractB(base_texel);
    477 
    478     float ipr = 1.0f - pr;
    479     if (ipr < 0.0f) ipr = 0.0f;
    480     float ipg = 1.0f - pg;
    481     if (ipg < 0.0f) ipg = 0.0f;
    482     float ipb = 1.0f - pb;
    483     if (ipb < 0.0f) ipb = 0.0f;
    484 
    485     // Look up night texel.
    486     int nix = static_cast<int>(u * night_tex_->width);
    487     int niy = static_cast<int>(v * night_tex_->height);
    488     int noffset = nix + niy * night_tex_->width;
    489     uint32_t night_texel = night_tex_->pixels[noffset];
    490     float nr = ExtractR(night_texel);
    491     float ng = ExtractG(night_texel);
    492     float nb = ExtractB(night_texel);
    493 
    494     // Final color value is lerp between day and night texels.
    495     unsigned int ir = Clamp255(pr * tr + nr * ipr);
    496     unsigned int ig = Clamp255(pg * tg + ng * ipg);
    497     unsigned int ib = Clamp255(pb * tb + nb * ipb);
    498 
    499     unsigned int color = MakeBGRA(ib, ig, ir, 0xFF);
    500 
    501     *pixels = color;
    502     ++pixels;
    503   }
    504 }
    505 
    506 // Renders a rectangular area of the screen, scan line at a time
    507 void Planet::wRenderRect(int x, int y, int w, int h) {
    508   for (int j = y; j < (y + h); ++j) {
    509     this->wRenderPixelSpan(x, x + w - 1, j);
    510   }
    511 }
    512 
    513 // If multithreading, this function is only called by the worker threads.
    514 void Planet::wRenderRegion(int region) {
    515   // convert region # into x0, y0, x1, y1 rectangle
    516   int x, y, w, h;
    517   wMakeRect(region, &x, &y, &w, &h);
    518   // render this rectangle
    519   wRenderRect(x, y, w, h);
    520 }
    521 
    522 // Entry point for worker thread.  Can't pass a member function around, so we
    523 // have to do this little round-about.
    524 void Planet::wRenderRegionEntry(int region, void* thiz) {
    525   static_cast<Planet*>(thiz)->wRenderRegion(region);
    526 }
    527 
    528 // Renders the planet, dispatching the work to multiple threads.
    529 void Planet::Render() {
    530   workers_->Dispatch(ps_context_->height, wRenderRegionEntry, this);
    531 }
    532 
    533 // Pre-calculations to make inner loops faster.
    534 void Planet::CacheCalcs() {
    535   planet_xyz_ = planet_x_ * planet_x_ +
    536                 planet_y_ * planet_y_ +
    537                 planet_z_ * planet_z_;
    538   planet_radius2_ = planet_radius_ * planet_radius_;
    539   planet_one_over_radius_ = 1.0f / planet_radius_;
    540   eye_xyz_ = eye_x_ * eye_x_ + eye_y_ * eye_y_ + eye_z_ * eye_z_;
    541   // spin vector from center->equator
    542   planet_equator_x_ = cos(planet_spin_x_);
    543   planet_equator_y_ = 0.0f;
    544   planet_equator_z_ = sin(planet_spin_x_);
    545 
    546   // cache cross product of pole & equator
    547   planet_pole_x_equator_x_ = planet_pole_y_ * planet_equator_z_ -
    548                              planet_pole_z_ * planet_equator_y_;
    549   planet_pole_x_equator_y_ = planet_pole_z_ * planet_equator_x_ -
    550                              planet_pole_x_ * planet_equator_z_;
    551   planet_pole_x_equator_z_ = planet_pole_x_ * planet_equator_y_ -
    552                              planet_pole_y_ * planet_equator_x_;
    553 }
    554 
    555 void Planet::SetPlanetXYZR(float x, float y, float z, float r) {
    556   planet_x_ = x;
    557   planet_y_ = y;
    558   planet_z_ = z;
    559   planet_radius_ = r;
    560   CacheCalcs();
    561 }
    562 
    563 void Planet::SetEyeXYZ(float x, float y, float z) {
    564   eye_x_ = x;
    565   eye_y_ = y;
    566   eye_z_ = z;
    567   CacheCalcs();
    568 }
    569 
    570 void Planet::SetLightXYZ(float x, float y, float z) {
    571   light_x_ = x;
    572   light_y_ = y;
    573   light_z_ = z;
    574   CacheCalcs();
    575 }
    576 
    577 void Planet::SetAmbientRGB(float r, float g, float b) {
    578   ambient_r_ = r;
    579   ambient_g_ = g;
    580   ambient_b_ = b;
    581   CacheCalcs();
    582 }
    583 
    584 void Planet::SetDiffuseRGB(float r, float g, float b) {
    585   diffuse_r_ = r;
    586   diffuse_g_ = g;
    587   diffuse_b_ = b;
    588   CacheCalcs();
    589 }
    590 
    591 void Planet::SetPlanetPole(float x, float y, float z) {
    592   planet_pole_x_ = x;
    593   planet_pole_y_ = y;
    594   planet_pole_z_ = z;
    595   CacheCalcs();
    596 }
    597 
    598 void Planet::SetPlanetEquator(float x, float y, float z) {
    599   // This is really over-ridden by spin at the momenent.
    600   planet_equator_x_ = x;
    601   planet_equator_y_ = y;
    602   planet_equator_z_ = z;
    603   CacheCalcs();
    604 }
    605 
    606 void Planet::SetPlanetSpin(float x, float y) {
    607   planet_spin_x_ = x;
    608   planet_spin_y_ = y;
    609   CacheCalcs();
    610 }
    611 
    612 // Run a simple sim to spin the planet.  Update loop is run once per frame.
    613 // Called from the main thread only and only when the worker threads are idle.
    614 void Planet::UpdateSim() {
    615   float x = planet_spin_x_ + ui_spin_x_;
    616   float y = planet_spin_y_ + ui_spin_y_;
    617   // keep in nice range
    618   if (x > (kPI * 2.0f))
    619     x = x - kPI * 2.0f;
    620   else if (x < (-kPI * 2.0f))
    621     x = x + kPI * 2.0f;
    622   if (y > (kPI * 2.0f))
    623     y = y - kPI * 2.0f;
    624   else if (y < (-kPI * 2.0f))
    625     y = y + kPI * 2.0f;
    626   SetPlanetSpin(x, y);
    627 }
    628 
    629 void Planet::StartBenchmark() {
    630   // For more consistent benchmark numbers, reset to default state.
    631   Reset();
    632   printf("Benchmark started...\n");
    633   benchmark_frame_counter_ = kFramesToBenchmark;
    634   benchmarking_ = true;
    635   benchmark_start_time_ = getseconds();
    636 }
    637 
    638 void Planet::EndBenchmark() {
    639   benchmark_end_time_ = getseconds();
    640   printf("Benchmark ended... time: %2.5f\n",
    641       benchmark_end_time_ - benchmark_start_time_);
    642   benchmarking_ = false;
    643   benchmark_frame_counter_ = 0;
    644   double total_time = benchmark_end_time_ - benchmark_start_time_;
    645   // Send benchmark result to JS.
    646   PostUpdateMessage("benchmark_result", total_time);
    647 }
    648 
    649 void Planet::SetZoom(float zoom) {
    650   ui_zoom_ = std::min(kZoomMax, std::max(kZoomMin, zoom));
    651   SetEyeXYZ(0.0f, 0.0f, -ui_zoom_);
    652 }
    653 
    654 void Planet::SetLight(float light) {
    655   ui_light_ = std::min(kLightMax, std::max(kLightMin, light));
    656   SetDiffuseRGB(0.8f * ui_light_, 0.8f * ui_light_, 0.8f * ui_light_);
    657   SetAmbientRGB(0.4f * ui_light_, 0.4f * ui_light_, 0.4f * ui_light_);
    658 }
    659 
    660 void Planet::SetTexture(const std::string& name, int width, int height,
    661                         uint32_t* pixels) {
    662   if (pixels) {
    663     if (name == "earth.jpg") {
    664       delete base_tex_;
    665       base_tex_ = new Texture(width, height, pixels);
    666     } else if (name == "earthnight.jpg") {
    667       delete night_tex_;
    668       night_tex_ = new Texture(width, height, pixels);
    669     }
    670   }
    671 }
    672 
    673 // Handle input events from the user and messages from JS.
    674 void Planet::HandleEvent(PSEvent* ps_event) {
    675   // Give the 2D context a chance to process the event.
    676   if (0 != PSContext2DHandleEvent(ps_context_, ps_event))
    677     return;
    678   if (ps_event->type == PSE_INSTANCE_HANDLEINPUT) {
    679     // Convert Pepper Simple event to a PPAPI C++ event
    680     pp::InputEvent event(ps_event->as_resource);
    681     switch (event.GetType()) {
    682       case PP_INPUTEVENT_TYPE_KEYDOWN: {
    683         pp::KeyboardInputEvent key(event);
    684         uint32_t key_code = key.GetKeyCode();
    685         if (key_code == 84)  // 't' key
    686           if (!benchmarking_)
    687             StartBenchmark();
    688         break;
    689       }
    690       case PP_INPUTEVENT_TYPE_MOUSEMOVE:
    691       case PP_INPUTEVENT_TYPE_MOUSEDOWN: {
    692         pp::MouseInputEvent mouse = pp::MouseInputEvent(event);
    693         if (mouse.GetModifiers() & PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN) {
    694           PP_Point delta = mouse.GetMovement();
    695           float delta_x = static_cast<float>(delta.x);
    696           float delta_y = static_cast<float>(delta.y);
    697           float spin_x = std::min(4.0f, std::max(-4.0f, delta_x * 0.5f));
    698           float spin_y = std::min(4.0f, std::max(-4.0f, delta_y * 0.5f));
    699           ui_spin_x_ = spin_x / 100.0f;
    700           ui_spin_y_ = spin_y / 100.0f;
    701         }
    702         break;
    703       }
    704       case PP_INPUTEVENT_TYPE_WHEEL: {
    705         pp::WheelInputEvent wheel = pp::WheelInputEvent(event);
    706         PP_FloatPoint ticks = wheel.GetTicks();
    707         SetZoom(ui_zoom_ + (ticks.x + ticks.y) * kWheelSpeed);
    708         // Update html slider by sending update message to JS.
    709         PostUpdateMessage("set_zoom", ui_zoom_);
    710         break;
    711       }
    712       default:
    713         break;
    714     }
    715   } else if (ps_event->type == PSE_INSTANCE_HANDLEMESSAGE) {
    716     // Convert Pepper Simple message to PPAPI C++ vars
    717     pp::Var var(ps_event->as_var);
    718     if (var.is_dictionary()) {
    719       pp::VarDictionary dictionary(var);
    720       std::string message = dictionary.Get("message").AsString();
    721       if (message == "run benchmark" && !benchmarking_) {
    722         StartBenchmark();
    723       } else if (message == "set_light") {
    724         SetLight(static_cast<float>(dictionary.Get("value").AsDouble()));
    725       } else if (message == "set_zoom") {
    726         SetZoom(static_cast<float>(dictionary.Get("value").AsDouble()));
    727       } else if (message == "set_threads") {
    728         int threads = dictionary.Get("value").AsInt();
    729         delete workers_;
    730         workers_ = new ThreadPool(threads);
    731       } else if (message == "texture") {
    732         std::string name = dictionary.Get("name").AsString();
    733         int width = dictionary.Get("width").AsInt();
    734         int height = dictionary.Get("height").AsInt();
    735         pp::VarArrayBuffer array_buffer(dictionary.Get("data"));
    736         if (!name.empty() && !array_buffer.is_null()) {
    737           if (width > 0 && height > 0) {
    738             uint32_t* pixels = static_cast<uint32_t*>(array_buffer.Map());
    739             SetTexture(name, width, height, pixels);
    740             array_buffer.Unmap();
    741           }
    742         }
    743       }
    744     } else {
    745       printf("Handle message unknown type: %s\n", var.DebugString().c_str());
    746     }
    747   }
    748 }
    749 
    750 // PostUpdateMessage() helper function for sending small messages to JS.
    751 void Planet::PostUpdateMessage(const char* message_name, double value) {
    752   pp::VarDictionary message;
    753   message.Set("message", message_name);
    754   message.Set("value", value);
    755   PSInterfaceMessaging()->PostMessage(PSGetInstanceId(), message.pp_var());
    756 }
    757 
    758 void Planet::Update() {
    759   // When benchmarking is running, don't update display via
    760   // PSContext2DSwapBuffer() - vsync is enabled by default, and will throttle
    761   // the benchmark results.
    762   PSContext2DGetBuffer(ps_context_);
    763   if (NULL == ps_context_->data)
    764     return;
    765 
    766   do {
    767     UpdateSim();
    768     Render();
    769     if (!benchmarking_) break;
    770     --benchmark_frame_counter_;
    771   } while (benchmark_frame_counter_ > 0);
    772   if (benchmarking_)
    773     EndBenchmark();
    774 
    775   PSContext2DSwapBuffer(ps_context_);
    776 }
    777 
    778 
    779 // Starting point for the module.  We do not use main since it would
    780 // collide with main in libppapi_cpp.
    781 int example_main(int argc, char* argv[]) {
    782   Planet earth;
    783   while (true) {
    784     PSEvent* ps_event;
    785     // Consume all available events
    786     while ((ps_event = PSEventTryAcquire()) != NULL) {
    787       earth.HandleEvent(ps_event);
    788       PSEventRelease(ps_event);
    789     }
    790     // Do simulation, render and present.
    791     earth.Update();
    792   }
    793 
    794   return 0;
    795 }
    796 
    797 // Register the function to call once the Instance Object is initialized.
    798 // see: pappi_simple/ps_main.h
    799 PPAPI_SIMPLE_REGISTER_MAIN(example_main);
    800