erebus

view liberebus/src/erebus.cc @ 44:a2afaf8af09b

helpful comment
author John Tsiombikas <nuclear@member.fsf.org>
date Fri, 18 Sep 2015 06:47:41 +0300
parents 9d6368850fe1
children c4d48a21bc4a
line source
1 #include <string.h>
2 #include <limits.h>
3 #include <algorithm>
4 #include <chrono>
5 #include <random>
6 #ifndef _MSC_VER
7 #include <alloca.h>
8 #else
9 #include <malloc.h>
10 #endif
11 #include "erebus.h"
12 #include "erebus_impl.h"
13 #include "scene.h"
14 #include "geomobj.h"
15 #include "rt.h"
17 #define INF_SAMPLES (INT_MAX / 2)
19 using namespace std::chrono;
21 static void render_block(struct erebus *ctx, Block blk);
22 static void render_pixel(struct erebus *ctx, int x, int y, int sample);
24 static std::mt19937 rnd_gen;
26 extern "C" {
28 struct erebus *erb_init(void)
29 {
30 struct erebus *ctx = 0;
31 try {
32 ctx = new struct erebus;
33 }
34 catch(...) {
35 return 0;
36 }
38 rnd_gen.seed(time(0));
40 ctx->rendering = false;
41 ctx->scn = 0;
42 ctx->cur_time = 0;
43 ctx->cur_frame = 0;
44 ctx->tpool = 0;
46 erb_setoptf(ctx, ERB_OPT_GAMMA, 2.2);
47 erb_setopti(ctx, ERB_OPT_MAX_ITER, 8);
48 erb_setopti(ctx, ERB_OPT_MAX_SAMPLES, INF_SAMPLES);
49 erb_setopti(ctx, ERB_OPT_NUM_THREADS, -1);
51 ctx->dbg_nodesel = -1;
52 return ctx;
53 }
55 void erb_destroy(struct erebus *ctx)
56 {
57 if(ctx) {
58 // make sure the threadpool stops BEFORE destroying the framebuffers etc in ctx
59 delete ctx->tpool;
60 delete ctx;
61 }
62 }
64 void erb_clear(struct erebus *ctx)
65 {
66 erb_end_frame(ctx);
67 ctx->scn->clear();
68 }
70 void erb_setopti(struct erebus *ctx, enum erb_option opt, int val)
71 {
72 ctx->options[opt].ival = val;
73 ctx->options[opt].type = Option::Type::INT;
74 }
76 void erb_setoptf(struct erebus *ctx, enum erb_option opt, float val)
77 {
78 ctx->options[opt].fval = val;
79 ctx->options[opt].type = Option::Type::FLOAT;
80 }
82 void erb_setoptfv(struct erebus *ctx, enum erb_option opt, float *vec)
83 {
84 for(int i=0; i<4; i++) {
85 ctx->options[opt].vval[i] = vec[i];
86 }
87 ctx->options[opt].type = Option::Type::VEC;
88 }
90 int erb_getopti(struct erebus *ctx, enum erb_option opt)
91 {
92 switch(ctx->options[opt].type) {
93 case Option::Type::INT:
94 return ctx->options[opt].ival;
95 case Option::Type::FLOAT:
96 return (int)ctx->options[opt].fval;
97 case Option::Type::VEC:
98 return (int)ctx->options[opt].vval.x;
99 }
100 return 0; // can't happen
101 }
103 float erb_getoptf(struct erebus *ctx, enum erb_option opt)
104 {
105 switch(ctx->options[opt].type) {
106 case Option::Type::INT:
107 return (float)ctx->options[opt].ival;
108 case Option::Type::FLOAT:
109 return ctx->options[opt].fval;
110 case Option::Type::VEC:
111 return ctx->options[opt].vval.x;
112 }
113 return 0.0f; // can't happen
114 }
116 float *erb_getoptfv(struct erebus *ctx, enum erb_option opt)
117 {
118 switch(ctx->options[opt].type) {
119 case Option::Type::INT:
120 {
121 int ival = ctx->options[opt].ival;
122 ctx->options[opt].vval = Vector4(ival, ival, ival, ival);
123 }
124 break;
125 case Option::Type::FLOAT:
126 {
127 float fval = ctx->options[opt].fval;
128 ctx->options[opt].vval = Vector4(fval, fval, fval, fval);
129 }
130 default:
131 break;
132 }
134 return &ctx->options[opt].vval.x;
135 }
137 float *erb_get_framebuffer(struct erebus *ctx)
138 {
139 return ctx->fbimg.get_pixels();
140 }
142 void erb_begin_frame(struct erebus *ctx, long ms)
143 {
144 if(!ctx->tpool) {
145 int num_threads = erb_getopti(ctx, ERB_OPT_NUM_THREADS);
146 ctx->tpool = new ThreadPool(num_threads);
147 }
148 ctx->tpool->clear_work(); // remove any previously pending jobs
150 ++ctx->cur_frame;
151 ctx->cur_sample = 0;
152 ctx->cur_time = ms;
154 int xsz = erb_getopti(ctx, ERB_OPT_WIDTH);
155 int ysz = erb_getopti(ctx, ERB_OPT_HEIGHT);
157 if(!ctx->fbimg.get_pixels() || ctx->fbimg.get_width() != xsz || ctx->fbimg.get_height() < ysz) {
158 ctx->fbimg.create(xsz, ysz);
159 ctx->accum.create(xsz, ysz);
160 } else {
161 ctx->fbimg.clear();
162 ctx->accum.clear();
163 }
165 ctx->inv_gamma = 1.0f / erb_getoptf(ctx, ERB_OPT_GAMMA);
167 ctx->scn->update(ctx->cur_time);
168 ctx->rendering = true;
169 }
171 void erb_end_frame(struct erebus *ctx)
172 {
173 if(ctx->tpool) {
174 ctx->tpool->clear_work();
175 ctx->tpool->wait();
176 }
177 ctx->rendering = false;
178 }
180 int erb_render(struct erebus *ctx, long timeout)
181 {
182 return erb_render_rect(ctx, 0, 0, ctx->fbimg.get_width(), ctx->fbimg.get_height(), timeout);
183 }
185 #define BLKSZ 32
187 int erb_render_rect(struct erebus *ctx, int x, int y, int width, int height, long timeout)
188 {
189 if(!ctx->rendering) {
190 return 0;
191 }
193 while(ctx->tpool->pending()) {
194 if(timeout > 0) {
195 long wait_interval = ctx->tpool->wait(timeout);
196 timeout -= wait_interval;
197 } else {
198 return 1;
199 }
200 }
202 if(!width || !height) return -1;
204 int startx = x;
205 int endx = x + width;
206 int endy = y + height;
208 while(y < endy) {
209 x = startx;
210 while(x < endx) {
211 Block blk;
212 blk.x = x;
213 blk.y = y;
214 blk.width = std::min(BLKSZ, endx - x);
215 blk.height = std::min(BLKSZ, endy - y);
216 blk.sample = ctx->cur_sample;
217 blk.frame = ctx->cur_frame;
219 ctx->tpool->add_work(std::bind(render_block, ctx, blk));
221 x += BLKSZ;
222 }
223 y += BLKSZ;
224 }
226 ++ctx->cur_sample;
227 ctx->tpool->wait(timeout); // wait for completion
228 return ctx->cur_sample > erb_getopti(ctx, ERB_OPT_MAX_SAMPLES) ? 0 : 1;
229 }
232 int erb_get_progress(struct erebus *ctx)
233 {
234 struct erb_render_status st;
235 if(erb_get_status(ctx, &st) == -1) {
236 return 0;
237 }
238 return st.progress_percent;
239 }
241 int erb_get_status(struct erebus *ctx, struct erb_render_status *stat)
242 {
243 long pending = ctx->tpool->pending();
245 int xsz = ctx->fbimg.get_width();
246 int ysz = ctx->fbimg.get_height();
247 int xblocks = (xsz + BLKSZ - 1) / BLKSZ;
248 int yblocks = (ysz + BLKSZ - 1) / BLKSZ;
249 long num_blocks = xblocks * yblocks;
251 stat->frames = stat->max_frames = 0; // TODO
253 stat->blocks = num_blocks - pending;
254 stat->max_blocks = num_blocks;
256 stat->samples = ctx->cur_sample ? ctx->cur_sample - 1 : 0;
257 if((stat->max_samples = erb_getopti(ctx, ERB_OPT_MAX_SAMPLES)) == INF_SAMPLES) {
258 stat->max_samples = stat->samples;
260 if(stat->max_blocks) {
261 stat->progress_percent = 100 * stat->blocks / stat->max_blocks;
262 } else {
263 stat->progress_percent = 0;
264 }
265 } else {
266 if(stat->max_samples) {
267 stat->progress_percent = 100 * stat->samples / stat->max_samples;
268 } else {
269 stat->progress_percent = 0;
270 }
271 }
272 return 0;
273 }
275 int erb_load_scene(struct erebus *ctx, const char *fname)
276 {
277 if(!ctx->scn) {
278 ctx->scn = new Scene;
279 }
281 if(!ctx->scn->load(fname)) {
282 return -1;
283 }
284 return 0;
285 }
287 int erb_proc_cmd(struct erebus *ctx, const char *cmd)
288 {
289 if(!ctx->scn) {
290 ctx->scn = new Scene;
291 }
293 char *buf = (char*)alloca(strlen(cmd) + 1);
294 strcpy(buf, cmd);
296 std::vector<char*> args;
297 char *tok;
298 while((tok = strtok(buf, " \t\v\n\r"))) {
299 buf = 0;
300 args.push_back(tok);
301 }
302 args.push_back(0);
304 if(!ctx->scn->proc_cmd(args.size() - 1, &args[0])) {
305 return -1;
306 }
307 return 0;
308 }
310 bool erb_input_keyboard(struct erebus *ctx, int key, bool pressed)
311 {
312 if(!ctx) return false;
313 if((int)ctx->keystate.size() <= key) {
314 ctx->keystate.resize(key < 256 ? 256 : key + 1);
315 }
317 ctx->keystate[key] = pressed;
319 if(pressed) {
320 switch(key) {
321 case '.':
322 {
323 int node_count = ctx->scn->get_node_count();
324 if(node_count && ++ctx->dbg_nodesel >= node_count) {
325 ctx->dbg_nodesel = 0;
326 }
327 printf("selected node: %d\n", ctx->dbg_nodesel);
328 }
329 break;
331 case ',':
332 {
333 int node_count = ctx->scn->get_node_count();
334 if(node_count && --ctx->dbg_nodesel < 0) {
335 ctx->dbg_nodesel = node_count - 1;
336 }
337 printf("selected node: %d\n", ctx->dbg_nodesel);
338 }
339 break;
341 case '=':
342 case '-':
343 case '0':
344 if(ctx->dbg_nodesel != -1) {
345 SceneNode *node = ctx->scn->get_node(ctx->dbg_nodesel);
346 Vector3 s = node->get_scaling();
347 switch(key) {
348 case '=':
349 node->set_scaling(s * 1.1);
350 break;
351 case '-':
352 node->set_scaling(s * 0.9);
353 break;
354 case '0':
355 node->set_scaling(Vector3(1, 1, 1));
356 break;
357 }
358 }
359 erb_begin_frame(ctx, 0);
360 return true;
361 }
362 }
363 return false;
364 }
366 bool erb_input_mouse_button(struct erebus *ctx, int bn, bool pressed, int x, int y)
367 {
368 if(!ctx) return false;
369 if((int)ctx->bnstate.size() <= bn) {
370 ctx->bnstate.resize(bn < 32 ? 32 : bn + 1);
371 }
373 ctx->bnstate[bn] = pressed;
374 ctx->mouse_pos[0] = x;
375 ctx->mouse_pos[1] = y;
376 return false;
377 }
379 bool erb_input_mouse_motion(struct erebus *ctx, int x, int y)
380 {
381 bool res = false;
383 if(!ctx) return res;
385 int dx = x - ctx->mouse_pos[0];
386 int dy = y - ctx->mouse_pos[1];
388 if(dx || dy) {
389 TargetCamera *cam = (TargetCamera*)ctx->scn->get_active_camera();
390 if(cam && ctx->bnstate[0]) {
391 Vector3 cpos = cam->get_position();
392 float mag = cpos.length();
394 float theta = atan2(cpos.z / mag, cpos.x / mag) - DEG_TO_RAD(dx * 0.5);
395 float phi = acos(cpos.y / mag) - DEG_TO_RAD(dy * 0.5);
397 if(phi < 0) phi = 0;
398 if(phi > M_PI) phi = M_PI;
400 cpos.x = cos(theta) * sin(phi) * mag;
401 cpos.y = cos(phi) * mag;
402 cpos.z = sin(theta) * sin(phi) * mag;
403 cam->set_position(cpos);
405 erb_begin_frame(ctx, 0);
406 res = true;
407 }
408 }
410 ctx->mouse_pos[0] = x;
411 ctx->mouse_pos[1] = y;
412 return res;
413 }
415 bool erb_input_6dof_button(struct erebus *ctx, int bn, bool pressed)
416 {
417 if(!ctx) return false;
418 return false;
419 }
421 bool erb_input_6dof_motion(struct erebus *ctx, float x, float y, float z)
422 {
423 if(!ctx) return false;
424 return false;
425 }
428 } // extern "C"
430 float randf(float low, float high)
431 {
432 std::uniform_real_distribution<float> unirnd(low, high);
433 return unirnd(rnd_gen);
434 }
436 static void render_block(struct erebus *ctx, Block blk)
437 {
438 if(blk.frame < ctx->cur_frame) {
439 return; // skip stale blocks
440 }
442 for(int i=0; i<blk.height; i++) {
443 for(int j=0; j<blk.width; j++) {
444 render_pixel(ctx, blk.x + j, blk.y + i, blk.sample);
445 }
446 }
447 }
449 static void render_pixel(struct erebus *ctx, int x, int y, int sample)
450 {
451 Camera *cam = ctx->scn->get_active_camera();
452 if(!cam) return;
454 int xsz = ctx->fbimg.get_width();
455 int ysz = ctx->fbimg.get_height();
456 int offs = (y * xsz + x) * 4;
458 float *pix = ctx->fbimg.get_pixels() + offs;
459 float *accum = ctx->accum.get_pixels() + offs;
461 Ray ray = cam->get_primary_ray(x, y, xsz, ysz, sample);
462 ray.energy = 1.0;
464 Color c = ray_trace(ctx, ray, 0);
465 accum[0] += c.x;
466 accum[1] += c.y;
467 accum[2] += c.z;
468 accum[3] += c.w;
470 float inv_samples = 1.0f / (float)(sample + 1);
471 pix[0] = pow(accum[0] * inv_samples, ctx->inv_gamma);
472 pix[1] = pow(accum[1] * inv_samples, ctx->inv_gamma);
473 pix[2] = pow(accum[2] * inv_samples, ctx->inv_gamma);
474 pix[3] = 1.0;//accum[3] * inv_samples;
475 }