erebus

annotate liberebus/src/erebus.cc @ 31:53a98c148bf8

- introduced SurfaceGeometry to carry all the geometric information input to BRDF sampling and evaluation functions. - made Reflectance keep an (optional) pointer to its material - simplified PhongRefl::sample_dir, with the help of SurfaceGeometry - worked around microsoft's broken std::thread implementation's deadlock on join
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 07 Jun 2014 09:14:17 +0300
parents 0ced900e15a7
children b1fc96c71bcc
rev   line source
nuclear@2 1 #include <string.h>
nuclear@2 2 #include <limits.h>
nuclear@27 3 #include <algorithm>
nuclear@2 4 #include <chrono>
nuclear@2 5 #include <random>
nuclear@2 6 #include "erebus.h"
nuclear@17 7 #include "erebus_impl.h"
nuclear@4 8 #include "scene.h"
nuclear@4 9 #include "geomobj.h"
nuclear@5 10 #include "rt.h"
nuclear@2 11
nuclear@8 12 #define INF_SAMPLES (INT_MAX / 2)
nuclear@8 13
nuclear@2 14 using namespace std::chrono;
nuclear@2 15
nuclear@26 16 static void render_block(struct erebus *ctx, Block blk);
nuclear@8 17 static void render_pixel(struct erebus *ctx, int x, int y, int sample);
nuclear@2 18
nuclear@2 19 static std::mt19937 rnd_gen;
nuclear@2 20
nuclear@2 21 extern "C" {
nuclear@2 22
nuclear@2 23 struct erebus *erb_init(void)
nuclear@2 24 {
nuclear@31 25 struct erebus *ctx = 0;
nuclear@2 26 try {
nuclear@2 27 ctx = new struct erebus;
nuclear@31 28 ctx->tpool = new ThreadPool;
nuclear@2 29 }
nuclear@2 30 catch(...) {
nuclear@31 31 delete ctx;
nuclear@2 32 return 0;
nuclear@2 33 }
nuclear@2 34
nuclear@10 35 rnd_gen.seed(time(0));
nuclear@10 36
nuclear@4 37 ctx->scn = 0;
nuclear@2 38 ctx->cur_time = 0;
nuclear@26 39 ctx->cur_frame = 0;
nuclear@8 40
nuclear@17 41 erb_setoptf(ctx, ERB_OPT_GAMMA, 2.2);
nuclear@17 42 erb_setopti(ctx, ERB_OPT_MAX_ITER, 6);
nuclear@17 43 erb_setopti(ctx, ERB_OPT_MAX_SAMPLES, INF_SAMPLES);
nuclear@17 44 erb_setopti(ctx, ERB_OPT_NUM_THREADS, -1);
nuclear@15 45
nuclear@15 46 ctx->dbg_nodesel = -1;
nuclear@2 47 return ctx;
nuclear@2 48 }
nuclear@2 49
nuclear@2 50 void erb_destroy(struct erebus *ctx)
nuclear@2 51 {
nuclear@31 52 if(ctx) {
nuclear@31 53 // make sure the threadpool stops BEFORE destroying the framebuffers etc in ctx
nuclear@31 54 delete ctx->tpool;
nuclear@31 55 delete ctx;
nuclear@31 56 }
nuclear@2 57 }
nuclear@2 58
nuclear@2 59 void erb_setopti(struct erebus *ctx, enum erb_option opt, int val)
nuclear@2 60 {
nuclear@17 61 ctx->options[opt].ival = val;
nuclear@17 62 ctx->options[opt].type = Option::Type::INT;
nuclear@2 63 }
nuclear@17 64
nuclear@2 65 void erb_setoptf(struct erebus *ctx, enum erb_option opt, float val)
nuclear@2 66 {
nuclear@17 67 ctx->options[opt].fval = val;
nuclear@17 68 ctx->options[opt].type = Option::Type::FLOAT;
nuclear@2 69 }
nuclear@17 70
nuclear@2 71 void erb_setoptfv(struct erebus *ctx, enum erb_option opt, float *vec)
nuclear@2 72 {
nuclear@2 73 for(int i=0; i<4; i++) {
nuclear@17 74 ctx->options[opt].vval[i] = vec[i];
nuclear@2 75 }
nuclear@17 76 ctx->options[opt].type = Option::Type::VEC;
nuclear@2 77 }
nuclear@2 78
nuclear@2 79 int erb_getopti(struct erebus *ctx, enum erb_option opt)
nuclear@2 80 {
nuclear@17 81 switch(ctx->options[opt].type) {
nuclear@17 82 case Option::Type::INT:
nuclear@17 83 return ctx->options[opt].ival;
nuclear@17 84 case Option::Type::FLOAT:
nuclear@17 85 return (int)ctx->options[opt].fval;
nuclear@17 86 case Option::Type::VEC:
nuclear@17 87 return (int)ctx->options[opt].vval.x;
nuclear@17 88 }
nuclear@17 89 return 0; // can't happen
nuclear@2 90 }
nuclear@17 91
nuclear@2 92 float erb_getoptf(struct erebus *ctx, enum erb_option opt)
nuclear@2 93 {
nuclear@17 94 switch(ctx->options[opt].type) {
nuclear@17 95 case Option::Type::INT:
nuclear@17 96 return (float)ctx->options[opt].ival;
nuclear@17 97 case Option::Type::FLOAT:
nuclear@17 98 return ctx->options[opt].fval;
nuclear@17 99 case Option::Type::VEC:
nuclear@17 100 return ctx->options[opt].vval.x;
nuclear@17 101 }
nuclear@17 102 return 0.0f; // can't happen
nuclear@2 103 }
nuclear@17 104
nuclear@2 105 float *erb_getoptfv(struct erebus *ctx, enum erb_option opt)
nuclear@2 106 {
nuclear@17 107 switch(ctx->options[opt].type) {
nuclear@17 108 case Option::Type::INT:
nuclear@17 109 {
nuclear@17 110 int ival = ctx->options[opt].ival;
nuclear@17 111 ctx->options[opt].vval = Vector4(ival, ival, ival, ival);
nuclear@17 112 }
nuclear@17 113 break;
nuclear@17 114 case Option::Type::FLOAT:
nuclear@17 115 {
nuclear@17 116 float fval = ctx->options[opt].fval;
nuclear@17 117 ctx->options[opt].vval = Vector4(fval, fval, fval, fval);
nuclear@17 118 }
nuclear@17 119 default:
nuclear@17 120 break;
nuclear@17 121 }
nuclear@17 122
nuclear@17 123 return &ctx->options[opt].vval.x;
nuclear@2 124 }
nuclear@2 125
nuclear@2 126 float *erb_get_framebuffer(struct erebus *ctx)
nuclear@2 127 {
nuclear@2 128 return ctx->fbimg.get_pixels();
nuclear@2 129 }
nuclear@2 130
nuclear@2 131 void erb_begin_frame(struct erebus *ctx, long ms)
nuclear@2 132 {
nuclear@8 133 printf("starting new frame...\n");
nuclear@26 134 ++ctx->cur_frame;
nuclear@26 135 ctx->cur_sample = 0;
nuclear@2 136 ctx->cur_time = ms;
nuclear@4 137
nuclear@17 138 int xsz = erb_getopti(ctx, ERB_OPT_WIDTH);
nuclear@17 139 int ysz = erb_getopti(ctx, ERB_OPT_HEIGHT);
nuclear@4 140
nuclear@26 141 if(!ctx->fbimg.get_pixels() || ctx->fbimg.get_width() != xsz || ctx->fbimg.get_height() < ysz) {
nuclear@26 142 ctx->fbimg.create(xsz, ysz);
nuclear@26 143 ctx->accum.create(xsz, ysz);
nuclear@26 144 } else {
nuclear@26 145 ctx->fbimg.clear();
nuclear@26 146 ctx->accum.clear();
nuclear@26 147 }
nuclear@15 148
nuclear@17 149 ctx->inv_gamma = 1.0f / erb_getoptf(ctx, ERB_OPT_GAMMA);
nuclear@26 150
nuclear@26 151 ctx->scn->update(ctx->cur_time);
nuclear@2 152 }
nuclear@2 153
nuclear@2 154 int erb_render(struct erebus *ctx, long timeout)
nuclear@2 155 {
nuclear@2 156 return erb_render_rect(ctx, 0, 0, ctx->fbimg.get_width(), ctx->fbimg.get_height(), timeout);
nuclear@2 157 }
nuclear@2 158
nuclear@26 159 #define BLKSZ 32
nuclear@26 160
nuclear@2 161 int erb_render_rect(struct erebus *ctx, int x, int y, int width, int height, long timeout)
nuclear@2 162 {
nuclear@31 163 while(ctx->tpool->pending()) {
nuclear@26 164 if(timeout > 0) {
nuclear@31 165 long wait_interval = ctx->tpool->wait(timeout);
nuclear@26 166 timeout -= wait_interval;
nuclear@26 167 } else {
nuclear@26 168 return 1;
nuclear@26 169 }
nuclear@26 170 }
nuclear@26 171
nuclear@2 172 if(!width || !height) return -1;
nuclear@2 173
nuclear@26 174 int startx = x;
nuclear@26 175 int endx = x + width;
nuclear@26 176 int endy = y + height;
nuclear@26 177
nuclear@26 178 while(y < endy) {
nuclear@26 179 x = startx;
nuclear@26 180 while(x < endx) {
nuclear@26 181 Block blk;
nuclear@26 182 blk.x = x;
nuclear@26 183 blk.y = y;
nuclear@26 184 blk.width = std::min(BLKSZ, endx - x);
nuclear@26 185 blk.height = std::min(BLKSZ, endy - y);
nuclear@26 186 blk.sample = ctx->cur_sample;
nuclear@26 187 blk.frame = ctx->cur_frame;
nuclear@26 188
nuclear@31 189 ctx->tpool->add_work(std::bind(render_block, ctx, blk));
nuclear@26 190
nuclear@26 191 x += BLKSZ;
nuclear@26 192 }
nuclear@26 193 y += BLKSZ;
nuclear@2 194 }
nuclear@2 195
nuclear@26 196 ++ctx->cur_sample;
nuclear@31 197 ctx->tpool->wait(timeout); // wait for completion
nuclear@26 198 return ctx->cur_sample > erb_getopti(ctx, ERB_OPT_MAX_SAMPLES) ? 0 : 1;
nuclear@26 199 }
nuclear@4 200
nuclear@2 201
nuclear@2 202 int erb_get_progress(struct erebus *ctx)
nuclear@2 203 {
nuclear@2 204 return 0; // TODO
nuclear@2 205 }
nuclear@2 206
nuclear@2 207 int erb_load_scene(struct erebus *ctx, const char *fname)
nuclear@2 208 {
nuclear@4 209 delete ctx->scn;
nuclear@4 210 ctx->scn = new Scene;
nuclear@2 211
nuclear@19 212 if(!ctx->scn->load(fname)) {
nuclear@19 213 return -1;
nuclear@19 214 }
nuclear@4 215 return 0;
nuclear@2 216 }
nuclear@2 217
nuclear@9 218 bool erb_input_keyboard(struct erebus *ctx, int key, bool pressed)
nuclear@9 219 {
nuclear@9 220 if(!ctx) return false;
nuclear@15 221 if((int)ctx->keystate.size() <= key) {
nuclear@15 222 ctx->keystate.resize(key < 256 ? 256 : key + 1);
nuclear@15 223 }
nuclear@9 224
nuclear@9 225 ctx->keystate[key] = pressed;
nuclear@10 226
nuclear@10 227 if(pressed) {
nuclear@10 228 switch(key) {
nuclear@15 229 case '.':
nuclear@15 230 {
nuclear@15 231 int node_count = ctx->scn->get_node_count();
nuclear@15 232 if(node_count && ++ctx->dbg_nodesel >= node_count) {
nuclear@15 233 ctx->dbg_nodesel = 0;
nuclear@15 234 }
nuclear@15 235 printf("selected node: %d\n", ctx->dbg_nodesel);
nuclear@15 236 }
nuclear@15 237 break;
nuclear@15 238
nuclear@15 239 case ',':
nuclear@15 240 {
nuclear@15 241 int node_count = ctx->scn->get_node_count();
nuclear@15 242 if(node_count && --ctx->dbg_nodesel < 0) {
nuclear@15 243 ctx->dbg_nodesel = node_count - 1;
nuclear@15 244 }
nuclear@15 245 printf("selected node: %d\n", ctx->dbg_nodesel);
nuclear@15 246 }
nuclear@15 247 break;
nuclear@15 248
nuclear@10 249 case '=':
nuclear@10 250 case '-':
nuclear@15 251 case '0':
nuclear@15 252 if(ctx->dbg_nodesel != -1) {
nuclear@15 253 SceneNode *node = ctx->scn->get_node(ctx->dbg_nodesel);
nuclear@15 254 Vector3 s = node->get_scaling();
nuclear@15 255 switch(key) {
nuclear@15 256 case '=':
nuclear@15 257 node->set_scaling(s * 1.1);
nuclear@15 258 break;
nuclear@15 259 case '-':
nuclear@15 260 node->set_scaling(s * 0.9);
nuclear@15 261 break;
nuclear@15 262 case '0':
nuclear@15 263 node->set_scaling(Vector3(1, 1, 1));
nuclear@15 264 break;
nuclear@15 265 }
nuclear@15 266 }
nuclear@15 267 erb_begin_frame(ctx, 0);
nuclear@15 268 return true;
nuclear@10 269 }
nuclear@10 270 }
nuclear@9 271 return false;
nuclear@9 272 }
nuclear@9 273
nuclear@9 274 bool erb_input_mouse_button(struct erebus *ctx, int bn, bool pressed, int x, int y)
nuclear@9 275 {
nuclear@9 276 if(!ctx) return false;
nuclear@15 277 if((int)ctx->bnstate.size() <= bn) {
nuclear@15 278 ctx->bnstate.resize(bn < 32 ? 32 : bn + 1);
nuclear@15 279 }
nuclear@9 280
nuclear@9 281 ctx->bnstate[bn] = pressed;
nuclear@9 282 ctx->mouse_pos[0] = x;
nuclear@9 283 ctx->mouse_pos[1] = y;
nuclear@10 284 return false;
nuclear@9 285 }
nuclear@9 286
nuclear@10 287 bool erb_input_mouse_motion(struct erebus *ctx, int x, int y)
nuclear@10 288 {
nuclear@18 289 bool res = false;
nuclear@18 290
nuclear@18 291 if(!ctx) return res;
nuclear@18 292
nuclear@18 293 int dx = x - ctx->mouse_pos[0];
nuclear@18 294 int dy = y - ctx->mouse_pos[1];
nuclear@18 295
nuclear@18 296 if(dx || dy) {
nuclear@18 297 TargetCamera *cam = (TargetCamera*)ctx->scn->get_active_camera();
nuclear@18 298 if(cam && ctx->bnstate[0]) {
nuclear@18 299 Vector3 cpos = cam->get_position();
nuclear@18 300 float mag = cpos.length();
nuclear@18 301
nuclear@19 302 float theta = atan2(cpos.z / mag, cpos.x / mag) - DEG_TO_RAD(dx * 0.5);
nuclear@19 303 float phi = acos(cpos.y / mag) - DEG_TO_RAD(dy * 0.5);
nuclear@18 304
nuclear@18 305 if(phi < 0) phi = 0;
nuclear@18 306 if(phi > M_PI) phi = M_PI;
nuclear@18 307
nuclear@18 308 cpos.x = cos(theta) * sin(phi) * mag;
nuclear@18 309 cpos.y = cos(phi) * mag;
nuclear@18 310 cpos.z = sin(theta) * sin(phi) * mag;
nuclear@18 311 cam->set_position(cpos);
nuclear@18 312
nuclear@18 313 erb_begin_frame(ctx, 0);
nuclear@18 314 res = true;
nuclear@18 315 }
nuclear@18 316 }
nuclear@10 317
nuclear@10 318 ctx->mouse_pos[0] = x;
nuclear@10 319 ctx->mouse_pos[1] = y;
nuclear@18 320 return res;
nuclear@10 321 }
nuclear@10 322
nuclear@10 323 bool erb_input_6dof_button(struct erebus *ctx, int bn, bool pressed)
nuclear@10 324 {
nuclear@10 325 if(!ctx) return false;
nuclear@10 326 return false;
nuclear@10 327 }
nuclear@10 328
nuclear@10 329 bool erb_input_6dof_motion(struct erebus *ctx, float x, float y, float z)
nuclear@10 330 {
nuclear@10 331 if(!ctx) return false;
nuclear@10 332 return false;
nuclear@10 333 }
nuclear@9 334
nuclear@9 335
nuclear@2 336 } // extern "C"
nuclear@2 337
nuclear@2 338 float randf(float low, float high)
nuclear@2 339 {
nuclear@2 340 std::uniform_real_distribution<float> unirnd(low, high);
nuclear@2 341 return unirnd(rnd_gen);
nuclear@2 342 }
nuclear@2 343
nuclear@26 344 static void render_block(struct erebus *ctx, Block blk)
nuclear@26 345 {
nuclear@26 346 if(blk.frame < ctx->cur_frame) {
nuclear@26 347 return; // skip stale blocks
nuclear@26 348 }
nuclear@26 349
nuclear@26 350 for(int i=0; i<blk.height; i++) {
nuclear@26 351 for(int j=0; j<blk.width; j++) {
nuclear@26 352 render_pixel(ctx, blk.x + j, blk.y + i, blk.sample);
nuclear@26 353 }
nuclear@26 354 }
nuclear@26 355 }
nuclear@26 356
nuclear@8 357 static void render_pixel(struct erebus *ctx, int x, int y, int sample)
nuclear@2 358 {
nuclear@5 359 Camera *cam = ctx->scn->get_active_camera();
nuclear@5 360 if(!cam) return;
nuclear@5 361
nuclear@5 362 int xsz = ctx->fbimg.get_width();
nuclear@5 363 int ysz = ctx->fbimg.get_height();
nuclear@8 364 int offs = (y * xsz + x) * 4;
nuclear@5 365
nuclear@8 366 float *pix = ctx->fbimg.get_pixels() + offs;
nuclear@8 367 float *accum = ctx->accum.get_pixels() + offs;
nuclear@8 368
nuclear@8 369 Ray ray = cam->get_primary_ray(x, y, xsz, ysz, sample);
nuclear@17 370 Color c = ray_trace(ctx, ray, 0);
nuclear@8 371 accum[0] += c.x;
nuclear@8 372 accum[1] += c.y;
nuclear@8 373 accum[2] += c.z;
nuclear@8 374 accum[3] += c.w;
nuclear@8 375
nuclear@8 376 float inv_samples = 1.0f / (float)(sample + 1);
nuclear@17 377 pix[0] = pow(accum[0] * inv_samples, ctx->inv_gamma);
nuclear@17 378 pix[1] = pow(accum[1] * inv_samples, ctx->inv_gamma);
nuclear@17 379 pix[2] = pow(accum[2] * inv_samples, ctx->inv_gamma);
nuclear@8 380 pix[3] = accum[3] * inv_samples;
nuclear@2 381 }