rev |
line source |
nuclear@2
|
1 #include <string.h>
|
nuclear@2
|
2 #include <limits.h>
|
nuclear@2
|
3 #include <chrono>
|
nuclear@2
|
4 #include <random>
|
nuclear@2
|
5 #include "erebus.h"
|
nuclear@2
|
6 #include "vmath/vector.h"
|
nuclear@2
|
7 #include "image.h"
|
nuclear@4
|
8 #include "scene.h"
|
nuclear@4
|
9 #include "geomobj.h"
|
nuclear@5
|
10 #include "rt.h"
|
nuclear@2
|
11
|
nuclear@8
|
12 #define INF_SAMPLES (INT_MAX / 2)
|
nuclear@8
|
13
|
nuclear@2
|
14 using namespace std::chrono;
|
nuclear@2
|
15
|
nuclear@2
|
16 struct Rect {
|
nuclear@2
|
17 int x, y, width, height;
|
nuclear@2
|
18
|
nuclear@2
|
19 bool operator ==(const Rect &r) { return memcmp(this, &r, sizeof r) == 0; }
|
nuclear@2
|
20 bool operator !=(const Rect &r) { return memcmp(this, &r, sizeof r) != 0; }
|
nuclear@2
|
21 };
|
nuclear@2
|
22
|
nuclear@2
|
23 #define INVALID_RECT Rect{0, 0, 0, 0}
|
nuclear@2
|
24
|
nuclear@2
|
25 struct erebus {
|
nuclear@4
|
26 Scene *scn;
|
nuclear@4
|
27
|
nuclear@2
|
28 Image<float> fbimg;
|
nuclear@8
|
29 Image<float> accum; // sample accumulator per pixel
|
nuclear@2
|
30 Vector4 options[ERB_NUM_OPTIONS];
|
nuclear@2
|
31
|
nuclear@2
|
32 // render state
|
nuclear@2
|
33 long cur_time;
|
nuclear@2
|
34 int cur_pixel_x, cur_pixel_y;
|
nuclear@2
|
35 Rect cur_rect;
|
nuclear@8
|
36 int cur_sample;
|
nuclear@9
|
37
|
nuclear@9
|
38 // interactive input
|
nuclear@9
|
39 std::vector<bool> keystate;
|
nuclear@9
|
40 std::vector<bool> bnstate;
|
nuclear@9
|
41 int mouse_pos[2];
|
nuclear@2
|
42 };
|
nuclear@2
|
43
|
nuclear@8
|
44 static void render_pixel(struct erebus *ctx, int x, int y, int sample);
|
nuclear@2
|
45
|
nuclear@2
|
46 static std::mt19937 rnd_gen;
|
nuclear@2
|
47
|
nuclear@2
|
48 extern "C" {
|
nuclear@2
|
49
|
nuclear@2
|
50 struct erebus *erb_init(void)
|
nuclear@2
|
51 {
|
nuclear@2
|
52 struct erebus *ctx;
|
nuclear@2
|
53 try {
|
nuclear@2
|
54 ctx = new struct erebus;
|
nuclear@2
|
55 }
|
nuclear@2
|
56 catch(...) {
|
nuclear@2
|
57 return 0;
|
nuclear@2
|
58 }
|
nuclear@2
|
59
|
nuclear@10
|
60 rnd_gen.seed(time(0));
|
nuclear@10
|
61
|
nuclear@4
|
62 ctx->scn = 0;
|
nuclear@2
|
63 ctx->cur_time = 0;
|
nuclear@2
|
64 ctx->cur_rect = INVALID_RECT;
|
nuclear@8
|
65
|
nuclear@8
|
66 ctx->options[ERB_OPT_MAX_SAMPLES].x = (float)INF_SAMPLES;
|
nuclear@2
|
67 return ctx;
|
nuclear@2
|
68 }
|
nuclear@2
|
69
|
nuclear@2
|
70 void erb_destroy(struct erebus *ctx)
|
nuclear@2
|
71 {
|
nuclear@2
|
72 delete ctx;
|
nuclear@2
|
73 }
|
nuclear@2
|
74
|
nuclear@2
|
75 void erb_setopti(struct erebus *ctx, enum erb_option opt, int val)
|
nuclear@2
|
76 {
|
nuclear@2
|
77 ctx->options[opt].x = val;
|
nuclear@2
|
78 }
|
nuclear@2
|
79 void erb_setoptf(struct erebus *ctx, enum erb_option opt, float val)
|
nuclear@2
|
80 {
|
nuclear@2
|
81 ctx->options[opt].x = val;
|
nuclear@2
|
82 }
|
nuclear@2
|
83 void erb_setoptfv(struct erebus *ctx, enum erb_option opt, float *vec)
|
nuclear@2
|
84 {
|
nuclear@2
|
85 for(int i=0; i<4; i++) {
|
nuclear@2
|
86 ctx->options[opt][i] = vec[i];
|
nuclear@2
|
87 }
|
nuclear@2
|
88 }
|
nuclear@2
|
89
|
nuclear@2
|
90 int erb_getopti(struct erebus *ctx, enum erb_option opt)
|
nuclear@2
|
91 {
|
nuclear@2
|
92 return ctx->options[opt].x;
|
nuclear@2
|
93 }
|
nuclear@2
|
94 float erb_getoptf(struct erebus *ctx, enum erb_option opt)
|
nuclear@2
|
95 {
|
nuclear@2
|
96 return ctx->options[opt].x;
|
nuclear@2
|
97 }
|
nuclear@2
|
98 float *erb_getoptfv(struct erebus *ctx, enum erb_option opt)
|
nuclear@2
|
99 {
|
nuclear@2
|
100 return &ctx->options[opt].x;
|
nuclear@2
|
101 }
|
nuclear@2
|
102
|
nuclear@2
|
103 float *erb_get_framebuffer(struct erebus *ctx)
|
nuclear@2
|
104 {
|
nuclear@2
|
105 return ctx->fbimg.get_pixels();
|
nuclear@2
|
106 }
|
nuclear@2
|
107
|
nuclear@2
|
108 void erb_begin_frame(struct erebus *ctx, long ms)
|
nuclear@2
|
109 {
|
nuclear@8
|
110 printf("starting new frame...\n");
|
nuclear@2
|
111 ctx->cur_time = ms;
|
nuclear@4
|
112
|
nuclear@4
|
113 int xsz = ctx->options[ERB_OPT_WIDTH].x;
|
nuclear@4
|
114 int ysz = ctx->options[ERB_OPT_HEIGHT].x;
|
nuclear@4
|
115
|
nuclear@4
|
116 ctx->fbimg.create(xsz, ysz);
|
nuclear@8
|
117 ctx->accum.create(xsz, ysz);
|
nuclear@2
|
118 }
|
nuclear@2
|
119
|
nuclear@2
|
120 int erb_render(struct erebus *ctx, long timeout)
|
nuclear@2
|
121 {
|
nuclear@2
|
122 return erb_render_rect(ctx, 0, 0, ctx->fbimg.get_width(), ctx->fbimg.get_height(), timeout);
|
nuclear@2
|
123 }
|
nuclear@2
|
124
|
nuclear@2
|
125 int erb_render_rect(struct erebus *ctx, int x, int y, int width, int height, long timeout)
|
nuclear@2
|
126 {
|
nuclear@2
|
127 if(!width || !height) return -1;
|
nuclear@2
|
128
|
nuclear@2
|
129 Rect rect{x, y, width, height};
|
nuclear@2
|
130 if(ctx->cur_rect != rect) {
|
nuclear@8
|
131 // starting a new rendering apparently
|
nuclear@2
|
132 ctx->cur_rect = rect;
|
nuclear@2
|
133 ctx->cur_pixel_x = x;
|
nuclear@2
|
134 ctx->cur_pixel_y = y;
|
nuclear@8
|
135 ctx->cur_sample = 0;
|
nuclear@2
|
136 }
|
nuclear@2
|
137
|
nuclear@4
|
138 ctx->scn->update();
|
nuclear@4
|
139
|
nuclear@8
|
140 int max_samples = ctx->options[ERB_OPT_MAX_SAMPLES].x;
|
nuclear@8
|
141
|
nuclear@2
|
142 if(timeout > 0) {
|
nuclear@2
|
143 auto start_time = steady_clock::now();
|
nuclear@2
|
144 while(duration_cast<milliseconds>(steady_clock::now() - start_time).count() < timeout) {
|
nuclear@8
|
145 render_pixel(ctx, ctx->cur_pixel_x, ctx->cur_pixel_y, ctx->cur_sample);
|
nuclear@2
|
146
|
nuclear@2
|
147 if(++ctx->cur_pixel_x >= ctx->cur_rect.width) {
|
nuclear@8
|
148 ctx->cur_pixel_x = ctx->cur_rect.x;
|
nuclear@2
|
149 if(++ctx->cur_pixel_y >= ctx->cur_rect.height) {
|
nuclear@8
|
150 ctx->cur_pixel_y = ctx->cur_rect.y;
|
nuclear@8
|
151 if(++ctx->cur_sample >= max_samples) {
|
nuclear@8
|
152 ctx->cur_rect = INVALID_RECT;
|
nuclear@8
|
153 return 0;
|
nuclear@8
|
154 }
|
nuclear@2
|
155 }
|
nuclear@2
|
156 }
|
nuclear@2
|
157 }
|
nuclear@2
|
158 return 1;
|
nuclear@2
|
159 }
|
nuclear@2
|
160
|
nuclear@8
|
161 if(ctx->options[ERB_OPT_MAX_SAMPLES].x == (float)INF_SAMPLES) {
|
nuclear@8
|
162 max_samples = 128;
|
nuclear@8
|
163 }
|
nuclear@8
|
164
|
nuclear@2
|
165 for(int i=0; i<height; i++) {
|
nuclear@2
|
166 for(int j=0; j<width; j++) {
|
nuclear@8
|
167 for(int k=0; k<max_samples; k++) {
|
nuclear@8
|
168 render_pixel(ctx, j, i, k);
|
nuclear@8
|
169 }
|
nuclear@2
|
170 }
|
nuclear@2
|
171 }
|
nuclear@2
|
172 return 0;
|
nuclear@2
|
173 }
|
nuclear@2
|
174
|
nuclear@2
|
175 int erb_get_progress(struct erebus *ctx)
|
nuclear@2
|
176 {
|
nuclear@2
|
177 return 0; // TODO
|
nuclear@2
|
178 }
|
nuclear@2
|
179
|
nuclear@2
|
180 int erb_load_scene(struct erebus *ctx, const char *fname)
|
nuclear@2
|
181 {
|
nuclear@4
|
182 delete ctx->scn;
|
nuclear@4
|
183 ctx->scn = new Scene;
|
nuclear@2
|
184
|
nuclear@4
|
185 // XXX for now just create a test scene here
|
nuclear@4
|
186 Sphere *sph = new Sphere;
|
nuclear@8
|
187 sph->mtl.set_attrib("albedo", Color(1.0, 0.3, 0.2));
|
nuclear@4
|
188 SceneNode *sph_node = new SceneNode(sph);
|
nuclear@4
|
189 ctx->scn->add_object(sph);
|
nuclear@4
|
190 ctx->scn->add_node(sph_node);
|
nuclear@4
|
191
|
nuclear@8
|
192 sph = new Sphere;
|
nuclear@8
|
193 sph->mtl.set_attrib("albedo", Color(0.3, 0.4, 1.0));
|
nuclear@8
|
194 sph_node = new SceneNode(sph);
|
nuclear@8
|
195 sph_node->set_position(Vector3(0, -3.0, 0));
|
nuclear@8
|
196 //sph_node->set_scaling(Vector3(4.0, 4.0, 4.0) * 0.3);
|
nuclear@8
|
197 ctx->scn->add_object(sph);
|
nuclear@8
|
198 ctx->scn->add_node(sph_node);
|
nuclear@8
|
199
|
nuclear@8
|
200 Sphere *lt = new Sphere;
|
nuclear@8
|
201 lt->mtl.set_attrib("emissive", Color(10, 10, 10));
|
nuclear@8
|
202 SceneNode *lt_node = new SceneNode(lt);
|
nuclear@8
|
203 lt_node->set_position(Vector3(-15, 15, -10));
|
nuclear@8
|
204 lt_node->set_scaling(Vector3(5, 5, 5));
|
nuclear@8
|
205 ctx->scn->add_object(lt);
|
nuclear@8
|
206 ctx->scn->add_node(lt_node);
|
nuclear@8
|
207
|
nuclear@8
|
208 TargetCamera *cam = new TargetCamera(Vector3(0, 4, -8), Vector3(0, 0, 0));
|
nuclear@4
|
209 //ctx->scn->add_object(cam);
|
nuclear@4
|
210 ctx->scn->use_camera(cam);
|
nuclear@4
|
211
|
nuclear@4
|
212 return 0;
|
nuclear@2
|
213 }
|
nuclear@2
|
214
|
nuclear@9
|
215 bool erb_input_keyboard(struct erebus *ctx, int key, bool pressed)
|
nuclear@9
|
216 {
|
nuclear@9
|
217 if(!ctx) return false;
|
nuclear@9
|
218 if(ctx->keystate.size() <= key) ctx->keystate.resize(key < 256 ? 256 : key + 1);
|
nuclear@9
|
219
|
nuclear@9
|
220 ctx->keystate[key] = pressed;
|
nuclear@10
|
221
|
nuclear@10
|
222 if(pressed) {
|
nuclear@10
|
223 switch(key) {
|
nuclear@10
|
224 case '=':
|
nuclear@10
|
225 case '-':
|
nuclear@10
|
226 }
|
nuclear@10
|
227 }
|
nuclear@9
|
228 return false;
|
nuclear@9
|
229 }
|
nuclear@9
|
230
|
nuclear@9
|
231 bool erb_input_mouse_button(struct erebus *ctx, int bn, bool pressed, int x, int y)
|
nuclear@9
|
232 {
|
nuclear@9
|
233 if(!ctx) return false;
|
nuclear@9
|
234 if(ctx->bnstate.size() <= bn) ctx->bnstate.resize(bn < 32 ? 32 : bn + 1);
|
nuclear@9
|
235
|
nuclear@9
|
236 ctx->bnstate[bn] = pressed;
|
nuclear@9
|
237 ctx->mouse_pos[0] = x;
|
nuclear@9
|
238 ctx->mouse_pos[1] = y;
|
nuclear@10
|
239 return false;
|
nuclear@9
|
240 }
|
nuclear@9
|
241
|
nuclear@10
|
242 bool erb_input_mouse_motion(struct erebus *ctx, int x, int y)
|
nuclear@10
|
243 {
|
nuclear@10
|
244 if(!ctx) return false;
|
nuclear@10
|
245
|
nuclear@10
|
246 ctx->mouse_pos[0] = x;
|
nuclear@10
|
247 ctx->mouse_pos[1] = y;
|
nuclear@10
|
248 return false;
|
nuclear@10
|
249 }
|
nuclear@10
|
250
|
nuclear@10
|
251 bool erb_input_6dof_button(struct erebus *ctx, int bn, bool pressed)
|
nuclear@10
|
252 {
|
nuclear@10
|
253 if(!ctx) return false;
|
nuclear@10
|
254 return false;
|
nuclear@10
|
255 }
|
nuclear@10
|
256
|
nuclear@10
|
257 bool erb_input_6dof_motion(struct erebus *ctx, float x, float y, float z)
|
nuclear@10
|
258 {
|
nuclear@10
|
259 if(!ctx) return false;
|
nuclear@10
|
260 return false;
|
nuclear@10
|
261 }
|
nuclear@9
|
262
|
nuclear@9
|
263
|
nuclear@2
|
264 } // extern "C"
|
nuclear@2
|
265
|
nuclear@2
|
266 float randf(float low, float high)
|
nuclear@2
|
267 {
|
nuclear@2
|
268 std::uniform_real_distribution<float> unirnd(low, high);
|
nuclear@2
|
269 return unirnd(rnd_gen);
|
nuclear@2
|
270 }
|
nuclear@2
|
271
|
nuclear@8
|
272 static void render_pixel(struct erebus *ctx, int x, int y, int sample)
|
nuclear@2
|
273 {
|
nuclear@5
|
274 Camera *cam = ctx->scn->get_active_camera();
|
nuclear@5
|
275 if(!cam) return;
|
nuclear@5
|
276
|
nuclear@5
|
277 int xsz = ctx->fbimg.get_width();
|
nuclear@5
|
278 int ysz = ctx->fbimg.get_height();
|
nuclear@8
|
279 int offs = (y * xsz + x) * 4;
|
nuclear@5
|
280
|
nuclear@8
|
281 float *pix = ctx->fbimg.get_pixels() + offs;
|
nuclear@8
|
282 float *accum = ctx->accum.get_pixels() + offs;
|
nuclear@8
|
283
|
nuclear@8
|
284 Ray ray = cam->get_primary_ray(x, y, xsz, ysz, sample);
|
nuclear@8
|
285 Color c = ray_trace(ray, ctx->scn, 0);
|
nuclear@8
|
286 accum[0] += c.x;
|
nuclear@8
|
287 accum[1] += c.y;
|
nuclear@8
|
288 accum[2] += c.z;
|
nuclear@8
|
289 accum[3] += c.w;
|
nuclear@8
|
290
|
nuclear@8
|
291 float inv_samples = 1.0f / (float)(sample + 1);
|
nuclear@8
|
292 pix[0] = accum[0] * inv_samples;
|
nuclear@8
|
293 pix[1] = accum[1] * inv_samples;
|
nuclear@8
|
294 pix[2] = accum[2] * inv_samples;
|
nuclear@8
|
295 pix[3] = accum[3] * inv_samples;
|
nuclear@2
|
296 }
|