erebus

view liberebus/src/erebus.cc @ 8:e2d9bf168a41

semi-works ...
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 24 May 2014 06:12:57 +0300
parents 9621beb22694
children d38e13d6063c
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;
37 };
39 static void render_pixel(struct erebus *ctx, int x, int y, int sample);
41 static std::mt19937 rnd_gen;
43 extern "C" {
45 struct erebus *erb_init(void)
46 {
47 struct erebus *ctx;
48 try {
49 ctx = new struct erebus;
50 }
51 catch(...) {
52 return 0;
53 }
55 ctx->scn = 0;
56 ctx->cur_time = 0;
57 ctx->cur_rect = INVALID_RECT;
59 ctx->options[ERB_OPT_MAX_SAMPLES].x = (float)INF_SAMPLES;
60 return ctx;
61 }
63 void erb_destroy(struct erebus *ctx)
64 {
65 delete ctx;
66 }
68 void erb_setopti(struct erebus *ctx, enum erb_option opt, int val)
69 {
70 ctx->options[opt].x = val;
71 }
72 void erb_setoptf(struct erebus *ctx, enum erb_option opt, float val)
73 {
74 ctx->options[opt].x = val;
75 }
76 void erb_setoptfv(struct erebus *ctx, enum erb_option opt, float *vec)
77 {
78 for(int i=0; i<4; i++) {
79 ctx->options[opt][i] = vec[i];
80 }
81 }
83 int erb_getopti(struct erebus *ctx, enum erb_option opt)
84 {
85 return ctx->options[opt].x;
86 }
87 float erb_getoptf(struct erebus *ctx, enum erb_option opt)
88 {
89 return ctx->options[opt].x;
90 }
91 float *erb_getoptfv(struct erebus *ctx, enum erb_option opt)
92 {
93 return &ctx->options[opt].x;
94 }
96 float *erb_get_framebuffer(struct erebus *ctx)
97 {
98 return ctx->fbimg.get_pixels();
99 }
101 void erb_begin_frame(struct erebus *ctx, long ms)
102 {
103 printf("starting new frame...\n");
104 ctx->cur_time = ms;
106 int xsz = ctx->options[ERB_OPT_WIDTH].x;
107 int ysz = ctx->options[ERB_OPT_HEIGHT].x;
109 ctx->fbimg.create(xsz, ysz);
110 ctx->accum.create(xsz, ysz);
111 }
113 int erb_render(struct erebus *ctx, long timeout)
114 {
115 return erb_render_rect(ctx, 0, 0, ctx->fbimg.get_width(), ctx->fbimg.get_height(), timeout);
116 }
118 int erb_render_rect(struct erebus *ctx, int x, int y, int width, int height, long timeout)
119 {
120 if(!width || !height) return -1;
122 Rect rect{x, y, width, height};
123 if(ctx->cur_rect != rect) {
124 // starting a new rendering apparently
125 ctx->cur_rect = rect;
126 ctx->cur_pixel_x = x;
127 ctx->cur_pixel_y = y;
128 ctx->cur_sample = 0;
129 }
131 ctx->scn->update();
133 int max_samples = ctx->options[ERB_OPT_MAX_SAMPLES].x;
135 if(timeout > 0) {
136 auto start_time = steady_clock::now();
137 while(duration_cast<milliseconds>(steady_clock::now() - start_time).count() < timeout) {
138 render_pixel(ctx, ctx->cur_pixel_x, ctx->cur_pixel_y, ctx->cur_sample);
140 if(++ctx->cur_pixel_x >= ctx->cur_rect.width) {
141 ctx->cur_pixel_x = ctx->cur_rect.x;
142 if(++ctx->cur_pixel_y >= ctx->cur_rect.height) {
143 ctx->cur_pixel_y = ctx->cur_rect.y;
144 if(++ctx->cur_sample >= max_samples) {
145 ctx->cur_rect = INVALID_RECT;
146 return 0;
147 }
148 }
149 }
150 }
151 return 1;
152 }
154 if(ctx->options[ERB_OPT_MAX_SAMPLES].x == (float)INF_SAMPLES) {
155 max_samples = 128;
156 }
158 for(int i=0; i<height; i++) {
159 for(int j=0; j<width; j++) {
160 for(int k=0; k<max_samples; k++) {
161 render_pixel(ctx, j, i, k);
162 }
163 }
164 }
165 return 0;
166 }
168 int erb_get_progress(struct erebus *ctx)
169 {
170 return 0; // TODO
171 }
173 int erb_load_scene(struct erebus *ctx, const char *fname)
174 {
175 delete ctx->scn;
176 ctx->scn = new Scene;
178 // XXX for now just create a test scene here
179 Sphere *sph = new Sphere;
180 sph->mtl.set_attrib("albedo", Color(1.0, 0.3, 0.2));
181 SceneNode *sph_node = new SceneNode(sph);
182 ctx->scn->add_object(sph);
183 ctx->scn->add_node(sph_node);
185 sph = new Sphere;
186 sph->mtl.set_attrib("albedo", Color(0.3, 0.4, 1.0));
187 sph_node = new SceneNode(sph);
188 sph_node->set_position(Vector3(0, -3.0, 0));
189 //sph_node->set_scaling(Vector3(4.0, 4.0, 4.0) * 0.3);
190 ctx->scn->add_object(sph);
191 ctx->scn->add_node(sph_node);
193 Sphere *lt = new Sphere;
194 lt->mtl.set_attrib("emissive", Color(10, 10, 10));
195 SceneNode *lt_node = new SceneNode(lt);
196 lt_node->set_position(Vector3(-15, 15, -10));
197 lt_node->set_scaling(Vector3(5, 5, 5));
198 ctx->scn->add_object(lt);
199 ctx->scn->add_node(lt_node);
201 TargetCamera *cam = new TargetCamera(Vector3(0, 4, -8), Vector3(0, 0, 0));
202 //ctx->scn->add_object(cam);
203 ctx->scn->use_camera(cam);
205 return 0;
206 }
208 } // extern "C"
210 float randf(float low, float high)
211 {
212 std::uniform_real_distribution<float> unirnd(low, high);
213 return unirnd(rnd_gen);
214 }
216 static void render_pixel(struct erebus *ctx, int x, int y, int sample)
217 {
218 Camera *cam = ctx->scn->get_active_camera();
219 if(!cam) return;
221 int xsz = ctx->fbimg.get_width();
222 int ysz = ctx->fbimg.get_height();
223 int offs = (y * xsz + x) * 4;
225 float *pix = ctx->fbimg.get_pixels() + offs;
226 float *accum = ctx->accum.get_pixels() + offs;
228 Ray ray = cam->get_primary_ray(x, y, xsz, ysz, sample);
229 Color c = ray_trace(ray, ctx->scn, 0);
230 accum[0] += c.x;
231 accum[1] += c.y;
232 accum[2] += c.z;
233 accum[3] += c.w;
235 float inv_samples = 1.0f / (float)(sample + 1);
236 pix[0] = accum[0] * inv_samples;
237 pix[1] = accum[1] * inv_samples;
238 pix[2] = accum[2] * inv_samples;
239 pix[3] = accum[3] * inv_samples;
240 }