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