erebus

annotate liberebus/src/erebus.cc @ 26:c8a6fb04fefa

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