| OLD | NEW |
| (Empty) | |
| 1 /* |
| 2 * Copyright (c) 2011 The Native Client Authors. All rights reserved. |
| 3 * Use of this source code is governed by a BSD-style license that can be |
| 4 * found in the LICENSE file. |
| 5 */ |
| 6 |
| 7 // NaCl Earth demo |
| 8 // Ray trace planet Earth |
| 9 |
| 10 #include "native_client/tests/earth/earth.h" |
| 11 |
| 12 #include <assert.h> |
| 13 #include <errno.h> |
| 14 #include <math.h> |
| 15 #include <pthread.h> |
| 16 #include <semaphore.h> |
| 17 #include <stdarg.h> |
| 18 #include <stdint.h> |
| 19 #include <stdio.h> |
| 20 #include <stdlib.h> |
| 21 #include <string.h> |
| 22 #include <unistd.h> |
| 23 |
| 24 #define HAVE_THREADS 1 |
| 25 #include "native_client/common/worker.h" |
| 26 |
| 27 // print/debug messages |
| 28 static void InfoPrintf(const char *fmt, ...) { |
| 29 va_list argptr; |
| 30 va_start (argptr, fmt); |
| 31 vfprintf (stderr, fmt, argptr); |
| 32 va_end (argptr); |
| 33 fflush(stderr); |
| 34 } |
| 35 |
| 36 extern "C" void DebugPrintf(const char *fmt, ...) { |
| 37 va_list argptr; |
| 38 fprintf (stderr, "@@@ EARTH "); |
| 39 |
| 40 va_start (argptr, fmt); |
| 41 vfprintf (stderr, fmt, argptr); |
| 42 va_end (argptr); |
| 43 fflush(stderr); |
| 44 } |
| 45 |
| 46 // global properties |
| 47 const float kPI = M_PI; |
| 48 const float kOneOverPI = 1.0f / kPI; |
| 49 const float kOneOver2PI = 1.0f / (2.0f * kPI); |
| 50 const float kOneOver255 = 1.0f / 255.0f; |
| 51 const int kArcCosineTableSize = 4096; |
| 52 const float kMaxWindow = 4096; |
| 53 const int kEarthTextureWidth = 1024; |
| 54 const int kEarthTextureHeight = 512; |
| 55 const int kMaxFrames = 1000000; |
| 56 const int kRegionRatio = 8; |
| 57 int g_window_width = 512; |
| 58 int g_window_height = 512; |
| 59 int g_num_frames = 300; |
| 60 bool g_ask_sysconf = true; |
| 61 int g_num_threads = 4; // possibly overridden by sysconf() |
| 62 int g_num_regions = 4; // possibly overridden by sysconf() |
| 63 bool g_multi_threading = false; // can be overridden on cmd line |
| 64 |
| 65 int g_frame_checksum = 0; // used for nacl module testing |
| 66 |
| 67 // seed for rand_r() - we only call rand_r from main thread. |
| 68 static unsigned int g_seed = 0xC0DE533D; |
| 69 |
| 70 // random number helper |
| 71 inline unsigned char rand255() { |
| 72 return static_cast<unsigned char>(rand_r(&g_seed) & 255); |
| 73 } |
| 74 |
| 75 // random number helper |
| 76 inline float frand() { |
| 77 return (static_cast<float>(rand_r(&g_seed)) / static_cast<float>(RAND_MAX)); |
| 78 } |
| 79 |
| 80 // build a packed color |
| 81 inline uint32_t MakeRGBA(uint32_t r, uint32_t g, uint32_t b, uint32_t a) { |
| 82 return (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)); |
| 83 } |
| 84 |
| 85 // extraction routines |
| 86 inline float ExtractR(uint32_t c) { |
| 87 return static_cast<float>(c & 0xFF) * kOneOver255; |
| 88 } |
| 89 |
| 90 inline float ExtractG(uint32_t c) { |
| 91 return static_cast<float>((c & 0xFF00) >> 8) * kOneOver255; |
| 92 } |
| 93 |
| 94 inline float ExtractB(uint32_t c) { |
| 95 return static_cast<float>((c & 0xFF0000) >> 16) * kOneOver255; |
| 96 } |
| 97 |
| 98 |
| 99 // simple container for earth texture |
| 100 struct Texture { |
| 101 int width, height; |
| 102 unsigned int pixels[kEarthTextureWidth * kEarthTextureHeight]; |
| 103 }; |
| 104 |
| 105 |
| 106 // compile our texture straight in to avoid runtime filesystem |
| 107 Texture g_earth = { |
| 108 kEarthTextureWidth, kEarthTextureHeight, { |
| 109 #include "native_client/tests/earth/earth_image.inc" |
| 110 } |
| 111 }; |
| 112 |
| 113 |
| 114 struct Surface { |
| 115 int width, height, pitch; |
| 116 uint32_t *pixels; |
| 117 Surface(uint32_t *pix, int w, int h) { |
| 118 width = w; |
| 119 height = h; |
| 120 pitch = w; |
| 121 pixels = pix; |
| 122 } |
| 123 }; |
| 124 |
| 125 |
| 126 struct ArcCosine { |
| 127 // slightly larger table so we can interpolate beyond table size |
| 128 float table[kArcCosineTableSize + 2]; |
| 129 float TableLerp(float x); |
| 130 ArcCosine(); |
| 131 ~ArcCosine() { ; } |
| 132 }; |
| 133 |
| 134 |
| 135 ArcCosine::ArcCosine() { |
| 136 // build a slightly larger table to allow for numeric imprecision |
| 137 for (int i = 0; i < (kArcCosineTableSize + 2); ++i) { |
| 138 float f = static_cast<float>(i) / kArcCosineTableSize; |
| 139 f = f * 2.0f - 1.0f; |
| 140 table[i] = acos(f); |
| 141 } |
| 142 } |
| 143 |
| 144 |
| 145 // looks up acos(f) using a table and lerping between entries |
| 146 // (it is expected that input f is between -1 and 1) |
| 147 float ArcCosine::TableLerp(float f) { |
| 148 float x = (f + 1.0f) * 0.5f; |
| 149 x = x * kArcCosineTableSize; |
| 150 int ix = static_cast<int>(x); |
| 151 float fx = static_cast<float>(ix); |
| 152 float dx = x - fx; |
| 153 float af = table[ix]; |
| 154 float af2 = table[ix + 1]; |
| 155 float a = af + (af2 - af) * dx; |
| 156 return a; |
| 157 } |
| 158 |
| 159 |
| 160 // takes a -0..1+ color, clamps it to 0..1 and maps it to 0..255 integer |
| 161 inline unsigned int Clamp255(float x) { |
| 162 if (x < 0.0f) { |
| 163 x = 0.0f; |
| 164 } else if (x > 1.0f) { |
| 165 x = 1.0f; |
| 166 } |
| 167 return (unsigned int)(x * 255.0f); |
| 168 } |
| 169 |
| 170 |
| 171 // Planet class holds information and functionality needed to render |
| 172 // a ray-traced planet into an raw pixel surface |
| 173 class Planet { |
| 174 float planet_radius_; |
| 175 float planet_spin_; |
| 176 float planet_x_, planet_y_, planet_z_; |
| 177 float planet_pole_x_, planet_pole_y_, planet_pole_z_; |
| 178 float planet_equator_x_, planet_equator_y_, planet_equator_z_; |
| 179 float eye_x_, eye_y_, eye_z_; |
| 180 float light_x_, light_y_, light_z_; |
| 181 float diffuse_r_, diffuse_g_, diffuse_b_; |
| 182 float ambient_r_, ambient_g_, ambient_b_; |
| 183 |
| 184 // cached calculations |
| 185 float planet_xyz_; |
| 186 float planet_pole_x_equator_x_; |
| 187 float planet_pole_x_equator_y_; |
| 188 float planet_pole_x_equator_z_; |
| 189 float planet_radius2_; |
| 190 float planet_one_over_radius_; |
| 191 float eye_xyz_; |
| 192 |
| 193 // misc |
| 194 Texture *tex_; |
| 195 Surface surface_; |
| 196 ArcCosine acos_; |
| 197 int num_regions_; |
| 198 WorkerThreadManager *workers_; |
| 199 volatile bool exiting_; |
| 200 bool rendering_; |
| 201 |
| 202 public: |
| 203 |
| 204 // methods prefixed with 'w' are only called by worker threads! |
| 205 // (unless MULTI_THREADING is false) |
| 206 uint32_t* wGetAddr(int x, int y); |
| 207 void wRenderPixelSpan(int x0, int x1, int y); |
| 208 void wMakeRect(int r, int *x, int *y, int *w, int *h); |
| 209 void wRenderRect(int x0, int y0, int x1, int y1); |
| 210 void wWorkerThreadEntry(); |
| 211 |
| 212 // these methods are only called by the main thread |
| 213 void CacheCalcs(); |
| 214 void SetPlanetXYZR(float x, float y, float z, float r); |
| 215 void SetPlanetPole(float x, float y, float z); |
| 216 void SetPlanetEquator(float x, float y, float z); |
| 217 void SetPlanetSpin(float a); |
| 218 void SetEyeXYZ(float x, float y, float z); |
| 219 void SetLightXYZ(float x, float y, float z); |
| 220 void SetAmbientRGB(float r, float g, float b); |
| 221 void SetDiffuseRGB(float r, float g, float b); |
| 222 void SetSurface(Surface surface); |
| 223 bool CreateWorkerThreads(int num); |
| 224 void UpdateSim(); |
| 225 void ParallelRender(); |
| 226 void ParallelRenderSync(); |
| 227 void SequentialRender(); |
| 228 void Render(); |
| 229 void Sync(); |
| 230 Planet(int numRegions, bool multithreading, Texture *tex); |
| 231 ~Planet(); |
| 232 }; |
| 233 |
| 234 // Given a region r, derive a rectangle. Currently this function |
| 235 // slices the main output buffer into equal sized rows. |
| 236 // This function is used to convert a mutex guarded counter into |
| 237 // a rectangular region for a given worker thread to process. |
| 238 // This rectangle shouldn't overlap with work being done by other workers. |
| 239 // If multithreading, this function is only called by the worker threads. |
| 240 void Planet::wMakeRect(int r, int *x, int *y, int *w, int *h) { |
| 241 int dy = surface_.height / num_regions_; |
| 242 *x = 0; |
| 243 *w = surface_.width; |
| 244 *y = r * dy; |
| 245 *h = dy; |
| 246 } |
| 247 |
| 248 |
| 249 inline uint32_t* Planet::wGetAddr(int x, int y) { |
| 250 assert(surface_.pixels); |
| 251 return (surface_.pixels + y * surface_.pitch) + x; |
| 252 } |
| 253 |
| 254 |
| 255 union Convert { |
| 256 float f; |
| 257 int i; |
| 258 Convert(int x) { i = x; } |
| 259 Convert(float x) { f = x; } |
| 260 const int asInt() { return i; } |
| 261 const float asFloat() { return f; } |
| 262 }; |
| 263 |
| 264 |
| 265 inline const int AsInteger(const float f) { |
| 266 Convert u(f); |
| 267 return u.asInt(); |
| 268 } |
| 269 |
| 270 |
| 271 inline const float AsFloat(const int i) { |
| 272 Convert u(i); |
| 273 return u.asFloat(); |
| 274 } |
| 275 |
| 276 |
| 277 const long int kOneAsInteger = AsInteger(1.0f); |
| 278 const float kScaleUp = float(0x00800000); |
| 279 const float kScaleDown = 1.0f / kScaleUp; |
| 280 |
| 281 |
| 282 inline float inline_quick_sqrt(float x) { |
| 283 int i; |
| 284 i = (AsInteger(x) >> 1) + (kOneAsInteger >> 1); |
| 285 return AsFloat(i); |
| 286 } |
| 287 |
| 288 |
| 289 inline float inline_sqrt(float x) { |
| 290 float y; |
| 291 y = inline_quick_sqrt(x); |
| 292 y = (y * y + x) / (2.0f * y); |
| 293 y = (y * y + x) / (2.0f * y); |
| 294 return y; |
| 295 } |
| 296 |
| 297 |
| 298 // This is the meat of the ray tracer. Given a pixel span (x0, x1) on |
| 299 // scanline y, shoot rays into the scene and render what they hit. Use |
| 300 // scanline coherence to do a few optimizations |
| 301 void Planet::wRenderPixelSpan(int x0, int x1, int y) { |
| 302 const int kColorBlack = MakeRGBA(0, 0, 0, 0xFF); |
| 303 float y0 = eye_y_; |
| 304 float z0 = eye_z_; |
| 305 float y1 = (static_cast<float>(y) / surface_.height) * 2.0f - 1.0f; |
| 306 float z1 = 0.0f; |
| 307 float dy = (y1 - y0); |
| 308 float dz = (z1 - z0); |
| 309 float dy_dy_dz_dz = dy * dy + dz * dz; |
| 310 float two_dy_y0_y_two_dz_z0_z = 2.0f * dy * (y0 - planet_y_) + |
| 311 2.0f * dz * (z0 - planet_z_); |
| 312 float planet_xyz_eye_xyz = planet_xyz_ + eye_xyz_; |
| 313 float y_y0_z_z0 = planet_y_ * y0 + planet_z_ * z0; |
| 314 float oowidth = 1.0f / surface_.width; |
| 315 uint32_t *pixels = this->wGetAddr(x0, y); |
| 316 for (int x = x0; x <= x1; ++x) { |
| 317 // scan normalized screen -1..1 |
| 318 float x1 = (static_cast<float>(x) * oowidth) * 2.0f - 1.0f; |
| 319 // eye |
| 320 float x0 = eye_x_; |
| 321 // delta from screen to eye |
| 322 float dx = (x1 - x0); |
| 323 // build a, b, c |
| 324 float a = dx * dx + dy_dy_dz_dz; |
| 325 float b = 2.0f * dx * (x0 - planet_x_) + two_dy_y0_y_two_dz_z0_z; |
| 326 float c = planet_xyz_eye_xyz + |
| 327 -2.0f * (planet_x_ * x0 + y_y0_z_z0) - (planet_radius2_); |
| 328 // calculate discriminant |
| 329 float disc = b * b - 4.0f * a * c; |
| 330 |
| 331 // did ray hit the sphere? |
| 332 if (disc < 0.0f) { |
| 333 *pixels = kColorBlack; |
| 334 ++pixels; |
| 335 continue; |
| 336 } |
| 337 |
| 338 // calc parametric t value |
| 339 float t = (-b - inline_sqrt(disc)) / (2.0f * a); |
| 340 float px = x0 + t * dx; |
| 341 float py = y0 + t * dy; |
| 342 float pz = z0 + t * dz; |
| 343 float nx = (px - planet_x_) * planet_one_over_radius_; |
| 344 float ny = (py - planet_y_) * planet_one_over_radius_; |
| 345 float nz = (pz - planet_z_) * planet_one_over_radius_; |
| 346 |
| 347 float Lx = (light_x_ - px); |
| 348 float Ly = (light_y_ - py); |
| 349 float Lz = (light_z_ - pz); |
| 350 float Lq = 1.0f / inline_quick_sqrt(Lx * Lx + Ly * Ly + Lz * Lz); |
| 351 Lx *= Lq; |
| 352 Ly *= Lq; |
| 353 Lz *= Lq; |
| 354 float d = (Lx * nx + Ly * ny + Lz * nz); |
| 355 float pr = (diffuse_r_ * d) + ambient_r_; |
| 356 float pg = (diffuse_g_ * d) + ambient_g_; |
| 357 float pb = (diffuse_b_ * d) + ambient_b_; |
| 358 float ds = -(nx * planet_pole_x_ + |
| 359 ny * planet_pole_y_ + |
| 360 nz * planet_pole_z_); |
| 361 float ang = acos_.TableLerp(ds); |
| 362 float v = ang * kOneOverPI; |
| 363 float dp = planet_equator_x_ * nx + |
| 364 planet_equator_y_ * ny + |
| 365 planet_equator_z_ * nz; |
| 366 float w = dp / sin(ang); |
| 367 if (w > 1.0f) w = 1.0f; |
| 368 if (w < -1.0f) w = -1.0f; |
| 369 float th = acos_.TableLerp(w) * kOneOver2PI; |
| 370 float dps = planet_pole_x_equator_x_ * nx + |
| 371 planet_pole_x_equator_y_ * ny + |
| 372 planet_pole_x_equator_z_ * nz; |
| 373 float u; |
| 374 if (dps < 0.0f) |
| 375 u = th; |
| 376 else |
| 377 u = 1.0f - th; |
| 378 |
| 379 int tx = static_cast<int>(u * tex_->width); |
| 380 int ty = static_cast<int>(v * tex_->height); |
| 381 int offset = tx + ty * tex_->width; |
| 382 uint32_t texel = tex_->pixels[offset]; |
| 383 float tr = ExtractR(texel); |
| 384 float tg = ExtractG(texel); |
| 385 float tb = ExtractB(texel); |
| 386 |
| 387 unsigned int ir = Clamp255(pr * tr); |
| 388 unsigned int ig = Clamp255(pg * tg); |
| 389 unsigned int ib = Clamp255(pb * tb); |
| 390 unsigned int color = MakeRGBA(ir, ig, ib, 0xFF); |
| 391 |
| 392 *pixels = color; |
| 393 ++pixels; |
| 394 } |
| 395 } |
| 396 |
| 397 |
| 398 // Renders a rectangular area of the screen, scan line at a time |
| 399 void Planet::wRenderRect(int x, int y, int w, int h) { |
| 400 for (int j = y; j < (y + h); ++j) { |
| 401 this->wRenderPixelSpan(x, x + w - 1, j); |
| 402 } |
| 403 } |
| 404 |
| 405 |
| 406 // Thread entry point Planet::wWorkerThread() |
| 407 // This is the main loop for the worker thread(s). It waits for work |
| 408 // by testing a semaphore, which will sleep this thread until work arrives. |
| 409 // It then grabs a mutex protected counter (which is also decremented) |
| 410 // and uses this value to determine which subregion this thread should be |
| 411 // processing. When rendering is finished the worker then pings the main |
| 412 // thread via PostDone() semaphore. |
| 413 // If multithreading, this function is only called by the worker threads. |
| 414 void Planet::wWorkerThreadEntry() { |
| 415 |
| 416 // we're a 'detached' thread... |
| 417 // (so we should automagically die when the main thread exits) |
| 418 while (!exiting_) { |
| 419 // wait for some work |
| 420 workers_->WaitWork(); |
| 421 |
| 422 // if main thread is exiting, have worker exit too |
| 423 if (exiting_) break; |
| 424 // okay, grab region to work on from worker counter |
| 425 int region = workers_->DecCounter(); |
| 426 if (region < 0) { |
| 427 // This indicates we're not sync'ing properly |
| 428 InfoPrintf("Region value went negative!\n"); |
| 429 exit(-1); |
| 430 } |
| 431 // convert region # into x0, y0, x1, y1 rectangle |
| 432 int x, y, w, h; |
| 433 this->wMakeRect(region, &x, &y, &w, &h); |
| 434 |
| 435 // render this rectangle |
| 436 this->wRenderRect(x, y, w, h); |
| 437 |
| 438 // let main thread know we've finished a region |
| 439 workers_->PostDone(); |
| 440 } |
| 441 } |
| 442 |
| 443 |
| 444 // Entry point for worker thread. (Can't really pass a member function to |
| 445 // pthread_create(), so we have to do this little round-about) |
| 446 // If multithreading, this function is only called by the worker threads. |
| 447 void* wWorkerThreadEntry(void *args) { |
| 448 // unpack this pointer |
| 449 Planet *pPlanet = reinterpret_cast<Planet*>(args); |
| 450 pPlanet->wWorkerThreadEntry(); |
| 451 return NULL; |
| 452 } |
| 453 |
| 454 |
| 455 // Create worker threads and pass along our this pointer. |
| 456 // If workers_ is NULL, we're running in non-threaded mode. |
| 457 bool Planet::CreateWorkerThreads(int num) { |
| 458 if (NULL != workers_) { |
| 459 return workers_->CreateThreadPool(num, ::wWorkerThreadEntry, this); |
| 460 } else { |
| 461 return true; |
| 462 } |
| 463 } |
| 464 |
| 465 |
| 466 // Run a simple sim to spin the planet. Update loop is run once per frame. |
| 467 // Called from the main thread only and only when the worker threads are idle. |
| 468 void Planet::UpdateSim() { |
| 469 float m = planet_spin_ + 0.01f; |
| 470 // keep in nice range |
| 471 if (m > (kPI * 2.0f)) |
| 472 m = m - kPI * 2.0f; |
| 473 SetPlanetSpin(m); |
| 474 } |
| 475 |
| 476 |
| 477 // This is the main thread's entry point to dispatch rendering of the planet. |
| 478 // First, it sets the region counter to its max value. This mutex guarded |
| 479 // counter is how worker threads determine which region of the diagram they |
| 480 // should be working on. Then it pings the PostWork semaphore multiple times |
| 481 // to wake up the sleeping worker threads. |
| 482 void Planet::ParallelRender() { |
| 483 |
| 484 // At this point, all worker threads are idle and sleeping |
| 485 |
| 486 // setup barrier counter before we wake workers |
| 487 workers_->SetCounter(num_regions_); |
| 488 rendering_ = true; |
| 489 |
| 490 // wake up the workers |
| 491 for (int i = 0; i < num_regions_; ++i) { |
| 492 workers_->PostWork(); |
| 493 } |
| 494 // At this point, all worker threads are awake and busy grabbing |
| 495 // work assignments by reading and decrementing the counter. |
| 496 } |
| 497 |
| 498 |
| 499 // ParallelRenderSync will sleep a little by waiting for the workers to |
| 500 // finish. It does that by waiting on the WaitDone semaphore. |
| 501 void Planet::ParallelRenderSync() { |
| 502 |
| 503 // Only wait if rendering is in progress. |
| 504 if (rendering_) { |
| 505 // wait for all work to be done |
| 506 for (int i = 0; i < num_regions_; ++i) { |
| 507 workers_->WaitDone(); |
| 508 } |
| 509 // verify that our counter is where we expect it to be |
| 510 int c = workers_->DecCounter(); |
| 511 if (-1 != c) { |
| 512 InfoPrintf("We're not syncing correctly! (%d)\n", c); |
| 513 exit(-1); |
| 514 } |
| 515 rendering_ = false; |
| 516 } |
| 517 // At this point, all worker threads are idle and sleeping again. |
| 518 // The main thread is free to muck with shared data, such |
| 519 // as updating the earth spin in the sim routine. |
| 520 } |
| 521 |
| 522 |
| 523 // Performs all rendering from the main thread. |
| 524 void Planet::SequentialRender() { |
| 525 this->wRenderRect(0, 0, surface_.width, surface_.height); |
| 526 } |
| 527 |
| 528 |
| 529 // Renders the Planet diagram. |
| 530 // Picks either parallel or sequential rendering implementation. |
| 531 void Planet::Render() { |
| 532 if (NULL == workers_) { |
| 533 this->SequentialRender(); |
| 534 } else { |
| 535 this->ParallelRender(); |
| 536 } |
| 537 } |
| 538 |
| 539 |
| 540 // Waits for a rendering to complete. |
| 541 void Planet::Sync() { |
| 542 if (NULL != workers_) { |
| 543 this->ParallelRenderSync(); |
| 544 } |
| 545 } |
| 546 |
| 547 |
| 548 // pre-calculations to make inner loops faster |
| 549 // these need to be recalculated when values change |
| 550 void Planet::CacheCalcs() { |
| 551 planet_xyz_ = planet_x_ * planet_x_ + |
| 552 planet_y_ * planet_y_ + |
| 553 planet_z_ * planet_z_; |
| 554 planet_radius2_ = planet_radius_ * planet_radius_; |
| 555 planet_one_over_radius_ = 1.0f / planet_radius_; |
| 556 eye_xyz_ = eye_x_ * eye_x_ + eye_y_ * eye_y_ + eye_z_ * eye_z_; |
| 557 // spin vector from center->equator |
| 558 planet_equator_x_ = cos(planet_spin_); |
| 559 planet_equator_y_ = 0.0f; |
| 560 planet_equator_z_ = sin(planet_spin_); |
| 561 // cache cross product of pole & equator |
| 562 planet_pole_x_equator_x_ = planet_pole_y_ * planet_equator_z_ - |
| 563 planet_pole_z_ * planet_equator_y_; |
| 564 planet_pole_x_equator_y_ = planet_pole_z_ * planet_equator_x_ - |
| 565 planet_pole_x_ * planet_equator_z_; |
| 566 planet_pole_x_equator_z_ = planet_pole_x_ * planet_equator_y_ - |
| 567 planet_pole_y_ * planet_equator_x_; |
| 568 } |
| 569 |
| 570 |
| 571 void Planet::SetPlanetXYZR(float x, float y, float z, float r) { |
| 572 planet_x_ = x; |
| 573 planet_y_ = y; |
| 574 planet_z_ = z; |
| 575 planet_radius_ = r; |
| 576 CacheCalcs(); |
| 577 } |
| 578 |
| 579 |
| 580 void Planet::SetEyeXYZ(float x, float y, float z) { |
| 581 eye_x_ = x; |
| 582 eye_y_ = y; |
| 583 eye_z_ = z; |
| 584 CacheCalcs(); |
| 585 } |
| 586 |
| 587 |
| 588 void Planet::SetLightXYZ(float x, float y, float z) { |
| 589 light_x_ = x; |
| 590 light_y_ = y; |
| 591 light_z_ = z; |
| 592 CacheCalcs(); |
| 593 } |
| 594 |
| 595 |
| 596 void Planet::SetAmbientRGB(float r, float g, float b) { |
| 597 ambient_r_ = r; |
| 598 ambient_g_ = g; |
| 599 ambient_b_ = b; |
| 600 CacheCalcs(); |
| 601 } |
| 602 |
| 603 |
| 604 void Planet::SetDiffuseRGB(float r, float g, float b) { |
| 605 diffuse_r_ = r; |
| 606 diffuse_g_ = g; |
| 607 diffuse_b_ = b; |
| 608 CacheCalcs(); |
| 609 } |
| 610 |
| 611 |
| 612 void Planet::SetPlanetPole(float x, float y, float z) { |
| 613 planet_pole_x_ = x; |
| 614 planet_pole_y_ = y; |
| 615 planet_pole_z_ = z; |
| 616 CacheCalcs(); |
| 617 } |
| 618 |
| 619 |
| 620 void Planet::SetPlanetEquator(float x, float y, float z) { |
| 621 // this is really over-ridden by spin at the momenent |
| 622 planet_equator_x_ = x; |
| 623 planet_equator_y_ = y; |
| 624 planet_equator_z_ = z; |
| 625 CacheCalcs(); |
| 626 } |
| 627 |
| 628 |
| 629 void Planet::SetPlanetSpin(float a) { |
| 630 planet_spin_ = a; |
| 631 CacheCalcs(); |
| 632 } |
| 633 |
| 634 |
| 635 void Planet::SetSurface(Surface surface) { |
| 636 surface_ = surface; |
| 637 } |
| 638 |
| 639 |
| 640 // Setups and initializes planet data structures. |
| 641 // Seed planet, eye, and light |
| 642 Planet::Planet(int numRegions, bool multi, Texture *tex) : |
| 643 planet_radius_(1.0f), |
| 644 planet_spin_(0.0f), |
| 645 planet_x_(0.0f), |
| 646 planet_y_(0.0f), |
| 647 planet_z_(0.0f), |
| 648 planet_pole_x_(0.0f), |
| 649 planet_pole_y_(0.0f), |
| 650 planet_pole_z_(0.0f), |
| 651 planet_equator_x_(0.0f), |
| 652 planet_equator_y_(0.0f), |
| 653 planet_equator_z_(0.0f), |
| 654 eye_x_(0.0f), |
| 655 eye_y_(0.0f), |
| 656 eye_z_(0.0f), |
| 657 light_x_(0.0f), |
| 658 light_y_(0.0f), |
| 659 light_z_(0.0f), |
| 660 diffuse_r_(0.0f), |
| 661 diffuse_g_(0.0f), |
| 662 diffuse_b_(0.0f), |
| 663 ambient_r_(0.0f), |
| 664 ambient_g_(0.0f), |
| 665 ambient_b_(0.0f), |
| 666 planet_xyz_(0.0f), |
| 667 planet_pole_x_equator_x_(0.0f), |
| 668 planet_pole_x_equator_y_(0.0f), |
| 669 planet_pole_x_equator_z_(0.0f), |
| 670 planet_radius2_(0.0f), |
| 671 planet_one_over_radius_(0.0f), |
| 672 eye_xyz_(0.0f), |
| 673 surface_(NULL, 0, 0) { |
| 674 num_regions_ = numRegions; |
| 675 workers_ = multi ? new WorkerThreadManager() : NULL; |
| 676 tex_ = tex; |
| 677 exiting_ = false; |
| 678 rendering_ = false; |
| 679 |
| 680 this->SetPlanetXYZR(0.0f, 0.0f, 48.0f, 4.0f); |
| 681 this->SetEyeXYZ(0.0f, 0.0f, -14.0f); |
| 682 this->SetLightXYZ(-8.0f, -4.0f, 2.0f); |
| 683 this->SetAmbientRGB(0.4f, 0.4f, 0.4f); |
| 684 this->SetDiffuseRGB(0.8f, 0.8f, 0.8f); |
| 685 this->SetPlanetPole(0.0f, 1.0f, 0.0f); |
| 686 this->SetPlanetEquator(1.0f, 0.0f, 0.0f); |
| 687 this->SetPlanetSpin(kPI / 2.0f); |
| 688 } |
| 689 |
| 690 |
| 691 // Frees up planet resources. |
| 692 Planet::~Planet() { |
| 693 if (workers_) { |
| 694 exiting_ = true; |
| 695 // wake up the worker threads from their slumber |
| 696 workers_->PostWorkAll(); |
| 697 workers_->JoinAll(); |
| 698 delete workers_; |
| 699 } |
| 700 } |
| 701 |
| 702 |
| 703 // Clamps input to the max we can realistically support. |
| 704 static int ClampThreads(int num) { |
| 705 const int max = 128; |
| 706 if (num > max) { |
| 707 return max; |
| 708 } |
| 709 return num; |
| 710 } |
| 711 |
| 712 |
| 713 static void PrintCredits() { |
| 714 static const char *credit = |
| 715 "\n" |
| 716 "Image Credit:\n" |
| 717 "\n" |
| 718 "NASA Goddard Space Flight Center Image by Reto Stöckli (land surface,\n" |
| 719 "shallow water, clouds). Enhancements by Robert Simmon (ocean color,\n" |
| 720 "compositing, 3D globes, animation).\n" |
| 721 "Data and technical support: MODIS Land Group; MODIS Science Data,\n" |
| 722 "Support Team; MODIS Atmosphere Group; MODIS Ocean Group\n" |
| 723 "Additional data:\n" |
| 724 "USGS EROS Data Center (topography); USGS Terrestrial Remote Sensing\n" |
| 725 "Flagstaff Field Center (Antarctica); Defense Meteorological\n" |
| 726 "Satellite Program (city lights).\n" |
| 727 "\n"; |
| 728 InfoPrintf(credit); |
| 729 } |
| 730 |
| 731 // If user specifies options on cmd line, parse them |
| 732 // here and update global settings as needed. |
| 733 static void ParseCmdLineArgs(int argc, const char *argn[], const char *argv[]) { |
| 734 // look for cmd line args |
| 735 PrintCredits(); |
| 736 if (argc > 1) { |
| 737 for (int i = 1; i < argc; ++i) { |
| 738 if (argn[i] == strstr(argn[i], "numthreads")) { |
| 739 int numthreads = atoi(argv[i]); |
| 740 if (numthreads > 1) { |
| 741 g_multi_threading = true; |
| 742 g_num_threads = numthreads; |
| 743 g_num_regions = numthreads * kRegionRatio; |
| 744 InfoPrintf("Using %d threads\n", numthreads); |
| 745 } else { |
| 746 InfoPrintf("Could not parse numthreads=%s.\n", argv[i]); |
| 747 } |
| 748 } else if (argn[i] == strstr(argn[i], "usesysconf")) { |
| 749 if (argv[i] == strstr(argv[i], "true")) { |
| 750 g_multi_threading = true; |
| 751 g_ask_sysconf = true; |
| 752 } else if (argv[i] == strstr(argv[i], "false")) { |
| 753 g_multi_threading = false; |
| 754 } else { |
| 755 InfoPrintf("Could not parse usesysconf=%s.\n", argv[i]); |
| 756 } |
| 757 } else if (argn[i] == strstr(argn[i], "xwidth")) { |
| 758 int w = atoi(argv[i]); |
| 759 if ((w > 0) && (w < kMaxWindow)) g_window_width = w; |
| 760 } else if (argn[i] == strstr(argn[i], "xheight")) { |
| 761 int h = atoi(argv[i]); |
| 762 if ((h > 0) && (h < kMaxWindow)) g_window_height = h; |
| 763 } else if (argn[i] == strstr(argn[i], "frames")) { |
| 764 int f = atoi(argv[i]); |
| 765 if ((f > 0) && (f < kMaxFrames)) g_num_frames = f; |
| 766 } else if (argn[i] == strstr(argn[i], "id")) { |
| 767 /* ignore id */ |
| 768 } else if (argn[i] == strstr(argn[i], "src")) { |
| 769 /* ignore src */ |
| 770 } else if (argn[i] == strstr(argn[i], "style")) { |
| 771 /* ignore style */ |
| 772 } else if (argn[i] == strstr(argn[i], "type")) { |
| 773 /* ignore type */ |
| 774 } else { |
| 775 if (argn[i] != strstr(argn[i], "help")) { |
| 776 InfoPrintf("unknown option %s=%s\n", argn[i], argv[i]); |
| 777 } |
| 778 InfoPrintf("Earth Pepper Demo\n" |
| 779 "usage: numthreads=\"n\" render using n threads.\n" |
| 780 " usesysconf=true use sysconf to set thread count." |
| 781 "\n" |
| 782 " xwidth=\"w\" width of window.\n" |
| 783 " xheight=\"h\" height of window.\n" |
| 784 " framecount=\"n\" number of frames.\n" |
| 785 " help show this screen.\n"); |
| 786 } |
| 787 } |
| 788 } |
| 789 |
| 790 InfoPrintf("Multi-threading %s.\n", |
| 791 g_multi_threading ? "enabled" : "disabled"); |
| 792 |
| 793 // see if the system can tell us # cpus |
| 794 if ((g_ask_sysconf) && (g_multi_threading)) { |
| 795 int ncpu = sysconf(_SC_NPROCESSORS_ONLN); |
| 796 if (ncpu > 1) { |
| 797 InfoPrintf("Using %d processors based on sysconf.\n", ncpu); |
| 798 g_num_threads = ncpu; |
| 799 g_num_regions = ncpu * kRegionRatio; |
| 800 } |
| 801 } |
| 802 |
| 803 // clamp threads and regions |
| 804 g_num_threads = ClampThreads(g_num_threads); |
| 805 g_num_regions = ClampThreads(g_num_regions); |
| 806 } |
| 807 |
| 808 Planet *g_planet = NULL; |
| 809 |
| 810 // Parses cmd line options, initializes surface, runs the demo & shuts down. |
| 811 extern "C" void Earth_Init(int argc, const char *argn[], const char *argv[]) { |
| 812 ParseCmdLineArgs(argc, argn, argv); |
| 813 g_planet = new Planet(g_num_regions, g_multi_threading, &g_earth); |
| 814 if (!g_planet->CreateWorkerThreads(g_num_threads)) { |
| 815 DebugPrintf("Earth_Init: thread creation failed. g_num_threads: %d\n", |
| 816 g_num_threads); |
| 817 exit(-1); |
| 818 } |
| 819 } |
| 820 |
| 821 extern "C" void Earth_Draw(uint32_t *image_data, int width, int height) { |
| 822 g_planet->SetSurface(Surface(image_data, width, height)); |
| 823 g_planet->UpdateSim(); |
| 824 g_planet->Render(); |
| 825 } |
| 826 |
| 827 extern "C" void Earth_Sync() { |
| 828 g_planet->Sync(); |
| 829 g_planet->SetSurface(Surface(NULL, 0, 0)); |
| 830 } |
| OLD | NEW |