erebus

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