Explicitly call deprecated AX name calculation API.
[chromium-blink-merge.git] / native_client_sdk / src / examples / demo / earth / earth.cc
blob1f750079aa0e3fc774c3357db3d84c5746e118bb
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.
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>
20 #include <algorithm>
21 #include <string>
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"
31 using namespace sdk_util; // For sdk_util::ThreadPool
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;
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);
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;
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;
66 inline float ExtractG(uint32_t c) {
67 return static_cast<float>((c & 0xFF00) >> 8) * kOneOver255;
70 inline float ExtractB(uint32_t c) {
71 return static_cast<float>((c & 0xFF0000) >> 16) * kOneOver255;
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));
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);
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);
91 ~Texture() { delete[] pixels; }
93 DISALLOW_COPY_AND_ASSIGN(Texture);
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();
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);
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;
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; }
137 inline const int AsInteger(const float f) {
138 Convert u(f);
139 return u.AsInt();
142 inline const float AsFloat(const int i) {
143 Convert u(i);
144 return u.AsFloat();
147 const long int kOneAsInteger = AsInteger(1.0f);
149 inline float inline_quick_sqrt(float x) {
150 int i;
151 i = (AsInteger(x) >> 1) + (kOneAsInteger >> 1);
152 return AsFloat(i);
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;
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;
170 return static_cast<uint32_t>(x * 255.0f);
172 } // namespace
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);
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);
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);
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);
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_;
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_;
237 // Observer's eye. Do not change these variables, instead use SetEyeXYZ().
238 float eye_x_, eye_y_, eye_z_;
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_;
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_;
256 // Source texture (earth map).
257 Texture* base_tex_;
258 Texture* night_tex_;
259 int width_for_tex_;
260 int height_for_tex_;
262 // Quick ArcCos helper.
263 ArcCosine acos_;
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_;
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());
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);
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_);
342 // Send UI values to JS to reset html sliders.
343 PostUpdateMessage("set_zoom", ui_zoom_);
344 PostUpdateMessage("set_light", ui_light_);
348 Planet::Planet() : base_tex_(NULL), night_tex_(NULL), num_threads_(0),
349 benchmarking_(false), benchmark_frame_counter_(0) {
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);
359 Planet::~Planet() {
360 delete workers_;
361 PSContext2DFree(ps_context_);
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;
375 inline uint32_t* Planet::wGetAddr(int x, int y) {
376 return ps_context_->data + x + y * ps_context_->stride / sizeof(uint32_t);
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;
419 // Did ray hit the sphere?
420 if (disc < 0.0f) {
421 *pixels = kColorBlack;
422 ++pixels;
423 continue;
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_;
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;
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);
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;
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);
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);
498 unsigned int color = MakeBGRA(ib, ig, ir, 0xFF);
500 *pixels = color;
501 ++pixels;
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);
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);
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);
527 // Renders the planet, dispatching the work to multiple threads.
528 void Planet::Render() {
529 workers_->Dispatch(ps_context_->height, wRenderRegionEntry, this);
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_);
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_;
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();
562 void Planet::SetEyeXYZ(float x, float y, float z) {
563 eye_x_ = x;
564 eye_y_ = y;
565 eye_z_ = z;
566 CacheCalcs();
569 void Planet::SetLightXYZ(float x, float y, float z) {
570 light_x_ = x;
571 light_y_ = y;
572 light_z_ = z;
573 CacheCalcs();
576 void Planet::SetAmbientRGB(float r, float g, float b) {
577 ambient_r_ = r;
578 ambient_g_ = g;
579 ambient_b_ = b;
580 CacheCalcs();
583 void Planet::SetDiffuseRGB(float r, float g, float b) {
584 diffuse_r_ = r;
585 diffuse_g_ = g;
586 diffuse_b_ = b;
587 CacheCalcs();
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();
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();
605 void Planet::SetPlanetSpin(float x, float y) {
606 planet_spin_x_ = x;
607 planet_spin_y_ = y;
608 CacheCalcs();
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);
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();
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);
648 void Planet::SetZoom(float zoom) {
649 ui_zoom_ = std::min(kZoomMax, std::max(kZoomMin, zoom));
650 SetEyeXYZ(0.0f, 0.0f, -ui_zoom_);
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_);
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);
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;
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;
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_);
708 break;
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;
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_);
733 break;
735 default:
736 break;
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();
767 } else {
768 printf("Handle message unknown type: %s\n", var.DebugString().c_str());
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());
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;
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();
798 PSContext2DSwapBuffer(ps_context_);
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);
813 // Do simulation, render and present.
814 earth.Update();
817 return 0;
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);