erebus

annotate liberebus/src/erebus.cc @ 18:09028848f276

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