erebus

view liberebus/src/erebus.cc @ 31:53a98c148bf8

- introduced SurfaceGeometry to carry all the geometric information input to BRDF sampling and evaluation functions. - made Reflectance keep an (optional) pointer to its material - simplified PhongRefl::sample_dir, with the help of SurfaceGeometry - worked around microsoft's broken std::thread implementation's deadlock on join
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 07 Jun 2014 09:14:17 +0300
parents 0ced900e15a7
children b1fc96c71bcc
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 ctx->tpool = new ThreadPool;
29 }
30 catch(...) {
31 delete ctx;
32 return 0;
33 }
35 rnd_gen.seed(time(0));
37 ctx->scn = 0;
38 ctx->cur_time = 0;
39 ctx->cur_frame = 0;
41 erb_setoptf(ctx, ERB_OPT_GAMMA, 2.2);
42 erb_setopti(ctx, ERB_OPT_MAX_ITER, 6);
43 erb_setopti(ctx, ERB_OPT_MAX_SAMPLES, INF_SAMPLES);
44 erb_setopti(ctx, ERB_OPT_NUM_THREADS, -1);
46 ctx->dbg_nodesel = -1;
47 return ctx;
48 }
50 void erb_destroy(struct erebus *ctx)
51 {
52 if(ctx) {
53 // make sure the threadpool stops BEFORE destroying the framebuffers etc in ctx
54 delete ctx->tpool;
55 delete ctx;
56 }
57 }
59 void erb_setopti(struct erebus *ctx, enum erb_option opt, int val)
60 {
61 ctx->options[opt].ival = val;
62 ctx->options[opt].type = Option::Type::INT;
63 }
65 void erb_setoptf(struct erebus *ctx, enum erb_option opt, float val)
66 {
67 ctx->options[opt].fval = val;
68 ctx->options[opt].type = Option::Type::FLOAT;
69 }
71 void erb_setoptfv(struct erebus *ctx, enum erb_option opt, float *vec)
72 {
73 for(int i=0; i<4; i++) {
74 ctx->options[opt].vval[i] = vec[i];
75 }
76 ctx->options[opt].type = Option::Type::VEC;
77 }
79 int erb_getopti(struct erebus *ctx, enum erb_option opt)
80 {
81 switch(ctx->options[opt].type) {
82 case Option::Type::INT:
83 return ctx->options[opt].ival;
84 case Option::Type::FLOAT:
85 return (int)ctx->options[opt].fval;
86 case Option::Type::VEC:
87 return (int)ctx->options[opt].vval.x;
88 }
89 return 0; // can't happen
90 }
92 float erb_getoptf(struct erebus *ctx, enum erb_option opt)
93 {
94 switch(ctx->options[opt].type) {
95 case Option::Type::INT:
96 return (float)ctx->options[opt].ival;
97 case Option::Type::FLOAT:
98 return ctx->options[opt].fval;
99 case Option::Type::VEC:
100 return ctx->options[opt].vval.x;
101 }
102 return 0.0f; // can't happen
103 }
105 float *erb_getoptfv(struct erebus *ctx, enum erb_option opt)
106 {
107 switch(ctx->options[opt].type) {
108 case Option::Type::INT:
109 {
110 int ival = ctx->options[opt].ival;
111 ctx->options[opt].vval = Vector4(ival, ival, ival, ival);
112 }
113 break;
114 case Option::Type::FLOAT:
115 {
116 float fval = ctx->options[opt].fval;
117 ctx->options[opt].vval = Vector4(fval, fval, fval, fval);
118 }
119 default:
120 break;
121 }
123 return &ctx->options[opt].vval.x;
124 }
126 float *erb_get_framebuffer(struct erebus *ctx)
127 {
128 return ctx->fbimg.get_pixels();
129 }
131 void erb_begin_frame(struct erebus *ctx, long ms)
132 {
133 printf("starting new frame...\n");
134 ++ctx->cur_frame;
135 ctx->cur_sample = 0;
136 ctx->cur_time = ms;
138 int xsz = erb_getopti(ctx, ERB_OPT_WIDTH);
139 int ysz = erb_getopti(ctx, ERB_OPT_HEIGHT);
141 if(!ctx->fbimg.get_pixels() || ctx->fbimg.get_width() != xsz || ctx->fbimg.get_height() < ysz) {
142 ctx->fbimg.create(xsz, ysz);
143 ctx->accum.create(xsz, ysz);
144 } else {
145 ctx->fbimg.clear();
146 ctx->accum.clear();
147 }
149 ctx->inv_gamma = 1.0f / erb_getoptf(ctx, ERB_OPT_GAMMA);
151 ctx->scn->update(ctx->cur_time);
152 }
154 int erb_render(struct erebus *ctx, long timeout)
155 {
156 return erb_render_rect(ctx, 0, 0, ctx->fbimg.get_width(), ctx->fbimg.get_height(), timeout);
157 }
159 #define BLKSZ 32
161 int erb_render_rect(struct erebus *ctx, int x, int y, int width, int height, long timeout)
162 {
163 while(ctx->tpool->pending()) {
164 if(timeout > 0) {
165 long wait_interval = ctx->tpool->wait(timeout);
166 timeout -= wait_interval;
167 } else {
168 return 1;
169 }
170 }
172 if(!width || !height) return -1;
174 int startx = x;
175 int endx = x + width;
176 int endy = y + height;
178 while(y < endy) {
179 x = startx;
180 while(x < endx) {
181 Block blk;
182 blk.x = x;
183 blk.y = y;
184 blk.width = std::min(BLKSZ, endx - x);
185 blk.height = std::min(BLKSZ, endy - y);
186 blk.sample = ctx->cur_sample;
187 blk.frame = ctx->cur_frame;
189 ctx->tpool->add_work(std::bind(render_block, ctx, blk));
191 x += BLKSZ;
192 }
193 y += BLKSZ;
194 }
196 ++ctx->cur_sample;
197 ctx->tpool->wait(timeout); // wait for completion
198 return ctx->cur_sample > erb_getopti(ctx, ERB_OPT_MAX_SAMPLES) ? 0 : 1;
199 }
202 int erb_get_progress(struct erebus *ctx)
203 {
204 return 0; // TODO
205 }
207 int erb_load_scene(struct erebus *ctx, const char *fname)
208 {
209 delete ctx->scn;
210 ctx->scn = new Scene;
212 if(!ctx->scn->load(fname)) {
213 return -1;
214 }
215 return 0;
216 }
218 bool erb_input_keyboard(struct erebus *ctx, int key, bool pressed)
219 {
220 if(!ctx) return false;
221 if((int)ctx->keystate.size() <= key) {
222 ctx->keystate.resize(key < 256 ? 256 : key + 1);
223 }
225 ctx->keystate[key] = pressed;
227 if(pressed) {
228 switch(key) {
229 case '.':
230 {
231 int node_count = ctx->scn->get_node_count();
232 if(node_count && ++ctx->dbg_nodesel >= node_count) {
233 ctx->dbg_nodesel = 0;
234 }
235 printf("selected node: %d\n", ctx->dbg_nodesel);
236 }
237 break;
239 case ',':
240 {
241 int node_count = ctx->scn->get_node_count();
242 if(node_count && --ctx->dbg_nodesel < 0) {
243 ctx->dbg_nodesel = node_count - 1;
244 }
245 printf("selected node: %d\n", ctx->dbg_nodesel);
246 }
247 break;
249 case '=':
250 case '-':
251 case '0':
252 if(ctx->dbg_nodesel != -1) {
253 SceneNode *node = ctx->scn->get_node(ctx->dbg_nodesel);
254 Vector3 s = node->get_scaling();
255 switch(key) {
256 case '=':
257 node->set_scaling(s * 1.1);
258 break;
259 case '-':
260 node->set_scaling(s * 0.9);
261 break;
262 case '0':
263 node->set_scaling(Vector3(1, 1, 1));
264 break;
265 }
266 }
267 erb_begin_frame(ctx, 0);
268 return true;
269 }
270 }
271 return false;
272 }
274 bool erb_input_mouse_button(struct erebus *ctx, int bn, bool pressed, int x, int y)
275 {
276 if(!ctx) return false;
277 if((int)ctx->bnstate.size() <= bn) {
278 ctx->bnstate.resize(bn < 32 ? 32 : bn + 1);
279 }
281 ctx->bnstate[bn] = pressed;
282 ctx->mouse_pos[0] = x;
283 ctx->mouse_pos[1] = y;
284 return false;
285 }
287 bool erb_input_mouse_motion(struct erebus *ctx, int x, int y)
288 {
289 bool res = false;
291 if(!ctx) return res;
293 int dx = x - ctx->mouse_pos[0];
294 int dy = y - ctx->mouse_pos[1];
296 if(dx || dy) {
297 TargetCamera *cam = (TargetCamera*)ctx->scn->get_active_camera();
298 if(cam && ctx->bnstate[0]) {
299 Vector3 cpos = cam->get_position();
300 float mag = cpos.length();
302 float theta = atan2(cpos.z / mag, cpos.x / mag) - DEG_TO_RAD(dx * 0.5);
303 float phi = acos(cpos.y / mag) - DEG_TO_RAD(dy * 0.5);
305 if(phi < 0) phi = 0;
306 if(phi > M_PI) phi = M_PI;
308 cpos.x = cos(theta) * sin(phi) * mag;
309 cpos.y = cos(phi) * mag;
310 cpos.z = sin(theta) * sin(phi) * mag;
311 cam->set_position(cpos);
313 erb_begin_frame(ctx, 0);
314 res = true;
315 }
316 }
318 ctx->mouse_pos[0] = x;
319 ctx->mouse_pos[1] = y;
320 return res;
321 }
323 bool erb_input_6dof_button(struct erebus *ctx, int bn, bool pressed)
324 {
325 if(!ctx) return false;
326 return false;
327 }
329 bool erb_input_6dof_motion(struct erebus *ctx, float x, float y, float z)
330 {
331 if(!ctx) return false;
332 return false;
333 }
336 } // extern "C"
338 float randf(float low, float high)
339 {
340 std::uniform_real_distribution<float> unirnd(low, high);
341 return unirnd(rnd_gen);
342 }
344 static void render_block(struct erebus *ctx, Block blk)
345 {
346 if(blk.frame < ctx->cur_frame) {
347 return; // skip stale blocks
348 }
350 for(int i=0; i<blk.height; i++) {
351 for(int j=0; j<blk.width; j++) {
352 render_pixel(ctx, blk.x + j, blk.y + i, blk.sample);
353 }
354 }
355 }
357 static void render_pixel(struct erebus *ctx, int x, int y, int sample)
358 {
359 Camera *cam = ctx->scn->get_active_camera();
360 if(!cam) return;
362 int xsz = ctx->fbimg.get_width();
363 int ysz = ctx->fbimg.get_height();
364 int offs = (y * xsz + x) * 4;
366 float *pix = ctx->fbimg.get_pixels() + offs;
367 float *accum = ctx->accum.get_pixels() + offs;
369 Ray ray = cam->get_primary_ray(x, y, xsz, ysz, sample);
370 Color c = ray_trace(ctx, ray, 0);
371 accum[0] += c.x;
372 accum[1] += c.y;
373 accum[2] += c.z;
374 accum[3] += c.w;
376 float inv_samples = 1.0f / (float)(sample + 1);
377 pix[0] = pow(accum[0] * inv_samples, ctx->inv_gamma);
378 pix[1] = pow(accum[1] * inv_samples, ctx->inv_gamma);
379 pix[2] = pow(accum[2] * inv_samples, ctx->inv_gamma);
380 pix[3] = accum[3] * inv_samples;
381 }