diff --git a/geom-exp.cpp b/geom-exp.cpp index 10ec8bd2..96221d2a 100644 --- a/geom-exp.cpp +++ b/geom-exp.cpp @@ -709,7 +709,24 @@ EX void showEuclideanMenu() { }); } - if(euwrap || geometry == gFieldQuotient || cryst || archimedean || (euclid && WDIM == 3)) { + else if(geometry == gRotSpace) { + constexpr auto& r = rots::underlying_scale; + dialog::addSelItem(XLAT("view the underlying geometry"), r > 0 ? fts(r)+"x" : "NO", '4'); + dialog::add_action([] { + dialog::editNumber(r, 0, 1, 0.05, 0.25, XLAT("view the underlying geometry"), + XLAT( + "The space you are currently in the space of rotations of the underlying hyperbolic or spherical geometry. " + "This option lets you see the underlying space. Lands and some walls (e.g. in the Graveyard) are based on " + "the respective features in the underlying world, but details such as monsters or items are ignored." + ) + ); + dialog::bound_low(0); + dialog::bound_up(1); + dialog::extra_options = [] () { rots::draw_underlying(true); }; + }); + } + + else if(euwrap || geometry == gFieldQuotient || cryst || archimedean || (euclid && WDIM == 3)) { dialog::addItem(XLAT("advanced parameters"), '4'); dialog::add_action([] { if(0); diff --git a/graph.cpp b/graph.cpp index 9db348e2..db18e529 100644 --- a/graph.cpp +++ b/graph.cpp @@ -7531,6 +7531,8 @@ EX bool dronemode; purehookset hooks_calcparam; +EX int corner_centering; + EX void calcparam() { DEBBI(DF_GRAPH, ("calc param")); @@ -7571,11 +7573,21 @@ EX void calcparam() { if(GDIM == 3 && in_perspective()) cd->radius = cd->scrsize; realradius = min(realradius, cd->radius); + ld aradius = sphere ? cd->radius / (vid.alpha - 1) : cd->radius; + if(dronemode) { cd->ycenter -= cd->radius; cd->ycenter += vid.fsize/2; cd->ycenter += vid.fsize/2; cd->radius *= 2; } + if(corner_centering) { + cd->ycenter = cd->ytop + cd->ysize - vid.fsize - aradius; + if(corner_centering == 1) + cd->xcenter = cd->xtop + vid.fsize + aradius; + if(corner_centering == 2) + cd->xcenter = cd->xtop + cd->xsize - vid.fsize - aradius; + } + cd->xcenter += cd->scrsize * vid.xposition; cd->ycenter += cd->scrsize * vid.yposition; - + cd->tanfov = tan(vid.fov * degree / 2); if(pmodel) diff --git a/hud.cpp b/hud.cpp index 181a85c4..9e4e96e8 100644 --- a/hud.cpp +++ b/hud.cpp @@ -481,6 +481,10 @@ EX void drawStats() { first_cell_to_draw = true; bool h = hide_player(); + bool cornermode = (vid.xres > vid.yres * 85/100 && vid.yres > vid.xres * 85/100); + + if(geometry == gRotSpace) rots::draw_underlying(!cornermode); + { dynamicval pm(pmodel, flat_model()); glClear(GL_DEPTH_BUFFER_BIT); @@ -492,9 +496,7 @@ EX void drawStats() { if(prod) vid.alpha = 30, vid.scale = 30; calcparam(); - - bool cornermode = (vid.xres > vid.yres * 85/100 && vid.yres > vid.xres * 85/100); - + if(vid.radarsize > 0 && h) #if CAP_RACING if(!racing::on) diff --git a/nonisotropic.cpp b/nonisotropic.cpp index 94eefd9c..fa3c6139 100644 --- a/nonisotropic.cpp +++ b/nonisotropic.cpp @@ -1217,6 +1217,89 @@ EX namespace rots { } }; + /** reinterpret the given point of rotspace as a rotation matrix in the underlying geometry */ + EX transmatrix qtm(hyperpoint h) { + if(hyperbolic) { + hyperpoint k = slr::to_phigans(h); + ld z = k[2]; k[2] = 0; + ld r = hypot_d(2, k); + // k[1] = -k[1]; + k[0] = -k[0]; + if(r) k = tangent_length(k, asinh(r) * 2); + return spin(-z * 2) * rgpushxto0(direct_exp(k, 0)); + } + + double sq0 = h[0]*h[0]; + double sq1 = h[1]*h[1]; + double sq2 = h[2]*h[2]; + double sq3 = h[3]*h[3]; + + transmatrix M; + + M[0][0] = sq0 - sq1 - sq2 + sq3; + M[1][1] = -sq0 + sq1 - sq2 + sq3; + M[2][2] = -sq0 - sq1 + sq2 + sq3; + + double tmp1 = h[0]*h[1]; + double tmp2 = h[2]*h[3]; + M[0][1] = -2 * (tmp1 + tmp2); + M[1][0] = -2 * (tmp1 - tmp2); + + tmp1 = h[0]*h[2]; + tmp2 = h[1]*h[3]; + M[0][2] = 2 * (tmp1 - tmp2); + M[2][0] = 2 * (tmp1 + tmp2); + + tmp1 = h[1]*h[2]; + tmp2 = h[0]*h[3]; + M[1][2] = -2 * (tmp1 + tmp2); + M[2][1] = -2 * (tmp1 - tmp2); + + return M; + } + + EX ld underlying_scale = 0; + + EX void draw_underlying(bool cornermode) { + if(underlying_scale <= 0) return; + ld d = hybrid::current_view_level; + d *= cgi.plevel; + transmatrix T = rots::uzpush(-d) * spin(-2*d); + + if(det(T) < 0) T = centralsym * T; + + hyperpoint h = inverse(View * spin(master_to_c7_angle()) * T) * C0; + + auto g = std::move(gmatrix); + auto g0 = std::move(gmatrix0); + + hybrid::in_underlying_map([&] { + cgi.require_shapes(); + dynamicval pcc(corner_centering, cornermode ? 1 : 2); + dynamicval pf(playerfound, true); + dynamicval m5(centerover, viewctr.at->c7); + dynamicval m2(View, ypush(0) * qtm(h)); + dynamicval m3(playerV, Id); + dynamicval m4(actual_view_transform, Id); + dynamicval pm(pmodel, mdDisk); + dynamicval pss(vid.scale, (sphere ? 10 : 1) * underlying_scale); + dynamicval psa(vid.alpha, sphere ? 10 : 1); + dynamicval p(hybrid::pmap, NULL); + dynamicval psr(sightrange_bonus, 0); + calcparam(); + reset_projection(); current_display->set_all(0); + ptds.clear(); + drawthemap(); + drawqueue(); + displaychr(current_display->xcenter, current_display->ycenter, 0, 24, '+', 0xFFFFFFFF); + glflush(); + }); + gmatrix = std::move(g); + gmatrix0 = std::move(g0); + calcparam(); + reset_projection(); current_display->set_all(0); + } + EX } EX namespace nisot { @@ -1370,6 +1453,11 @@ EX namespace nisot { set_geometry(gRotSpace); return 0; } + else if(argis("-rot_uscale")) { + PHASEFROM(2); + shift_arg_formula(rots::underlying_scale); + return 0; + } return 1; });