Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / native_client_sdk / src / examples / demo / earth_simd / earth.cc
blob2cf29e331d30d9d34b03342d5e18720180a682d3
1 // Copyright 2014 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 #define INLINE inline __attribute__((always_inline))
35 // 128 bit SIMD vector types
36 typedef uint8_t u8x16_t __attribute__ ((vector_size (16)));
37 typedef int32_t i32x4_t __attribute__ ((vector_size (16)));
38 typedef uint32_t u32x4_t __attribute__ ((vector_size (16)));
39 typedef float f32x4_t __attribute__ ((vector_size (16)));
41 // Global properties used to setup Earth demo.
42 namespace {
43 const float kPI = M_PI;
44 const float kTwoPI = kPI * 2.0f;
45 const float kOneOverPI = 1.0f / kPI;
46 const float kOneOver2PI = 1.0f / kTwoPI;
47 const int kArcCosineTableSize = 4096;
48 const int kFramesToBenchmark = 100;
49 const float kZoomMin = 1.0f;
50 const float kZoomMax = 50.0f;
51 const float kWheelSpeed = 2.0f;
52 const float kLightMin = 0.0f;
53 const float kLightMax = 2.0f;
55 // Timer helper for benchmarking. Returns seconds elapsed since program start,
56 // as a double.
57 timeval start_tv;
58 int start_tv_retv = gettimeofday(&start_tv, NULL);
60 inline double getseconds() {
61 const double usec_to_sec = 0.000001;
62 timeval tv;
63 if ((0 == start_tv_retv) && (0 == gettimeofday(&tv, NULL)))
64 return (tv.tv_sec - start_tv.tv_sec) + tv.tv_usec * usec_to_sec;
65 return 0.0;
68 // SIMD Vector helper functions.
70 // Note that a compare between two vectors will return a signed integer vector
71 // with the same number of elements, where each element will be all bits set
72 // for true (-1), and all bits clear for false (0) This integer vector can be
73 // useful as a mask.
75 // Also note that c-style casts do not mutate the bits of a vector - only the
76 // type. Boolean operators can't operate on float vectors, but it is possible
77 // to cast them temporarily to integer vector, perform the mask, and cast
78 // them back to float.
80 // To convert a float vector to an integer vector using trunction, or to
81 // convert an integer vector to a float vector, use __builtin_convertvector().
83 INLINE f32x4_t min(f32x4_t a, f32x4_t b) {
84 i32x4_t m = a < b;
85 return (f32x4_t)(((i32x4_t)a & m) | ((i32x4_t)b & ~m));
88 INLINE f32x4_t max(f32x4_t a, f32x4_t b) {
89 i32x4_t m = a > b;
90 return (f32x4_t)(((i32x4_t)a & m) | ((i32x4_t)b & ~m));
93 INLINE float dot3(f32x4_t a, f32x4_t b) {
94 f32x4_t c = a * b;
95 return c[0] + c[1] + c[2];
98 INLINE f32x4_t broadcast(float x) {
99 f32x4_t r = {x, x, x, x};
100 return r;
103 // SIMD RGBA helper functions, used for extracting color from RGBA source image.
104 INLINE f32x4_t ExtractRGBA(uint32_t c) {
105 const f32x4_t kOneOver255 = broadcast(1.0f / 255.0f);
106 const i32x4_t kZero = {0, 0, 0, 0};
107 i32x4_t v = {c, c, c, c};
108 // zero extend packed color into 32x4 integer vector
109 v = (i32x4_t)__builtin_shufflevector((u8x16_t)v, (u8x16_t)kZero,
110 0, 16, 16, 16, 1, 16, 16, 16, 2, 16, 16, 16, 3, 16, 16, 16);
111 // convert color values to float, range 0..1
112 f32x4_t f = __builtin_convertvector(v, f32x4_t) * kOneOver255;
113 return f;
116 // SIMD BGRA helper function, for constructing a pixel for a BGRA buffer.
117 INLINE uint32_t PackBGRA(f32x4_t f) {
118 const f32x4_t kZero = broadcast(0.0f);
119 const f32x4_t kHalf = broadcast(0.5f);
120 const f32x4_t k255 = broadcast(255.0f);
121 f = max(f, kZero);
122 // Add 0.5 to perform rounding instead of truncation.
123 f = f * k255 + kHalf;
124 f = min(f, k255);
125 i32x4_t i = __builtin_convertvector(f, i32x4_t);
126 u32x4_t p = (u32x4_t)__builtin_shufflevector((u8x16_t)i, (u8x16_t)i,
127 8, 4, 0, 12, 8, 4, 0, 12, 8, 4, 0, 12, 8, 4, 0, 12);
128 return p[0];
131 // BGRA helper function, for constructing a pixel for a BGRA buffer.
132 INLINE uint32_t MakeBGRA(uint32_t b, uint32_t g, uint32_t r, uint32_t a) {
133 return (((a) << 24) | ((r) << 16) | ((g) << 8) | (b));
136 // simple container for earth texture
137 struct Texture {
138 int width, height;
139 uint32_t* pixels;
140 Texture(int w, int h) : width(w), height(h) {
141 pixels = new uint32_t[w * h];
142 memset(pixels, 0, sizeof(uint32_t) * w * h);
144 explicit Texture(int w, int h, uint32_t* p) : width(w), height(h) {
145 pixels = new uint32_t[w * h];
146 memcpy(pixels, p, sizeof(uint32_t) * w * h);
148 ~Texture() { delete[] pixels; }
150 DISALLOW_COPY_AND_ASSIGN(Texture);
155 struct ArcCosine {
156 // slightly larger table so we can interpolate beyond table size
157 float table[kArcCosineTableSize + 2];
158 float TableLerp(float x);
159 ArcCosine();
162 ArcCosine::ArcCosine() {
163 // build a slightly larger table to allow for numeric imprecision
164 for (int i = 0; i < (kArcCosineTableSize + 2); ++i) {
165 float f = static_cast<float>(i) / kArcCosineTableSize;
166 f = f * 2.0f - 1.0f;
167 table[i] = acos(f);
171 // looks up acos(f) using a table and lerping between entries
172 // (it is expected that input f is between -1 and 1)
173 INLINE float ArcCosine::TableLerp(float f) {
174 float x = (f + 1.0f) * 0.5f;
175 x = x * kArcCosineTableSize;
176 int ix = static_cast<int>(x);
177 float fx = static_cast<float>(ix);
178 float dx = x - fx;
179 float af = table[ix];
180 float af2 = table[ix + 1];
181 return af + (af2 - af) * dx;
184 // Helper functions for quick but approximate sqrt.
185 union Convert {
186 float f;
187 int i;
188 Convert(int x) { i = x; }
189 Convert(float x) { f = x; }
190 int AsInt() { return i; }
191 float AsFloat() { return f; }
194 INLINE const int AsInteger(const float f) {
195 Convert u(f);
196 return u.AsInt();
199 INLINE const float AsFloat(const int i) {
200 Convert u(i);
201 return u.AsFloat();
204 const long int kOneAsInteger = AsInteger(1.0f);
206 INLINE float inline_quick_sqrt(float x) {
207 int i;
208 i = (AsInteger(x) >> 1) + (kOneAsInteger >> 1);
209 return AsFloat(i);
212 INLINE float inline_sqrt(float x) {
213 float y;
214 y = inline_quick_sqrt(x);
215 y = (y * y + x) / (2.0f * y);
216 y = (y * y + x) / (2.0f * y);
217 return y;
220 } // namespace
223 // The main object that runs the Earth demo.
224 class Planet {
225 public:
226 Planet();
227 virtual ~Planet();
228 // Runs a tick of the simulations, update 2D output.
229 void Update();
230 // Handle event from user, or message from JS.
231 void HandleEvent(PSEvent* ps_event);
233 private:
234 // Methods prefixed with 'w' are run on worker threads.
235 uint32_t* wGetAddr(int x, int y);
236 void wRenderPixelSpan(int x0, int x1, int y);
237 void wMakeRect(int r, int *x, int *y, int *w, int *h);
238 void wRenderRect(int x0, int y0, int x1, int y1);
239 void wRenderRegion(int region);
240 static void wRenderRegionEntry(int region, void *thiz);
242 // These methods are only called by the main thread.
243 void CacheCalcs();
244 void SetPlanetXYZR(float x, float y, float z, float r);
245 void SetPlanetPole(float x, float y, float z);
246 void SetPlanetEquator(float x, float y, float z);
247 void SetPlanetSpin(float x, float y);
248 void SetEyeXYZ(float x, float y, float z);
249 void SetLightXYZ(float x, float y, float z);
250 void SetAmbientRGB(float r, float g, float b);
251 void SetDiffuseRGB(float r, float g, float b);
252 void SetZoom(float zoom);
253 void SetLight(float zoom);
254 void SetTexture(const std::string& name, int width, int height,
255 uint32_t* pixels);
256 void SpinPlanet(pp::Point new_point, pp::Point last_point);
258 void Reset();
259 void RequestTextures();
260 void UpdateSim();
261 void Render();
262 void Draw();
263 void StartBenchmark();
264 void EndBenchmark();
265 // Post a small key-value message to update JS.
266 void PostUpdateMessage(const char* message_name, double value);
268 // User Interface settings. These settings are controlled via html
269 // controls or via user input.
270 float ui_light_;
271 float ui_zoom_;
272 float ui_spin_x_;
273 float ui_spin_y_;
274 pp::Point ui_last_point_;
276 // Various settings for position & orientation of planet. Do not change
277 // these variables, instead use SetPlanet*() functions.
278 float planet_radius_;
279 float planet_spin_x_;
280 float planet_spin_y_;
281 float planet_x_, planet_y_, planet_z_;
282 float planet_pole_x_, planet_pole_y_, planet_pole_z_;
283 float planet_equator_x_, planet_equator_y_, planet_equator_z_;
285 // Observer's eye. Do not change these variables, instead use SetEyeXYZ().
286 float eye_x_, eye_y_, eye_z_;
288 // Light position, ambient and diffuse settings. Do not change these
289 // variables, instead use SetLightXYZ(), SetAmbientRGB() and SetDiffuseRGB().
290 float light_x_, light_y_, light_z_;
291 float diffuse_r_, diffuse_g_, diffuse_b_;
292 float ambient_r_, ambient_g_, ambient_b_;
294 // Cached calculations. Do not change these variables - they are updated by
295 // CacheCalcs() function.
296 float planet_xyz_;
297 float planet_pole_x_equator_x_;
298 float planet_pole_x_equator_y_;
299 float planet_pole_x_equator_z_;
300 float planet_radius2_;
301 float planet_one_over_radius_;
302 float eye_xyz_;
304 // Source texture (earth map).
305 Texture* base_tex_;
306 Texture* night_tex_;
307 int width_for_tex_;
308 int height_for_tex_;
310 // Quick ArcCos helper.
311 ArcCosine acos_;
313 // Misc.
314 PSContext2D_t* ps_context_;
315 int num_threads_;
316 ThreadPool* workers_;
317 bool benchmarking_;
318 int benchmark_frame_counter_;
319 double benchmark_start_time_;
320 double benchmark_end_time_;
324 void Planet::RequestTextures() {
325 // Request a set of images from JS. After images are loaded by JS, a
326 // message from JS -> NaCl will arrive containing the pixel data. See
327 // HandleMessage() method in this file.
328 pp::VarDictionary message;
329 message.Set("message", "request_textures");
330 pp::VarArray names;
331 names.Set(0, "earth.jpg");
332 names.Set(1, "earthnight.jpg");
333 message.Set("names", names);
334 PSInterfaceMessaging()->PostMessage(PSGetInstanceId(), message.pp_var());
337 void Planet::Reset() {
338 // Reset has to first fill in all variables with valid floats, so
339 // CacheCalcs() doesn't potentially propagate NaNs when calling Set*()
340 // functions further below.
341 planet_radius_ = 1.0f;
342 planet_spin_x_ = 0.0f;
343 planet_spin_y_ = 0.0f;
344 planet_x_ = 0.0f;
345 planet_y_ = 0.0f;
346 planet_z_ = 0.0f;
347 planet_pole_x_ = 0.0f;
348 planet_pole_y_ = 0.0f;
349 planet_pole_z_ = 0.0f;
350 planet_equator_x_ = 0.0f;
351 planet_equator_y_ = 0.0f;
352 planet_equator_z_ = 0.0f;
353 eye_x_ = 0.0f;
354 eye_y_ = 0.0f;
355 eye_z_ = 0.0f;
356 light_x_ = 0.0f;
357 light_y_ = 0.0f;
358 light_z_ = 0.0f;
359 diffuse_r_ = 0.0f;
360 diffuse_g_ = 0.0f;
361 diffuse_b_ = 0.0f;
362 ambient_r_ = 0.0f;
363 ambient_g_ = 0.0f;
364 ambient_b_ = 0.0f;
365 planet_xyz_ = 0.0f;
366 planet_pole_x_equator_x_ = 0.0f;
367 planet_pole_x_equator_y_ = 0.0f;
368 planet_pole_x_equator_z_ = 0.0f;
369 planet_radius2_ = 0.0f;
370 planet_one_over_radius_ = 0.0f;
371 eye_xyz_ = 0.0f;
372 ui_zoom_ = 14.0f;
373 ui_light_ = 1.0f;
374 ui_spin_x_ = 0.01f;
375 ui_spin_y_ = 0.0f;
376 ui_last_point_ = pp::Point(0, 0);
378 // Set up reasonable default values.
379 SetPlanetXYZR(0.0f, 0.0f, 48.0f, 4.0f);
380 SetEyeXYZ(0.0f, 0.0f, -ui_zoom_);
381 SetLightXYZ(-60.0f, -30.0f, 0.0f);
382 SetAmbientRGB(0.05f, 0.05f, 0.05f);
383 SetDiffuseRGB(0.8f, 0.8f, 0.8f);
384 SetPlanetPole(0.0f, 1.0f, 0.0f);
385 SetPlanetEquator(1.0f, 0.0f, 0.0f);
386 SetPlanetSpin(kPI / 2.0f, kPI / 2.0f);
387 SetZoom(ui_zoom_);
388 SetLight(ui_light_);
390 // Send UI values to JS to reset html sliders.
391 PostUpdateMessage("set_zoom", ui_zoom_);
392 PostUpdateMessage("set_light", ui_light_);
396 Planet::Planet() : base_tex_(NULL), night_tex_(NULL), num_threads_(0),
397 benchmarking_(false), benchmark_frame_counter_(0) {
399 Reset();
400 RequestTextures();
401 // By default, render from the dispatch thread.
402 workers_ = new ThreadPool(num_threads_);
403 PSEventSetFilter(PSE_ALL);
404 ps_context_ = PSContext2DAllocate(PP_IMAGEDATAFORMAT_BGRA_PREMUL);
407 Planet::~Planet() {
408 delete workers_;
409 PSContext2DFree(ps_context_);
412 // Given a region r, derive a rectangle.
413 // This rectangle shouldn't overlap with work being done by other workers.
414 // If multithreading, this function is only called by the worker threads.
415 void Planet::wMakeRect(int r, int *x, int *y, int *w, int *h) {
416 *x = 0;
417 *w = ps_context_->width;
418 *y = r;
419 *h = 1;
423 inline uint32_t* Planet::wGetAddr(int x, int y) {
424 return ps_context_->data + x + y * ps_context_->stride / sizeof(uint32_t);
427 // This is the inner loop of the ray tracer. Given a pixel span (x0, x1) on
428 // scanline y, shoot rays into the scene and render what they hit. Use
429 // scanline coherence to do a few optimizations.
430 // This version uses portable SIMD 4 element single precision floating point
431 // vectors to perform many of the calculations, and builds only on PNaCl.
432 void Planet::wRenderPixelSpan(int x0, int x1, int y) {
433 if (!base_tex_ || !night_tex_)
434 return;
435 const uint32_t kColorBlack = MakeBGRA(0, 0, 0, 0xFF);
436 const uint32_t kSolidAlpha = MakeBGRA(0, 0, 0, 0xFF);
437 const f32x4_t kOne = {1.0f, 1.0f, 1.0f, 1.0f};
438 const f32x4_t diffuse = {diffuse_r_, diffuse_g_, diffuse_b_, 0.0f};
439 const f32x4_t ambient = {ambient_r_, ambient_g_, ambient_b_, 0.0f};
440 const f32x4_t light_pos = {light_x_, light_y_, light_z_, 1.0f};
441 const f32x4_t planet_pos = {planet_x_, planet_y_, planet_z_, 1.0f};
442 const f32x4_t planet_one_over_radius = broadcast(planet_one_over_radius_);
443 const f32x4_t planet_equator = {
444 planet_equator_x_, planet_equator_y_, planet_equator_z_, 0.0f};
445 const f32x4_t planet_pole = {
446 planet_pole_x_, planet_pole_y_, planet_pole_z_, 1.0f};
447 const f32x4_t planet_pole_x_equator = {
448 planet_pole_x_equator_x_, planet_pole_x_equator_y_,
449 planet_pole_x_equator_z_, 0.0f};
451 float width = ps_context_->width;
452 float height = ps_context_->height;
453 float min_dim = width < height ? width : height;
454 float offset_x = width < height ? 0 : (width - min_dim) * 0.5f;
455 float offset_y = width < height ? (height - min_dim) * 0.5f : 0;
456 float y0 = eye_y_;
457 float z0 = eye_z_;
458 float y1 = (static_cast<float>(y - offset_y) / min_dim) * 2.0f - 1.0f;
459 float z1 = 0.0f;
460 float dy = (y1 - y0);
461 float dz = (z1 - z0);
462 float dy_dy_dz_dz = dy * dy + dz * dz;
463 float two_dy_y0_y_two_dz_z0_z = 2.0f * dy * (y0 - planet_y_) +
464 2.0f * dz * (z0 - planet_z_);
465 float planet_xyz_eye_xyz = planet_xyz_ + eye_xyz_;
466 float y_y0_z_z0 = planet_y_ * y0 + planet_z_ * z0;
467 float oowidth = 1.0f / min_dim;
468 uint32_t* pixels = this->wGetAddr(x0, y);
469 for (int x = x0; x <= x1; ++x) {
470 // scan normalized screen -1..1
471 float x1 = (static_cast<float>(x - offset_x) * oowidth) * 2.0f - 1.0f;
472 // eye
473 float x0 = eye_x_;
474 // delta from screen to eye
475 float dx = (x1 - x0);
476 // build a, b, c
477 float a = dx * dx + dy_dy_dz_dz;
478 float b = 2.0f * dx * (x0 - planet_x_) + two_dy_y0_y_two_dz_z0_z;
479 float c = planet_xyz_eye_xyz +
480 -2.0f * (planet_x_ * x0 + y_y0_z_z0) - (planet_radius2_);
481 // calculate discriminant
482 float disc = b * b - 4.0f * a * c;
484 // Did ray hit the sphere?
485 if (disc < 0.0f) {
486 *pixels = kColorBlack;
487 ++pixels;
488 continue;
491 f32x4_t delta = {dx, dy, dz, 1.0f};
492 f32x4_t base = {x0, y0, z0, 1.0f};
494 // Calc parametric t value.
495 float t = (-b - inline_sqrt(disc)) / (2.0f * a);
497 f32x4_t pos = base + broadcast(t) * delta;
498 f32x4_t normal = (pos - planet_pos) * planet_one_over_radius;
500 // Misc raytrace calculations.
501 f32x4_t L = light_pos - pos;
502 float Lq = 1.0f / inline_quick_sqrt(dot3(L, L));
503 L = L * broadcast(Lq);
504 float d = dot3(L, normal);
505 f32x4_t p = diffuse * broadcast(d) + ambient;
506 float ds = -dot3(normal, planet_pole);
507 float ang = acos_.TableLerp(ds);
508 float v = ang * kOneOverPI;
509 float dp = dot3(planet_equator, normal);
510 float w = dp / sinf(ang);
511 if (w > 1.0f) w = 1.0f;
512 if (w < -1.0f) w = -1.0f;
513 float th = acos_.TableLerp(w) * kOneOver2PI;
514 float dps = dot3(planet_pole_x_equator, normal);
515 float u;
516 if (dps < 0.0f)
517 u = th;
518 else
519 u = 1.0f - th;
521 // Look up daylight texel.
522 int tx = static_cast<int>(u * base_tex_->width);
523 int ty = static_cast<int>(v * base_tex_->height);
524 int offset = tx + ty * base_tex_->width;
525 uint32_t base_texel = base_tex_->pixels[offset];
526 f32x4_t dc = ExtractRGBA(base_texel);
528 // Look up night texel.
529 int nix = static_cast<int>(u * night_tex_->width);
530 int niy = static_cast<int>(v * night_tex_->height);
531 int noffset = nix + niy * night_tex_->width;
532 uint32_t night_texel = night_tex_->pixels[noffset];
533 f32x4_t nc = ExtractRGBA(night_texel);
535 // Blend between daylight (dc) and nighttime (nc) color.
536 f32x4_t pc = min(p, kOne);
537 f32x4_t fc = dc * p + nc * (kOne - pc);
538 uint32_t color = PackBGRA(fc);
540 *pixels = color | kSolidAlpha;
541 ++pixels;
545 // Renders a rectangular area of the screen, scan line at a time
546 void Planet::wRenderRect(int x, int y, int w, int h) {
547 for (int j = y; j < (y + h); ++j) {
548 this->wRenderPixelSpan(x, x + w - 1, j);
552 // If multithreading, this function is only called by the worker threads.
553 void Planet::wRenderRegion(int region) {
554 // convert region # into x0, y0, x1, y1 rectangle
555 int x, y, w, h;
556 wMakeRect(region, &x, &y, &w, &h);
557 // render this rectangle
558 wRenderRect(x, y, w, h);
561 // Entry point for worker thread. Can't pass a member function around, so we
562 // have to do this little round-about.
563 void Planet::wRenderRegionEntry(int region, void* thiz) {
564 static_cast<Planet*>(thiz)->wRenderRegion(region);
567 // Renders the planet, dispatching the work to multiple threads.
568 void Planet::Render() {
569 workers_->Dispatch(ps_context_->height, wRenderRegionEntry, this);
572 // Pre-calculations to make inner loops faster.
573 void Planet::CacheCalcs() {
574 planet_xyz_ = planet_x_ * planet_x_ +
575 planet_y_ * planet_y_ +
576 planet_z_ * planet_z_;
577 planet_radius2_ = planet_radius_ * planet_radius_;
578 planet_one_over_radius_ = 1.0f / planet_radius_;
579 eye_xyz_ = eye_x_ * eye_x_ + eye_y_ * eye_y_ + eye_z_ * eye_z_;
580 // spin vector from center->equator
581 planet_equator_x_ = cos(planet_spin_x_);
582 planet_equator_y_ = 0.0f;
583 planet_equator_z_ = sin(planet_spin_x_);
585 // cache cross product of pole & equator
586 planet_pole_x_equator_x_ = planet_pole_y_ * planet_equator_z_ -
587 planet_pole_z_ * planet_equator_y_;
588 planet_pole_x_equator_y_ = planet_pole_z_ * planet_equator_x_ -
589 planet_pole_x_ * planet_equator_z_;
590 planet_pole_x_equator_z_ = planet_pole_x_ * planet_equator_y_ -
591 planet_pole_y_ * planet_equator_x_;
594 void Planet::SetPlanetXYZR(float x, float y, float z, float r) {
595 planet_x_ = x;
596 planet_y_ = y;
597 planet_z_ = z;
598 planet_radius_ = r;
599 CacheCalcs();
602 void Planet::SetEyeXYZ(float x, float y, float z) {
603 eye_x_ = x;
604 eye_y_ = y;
605 eye_z_ = z;
606 CacheCalcs();
609 void Planet::SetLightXYZ(float x, float y, float z) {
610 light_x_ = x;
611 light_y_ = y;
612 light_z_ = z;
613 CacheCalcs();
616 void Planet::SetAmbientRGB(float r, float g, float b) {
617 ambient_r_ = r;
618 ambient_g_ = g;
619 ambient_b_ = b;
620 CacheCalcs();
623 void Planet::SetDiffuseRGB(float r, float g, float b) {
624 diffuse_r_ = r;
625 diffuse_g_ = g;
626 diffuse_b_ = b;
627 CacheCalcs();
630 void Planet::SetPlanetPole(float x, float y, float z) {
631 planet_pole_x_ = x;
632 planet_pole_y_ = y;
633 planet_pole_z_ = z;
634 CacheCalcs();
637 void Planet::SetPlanetEquator(float x, float y, float z) {
638 // This is really over-ridden by spin at the momenent.
639 planet_equator_x_ = x;
640 planet_equator_y_ = y;
641 planet_equator_z_ = z;
642 CacheCalcs();
645 void Planet::SetPlanetSpin(float x, float y) {
646 planet_spin_x_ = x;
647 planet_spin_y_ = y;
648 CacheCalcs();
651 // Run a simple sim to spin the planet. Update loop is run once per frame.
652 // Called from the main thread only and only when the worker threads are idle.
653 void Planet::UpdateSim() {
654 float x = planet_spin_x_ + ui_spin_x_;
655 float y = planet_spin_y_ + ui_spin_y_;
656 // keep in nice range
657 if (x > (kPI * 2.0f))
658 x = x - kPI * 2.0f;
659 else if (x < (-kPI * 2.0f))
660 x = x + kPI * 2.0f;
661 if (y > (kPI * 2.0f))
662 y = y - kPI * 2.0f;
663 else if (y < (-kPI * 2.0f))
664 y = y + kPI * 2.0f;
665 SetPlanetSpin(x, y);
668 void Planet::StartBenchmark() {
669 // For more consistent benchmark numbers, reset to default state.
670 Reset();
671 printf("Benchmark started...\n");
672 benchmark_frame_counter_ = kFramesToBenchmark;
673 benchmarking_ = true;
674 benchmark_start_time_ = getseconds();
677 void Planet::EndBenchmark() {
678 benchmark_end_time_ = getseconds();
679 printf("Benchmark ended... time: %2.5f\n",
680 benchmark_end_time_ - benchmark_start_time_);
681 benchmarking_ = false;
682 benchmark_frame_counter_ = 0;
683 double total_time = benchmark_end_time_ - benchmark_start_time_;
684 // Send benchmark result to JS.
685 PostUpdateMessage("benchmark_result", total_time);
688 void Planet::SetZoom(float zoom) {
689 ui_zoom_ = std::min(kZoomMax, std::max(kZoomMin, zoom));
690 SetEyeXYZ(0.0f, 0.0f, -ui_zoom_);
693 void Planet::SetLight(float light) {
694 ui_light_ = std::min(kLightMax, std::max(kLightMin, light));
695 SetDiffuseRGB(0.8f * ui_light_, 0.8f * ui_light_, 0.8f * ui_light_);
696 SetAmbientRGB(0.4f * ui_light_, 0.4f * ui_light_, 0.4f * ui_light_);
699 void Planet::SetTexture(const std::string& name, int width, int height,
700 uint32_t* pixels) {
701 if (pixels) {
702 if (name == "earth.jpg") {
703 delete base_tex_;
704 base_tex_ = new Texture(width, height, pixels);
705 } else if (name == "earthnight.jpg") {
706 delete night_tex_;
707 night_tex_ = new Texture(width, height, pixels);
712 void Planet::SpinPlanet(pp::Point new_point, pp::Point last_point) {
713 float delta_x = static_cast<float>(new_point.x() - last_point.x());
714 float delta_y = static_cast<float>(new_point.y() - last_point.y());
715 float spin_x = std::min(10.0f, std::max(-10.0f, delta_x * 0.5f));
716 float spin_y = std::min(10.0f, std::max(-10.0f, delta_y * 0.5f));
717 ui_spin_x_ = spin_x / 100.0f;
718 ui_spin_y_ = spin_y / 100.0f;
719 ui_last_point_ = new_point;
722 // Handle input events from the user and messages from JS.
723 void Planet::HandleEvent(PSEvent* ps_event) {
724 // Give the 2D context a chance to process the event.
725 if (0 != PSContext2DHandleEvent(ps_context_, ps_event))
726 return;
727 if (ps_event->type == PSE_INSTANCE_HANDLEINPUT) {
728 // Convert Pepper Simple event to a PPAPI C++ event
729 pp::InputEvent event(ps_event->as_resource);
730 switch (event.GetType()) {
731 case PP_INPUTEVENT_TYPE_KEYDOWN: {
732 pp::KeyboardInputEvent key(event);
733 uint32_t key_code = key.GetKeyCode();
734 if (key_code == 84) // 't' key
735 if (!benchmarking_)
736 StartBenchmark();
737 break;
739 case PP_INPUTEVENT_TYPE_MOUSEDOWN:
740 case PP_INPUTEVENT_TYPE_MOUSEMOVE: {
741 pp::MouseInputEvent mouse = pp::MouseInputEvent(event);
742 if (mouse.GetModifiers() & PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN) {
743 if (event.GetType() == PP_INPUTEVENT_TYPE_MOUSEDOWN)
744 SpinPlanet(mouse.GetPosition(), mouse.GetPosition());
745 else
746 SpinPlanet(mouse.GetPosition(), ui_last_point_);
748 break;
750 case PP_INPUTEVENT_TYPE_WHEEL: {
751 pp::WheelInputEvent wheel = pp::WheelInputEvent(event);
752 PP_FloatPoint ticks = wheel.GetTicks();
753 SetZoom(ui_zoom_ + (ticks.x + ticks.y) * kWheelSpeed);
754 // Update html slider by sending update message to JS.
755 PostUpdateMessage("set_zoom", ui_zoom_);
756 break;
758 case PP_INPUTEVENT_TYPE_TOUCHSTART:
759 case PP_INPUTEVENT_TYPE_TOUCHMOVE: {
760 pp::TouchInputEvent touches = pp::TouchInputEvent(event);
761 uint32_t count = touches.GetTouchCount(PP_TOUCHLIST_TYPE_TOUCHES);
762 if (count > 0) {
763 // Use first touch point to spin planet.
764 pp::TouchPoint touch =
765 touches.GetTouchByIndex(PP_TOUCHLIST_TYPE_TOUCHES, 0);
766 pp::Point screen_point(touch.position().x(),
767 touch.position().y());
768 if (event.GetType() == PP_INPUTEVENT_TYPE_TOUCHSTART)
769 SpinPlanet(screen_point, screen_point);
770 else
771 SpinPlanet(screen_point, ui_last_point_);
773 break;
775 default:
776 break;
778 } else if (ps_event->type == PSE_INSTANCE_HANDLEMESSAGE) {
779 // Convert Pepper Simple message to PPAPI C++ vars
780 pp::Var var(ps_event->as_var);
781 if (var.is_dictionary()) {
782 pp::VarDictionary dictionary(var);
783 std::string message = dictionary.Get("message").AsString();
784 if (message == "run benchmark" && !benchmarking_) {
785 StartBenchmark();
786 } else if (message == "set_light") {
787 SetLight(static_cast<float>(dictionary.Get("value").AsDouble()));
788 } else if (message == "set_zoom") {
789 SetZoom(static_cast<float>(dictionary.Get("value").AsDouble()));
790 } else if (message == "set_threads") {
791 int threads = dictionary.Get("value").AsInt();
792 delete workers_;
793 workers_ = new ThreadPool(threads);
794 } else if (message == "texture") {
795 std::string name = dictionary.Get("name").AsString();
796 int width = dictionary.Get("width").AsInt();
797 int height = dictionary.Get("height").AsInt();
798 pp::VarArrayBuffer array_buffer(dictionary.Get("data"));
799 if (!name.empty() && !array_buffer.is_null()) {
800 if (width > 0 && height > 0) {
801 uint32_t* pixels = static_cast<uint32_t*>(array_buffer.Map());
802 SetTexture(name, width, height, pixels);
803 array_buffer.Unmap();
807 } else {
808 printf("Handle message unknown type: %s\n", var.DebugString().c_str());
813 // PostUpdateMessage() helper function for sending small messages to JS.
814 void Planet::PostUpdateMessage(const char* message_name, double value) {
815 pp::VarDictionary message;
816 message.Set("message", message_name);
817 message.Set("value", value);
818 PSInterfaceMessaging()->PostMessage(PSGetInstanceId(), message.pp_var());
821 void Planet::Update() {
822 // When benchmarking is running, don't update display via
823 // PSContext2DSwapBuffer() - vsync is enabled by default, and will throttle
824 // the benchmark results.
825 PSContext2DGetBuffer(ps_context_);
826 if (NULL == ps_context_->data)
827 return;
829 do {
830 UpdateSim();
831 Render();
832 if (!benchmarking_) break;
833 --benchmark_frame_counter_;
834 } while (benchmark_frame_counter_ > 0);
835 if (benchmarking_)
836 EndBenchmark();
838 PSContext2DSwapBuffer(ps_context_);
842 // Starting point for the module. We do not use main since it would
843 // collide with main in libppapi_cpp.
844 int example_main(int argc, char* argv[]) {
845 Planet earth;
846 while (true) {
847 PSEvent* ps_event;
848 // Consume all available events
849 while ((ps_event = PSEventTryAcquire()) != NULL) {
850 earth.HandleEvent(ps_event);
851 PSEventRelease(ps_event);
853 // Do simulation, render and present.
854 earth.Update();
857 return 0;
860 // Register the function to call once the Instance Object is initialized.
861 // see: pappi_simple/ps_main.h
862 PPAPI_SIMPLE_REGISTER_MAIN(example_main);