erebus

view liberebus/src/erebus.cc @ 34:d15ee526daa6

- changed the UI font, made it a bit smaller - fixed the text positioning in the status bar - added ThreadPool::clear to remove all pending jobs - fixed the TargetCamera matrix calculation to avoid singularities when the camera looks straight up or down. - fixed ommited normalization in TargetCamera's matrix calculation - added paths/sec display in the status bar
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 08 Jun 2014 08:12:05 +0300
parents b1fc96c71bcc
children db8a90307386
line source
1 #include <string.h>
2 #include <limits.h>
3 #include <algorithm>
4 #include <chrono>
5 #include <random>
6 #include "erebus.h"
7 #include "erebus_impl.h"
8 #include "scene.h"
9 #include "geomobj.h"
10 #include "rt.h"
12 #define INF_SAMPLES (INT_MAX / 2)
14 using namespace std::chrono;
16 static void render_block(struct erebus *ctx, Block blk);
17 static void render_pixel(struct erebus *ctx, int x, int y, int sample);
19 static std::mt19937 rnd_gen;
21 extern "C" {
23 struct erebus *erb_init(void)
24 {
25 struct erebus *ctx = 0;
26 try {
27 ctx = new struct erebus;
28 }
29 catch(...) {
30 return 0;
31 }
33 rnd_gen.seed(time(0));
35 ctx->scn = 0;
36 ctx->cur_time = 0;
37 ctx->cur_frame = 0;
38 ctx->tpool = 0;
40 erb_setoptf(ctx, ERB_OPT_GAMMA, 2.2);
41 erb_setopti(ctx, ERB_OPT_MAX_ITER, 6);
42 erb_setopti(ctx, ERB_OPT_MAX_SAMPLES, INF_SAMPLES);
43 erb_setopti(ctx, ERB_OPT_NUM_THREADS, -1);
45 ctx->dbg_nodesel = -1;
46 return ctx;
47 }
49 void erb_destroy(struct erebus *ctx)
50 {
51 if(ctx) {
52 // make sure the threadpool stops BEFORE destroying the framebuffers etc in ctx
53 delete ctx->tpool;
54 delete ctx;
55 }
56 }
58 void erb_setopti(struct erebus *ctx, enum erb_option opt, int val)
59 {
60 ctx->options[opt].ival = val;
61 ctx->options[opt].type = Option::Type::INT;
62 }
64 void erb_setoptf(struct erebus *ctx, enum erb_option opt, float val)
65 {
66 ctx->options[opt].fval = val;
67 ctx->options[opt].type = Option::Type::FLOAT;
68 }
70 void erb_setoptfv(struct erebus *ctx, enum erb_option opt, float *vec)
71 {
72 for(int i=0; i<4; i++) {
73 ctx->options[opt].vval[i] = vec[i];
74 }
75 ctx->options[opt].type = Option::Type::VEC;
76 }
78 int erb_getopti(struct erebus *ctx, enum erb_option opt)
79 {
80 switch(ctx->options[opt].type) {
81 case Option::Type::INT:
82 return ctx->options[opt].ival;
83 case Option::Type::FLOAT:
84 return (int)ctx->options[opt].fval;
85 case Option::Type::VEC:
86 return (int)ctx->options[opt].vval.x;
87 }
88 return 0; // can't happen
89 }
91 float erb_getoptf(struct erebus *ctx, enum erb_option opt)
92 {
93 switch(ctx->options[opt].type) {
94 case Option::Type::INT:
95 return (float)ctx->options[opt].ival;
96 case Option::Type::FLOAT:
97 return ctx->options[opt].fval;
98 case Option::Type::VEC:
99 return ctx->options[opt].vval.x;
100 }
101 return 0.0f; // can't happen
102 }
104 float *erb_getoptfv(struct erebus *ctx, enum erb_option opt)
105 {
106 switch(ctx->options[opt].type) {
107 case Option::Type::INT:
108 {
109 int ival = ctx->options[opt].ival;
110 ctx->options[opt].vval = Vector4(ival, ival, ival, ival);
111 }
112 break;
113 case Option::Type::FLOAT:
114 {
115 float fval = ctx->options[opt].fval;
116 ctx->options[opt].vval = Vector4(fval, fval, fval, fval);
117 }
118 default:
119 break;
120 }
122 return &ctx->options[opt].vval.x;
123 }
125 float *erb_get_framebuffer(struct erebus *ctx)
126 {
127 return ctx->fbimg.get_pixels();
128 }
130 void erb_begin_frame(struct erebus *ctx, long ms)
131 {
132 if(!ctx->tpool) {
133 int num_threads = erb_getopti(ctx, ERB_OPT_NUM_THREADS);
134 ctx->tpool = new ThreadPool(num_threads);
135 }
136 ctx->tpool->clear_work(); // remove any previously pending jobs
138 ++ctx->cur_frame;
139 ctx->cur_sample = 0;
140 ctx->cur_time = ms;
142 int xsz = erb_getopti(ctx, ERB_OPT_WIDTH);
143 int ysz = erb_getopti(ctx, ERB_OPT_HEIGHT);
145 if(!ctx->fbimg.get_pixels() || ctx->fbimg.get_width() != xsz || ctx->fbimg.get_height() < ysz) {
146 ctx->fbimg.create(xsz, ysz);
147 ctx->accum.create(xsz, ysz);
148 } else {
149 ctx->fbimg.clear();
150 ctx->accum.clear();
151 }
153 ctx->inv_gamma = 1.0f / erb_getoptf(ctx, ERB_OPT_GAMMA);
155 ctx->scn->update(ctx->cur_time);
156 }
158 int erb_render(struct erebus *ctx, long timeout)
159 {
160 return erb_render_rect(ctx, 0, 0, ctx->fbimg.get_width(), ctx->fbimg.get_height(), timeout);
161 }
163 #define BLKSZ 32
165 int erb_render_rect(struct erebus *ctx, int x, int y, int width, int height, long timeout)
166 {
167 while(ctx->tpool->pending()) {
168 if(timeout > 0) {
169 long wait_interval = ctx->tpool->wait(timeout);
170 timeout -= wait_interval;
171 } else {
172 return 1;
173 }
174 }
176 if(!width || !height) return -1;
178 int startx = x;
179 int endx = x + width;
180 int endy = y + height;
182 while(y < endy) {
183 x = startx;
184 while(x < endx) {
185 Block blk;
186 blk.x = x;
187 blk.y = y;
188 blk.width = std::min(BLKSZ, endx - x);
189 blk.height = std::min(BLKSZ, endy - y);
190 blk.sample = ctx->cur_sample;
191 blk.frame = ctx->cur_frame;
193 ctx->tpool->add_work(std::bind(render_block, ctx, blk));
195 x += BLKSZ;
196 }
197 y += BLKSZ;
198 }
200 ++ctx->cur_sample;
201 ctx->tpool->wait(timeout); // wait for completion
202 return ctx->cur_sample > erb_getopti(ctx, ERB_OPT_MAX_SAMPLES) ? 0 : 1;
203 }
206 int erb_get_progress(struct erebus *ctx)
207 {
208 struct erb_render_status st;
209 if(erb_get_status(ctx, &st) == -1) {
210 return 0;
211 }
212 return st.progress_percent;
213 }
215 int erb_get_status(struct erebus *ctx, struct erb_render_status *stat)
216 {
217 long pending = ctx->tpool->pending();
219 int xsz = ctx->fbimg.get_width();
220 int ysz = ctx->fbimg.get_height();
221 int xblocks = (xsz + BLKSZ - 1) / BLKSZ;
222 int yblocks = (ysz + BLKSZ - 1) / BLKSZ;
223 long num_blocks = xblocks * yblocks;
225 stat->frames = stat->max_frames = 0; // TODO
227 stat->blocks = num_blocks - pending;
228 stat->max_blocks = num_blocks;
230 stat->samples = ctx->cur_sample ? ctx->cur_sample - 1 : 0;
231 if((stat->max_samples = erb_getopti(ctx, ERB_OPT_MAX_SAMPLES)) == INF_SAMPLES) {
232 stat->max_samples = stat->samples;
234 if(stat->max_blocks) {
235 stat->progress_percent = 100 * stat->blocks / stat->max_blocks;
236 } else {
237 stat->progress_percent = 0;
238 }
239 } else {
240 if(stat->max_samples) {
241 stat->progress_percent = 100 * stat->samples / stat->max_samples;
242 } else {
243 stat->progress_percent = 0;
244 }
245 }
246 return 0;
247 }
249 int erb_load_scene(struct erebus *ctx, const char *fname)
250 {
251 delete ctx->scn;
252 ctx->scn = new Scene;
254 if(!ctx->scn->load(fname)) {
255 return -1;
256 }
257 return 0;
258 }
260 bool erb_input_keyboard(struct erebus *ctx, int key, bool pressed)
261 {
262 if(!ctx) return false;
263 if((int)ctx->keystate.size() <= key) {
264 ctx->keystate.resize(key < 256 ? 256 : key + 1);
265 }
267 ctx->keystate[key] = pressed;
269 if(pressed) {
270 switch(key) {
271 case '.':
272 {
273 int node_count = ctx->scn->get_node_count();
274 if(node_count && ++ctx->dbg_nodesel >= node_count) {
275 ctx->dbg_nodesel = 0;
276 }
277 printf("selected node: %d\n", ctx->dbg_nodesel);
278 }
279 break;
281 case ',':
282 {
283 int node_count = ctx->scn->get_node_count();
284 if(node_count && --ctx->dbg_nodesel < 0) {
285 ctx->dbg_nodesel = node_count - 1;
286 }
287 printf("selected node: %d\n", ctx->dbg_nodesel);
288 }
289 break;
291 case '=':
292 case '-':
293 case '0':
294 if(ctx->dbg_nodesel != -1) {
295 SceneNode *node = ctx->scn->get_node(ctx->dbg_nodesel);
296 Vector3 s = node->get_scaling();
297 switch(key) {
298 case '=':
299 node->set_scaling(s * 1.1);
300 break;
301 case '-':
302 node->set_scaling(s * 0.9);
303 break;
304 case '0':
305 node->set_scaling(Vector3(1, 1, 1));
306 break;
307 }
308 }
309 erb_begin_frame(ctx, 0);
310 return true;
311 }
312 }
313 return false;
314 }
316 bool erb_input_mouse_button(struct erebus *ctx, int bn, bool pressed, int x, int y)
317 {
318 if(!ctx) return false;
319 if((int)ctx->bnstate.size() <= bn) {
320 ctx->bnstate.resize(bn < 32 ? 32 : bn + 1);
321 }
323 ctx->bnstate[bn] = pressed;
324 ctx->mouse_pos[0] = x;
325 ctx->mouse_pos[1] = y;
326 return false;
327 }
329 bool erb_input_mouse_motion(struct erebus *ctx, int x, int y)
330 {
331 bool res = false;
333 if(!ctx) return res;
335 int dx = x - ctx->mouse_pos[0];
336 int dy = y - ctx->mouse_pos[1];
338 if(dx || dy) {
339 TargetCamera *cam = (TargetCamera*)ctx->scn->get_active_camera();
340 if(cam && ctx->bnstate[0]) {
341 Vector3 cpos = cam->get_position();
342 float mag = cpos.length();
344 float theta = atan2(cpos.z / mag, cpos.x / mag) - DEG_TO_RAD(dx * 0.5);
345 float phi = acos(cpos.y / mag) - DEG_TO_RAD(dy * 0.5);
347 if(phi < 0) phi = 0;
348 if(phi > M_PI) phi = M_PI;
350 cpos.x = cos(theta) * sin(phi) * mag;
351 cpos.y = cos(phi) * mag;
352 cpos.z = sin(theta) * sin(phi) * mag;
353 cam->set_position(cpos);
355 erb_begin_frame(ctx, 0);
356 res = true;
357 }
358 }
360 ctx->mouse_pos[0] = x;
361 ctx->mouse_pos[1] = y;
362 return res;
363 }
365 bool erb_input_6dof_button(struct erebus *ctx, int bn, bool pressed)
366 {
367 if(!ctx) return false;
368 return false;
369 }
371 bool erb_input_6dof_motion(struct erebus *ctx, float x, float y, float z)
372 {
373 if(!ctx) return false;
374 return false;
375 }
378 } // extern "C"
380 float randf(float low, float high)
381 {
382 std::uniform_real_distribution<float> unirnd(low, high);
383 return unirnd(rnd_gen);
384 }
386 static void render_block(struct erebus *ctx, Block blk)
387 {
388 if(blk.frame < ctx->cur_frame) {
389 return; // skip stale blocks
390 }
392 for(int i=0; i<blk.height; i++) {
393 for(int j=0; j<blk.width; j++) {
394 render_pixel(ctx, blk.x + j, blk.y + i, blk.sample);
395 }
396 }
397 }
399 static void render_pixel(struct erebus *ctx, int x, int y, int sample)
400 {
401 Camera *cam = ctx->scn->get_active_camera();
402 if(!cam) return;
404 int xsz = ctx->fbimg.get_width();
405 int ysz = ctx->fbimg.get_height();
406 int offs = (y * xsz + x) * 4;
408 float *pix = ctx->fbimg.get_pixels() + offs;
409 float *accum = ctx->accum.get_pixels() + offs;
411 Ray ray = cam->get_primary_ray(x, y, xsz, ysz, sample);
412 Color c = ray_trace(ctx, ray, 0);
413 accum[0] += c.x;
414 accum[1] += c.y;
415 accum[2] += c.z;
416 accum[3] += c.w;
418 float inv_samples = 1.0f / (float)(sample + 1);
419 pix[0] = pow(accum[0] * inv_samples, ctx->inv_gamma);
420 pix[1] = pow(accum[1] * inv_samples, ctx->inv_gamma);
421 pix[2] = pow(accum[2] * inv_samples, ctx->inv_gamma);
422 pix[3] = accum[3] * inv_samples;
423 }