erebus

view liberebus/src/erebus.cc @ 38:5e27c85e79ca

cursor handling in the console
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 09 Jun 2014 18:40:30 +0300
parents db8a90307386
children 9d6368850fe1
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->scn = 0;
41 ctx->cur_time = 0;
42 ctx->cur_frame = 0;
43 ctx->tpool = 0;
45 erb_setoptf(ctx, ERB_OPT_GAMMA, 2.2);
46 erb_setopti(ctx, ERB_OPT_MAX_ITER, 8);
47 erb_setopti(ctx, ERB_OPT_MAX_SAMPLES, INF_SAMPLES);
48 erb_setopti(ctx, ERB_OPT_NUM_THREADS, -1);
50 ctx->dbg_nodesel = -1;
51 return ctx;
52 }
54 void erb_destroy(struct erebus *ctx)
55 {
56 if(ctx) {
57 // make sure the threadpool stops BEFORE destroying the framebuffers etc in ctx
58 delete ctx->tpool;
59 delete ctx;
60 }
61 }
63 void erb_setopti(struct erebus *ctx, enum erb_option opt, int val)
64 {
65 ctx->options[opt].ival = val;
66 ctx->options[opt].type = Option::Type::INT;
67 }
69 void erb_setoptf(struct erebus *ctx, enum erb_option opt, float val)
70 {
71 ctx->options[opt].fval = val;
72 ctx->options[opt].type = Option::Type::FLOAT;
73 }
75 void erb_setoptfv(struct erebus *ctx, enum erb_option opt, float *vec)
76 {
77 for(int i=0; i<4; i++) {
78 ctx->options[opt].vval[i] = vec[i];
79 }
80 ctx->options[opt].type = Option::Type::VEC;
81 }
83 int erb_getopti(struct erebus *ctx, enum erb_option opt)
84 {
85 switch(ctx->options[opt].type) {
86 case Option::Type::INT:
87 return ctx->options[opt].ival;
88 case Option::Type::FLOAT:
89 return (int)ctx->options[opt].fval;
90 case Option::Type::VEC:
91 return (int)ctx->options[opt].vval.x;
92 }
93 return 0; // can't happen
94 }
96 float erb_getoptf(struct erebus *ctx, enum erb_option opt)
97 {
98 switch(ctx->options[opt].type) {
99 case Option::Type::INT:
100 return (float)ctx->options[opt].ival;
101 case Option::Type::FLOAT:
102 return ctx->options[opt].fval;
103 case Option::Type::VEC:
104 return ctx->options[opt].vval.x;
105 }
106 return 0.0f; // can't happen
107 }
109 float *erb_getoptfv(struct erebus *ctx, enum erb_option opt)
110 {
111 switch(ctx->options[opt].type) {
112 case Option::Type::INT:
113 {
114 int ival = ctx->options[opt].ival;
115 ctx->options[opt].vval = Vector4(ival, ival, ival, ival);
116 }
117 break;
118 case Option::Type::FLOAT:
119 {
120 float fval = ctx->options[opt].fval;
121 ctx->options[opt].vval = Vector4(fval, fval, fval, fval);
122 }
123 default:
124 break;
125 }
127 return &ctx->options[opt].vval.x;
128 }
130 float *erb_get_framebuffer(struct erebus *ctx)
131 {
132 return ctx->fbimg.get_pixels();
133 }
135 void erb_begin_frame(struct erebus *ctx, long ms)
136 {
137 if(!ctx->tpool) {
138 int num_threads = erb_getopti(ctx, ERB_OPT_NUM_THREADS);
139 ctx->tpool = new ThreadPool(num_threads);
140 }
141 ctx->tpool->clear_work(); // remove any previously pending jobs
143 ++ctx->cur_frame;
144 ctx->cur_sample = 0;
145 ctx->cur_time = ms;
147 int xsz = erb_getopti(ctx, ERB_OPT_WIDTH);
148 int ysz = erb_getopti(ctx, ERB_OPT_HEIGHT);
150 if(!ctx->fbimg.get_pixels() || ctx->fbimg.get_width() != xsz || ctx->fbimg.get_height() < ysz) {
151 ctx->fbimg.create(xsz, ysz);
152 ctx->accum.create(xsz, ysz);
153 } else {
154 ctx->fbimg.clear();
155 ctx->accum.clear();
156 }
158 ctx->inv_gamma = 1.0f / erb_getoptf(ctx, ERB_OPT_GAMMA);
160 ctx->scn->update(ctx->cur_time);
161 }
163 int erb_render(struct erebus *ctx, long timeout)
164 {
165 return erb_render_rect(ctx, 0, 0, ctx->fbimg.get_width(), ctx->fbimg.get_height(), timeout);
166 }
168 #define BLKSZ 32
170 int erb_render_rect(struct erebus *ctx, int x, int y, int width, int height, long timeout)
171 {
172 while(ctx->tpool->pending()) {
173 if(timeout > 0) {
174 long wait_interval = ctx->tpool->wait(timeout);
175 timeout -= wait_interval;
176 } else {
177 return 1;
178 }
179 }
181 if(!width || !height) return -1;
183 int startx = x;
184 int endx = x + width;
185 int endy = y + height;
187 while(y < endy) {
188 x = startx;
189 while(x < endx) {
190 Block blk;
191 blk.x = x;
192 blk.y = y;
193 blk.width = std::min(BLKSZ, endx - x);
194 blk.height = std::min(BLKSZ, endy - y);
195 blk.sample = ctx->cur_sample;
196 blk.frame = ctx->cur_frame;
198 ctx->tpool->add_work(std::bind(render_block, ctx, blk));
200 x += BLKSZ;
201 }
202 y += BLKSZ;
203 }
205 ++ctx->cur_sample;
206 ctx->tpool->wait(timeout); // wait for completion
207 return ctx->cur_sample > erb_getopti(ctx, ERB_OPT_MAX_SAMPLES) ? 0 : 1;
208 }
211 int erb_get_progress(struct erebus *ctx)
212 {
213 struct erb_render_status st;
214 if(erb_get_status(ctx, &st) == -1) {
215 return 0;
216 }
217 return st.progress_percent;
218 }
220 int erb_get_status(struct erebus *ctx, struct erb_render_status *stat)
221 {
222 long pending = ctx->tpool->pending();
224 int xsz = ctx->fbimg.get_width();
225 int ysz = ctx->fbimg.get_height();
226 int xblocks = (xsz + BLKSZ - 1) / BLKSZ;
227 int yblocks = (ysz + BLKSZ - 1) / BLKSZ;
228 long num_blocks = xblocks * yblocks;
230 stat->frames = stat->max_frames = 0; // TODO
232 stat->blocks = num_blocks - pending;
233 stat->max_blocks = num_blocks;
235 stat->samples = ctx->cur_sample ? ctx->cur_sample - 1 : 0;
236 if((stat->max_samples = erb_getopti(ctx, ERB_OPT_MAX_SAMPLES)) == INF_SAMPLES) {
237 stat->max_samples = stat->samples;
239 if(stat->max_blocks) {
240 stat->progress_percent = 100 * stat->blocks / stat->max_blocks;
241 } else {
242 stat->progress_percent = 0;
243 }
244 } else {
245 if(stat->max_samples) {
246 stat->progress_percent = 100 * stat->samples / stat->max_samples;
247 } else {
248 stat->progress_percent = 0;
249 }
250 }
251 return 0;
252 }
254 int erb_load_scene(struct erebus *ctx, const char *fname)
255 {
256 if(!ctx->scn) {
257 ctx->scn = new Scene;
258 }
260 if(!ctx->scn->load(fname)) {
261 return -1;
262 }
263 return 0;
264 }
266 int erb_proc_cmd(struct erebus *ctx, const char *cmd)
267 {
268 if(!ctx->scn) {
269 ctx->scn = new Scene;
270 }
272 char *buf = (char*)alloca(strlen(cmd) + 1);
273 strcpy(buf, cmd);
275 std::vector<char*> args;
276 char *tok;
277 while((tok = strtok(buf, " \t\v\n\r"))) {
278 buf = 0;
279 args.push_back(tok);
280 }
281 args.push_back(0);
283 if(!ctx->scn->proc_cmd(args.size() - 1, &args[0])) {
284 return -1;
285 }
286 return 0;
287 }
289 bool erb_input_keyboard(struct erebus *ctx, int key, bool pressed)
290 {
291 if(!ctx) return false;
292 if((int)ctx->keystate.size() <= key) {
293 ctx->keystate.resize(key < 256 ? 256 : key + 1);
294 }
296 ctx->keystate[key] = pressed;
298 if(pressed) {
299 switch(key) {
300 case '.':
301 {
302 int node_count = ctx->scn->get_node_count();
303 if(node_count && ++ctx->dbg_nodesel >= node_count) {
304 ctx->dbg_nodesel = 0;
305 }
306 printf("selected node: %d\n", ctx->dbg_nodesel);
307 }
308 break;
310 case ',':
311 {
312 int node_count = ctx->scn->get_node_count();
313 if(node_count && --ctx->dbg_nodesel < 0) {
314 ctx->dbg_nodesel = node_count - 1;
315 }
316 printf("selected node: %d\n", ctx->dbg_nodesel);
317 }
318 break;
320 case '=':
321 case '-':
322 case '0':
323 if(ctx->dbg_nodesel != -1) {
324 SceneNode *node = ctx->scn->get_node(ctx->dbg_nodesel);
325 Vector3 s = node->get_scaling();
326 switch(key) {
327 case '=':
328 node->set_scaling(s * 1.1);
329 break;
330 case '-':
331 node->set_scaling(s * 0.9);
332 break;
333 case '0':
334 node->set_scaling(Vector3(1, 1, 1));
335 break;
336 }
337 }
338 erb_begin_frame(ctx, 0);
339 return true;
340 }
341 }
342 return false;
343 }
345 bool erb_input_mouse_button(struct erebus *ctx, int bn, bool pressed, int x, int y)
346 {
347 if(!ctx) return false;
348 if((int)ctx->bnstate.size() <= bn) {
349 ctx->bnstate.resize(bn < 32 ? 32 : bn + 1);
350 }
352 ctx->bnstate[bn] = pressed;
353 ctx->mouse_pos[0] = x;
354 ctx->mouse_pos[1] = y;
355 return false;
356 }
358 bool erb_input_mouse_motion(struct erebus *ctx, int x, int y)
359 {
360 bool res = false;
362 if(!ctx) return res;
364 int dx = x - ctx->mouse_pos[0];
365 int dy = y - ctx->mouse_pos[1];
367 if(dx || dy) {
368 TargetCamera *cam = (TargetCamera*)ctx->scn->get_active_camera();
369 if(cam && ctx->bnstate[0]) {
370 Vector3 cpos = cam->get_position();
371 float mag = cpos.length();
373 float theta = atan2(cpos.z / mag, cpos.x / mag) - DEG_TO_RAD(dx * 0.5);
374 float phi = acos(cpos.y / mag) - DEG_TO_RAD(dy * 0.5);
376 if(phi < 0) phi = 0;
377 if(phi > M_PI) phi = M_PI;
379 cpos.x = cos(theta) * sin(phi) * mag;
380 cpos.y = cos(phi) * mag;
381 cpos.z = sin(theta) * sin(phi) * mag;
382 cam->set_position(cpos);
384 erb_begin_frame(ctx, 0);
385 res = true;
386 }
387 }
389 ctx->mouse_pos[0] = x;
390 ctx->mouse_pos[1] = y;
391 return res;
392 }
394 bool erb_input_6dof_button(struct erebus *ctx, int bn, bool pressed)
395 {
396 if(!ctx) return false;
397 return false;
398 }
400 bool erb_input_6dof_motion(struct erebus *ctx, float x, float y, float z)
401 {
402 if(!ctx) return false;
403 return false;
404 }
407 } // extern "C"
409 float randf(float low, float high)
410 {
411 std::uniform_real_distribution<float> unirnd(low, high);
412 return unirnd(rnd_gen);
413 }
415 static void render_block(struct erebus *ctx, Block blk)
416 {
417 if(blk.frame < ctx->cur_frame) {
418 return; // skip stale blocks
419 }
421 for(int i=0; i<blk.height; i++) {
422 for(int j=0; j<blk.width; j++) {
423 render_pixel(ctx, blk.x + j, blk.y + i, blk.sample);
424 }
425 }
426 }
428 static void render_pixel(struct erebus *ctx, int x, int y, int sample)
429 {
430 Camera *cam = ctx->scn->get_active_camera();
431 if(!cam) return;
433 int xsz = ctx->fbimg.get_width();
434 int ysz = ctx->fbimg.get_height();
435 int offs = (y * xsz + x) * 4;
437 float *pix = ctx->fbimg.get_pixels() + offs;
438 float *accum = ctx->accum.get_pixels() + offs;
440 Ray ray = cam->get_primary_ray(x, y, xsz, ysz, sample);
441 Color c = ray_trace(ctx, ray, 0);
442 accum[0] += c.x;
443 accum[1] += c.y;
444 accum[2] += c.z;
445 accum[3] += c.w;
447 float inv_samples = 1.0f / (float)(sample + 1);
448 pix[0] = pow(accum[0] * inv_samples, ctx->inv_gamma);
449 pix[1] = pow(accum[1] * inv_samples, ctx->inv_gamma);
450 pix[2] = pow(accum[2] * inv_samples, ctx->inv_gamma);
451 pix[3] = accum[3] * inv_samples;
452 }