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.
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>
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.
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,
58 int start_tv_retv
= gettimeofday(&start_tv
, NULL
);
60 inline double getseconds() {
61 const double usec_to_sec
= 0.000001;
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
;
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
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
) {
85 return (f32x4_t
)(((i32x4_t
)a
& m
) | ((i32x4_t
)b
& ~m
));
88 INLINE f32x4_t
max(f32x4_t a
, f32x4_t b
) {
90 return (f32x4_t
)(((i32x4_t
)a
& m
) | ((i32x4_t
)b
& ~m
));
93 INLINE
float dot3(f32x4_t a
, f32x4_t b
) {
95 return c
[0] + c
[1] + c
[2];
98 INLINE f32x4_t
broadcast(float x
) {
99 f32x4_t r
= {x
, x
, x
, x
};
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
;
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
);
122 // Add 0.5 to perform rounding instead of truncation.
123 f
= f
* k255
+ kHalf
;
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);
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
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
);
156 // slightly larger table so we can interpolate beyond table size
157 float table
[kArcCosineTableSize
+ 2];
158 float TableLerp(float x
);
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
;
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
);
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.
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
) {
199 INLINE
const float AsFloat(const int i
) {
204 const long int kOneAsInteger
= AsInteger(1.0f
);
206 INLINE
float inline_quick_sqrt(float x
) {
208 i
= (AsInteger(x
) >> 1) + (kOneAsInteger
>> 1);
212 INLINE
float inline_sqrt(float x
) {
214 y
= inline_quick_sqrt(x
);
215 y
= (y
* y
+ x
) / (2.0f
* y
);
216 y
= (y
* y
+ x
) / (2.0f
* y
);
223 // The main object that runs the Earth demo.
228 // Runs a tick of the simulations, update 2D output.
230 // Handle event from user, or message from JS.
231 void HandleEvent(PSEvent
* ps_event
);
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.
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
,
256 void SpinPlanet(pp::Point new_point
, pp::Point last_point
);
259 void RequestTextures();
263 void StartBenchmark();
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.
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.
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_
;
304 // Source texture (earth map).
310 // Quick ArcCos helper.
314 PSContext2D_t
* ps_context_
;
316 ThreadPool
* workers_
;
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");
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
;
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
;
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
;
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
);
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) {
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
);
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
) {
417 *w
= ps_context_
->width
;
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_
)
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;
458 float y1
= (static_cast<float>(y
- offset_y
) / min_dim
) * 2.0f
- 1.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
;
474 // delta from screen to eye
475 float dx
= (x1
- x0
);
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?
486 *pixels
= kColorBlack
;
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
);
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
;
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
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
) {
602 void Planet::SetEyeXYZ(float x
, float y
, float z
) {
609 void Planet::SetLightXYZ(float x
, float y
, float z
) {
616 void Planet::SetAmbientRGB(float r
, float g
, float b
) {
623 void Planet::SetDiffuseRGB(float r
, float g
, float b
) {
630 void Planet::SetPlanetPole(float x
, float y
, float z
) {
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
;
645 void Planet::SetPlanetSpin(float x
, float y
) {
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
))
659 else if (x
< (-kPI
* 2.0f
))
661 if (y
> (kPI
* 2.0f
))
663 else if (y
< (-kPI
* 2.0f
))
668 void Planet::StartBenchmark() {
669 // For more consistent benchmark numbers, reset to default state.
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
,
702 if (name
== "earth.jpg") {
704 base_tex_
= new Texture(width
, height
, pixels
);
705 } else if (name
== "earthnight.jpg") {
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
))
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
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());
746 SpinPlanet(mouse
.GetPosition(), ui_last_point_
);
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_
);
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
);
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
);
771 SpinPlanet(screen_point
, ui_last_point_
);
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_
) {
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();
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();
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
)
832 if (!benchmarking_
) break;
833 --benchmark_frame_counter_
;
834 } while (benchmark_frame_counter_
> 0);
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
[]) {
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.
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
);