Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / native_client_sdk / src / examples / demo / earth / earth.cc
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);