earth.cc revision 3240926e260ce088908e02ac07a6cf7b0c0cbf44
1eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// found in the LICENSE file.
4eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
5eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <assert.h>
6eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <math.h>
7eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <ppapi/c/ppb_input_event.h>
8eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <ppapi/cpp/input_event.h>
9eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <ppapi/cpp/var.h>
10eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <ppapi/cpp/var_array.h>
11eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <ppapi/cpp/var_array_buffer.h>
12eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <ppapi/cpp/var_dictionary.h>
13eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <pthread.h>
14eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <stdio.h>
15eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <stdlib.h>
16eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <string.h>
17eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <sys/time.h>
18eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <unistd.h>
19eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
20eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <algorithm>
21eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <string>
22eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
233240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch#include "ppapi_simple/ps.h"
243240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch#include "ppapi_simple/ps_context_2d.h"
253240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch#include "ppapi_simple/ps_event.h"
263240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch#include "ppapi_simple/ps_interface.h"
273240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch#include "ppapi_simple/ps_main.h"
28eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "sdk_util/macros.h"
29eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "sdk_util/thread_pool.h"
30eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
31ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochusing namespace sdk_util;  // For sdk_util::ThreadPool
32ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
33eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Global properties used to setup Earth demo.
34eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochnamespace {
35eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochconst float kHugeZ = 1.0e38f;
36eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochconst float kPI = M_PI;
37eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochconst float kTwoPI = kPI * 2.0f;
38eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochconst float kOneOverPI = 1.0f / kPI;
39eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochconst float kOneOver2PI = 1.0f / kTwoPI;
40eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochconst float kOneOver255 = 1.0f / 255.0f;
41eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochconst int kArcCosineTableSize = 4096;
42eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochconst int kFramesToBenchmark = 100;
43eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochconst float kZoomMin = 1.0f;
44eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochconst float kZoomMax = 50.0f;
45eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochconst float kWheelSpeed = 2.0f;
46eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochconst float kLightMin = 0.0f;
47eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochconst float kLightMax = 2.0f;
48eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochconst int kFrameTimeBufferSize = 512;
49eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
50eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Timer helper for benchmarking.  Returns seconds elapsed since program start,
51eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// as a double.
52eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochtimeval start_tv;
53eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochint start_tv_retv = gettimeofday(&start_tv, NULL);
54eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
55eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochinline double getseconds() {
56eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  const double usec_to_sec = 0.000001;
57eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  timeval tv;
58eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if ((0 == start_tv_retv) && (0 == gettimeofday(&tv, NULL)))
59eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return (tv.tv_sec - start_tv.tv_sec) + tv.tv_usec * usec_to_sec;
60eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return 0.0;
61eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
62eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
63eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// RGBA helper functions.
64eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochinline float ExtractR(uint32_t c) {
65eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return static_cast<float>(c & 0xFF) * kOneOver255;
66eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
67eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
68eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochinline float ExtractG(uint32_t c) {
69eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return static_cast<float>((c & 0xFF00) >> 8) * kOneOver255;
70eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
71eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
72eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochinline float ExtractB(uint32_t c) {
73eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return static_cast<float>((c & 0xFF0000) >> 16) * kOneOver255;
74eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
75eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
76eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochinline uint32_t MakeRGBA(uint32_t r, uint32_t g, uint32_t b, uint32_t a) {
77eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return (((a) << 24) | ((r) << 16) | ((g) << 8) | (b));
78eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
79eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
80eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// simple container for earth texture
81eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochstruct Texture {
82eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  int width, height;
83eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  uint32_t* pixels;
84eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  Texture(int w, int h) : width(w), height(h) {
85eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    pixels = new uint32_t[w * h];
86eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    memset(pixels, 0, sizeof(uint32_t) * w * h);
87eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
88eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  explicit Texture(int w, int h, uint32_t* p) : width(w), height(h) {
89eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    pixels = new uint32_t[w * h];
90eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    memcpy(pixels, p, sizeof(uint32_t) * w * h);
91eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
92eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  ~Texture() { delete[] pixels; }
93eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
94eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DISALLOW_COPY_AND_ASSIGN(Texture);
95eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch};
96eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
97eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
98eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
99eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochstruct ArcCosine {
100eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // slightly larger table so we can interpolate beyond table size
101eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float table[kArcCosineTableSize + 2];
102eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float TableLerp(float x);
103eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  ArcCosine();
104eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch};
105eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
106eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochArcCosine::ArcCosine() {
107eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // build a slightly larger table to allow for numeric imprecision
108eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  for (int i = 0; i < (kArcCosineTableSize + 2); ++i) {
109eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float f = static_cast<float>(i) / kArcCosineTableSize;
110eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    f = f * 2.0f - 1.0f;
111eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    table[i] = acos(f);
112eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
113eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
114eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
115eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// looks up acos(f) using a table and lerping between entries
116eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// (it is expected that input f is between -1 and 1)
117eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochfloat ArcCosine::TableLerp(float f) {
118eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float x = (f + 1.0f) * 0.5f;
119eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  x = x * kArcCosineTableSize;
120eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  int ix = static_cast<int>(x);
121eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float fx = static_cast<float>(ix);
122eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float dx = x - fx;
123eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float af = table[ix];
124eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float af2 = table[ix + 1];
125eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return af + (af2 - af) * dx;
126eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
127eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
128eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Helper functions for quick but approximate sqrt.
129eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochunion Convert {
130eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float f;
131eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  int i;
132eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  Convert(int x) { i = x; }
133eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  Convert(float x) { f = x; }
134eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  int AsInt() { return i; }
135eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float AsFloat() { return f; }
136eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch};
137eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
138eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochinline const int AsInteger(const float f) {
139eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  Convert u(f);
140eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return u.AsInt();
141eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
142eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
143eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochinline const float AsFloat(const int i) {
144eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  Convert u(i);
145eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return u.AsFloat();
146eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
147eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
148eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochconst long int kOneAsInteger = AsInteger(1.0f);
149eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochconst float kScaleUp = float(0x00800000);
150eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochconst float kScaleDown = 1.0f / kScaleUp;
151eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
152eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochinline float inline_quick_sqrt(float x) {
153eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  int i;
154eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  i = (AsInteger(x) >> 1) + (kOneAsInteger >> 1);
155eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return AsFloat(i);
156eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
157eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
158eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochinline float inline_sqrt(float x) {
159eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float y;
160eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  y = inline_quick_sqrt(x);
161eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  y = (y * y + x) / (2.0f * y);
162eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  y = (y * y + x) / (2.0f * y);
163eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return y;
164eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
165eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
166eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// takes a -0..1+ color, clamps it to 0..1 and maps it to 0..255 integer
167eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochinline uint32_t Clamp255(float x) {
168eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (x < 0.0f) {
169eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    x = 0.0f;
170eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  } else if (x > 1.0f) {
171eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    x = 1.0f;
172eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
173eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return static_cast<uint32_t>(x * 255.0f);
174eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
175eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}  // namespace
176eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
177eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
178eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// The main object that runs the Earth demo.
1793240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdochclass Planet {
180eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch public:
1813240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  Planet();
182eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  virtual ~Planet();
1833240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  // Runs a tick of the simulations, update 2D output.
1843240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  void Update();
1853240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  // Handle event from user, or message from JS.
1863240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  void HandleEvent(PSEvent* ps_event);
187eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
188eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch private:
189eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Methods prefixed with 'w' are run on worker threads.
190eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  uint32_t* wGetAddr(int x, int y);
191eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void wRenderPixelSpan(int x0, int x1, int y);
192eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void wMakeRect(int r, int *x, int *y, int *w, int *h);
193eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void wRenderRect(int x0, int y0, int x1, int y1);
194eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void wRenderRegion(int region);
195eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  static void wRenderRegionEntry(int region, void *thiz);
196eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
197eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // These methods are only called by the main thread.
198eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void CacheCalcs();
199eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void SetPlanetXYZR(float x, float y, float z, float r);
200eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void SetPlanetPole(float x, float y, float z);
201eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void SetPlanetEquator(float x, float y, float z);
202eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void SetPlanetSpin(float x, float y);
203eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void SetEyeXYZ(float x, float y, float z);
204eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void SetLightXYZ(float x, float y, float z);
205eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void SetAmbientRGB(float r, float g, float b);
206eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void SetDiffuseRGB(float r, float g, float b);
207eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void SetZoom(float zoom);
208eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void SetLight(float zoom);
209eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void SetTexture(const std::string& name, int width, int height,
210eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      uint32_t* pixels);
211eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
212eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void Reset();
2133240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  void RequestTextures();
214eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void UpdateSim();
215eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void Render();
216eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void Draw();
217eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void StartBenchmark();
218eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void EndBenchmark();
219eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Post a small key-value message to update JS.
220eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void PostUpdateMessage(const char* message_name, double value);
221eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
222eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // User Interface settings.  These settings are controlled via html
223eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // controls or via user input.
224eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float ui_light_;
225eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float ui_zoom_;
226eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float ui_spin_x_;
227eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float ui_spin_y_;
228eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
229eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Various settings for position & orientation of planet.  Do not change
230eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // these variables, instead use SetPlanet*() functions.
231eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float planet_radius_;
232eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float planet_spin_x_;
233eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float planet_spin_y_;
234eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float planet_x_, planet_y_, planet_z_;
235eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float planet_pole_x_, planet_pole_y_, planet_pole_z_;
236eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float planet_equator_x_, planet_equator_y_, planet_equator_z_;
237eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
238eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Observer's eye.  Do not change these variables, instead use SetEyeXYZ().
239eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float eye_x_, eye_y_, eye_z_;
240eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
241eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Light position, ambient and diffuse settings.  Do not change these
242eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // variables, instead use SetLightXYZ(), SetAmbientRGB() and SetDiffuseRGB().
243eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float light_x_, light_y_, light_z_;
244eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float diffuse_r_, diffuse_g_, diffuse_b_;
245eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float ambient_r_, ambient_g_, ambient_b_;
246eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
247eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Cached calculations.  Do not change these variables - they are updated by
248eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // CacheCalcs() function.
249eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float planet_xyz_;
250eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float planet_pole_x_equator_x_;
251eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float planet_pole_x_equator_y_;
252eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float planet_pole_x_equator_z_;
253eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float planet_radius2_;
254eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float planet_one_over_radius_;
255eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float eye_xyz_;
256eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
257eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Source texture (earth map).
258eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  Texture* base_tex_;
259eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  Texture* night_tex_;
260eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  int width_for_tex_;
261eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  int height_for_tex_;
262eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
263eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Quick ArcCos helper.
264eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  ArcCosine acos_;
265eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
266eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Misc.
2673240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  PSContext2D_t* ps_context_;
268eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  int num_threads_;
269eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  ThreadPool* workers_;
270eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  bool benchmarking_;
2713240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  int benchmark_frame_counter_;
272eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  double benchmark_start_time_;
273eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  double benchmark_end_time_;
274eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch};
275eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
276eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
2773240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdochvoid Planet::RequestTextures() {
278eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Request a set of images from JS.  After images are loaded by JS, a
279eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // message from JS -> NaCl will arrive containing the pixel data.  See
280eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // HandleMessage() method in this file.
281eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  pp::VarDictionary message;
282eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  message.Set("message", "request_textures");
283eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  pp::VarArray names;
284eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  names.Set(0, "earth.jpg");
285eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  names.Set(1, "earthnight.jpg");
286eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  message.Set("names", names);
2873240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  PSInterfaceMessaging()->PostMessage(PSGetInstanceId(), message.pp_var());
288eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
289eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
290eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Planet::Reset() {
291eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Reset has to first fill in all variables with valid floats, so
292eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // CacheCalcs() doesn't potentially propagate NaNs when calling Set*()
293eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // functions further below.
294eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_radius_ = 1.0f;
295eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_spin_x_ = 0.0f;
296eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_spin_y_ = 0.0f;
297eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_x_ = 0.0f;
298eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_y_ = 0.0f;
299eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_z_ = 0.0f;
300eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_pole_x_ = 0.0f;
301eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_pole_y_ = 0.0f;
302eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_pole_z_ = 0.0f;
303eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_equator_x_ = 0.0f;
304eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_equator_y_ = 0.0f;
305eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_equator_z_ = 0.0f;
306eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  eye_x_ = 0.0f;
307eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  eye_y_ = 0.0f;
308eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  eye_z_ = 0.0f;
309eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  light_x_ = 0.0f;
310eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  light_y_ = 0.0f;
311eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  light_z_ = 0.0f;
312eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  diffuse_r_ = 0.0f;
313eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  diffuse_g_ = 0.0f;
314eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  diffuse_b_ = 0.0f;
315eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  ambient_r_ = 0.0f;
316eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  ambient_g_ = 0.0f;
317eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  ambient_b_ = 0.0f;
318eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_xyz_ = 0.0f;
319eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_pole_x_equator_x_ = 0.0f;
320eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_pole_x_equator_y_ = 0.0f;
321eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_pole_x_equator_z_ = 0.0f;
322eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_radius2_ = 0.0f;
323eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_one_over_radius_ = 0.0f;
324eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  eye_xyz_ = 0.0f;
325eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  ui_zoom_ = 14.0f;
326eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  ui_light_ = 1.0f;
327eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  ui_spin_x_ = 0.01f;
328eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  ui_spin_y_ = 0.0f;
329eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
330eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Set up reasonable default values.
331eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  SetPlanetXYZR(0.0f, 0.0f, 48.0f, 4.0f);
332eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  SetEyeXYZ(0.0f, 0.0f, -ui_zoom_);
333eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  SetLightXYZ(-60.0f, -30.0f, 0.0f);
334eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  SetAmbientRGB(0.05f, 0.05f, 0.05f);
335eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  SetDiffuseRGB(0.8f, 0.8f, 0.8f);
336eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  SetPlanetPole(0.0f, 1.0f, 0.0f);
337eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  SetPlanetEquator(1.0f, 0.0f, 0.0f);
338eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  SetPlanetSpin(kPI / 2.0f, kPI / 2.0f);
339eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  SetZoom(ui_zoom_);
340eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  SetLight(ui_light_);
341eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
342eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Send UI values to JS to reset html sliders.
343eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  PostUpdateMessage("set_zoom", ui_zoom_);
344eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  PostUpdateMessage("set_light", ui_light_);
345eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
346eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
347eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
3483240926e260ce088908e02ac07a6cf7b0c0cbf44Ben MurdochPlanet::Planet() : base_tex_(NULL), night_tex_(NULL), num_threads_(0),
3493240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    benchmarking_(false), benchmark_frame_counter_(0) {
350eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
351eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  Reset();
3523240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  RequestTextures();
353eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // By default, render from the dispatch thread.
354eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  workers_ = new ThreadPool(num_threads_);
3553240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  PSEventSetFilter(PSE_ALL);
3563240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  ps_context_ = PSContext2DAllocate();
357eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
358eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
359eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochPlanet::~Planet() {
360eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  delete workers_;
3613240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  PSContext2DFree(ps_context_);
362eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
363eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
364eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Given a region r, derive a rectangle.
365eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// This rectangle shouldn't overlap with work being done by other workers.
366eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// If multithreading, this function is only called by the worker threads.
367eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Planet::wMakeRect(int r, int *x, int *y, int *w, int *h) {
368eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  *x = 0;
3693240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  *w = ps_context_->width;
3703240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  *y = r;
3713240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  *h = 1;
372eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
373eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
374eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
375eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochinline uint32_t* Planet::wGetAddr(int x, int y) {
3763240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  return ps_context_->data + x + y * ps_context_->stride / sizeof(uint32_t);
377eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
378eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
379eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// This is the meat of the ray tracer.  Given a pixel span (x0, x1) on
380eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// scanline y, shoot rays into the scene and render what they hit.  Use
381eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// scanline coherence to do a few optimizations
382eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Planet::wRenderPixelSpan(int x0, int x1, int y) {
383eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (!base_tex_ || !night_tex_)
384eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return;
385eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  const int kColorBlack = MakeRGBA(0, 0, 0, 0xFF);
386eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float y0 = eye_y_;
387eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float z0 = eye_z_;
3883240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  float y1 = (static_cast<float>(y) / ps_context_->height) * 2.0f - 1.0f;
389eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float z1 = 0.0f;
390eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float dy = (y1 - y0);
391eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float dz = (z1 - z0);
392eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float dy_dy_dz_dz = dy * dy + dz * dz;
393eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float two_dy_y0_y_two_dz_z0_z = 2.0f * dy * (y0 - planet_y_) +
394eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                  2.0f * dz * (z0 - planet_z_);
395eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float planet_xyz_eye_xyz = planet_xyz_ + eye_xyz_;
396eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float y_y0_z_z0 = planet_y_ * y0 + planet_z_ * z0;
3973240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  float oowidth = 1.0f / ps_context_->width;
398eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  uint32_t* pixels = this->wGetAddr(x0, y);
399eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  for (int x = x0; x <= x1; ++x) {
400eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // scan normalized screen -1..1
401eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float x1 = (static_cast<float>(x) * oowidth) * 2.0f - 1.0f;
402eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // eye
403eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float x0 = eye_x_;
404eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // delta from screen to eye
405eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float dx = (x1 - x0);
406eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // build a, b, c
407eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float a = dx * dx + dy_dy_dz_dz;
408eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float b = 2.0f * dx * (x0 - planet_x_) + two_dy_y0_y_two_dz_z0_z;
409eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float c = planet_xyz_eye_xyz +
410eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch              -2.0f * (planet_x_ * x0 + y_y0_z_z0) - (planet_radius2_);
411eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // calculate discriminant
412eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float disc = b * b - 4.0f * a * c;
413eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
414eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // Did ray hit the sphere?
415eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (disc < 0.0f) {
416eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      *pixels = kColorBlack;
417eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      ++pixels;
418eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      continue;
419eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
420eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
421eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // calc parametric t value
422eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float t = (-b - inline_sqrt(disc)) / (2.0f * a);
423eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float px = x0 + t * dx;
424eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float py = y0 + t * dy;
425eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float pz = z0 + t * dz;
426eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float nx = (px - planet_x_) * planet_one_over_radius_;
427eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float ny = (py - planet_y_) * planet_one_over_radius_;
428eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float nz = (pz - planet_z_) * planet_one_over_radius_;
429eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
430eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // Misc raytrace calculations.
431eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float Lx = (light_x_ - px);
432eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float Ly = (light_y_ - py);
433eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float Lz = (light_z_ - pz);
434eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float Lq = 1.0f / inline_quick_sqrt(Lx * Lx + Ly * Ly + Lz * Lz);
435eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    Lx *= Lq;
436eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    Ly *= Lq;
437eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    Lz *= Lq;
438eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float d = (Lx * nx + Ly * ny + Lz * nz);
439eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float pr = (diffuse_r_ * d) + ambient_r_;
440eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float pg = (diffuse_g_ * d) + ambient_g_;
441eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float pb = (diffuse_b_ * d) + ambient_b_;
442eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float ds = -(nx * planet_pole_x_ +
443eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                 ny * planet_pole_y_ +
444eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                 nz * planet_pole_z_);
445eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float ang = acos_.TableLerp(ds);
446eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float v = ang * kOneOverPI;
447eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float dp = planet_equator_x_ * nx +
448eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch               planet_equator_y_ * ny +
449eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch               planet_equator_z_ * nz;
450eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float w = dp / sin(ang);
451eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (w > 1.0f) w = 1.0f;
452eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (w < -1.0f) w = -1.0f;
453eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float th = acos_.TableLerp(w) * kOneOver2PI;
454eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float dps = planet_pole_x_equator_x_ * nx +
455eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                planet_pole_x_equator_y_ * ny +
456eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                planet_pole_x_equator_z_ * nz;
457eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float u;
458eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (dps < 0.0f)
459eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      u = th;
460eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    else
461eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      u = 1.0f - th;
462eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
463eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // Look up daylight texel.
464eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    int tx = static_cast<int>(u * base_tex_->width);
465eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    int ty = static_cast<int>(v * base_tex_->height);
466eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    int offset = tx + ty * base_tex_->width;
467eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    uint32_t base_texel = base_tex_->pixels[offset];
468eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float tr = ExtractR(base_texel);
469eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float tg = ExtractG(base_texel);
470eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float tb = ExtractB(base_texel);
471eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
472eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float ipr = 1.0f - pr;
473eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (ipr < 0.0f) ipr = 0.0f;
474eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float ipg = 1.0f - pg;
475eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (ipg < 0.0f) ipg = 0.0f;
476eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float ipb = 1.0f - pb;
477eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (ipb < 0.0f) ipb = 0.0f;
478eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
479eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // Look up night texel.
480eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    int nix = static_cast<int>(u * night_tex_->width);
481eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    int niy = static_cast<int>(v * night_tex_->height);
482eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    int noffset = nix + niy * night_tex_->width;
483eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    uint32_t night_texel = night_tex_->pixels[noffset];
484eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float nr = ExtractR(night_texel);
485eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float ng = ExtractG(night_texel);
486eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    float nb = ExtractB(night_texel);
487eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
488eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // Final color value is lerp between day and night texels.
489eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    unsigned int ir = Clamp255(pr * tr + nr * ipr);
490eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    unsigned int ig = Clamp255(pg * tg + ng * ipg);
491eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    unsigned int ib = Clamp255(pb * tb + nb * ipb);
492eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
493eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    unsigned int color = MakeRGBA(ir, ig, ib, 0xFF);
494eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
495eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    *pixels = color;
496eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    ++pixels;
497eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
498eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
499eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
500eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Renders a rectangular area of the screen, scan line at a time
501eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Planet::wRenderRect(int x, int y, int w, int h) {
502eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  for (int j = y; j < (y + h); ++j) {
503eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    this->wRenderPixelSpan(x, x + w - 1, j);
504eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
505eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
506eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
507eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// If multithreading, this function is only called by the worker threads.
508eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Planet::wRenderRegion(int region) {
509eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // convert region # into x0, y0, x1, y1 rectangle
510eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  int x, y, w, h;
511eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  wMakeRect(region, &x, &y, &w, &h);
512eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // render this rectangle
513eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  wRenderRect(x, y, w, h);
514eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
515eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
516eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Entry point for worker thread.  Can't pass a member function around, so we
517eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// have to do this little round-about.
518eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Planet::wRenderRegionEntry(int region, void* thiz) {
519eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  static_cast<Planet*>(thiz)->wRenderRegion(region);
520eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
521eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
522eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Renders the planet, dispatching the work to multiple threads.
523eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Planet::Render() {
5243240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  workers_->Dispatch(ps_context_->height, wRenderRegionEntry, this);
525eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
526eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
527eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Pre-calculations to make inner loops faster.
528eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Planet::CacheCalcs() {
529eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_xyz_ = planet_x_ * planet_x_ +
530eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                planet_y_ * planet_y_ +
531eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                planet_z_ * planet_z_;
532eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_radius2_ = planet_radius_ * planet_radius_;
533eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_one_over_radius_ = 1.0f / planet_radius_;
534eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  eye_xyz_ = eye_x_ * eye_x_ + eye_y_ * eye_y_ + eye_z_ * eye_z_;
535eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // spin vector from center->equator
536eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_equator_x_ = cos(planet_spin_x_);
537eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_equator_y_ = 0.0f;
538eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_equator_z_ = sin(planet_spin_x_);
539eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
540eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // cache cross product of pole & equator
541eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_pole_x_equator_x_ = planet_pole_y_ * planet_equator_z_ -
542eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                             planet_pole_z_ * planet_equator_y_;
543eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_pole_x_equator_y_ = planet_pole_z_ * planet_equator_x_ -
544eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                             planet_pole_x_ * planet_equator_z_;
545eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_pole_x_equator_z_ = planet_pole_x_ * planet_equator_y_ -
546eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                             planet_pole_y_ * planet_equator_x_;
547eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
548eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
549eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Planet::SetPlanetXYZR(float x, float y, float z, float r) {
550eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_x_ = x;
551eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_y_ = y;
552eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_z_ = z;
553eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_radius_ = r;
554eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  CacheCalcs();
555eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
556eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
557eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Planet::SetEyeXYZ(float x, float y, float z) {
558eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  eye_x_ = x;
559eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  eye_y_ = y;
560eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  eye_z_ = z;
561eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  CacheCalcs();
562eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
563eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
564eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Planet::SetLightXYZ(float x, float y, float z) {
565eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  light_x_ = x;
566eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  light_y_ = y;
567eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  light_z_ = z;
568eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  CacheCalcs();
569eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
570eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
571eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Planet::SetAmbientRGB(float r, float g, float b) {
572eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  ambient_r_ = r;
573eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  ambient_g_ = g;
574eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  ambient_b_ = b;
575eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  CacheCalcs();
576eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
577eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
578eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Planet::SetDiffuseRGB(float r, float g, float b) {
579eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  diffuse_r_ = r;
580eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  diffuse_g_ = g;
581eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  diffuse_b_ = b;
582eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  CacheCalcs();
583eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
584eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
585eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Planet::SetPlanetPole(float x, float y, float z) {
586eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_pole_x_ = x;
587eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_pole_y_ = y;
588eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_pole_z_ = z;
589eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  CacheCalcs();
590eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
591eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
592eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Planet::SetPlanetEquator(float x, float y, float z) {
593eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // This is really over-ridden by spin at the momenent.
594eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_equator_x_ = x;
595eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_equator_y_ = y;
596eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_equator_z_ = z;
597eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  CacheCalcs();
598eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
599eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
600eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Planet::SetPlanetSpin(float x, float y) {
601eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_spin_x_ = x;
602eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  planet_spin_y_ = y;
603eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  CacheCalcs();
604eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
605eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
606eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Run a simple sim to spin the planet.  Update loop is run once per frame.
607eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Called from the main thread only and only when the worker threads are idle.
608eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Planet::UpdateSim() {
609eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float x = planet_spin_x_ + ui_spin_x_;
610eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  float y = planet_spin_y_ + ui_spin_y_;
611eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // keep in nice range
612eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (x > (kPI * 2.0f))
613eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    x = x - kPI * 2.0f;
614eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  else if (x < (-kPI * 2.0f))
615eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    x = x + kPI * 2.0f;
616eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (y > (kPI * 2.0f))
617eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    y = y - kPI * 2.0f;
618eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  else if (y < (-kPI * 2.0f))
619eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    y = y + kPI * 2.0f;
620eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  SetPlanetSpin(x, y);
621eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
622eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
623eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Planet::StartBenchmark() {
624eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // For more consistent benchmark numbers, reset to default state.
625eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  Reset();
626eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  printf("Benchmark started...\n");
627eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  benchmark_frame_counter_ = kFramesToBenchmark;
628eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  benchmarking_ = true;
629eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  benchmark_start_time_ = getseconds();
630eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
631eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
632eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Planet::EndBenchmark() {
633eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  benchmark_end_time_ = getseconds();
634eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  printf("Benchmark ended... time: %2.5f\n",
635eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      benchmark_end_time_ - benchmark_start_time_);
636eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  benchmarking_ = false;
637eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  benchmark_frame_counter_ = 0;
638eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  double total_time = benchmark_end_time_ - benchmark_start_time_;
639eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Send benchmark result to JS.
640eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  PostUpdateMessage("benchmark_result", total_time);
641eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
642eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
643eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Planet::SetZoom(float zoom) {
644eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  ui_zoom_ = std::min(kZoomMax, std::max(kZoomMin, zoom));
645eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  SetEyeXYZ(0.0f, 0.0f, -ui_zoom_);
646eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
647eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
648eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Planet::SetLight(float light) {
649eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  ui_light_ = std::min(kLightMax, std::max(kLightMin, light));
650eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  SetDiffuseRGB(0.8f * ui_light_, 0.8f * ui_light_, 0.8f * ui_light_);
651eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  SetAmbientRGB(0.4f * ui_light_, 0.4f * ui_light_, 0.4f * ui_light_);
652eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
653eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
654eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Planet::SetTexture(const std::string& name, int width, int height,
655eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                        uint32_t* pixels) {
656eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (pixels) {
657eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (name == "earth.jpg") {
658eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      delete base_tex_;
659eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      base_tex_ = new Texture(width, height, pixels);
660eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    } else if (name == "earthnight.jpg") {
661eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      delete night_tex_;
662eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      night_tex_ = new Texture(width, height, pixels);
663eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
664eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
665eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
666eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
6673240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch// Handle input events from the user and messages from JS.
6683240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdochvoid Planet::HandleEvent(PSEvent* ps_event) {
6693240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  // Give the 2D context a chance to process the event.
6703240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  if (0 != PSContext2DHandleEvent(ps_context_, ps_event))
6713240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    return;
6723240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  if (ps_event->type == PSE_INSTANCE_HANDLEINPUT) {
6733240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    // Convert Pepper Simple event to a PPAPI C++ event
6743240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    pp::InputEvent event(ps_event->as_resource);
6753240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    switch (event.GetType()) {
6763240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      case PP_INPUTEVENT_TYPE_KEYDOWN: {
6773240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        pp::KeyboardInputEvent key(event);
6783240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        uint32_t key_code = key.GetKeyCode();
6793240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        if (key_code == 84)  // 't' key
6803240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch          if (!benchmarking_)
6813240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch            StartBenchmark();
6823240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        break;
683eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      }
6843240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      case PP_INPUTEVENT_TYPE_MOUSEMOVE:
6853240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      case PP_INPUTEVENT_TYPE_MOUSEDOWN: {
6863240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        pp::MouseInputEvent mouse = pp::MouseInputEvent(event);
6873240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        if (mouse.GetModifiers() & PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN) {
6883240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch          PP_Point delta = mouse.GetMovement();
6893240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch          float delta_x = static_cast<float>(delta.x);
6903240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch          float delta_y = static_cast<float>(delta.y);
6913240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch          float spin_x = std::min(4.0f, std::max(-4.0f, delta_x * 0.5f));
6923240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch          float spin_y = std::min(4.0f, std::max(-4.0f, delta_y * 0.5f));
6933240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch          ui_spin_x_ = spin_x / 100.0f;
6943240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch          ui_spin_y_ = spin_y / 100.0f;
6953240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        }
6963240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        break;
6973240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      }
6983240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      case PP_INPUTEVENT_TYPE_WHEEL: {
6993240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        pp::WheelInputEvent wheel = pp::WheelInputEvent(event);
7003240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        PP_FloatPoint ticks = wheel.GetTicks();
7013240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        SetZoom(ui_zoom_ + (ticks.x + ticks.y) * kWheelSpeed);
7023240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        // Update html slider by sending update message to JS.
7033240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        PostUpdateMessage("set_zoom", ui_zoom_);
7043240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        break;
7053240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      }
7063240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      default:
7073240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        break;
708eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
7093240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  } else if (ps_event->type == PSE_INSTANCE_HANDLEMESSAGE) {
7103240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    // Convert Pepper Simple message to PPAPI C++ vars
7113240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    pp::Var var(ps_event->as_var);
7123240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    if (var.is_dictionary()) {
7133240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      pp::VarDictionary dictionary(var);
7143240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      std::string message = dictionary.Get("message").AsString();
7153240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      if (message == "run benchmark" && !benchmarking_) {
7163240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        StartBenchmark();
7173240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      } else if (message == "set_light") {
7183240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        SetLight(static_cast<float>(dictionary.Get("value").AsDouble()));
7193240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      } else if (message == "set_zoom") {
7203240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        SetZoom(static_cast<float>(dictionary.Get("value").AsDouble()));
7213240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      } else if (message == "set_threads") {
7223240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        int threads = dictionary.Get("value").AsInt();
7233240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        delete workers_;
7243240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        workers_ = new ThreadPool(threads);
7253240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      } else if (message == "texture") {
7263240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        std::string name = dictionary.Get("name").AsString();
7273240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        int width = dictionary.Get("width").AsInt();
7283240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        int height = dictionary.Get("height").AsInt();
7293240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        pp::VarArrayBuffer array_buffer(dictionary.Get("data"));
7303240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        if (!name.empty() && !array_buffer.is_null()) {
7313240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch          if (width > 0 && height > 0) {
7323240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch            uint32_t* pixels = static_cast<uint32_t*>(array_buffer.Map());
7333240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch            SetTexture(name, width, height, pixels);
7343240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch            array_buffer.Unmap();
7353240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch          }
7363240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        }
7373240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      }
7383240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    } else {
7393240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      printf("Handle message unknown type: %s\n", var.DebugString().c_str());
740eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
741eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
742eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
743eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
744eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// PostUpdateMessage() helper function for sending small messages to JS.
745eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Planet::PostUpdateMessage(const char* message_name, double value) {
746eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  pp::VarDictionary message;
747eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  message.Set("message", message_name);
748eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  message.Set("value", value);
7493240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  PSInterfaceMessaging()->PostMessage(PSGetInstanceId(), message.pp_var());
750eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
751eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
752eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Planet::Update() {
7533240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  // When benchmarking is running, don't update display via
7543240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  // PSContext2DSwapBuffer() - vsync is enabled by default, and will throttle
7553240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  // the benchmark results.
7563240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  PSContext2DGetBuffer(ps_context_);
7573240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  if (NULL == ps_context_->data)
7583240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    return;
7593240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch
760eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  do {
761eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    UpdateSim();
762eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    Render();
763eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (!benchmarking_) break;
764eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    --benchmark_frame_counter_;
765eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  } while (benchmark_frame_counter_ > 0);
766eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (benchmarking_)
767eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    EndBenchmark();
768eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
7693240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  PSContext2DSwapBuffer(ps_context_);
7703240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch}
7713240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch
772eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
7733240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch// Starting point for the module.  We do not use main since it would
7743240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch// collide with main in libppapi_cpp.
7753240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdochint example_main(int argc, char* argv[]) {
7763240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  Planet earth;
7773240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  while (true) {
7783240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    PSEvent* ps_event;
7793240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    // Consume all available events
7803240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    while ((ps_event = PSEventTryAcquire()) != NULL) {
7813240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      earth.HandleEvent(ps_event);
7823240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      PSEventRelease(ps_event);
7833240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    }
7843240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    // Do simulation, render and present.
7853240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    earth.Update();
786eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
787eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
7883240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  return 0;
789eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
790eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
7913240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch// Register the function to call once the Instance Object is initialized.
7923240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch// see: pappi_simple/ps_main.h
7933240926e260ce088908e02ac07a6cf7b0c0cbf44Ben MurdochPPAPI_SIMPLE_REGISTER_MAIN(example_main);
794