nuclear@2: #include nuclear@2: #include nuclear@2: #include nuclear@2: #include nuclear@2: #include "erebus.h" nuclear@2: #include "vmath/vector.h" nuclear@2: #include "image.h" nuclear@4: #include "scene.h" nuclear@4: #include "geomobj.h" nuclear@5: #include "rt.h" nuclear@2: nuclear@2: using namespace std::chrono; nuclear@2: nuclear@2: struct Rect { nuclear@2: int x, y, width, height; nuclear@2: nuclear@2: bool operator ==(const Rect &r) { return memcmp(this, &r, sizeof r) == 0; } nuclear@2: bool operator !=(const Rect &r) { return memcmp(this, &r, sizeof r) != 0; } nuclear@2: }; nuclear@2: nuclear@2: #define INVALID_RECT Rect{0, 0, 0, 0} nuclear@2: nuclear@2: struct erebus { nuclear@4: Scene *scn; nuclear@4: nuclear@2: Image fbimg; nuclear@2: Vector4 options[ERB_NUM_OPTIONS]; nuclear@2: nuclear@2: // render state nuclear@2: long cur_time; nuclear@2: int cur_pixel_x, cur_pixel_y; nuclear@2: Rect cur_rect; nuclear@2: }; nuclear@2: nuclear@2: static void render_pixel(struct erebus *ctx, int x, int y); nuclear@2: nuclear@2: static std::mt19937 rnd_gen; nuclear@2: nuclear@2: extern "C" { nuclear@2: nuclear@2: struct erebus *erb_init(void) nuclear@2: { nuclear@2: struct erebus *ctx; nuclear@2: try { nuclear@2: ctx = new struct erebus; nuclear@2: } nuclear@2: catch(...) { nuclear@2: return 0; nuclear@2: } nuclear@2: nuclear@4: ctx->scn = 0; nuclear@2: ctx->cur_time = 0; nuclear@2: ctx->cur_rect = INVALID_RECT; nuclear@2: return ctx; nuclear@2: } nuclear@2: nuclear@2: void erb_destroy(struct erebus *ctx) nuclear@2: { nuclear@2: delete ctx; nuclear@2: } nuclear@2: nuclear@2: void erb_setopti(struct erebus *ctx, enum erb_option opt, int val) nuclear@2: { nuclear@2: ctx->options[opt].x = val; nuclear@2: } nuclear@2: void erb_setoptf(struct erebus *ctx, enum erb_option opt, float val) nuclear@2: { nuclear@2: ctx->options[opt].x = val; nuclear@2: } nuclear@2: void erb_setoptfv(struct erebus *ctx, enum erb_option opt, float *vec) nuclear@2: { nuclear@2: for(int i=0; i<4; i++) { nuclear@2: ctx->options[opt][i] = vec[i]; nuclear@2: } nuclear@2: } nuclear@2: nuclear@2: int erb_getopti(struct erebus *ctx, enum erb_option opt) nuclear@2: { nuclear@2: return ctx->options[opt].x; nuclear@2: } nuclear@2: float erb_getoptf(struct erebus *ctx, enum erb_option opt) nuclear@2: { nuclear@2: return ctx->options[opt].x; nuclear@2: } nuclear@2: float *erb_getoptfv(struct erebus *ctx, enum erb_option opt) nuclear@2: { nuclear@2: return &ctx->options[opt].x; nuclear@2: } nuclear@2: nuclear@2: float *erb_get_framebuffer(struct erebus *ctx) nuclear@2: { nuclear@2: return ctx->fbimg.get_pixels(); nuclear@2: } nuclear@2: nuclear@2: void erb_begin_frame(struct erebus *ctx, long ms) nuclear@2: { nuclear@2: ctx->cur_time = ms; nuclear@4: nuclear@4: int xsz = ctx->options[ERB_OPT_WIDTH].x; nuclear@4: int ysz = ctx->options[ERB_OPT_HEIGHT].x; nuclear@4: nuclear@4: ctx->fbimg.create(xsz, ysz); nuclear@2: } nuclear@2: nuclear@2: int erb_render(struct erebus *ctx, long timeout) nuclear@2: { nuclear@2: return erb_render_rect(ctx, 0, 0, ctx->fbimg.get_width(), ctx->fbimg.get_height(), timeout); nuclear@2: } nuclear@2: nuclear@2: int erb_render_rect(struct erebus *ctx, int x, int y, int width, int height, long timeout) nuclear@2: { nuclear@2: if(!width || !height) return -1; nuclear@2: nuclear@2: Rect rect{x, y, width, height}; nuclear@2: if(ctx->cur_rect != rect) { nuclear@2: ctx->cur_rect = rect; nuclear@2: ctx->cur_pixel_x = x; nuclear@2: ctx->cur_pixel_y = y; nuclear@2: } nuclear@2: nuclear@4: ctx->scn->update(); nuclear@4: nuclear@2: if(timeout > 0) { nuclear@2: auto start_time = steady_clock::now(); nuclear@2: while(duration_cast(steady_clock::now() - start_time).count() < timeout) { nuclear@2: render_pixel(ctx, ctx->cur_pixel_x, ctx->cur_pixel_y); nuclear@2: nuclear@2: if(++ctx->cur_pixel_x >= ctx->cur_rect.width) { nuclear@2: if(++ctx->cur_pixel_y >= ctx->cur_rect.height) { nuclear@2: ctx->cur_rect = INVALID_RECT; nuclear@2: return 0; nuclear@2: } nuclear@2: } nuclear@2: } nuclear@2: return 1; nuclear@2: } nuclear@2: nuclear@2: for(int i=0; iscn; nuclear@4: ctx->scn = new Scene; nuclear@2: nuclear@4: // XXX for now just create a test scene here nuclear@4: Sphere *sph = new Sphere; nuclear@4: SceneNode *sph_node = new SceneNode(sph); nuclear@4: ctx->scn->add_object(sph); nuclear@4: ctx->scn->add_node(sph_node); nuclear@4: nuclear@4: TargetCamera *cam = new TargetCamera(Vector3(0, 0, -10), Vector3(0, 0, 0)); nuclear@4: //ctx->scn->add_object(cam); nuclear@4: ctx->scn->use_camera(cam); nuclear@4: nuclear@4: return 0; nuclear@2: } nuclear@2: nuclear@2: } // extern "C" nuclear@2: nuclear@2: float randf(float low, float high) nuclear@2: { nuclear@2: std::uniform_real_distribution unirnd(low, high); nuclear@2: return unirnd(rnd_gen); nuclear@2: } nuclear@2: nuclear@2: static void render_pixel(struct erebus *ctx, int x, int y) nuclear@2: { nuclear@5: Camera *cam = ctx->scn->get_active_camera(); nuclear@5: if(!cam) return; nuclear@5: nuclear@5: int xsz = ctx->fbimg.get_width(); nuclear@5: int ysz = ctx->fbimg.get_height(); nuclear@5: float *pix = ctx->fbimg.get_pixels() + (y * xsz + x) * 4; nuclear@5: nuclear@5: Ray ray = cam->get_primary_ray(x, y, xsz, ysz, 0); nuclear@5: //Color c = ray_trace(ray, ctx->scn, 0); nuclear@5: Color c = ray.dir.normalized() * 0.5 + Vector3(0.5, 0.5, 0.5); nuclear@5: pix[0] = 1.0;//c.x; nuclear@5: pix[1] = c.y; nuclear@5: pix[2] = c.z; nuclear@5: pix[3] = c.w; nuclear@2: }