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