erebus

view liberebus/src/erebus.cc @ 15:20d6c05529f1

debugging the sphere problem
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 25 May 2014 02:00:07 +0300
parents 506e114b7ca2
children e9da2916bc79
line source
1 #include <string.h>
2 #include <limits.h>
3 #include <chrono>
4 #include <random>
5 #include "erebus.h"
6 #include "vmath/vector.h"
7 #include "image.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 struct Rect {
17 int x, y, width, height;
19 bool operator ==(const Rect &r) { return memcmp(this, &r, sizeof r) == 0; }
20 bool operator !=(const Rect &r) { return memcmp(this, &r, sizeof r) != 0; }
21 };
23 #define INVALID_RECT Rect{0, 0, 0, 0}
25 struct erebus {
26 Scene *scn;
28 Image<float> fbimg;
29 Image<float> accum; // sample accumulator per pixel
30 Vector4 options[ERB_NUM_OPTIONS];
32 // render state
33 long cur_time;
34 int cur_pixel_x, cur_pixel_y;
35 Rect cur_rect;
36 int cur_sample;
38 // interactive input
39 std::vector<bool> keystate;
40 std::vector<bool> bnstate;
41 int mouse_pos[2];
43 // debugging stuff
44 int dbg_nodesel;
45 };
47 static void render_pixel(struct erebus *ctx, int x, int y, int sample);
49 static std::mt19937 rnd_gen;
51 extern "C" {
53 struct erebus *erb_init(void)
54 {
55 struct erebus *ctx;
56 try {
57 ctx = new struct erebus;
58 }
59 catch(...) {
60 return 0;
61 }
63 rnd_gen.seed(time(0));
65 ctx->scn = 0;
66 ctx->cur_time = 0;
67 ctx->cur_rect = INVALID_RECT;
69 ctx->options[ERB_OPT_MAX_SAMPLES].x = (float)INF_SAMPLES;
71 ctx->dbg_nodesel = -1;
72 return ctx;
73 }
75 void erb_destroy(struct erebus *ctx)
76 {
77 delete ctx;
78 }
80 void erb_setopti(struct erebus *ctx, enum erb_option opt, int val)
81 {
82 ctx->options[opt].x = val;
83 }
84 void erb_setoptf(struct erebus *ctx, enum erb_option opt, float val)
85 {
86 ctx->options[opt].x = val;
87 }
88 void erb_setoptfv(struct erebus *ctx, enum erb_option opt, float *vec)
89 {
90 for(int i=0; i<4; i++) {
91 ctx->options[opt][i] = vec[i];
92 }
93 }
95 int erb_getopti(struct erebus *ctx, enum erb_option opt)
96 {
97 return ctx->options[opt].x;
98 }
99 float erb_getoptf(struct erebus *ctx, enum erb_option opt)
100 {
101 return ctx->options[opt].x;
102 }
103 float *erb_getoptfv(struct erebus *ctx, enum erb_option opt)
104 {
105 return &ctx->options[opt].x;
106 }
108 float *erb_get_framebuffer(struct erebus *ctx)
109 {
110 return ctx->fbimg.get_pixels();
111 }
113 void erb_begin_frame(struct erebus *ctx, long ms)
114 {
115 printf("starting new frame...\n");
116 ctx->cur_time = ms;
118 int xsz = ctx->options[ERB_OPT_WIDTH].x;
119 int ysz = ctx->options[ERB_OPT_HEIGHT].x;
121 ctx->fbimg.create(xsz, ysz);
122 ctx->accum.create(xsz, ysz);
124 ctx->cur_rect = INVALID_RECT;
125 }
127 int erb_render(struct erebus *ctx, long timeout)
128 {
129 return erb_render_rect(ctx, 0, 0, ctx->fbimg.get_width(), ctx->fbimg.get_height(), timeout);
130 }
132 int erb_render_rect(struct erebus *ctx, int x, int y, int width, int height, long timeout)
133 {
134 if(!width || !height) return -1;
136 Rect rect{x, y, width, height};
137 if(ctx->cur_rect != rect) {
138 // starting a new rendering apparently
139 ctx->cur_rect = rect;
140 ctx->cur_pixel_x = x;
141 ctx->cur_pixel_y = y;
142 ctx->cur_sample = 0;
143 }
145 ctx->scn->update();
147 int max_samples = ctx->options[ERB_OPT_MAX_SAMPLES].x;
149 if(timeout > 0) {
150 auto start_time = steady_clock::now();
151 while(duration_cast<milliseconds>(steady_clock::now() - start_time).count() < timeout) {
152 render_pixel(ctx, ctx->cur_pixel_x, ctx->cur_pixel_y, ctx->cur_sample);
154 if(++ctx->cur_pixel_x >= ctx->cur_rect.width) {
155 ctx->cur_pixel_x = ctx->cur_rect.x;
156 if(++ctx->cur_pixel_y >= ctx->cur_rect.height) {
157 ctx->cur_pixel_y = ctx->cur_rect.y;
158 if(++ctx->cur_sample >= max_samples) {
159 ctx->cur_rect = INVALID_RECT;
160 return 0;
161 }
162 }
163 }
164 }
165 return 1;
166 }
168 if(ctx->options[ERB_OPT_MAX_SAMPLES].x == (float)INF_SAMPLES) {
169 max_samples = 128;
170 }
172 for(int i=0; i<height; i++) {
173 for(int j=0; j<width; j++) {
174 for(int k=0; k<max_samples; k++) {
175 render_pixel(ctx, j, i, k);
176 }
177 }
178 }
179 return 0;
180 }
182 int erb_get_progress(struct erebus *ctx)
183 {
184 return 0; // TODO
185 }
187 int erb_load_scene(struct erebus *ctx, const char *fname)
188 {
189 delete ctx->scn;
190 ctx->scn = new Scene;
192 // XXX for now just create a test scene here
193 Sphere *sph = new Sphere;
194 sph->mtl.set_attrib("albedo", Color(1.0, 0.3, 0.2));
195 SceneNode *sph_node = new SceneNode(sph);
196 ctx->scn->add_object(sph);
197 ctx->scn->add_node(sph_node);
199 sph = new Sphere;
200 sph->mtl.set_attrib("albedo", Color(0.3, 0.4, 1.0));
201 sph_node = new SceneNode(sph);
202 sph_node->set_position(Vector3(0, -3.0, 0));
203 //sph_node->set_scaling(Vector3(4.0, 4.0, 4.0) * 0.3);
204 ctx->scn->add_object(sph);
205 ctx->scn->add_node(sph_node);
207 Sphere *lt = new Sphere;
208 lt->mtl.set_attrib("emissive", Color(10, 10, 10));
209 SceneNode *lt_node = new SceneNode(lt);
210 lt_node->set_position(Vector3(-15, 15, -10));
211 lt_node->set_scaling(Vector3(5, 5, 5));
212 ctx->scn->add_object(lt);
213 ctx->scn->add_node(lt_node);
215 TargetCamera *cam = new TargetCamera(Vector3(0, 4, -8), Vector3(0, 0, 0));
216 //ctx->scn->add_object(cam);
217 ctx->scn->use_camera(cam);
219 return 0;
220 }
222 bool erb_input_keyboard(struct erebus *ctx, int key, bool pressed)
223 {
224 if(!ctx) return false;
225 if((int)ctx->keystate.size() <= key) {
226 ctx->keystate.resize(key < 256 ? 256 : key + 1);
227 }
229 ctx->keystate[key] = pressed;
231 if(pressed) {
232 switch(key) {
233 case '.':
234 {
235 int node_count = ctx->scn->get_node_count();
236 if(node_count && ++ctx->dbg_nodesel >= node_count) {
237 ctx->dbg_nodesel = 0;
238 }
239 printf("selected node: %d\n", ctx->dbg_nodesel);
240 }
241 break;
243 case ',':
244 {
245 int node_count = ctx->scn->get_node_count();
246 if(node_count && --ctx->dbg_nodesel < 0) {
247 ctx->dbg_nodesel = node_count - 1;
248 }
249 printf("selected node: %d\n", ctx->dbg_nodesel);
250 }
251 break;
253 case '=':
254 case '-':
255 case '0':
256 if(ctx->dbg_nodesel != -1) {
257 SceneNode *node = ctx->scn->get_node(ctx->dbg_nodesel);
258 Vector3 s = node->get_scaling();
259 switch(key) {
260 case '=':
261 node->set_scaling(s * 1.1);
262 break;
263 case '-':
264 node->set_scaling(s * 0.9);
265 break;
266 case '0':
267 node->set_scaling(Vector3(1, 1, 1));
268 break;
269 }
270 }
271 erb_begin_frame(ctx, 0);
272 return true;
273 }
274 }
275 return false;
276 }
278 bool erb_input_mouse_button(struct erebus *ctx, int bn, bool pressed, int x, int y)
279 {
280 if(!ctx) return false;
281 if((int)ctx->bnstate.size() <= bn) {
282 ctx->bnstate.resize(bn < 32 ? 32 : bn + 1);
283 }
285 ctx->bnstate[bn] = pressed;
286 ctx->mouse_pos[0] = x;
287 ctx->mouse_pos[1] = y;
288 return false;
289 }
291 bool erb_input_mouse_motion(struct erebus *ctx, int x, int y)
292 {
293 if(!ctx) return false;
295 ctx->mouse_pos[0] = x;
296 ctx->mouse_pos[1] = y;
297 return false;
298 }
300 bool erb_input_6dof_button(struct erebus *ctx, int bn, bool pressed)
301 {
302 if(!ctx) return false;
303 return false;
304 }
306 bool erb_input_6dof_motion(struct erebus *ctx, float x, float y, float z)
307 {
308 if(!ctx) return false;
309 return false;
310 }
313 } // extern "C"
315 float randf(float low, float high)
316 {
317 std::uniform_real_distribution<float> unirnd(low, high);
318 return unirnd(rnd_gen);
319 }
321 static void render_pixel(struct erebus *ctx, int x, int y, int sample)
322 {
323 Camera *cam = ctx->scn->get_active_camera();
324 if(!cam) return;
326 int xsz = ctx->fbimg.get_width();
327 int ysz = ctx->fbimg.get_height();
328 int offs = (y * xsz + x) * 4;
330 float *pix = ctx->fbimg.get_pixels() + offs;
331 float *accum = ctx->accum.get_pixels() + offs;
333 Ray ray = cam->get_primary_ray(x, y, xsz, ysz, sample);
334 Color c = ray_trace(ray, ctx->scn, 0);
335 accum[0] += c.x;
336 accum[1] += c.y;
337 accum[2] += c.z;
338 accum[3] += c.w;
340 float inv_samples = 1.0f / (float)(sample + 1);
341 pix[0] = accum[0] * inv_samples;
342 pix[1] = accum[1] * inv_samples;
343 pix[2] = accum[2] * inv_samples;
344 pix[3] = accum[3] * inv_samples;
345 }