diff --git a/hypgraph.cpp b/hypgraph.cpp index 49a9fddd..5bfa3c9e 100644 --- a/hypgraph.cpp +++ b/hypgraph.cpp @@ -2013,6 +2013,8 @@ EX void optimizeview() { fixmatrix(View); callhooks(hooks_postoptimize); + walking::handle(); + if(is_boundary(centerover)) centerover = c, View = oView; else diff --git a/intra.cpp b/intra.cpp index 8269ac5f..45eb6d9a 100644 --- a/intra.cpp +++ b/intra.cpp @@ -541,11 +541,9 @@ EX const connection_data* through_portal() { EX void check_portal_movement() { auto p = through_portal(); - ld c = camera_speed; + if(p) { ld eps = 1e-5; - c /= p->id1.scale; - anims::cycle_length /= p->id1.scale; ld ss = pow(eps, -2); array ds; /* camera, forward, upward */ @@ -605,9 +603,16 @@ EX void check_portal_movement() { println(hlog, "goal: at = ", xds[0], " det = ", dsdet(xds), " bt = ", bt::minkowski_to_bt(xds[0])); } - c *= p->id2.scale; - anims::cycle_length *= p->id2.scale; - camera_speed = c; + ld scale = p->id2.scale / p->id1.scale; + + camera_speed *= scale; + anims::cycle_length *= scale; + #if CAP_VR + absolute_units_in_meters *= scale; + #endif + if(walking::eye_level != -1) walking::eye_level *= scale; + + walking::floor_dir = -1; } } @@ -691,6 +696,8 @@ void show_portals() { } } + walking::add_options(); + dialog::display(); } @@ -790,8 +797,182 @@ auto hooks1 = arg::shift(); int i = arg::argi(); be_ratio_edge(i); }) + arg::add3("-debug-portal", [] { arg::shift(); debug_portal = arg::argi(); }); - - #endif EX } -} \ No newline at end of file + +EX namespace walking { + +EX bool on; + +EX bool auto_eyelevel; + +EX int floor_dir = -1; +EX cell *on_floor_of = nullptr; +EX ld eye_level = 0.2174492; +EX ld eye_angle = 0; +EX ld eye_angle_scale = 1; + +map recorded_floor_dir; + +int ticks_end, ticks_last; + +EX set colors_of_floors; + +EX bool isFloor(cell *c) { + if(!isWall(c)) return false; + return colors_of_floors.count(c->landparam); + } + +EX void handle() { + if(!on) return; + + if(floor_dir == -1 || on_floor_of != centerover) { + vector choices; + for(int i=0; itype; i++) + if(isFloor(centerover->cmove(i))) + choices.push_back(i); + + if(sol && isize(choices) == 2) choices.pop_back(); + + if(isize(choices) == 1) { + on_floor_of = centerover; + floor_dir = choices[0]; + println(hlog, "set floor_dir to ", floor_dir); + } + else { + println(hlog, "there are ", isize(choices), " choices for floor_dir"); + if(!on_floor_of) return; + } + } + + struct face { + hyperpoint h0, hx, hy; + }; + + transmatrix ToOld = currentmap->relative_matrix(on_floor_of, centerover, C0); + auto& csh = currentmap->get_cellshape(on_floor_of); + face f; + f.h0 = ToOld * csh.faces_local[floor_dir][0]; + f.hx = ToOld * csh.faces_local[floor_dir][1]; + f.hy = ToOld * csh.faces_local[floor_dir][2]; + + auto find_nearest = [&] (const face& fac, hyperpoint at) { + if(sol) { at[2] = fac.h0[2]; return at; } + else if(sphere && false) { + hyperpoint h = + project_on_triangle(csh.faces_local[floor_dir][0], csh.faces_local[floor_dir][1], csh.faces_local[floor_dir][2]); + transmatrix T = rspintox(h); + T = T * MirrorX; + transmatrix M = ToOld * T * xpush(-2*hdist0(h)) * spintox(h); + return mid(at, M * at); + } + else if(prod && bt::in()) { + auto dec = product_decompose(at); + hyperpoint dep = PIU( deparabolic13(dec.second) ); + hyperpoint h = product_decompose(fac.h0).second; + h = PIU( deparabolic13(h) ); + dep[0] = h[0]; + return zshift(PIU(parabolic13(dep)), dec.first); + } + else { + transmatrix M = ray::mirrorize(currentmap->ray_iadj(on_floor_of, floor_dir)); + M = ToOld * M * inverse(ToOld); + return mid(at, M * at); + } + }; + + hyperpoint at = tC0(inverse(View)); + if(invalid_point(at)) { + println(hlog, "at is invalid!"); + on = false; + return; + } + + auto wallpt = find_nearest(f, at); + + if(on_floor_of == centerover) + recorded_floor_dir[centerover] = floor_dir; + + ld view_eps = 1e-5; + + if(eye_angle) rotate_view(cspin(1, 2, -eye_angle * degree)); + hyperpoint front = inverse(get_shift_view_of(ctangent(2, -view_eps), View)) * C0; + hyperpoint up = inverse(get_shift_view_of(ctangent(1, +view_eps), View)) * C0; + + auto fwallpt = find_nearest(f, front); + + transmatrix T = nonisotropic ? nisot::translate(wallpt, -1) : gpushxto0(wallpt); + hyperpoint dx = inverse_exp(shiftless(T * at)); + + transmatrix Tf = nonisotropic ? nisot::translate(fwallpt, -1) : gpushxto0(fwallpt); + hyperpoint dxf = inverse_exp(shiftless(Tf * front)); + + if(eye_level == -1) eye_level = hypot_d(3, dx); + + auto smooth = [&] (hyperpoint h1, hyperpoint h2) { + if(ticks < ticks_end) { + ld last_t = ilerp(ticks_end-1000, ticks_end, ticks_last); + ld curr_t = ilerp(ticks_end-1000, ticks_end, ticks); + last_t = last_t * last_t * (3-2*last_t); + curr_t = curr_t * curr_t * (3-2*curr_t); + ld t = ilerp(last_t, 1, curr_t); + return lerp(h1, h2, t); + } + return h2; + }; + + auto oView = View; + set_view( + smooth(at, inverse(T) * direct_exp(dx / hypot_d(3, dx) * eye_level)), + smooth(front, inverse(Tf) * direct_exp(dxf / hypot_d(3, dxf) * eye_level)), + smooth(up, inverse(T) * direct_exp(dx / hypot_d(3, dx) * (eye_level + view_eps))) + ); + if(eye_angle) rotate_view(cspin(1, 2, eye_angle * degree)); + playermoved = false; + + auto nat = tC0(inverse(View)); + if(invalid_point(nat)) { + println(hlog, "at is invalid after fixing!"); + View = oView; + return; + } + + ticks_last = ticks; + } + +EX void add_options() { + dialog::addBoolItem("walking mode", on, 'w'); + dialog::add_action([] { + on = !on; + if(on && auto_eyelevel) eye_level = -1; + floor_dir = -1; + on_floor_of = nullptr; + ticks_last = ticks; + ticks_end = ticks + 1000; + }); + add_edit(eye_level); + add_edit(eye_angle); + } + +auto a = addHook(hooks_configfile, 100, [] { + param_b(auto_eyelevel, "auto_eyelevel") + -> editable("keep eye level when walking enabled", 'L'); + param_f(eye_level, "eye_level") + -> editable(0, 5, .1, "walking eye level", + "Distance from the floor to the eye in the walking mode, in absolute units. In VR this is adjusted automatically.", + 'e') + ->set_extra([] { add_edit(auto_eyelevel); }); + param_f(eye_angle, "eye_angle") + -> editable(-90, 90, 15, "walking eye angle", + "0 = looking forward, 90 = looking upward. In VR this is adjusted automatically.", + 'k') + ->set_extra([] { add_edit(eye_angle_scale); }); + param_f(eye_angle_scale, "eye_angle_scale") + -> editable(-2, 0, 2, "eye angle scale", + "1 = the angle can be changed with keyboard or mouse movements, 0 = the angle is fixed", + 'k'); + }) + + addHook(hooks_clearmemory, 40, [] { recorded_floor_dir.clear(); on_floor_of = nullptr; floor_dir = -1; }); + +EX } +} diff --git a/raycaster.cpp b/raycaster.cpp index f5dba4c7..e9f25a0d 100644 --- a/raycaster.cpp +++ b/raycaster.cpp @@ -2066,7 +2066,7 @@ void uniform2(GLint id, array fl) { color_t color_out_of_range = 0x0F0800FF; -transmatrix get_ms(cell *c, int a, bool mirror) { +EX transmatrix get_ms(cell *c, int a, bool mirror) { int z = a ? 1 : -1; if(c->type == 3) { @@ -2096,7 +2096,7 @@ transmatrix get_ms(cell *c, int a, bool mirror) { int nesting; -transmatrix mirrorize(transmatrix T) { +EX transmatrix mirrorize(transmatrix T) { T = inverse(T); hyperpoint h = tC0(T); ld d = hdist0(h);