diff --git a/config.cpp b/config.cpp index ce26005a..ece738b1 100644 --- a/config.cpp +++ b/config.cpp @@ -2257,6 +2257,10 @@ EX void display_embedded_errors() { dialog::addInfo(XLAT("error: currently works only in binary tiling and similar"), 0xC00000); return; } + if(shmup::on && cgi.emb->no_spin()) { + dialog::addInfo(XLAT("error: this embedding does not work in shmup"), 0xC00000); + return; + } if(meuclid && spatial_embedding == seCliffordTorus) { rug::clifford_torus ct; ld h = ct.xh | ct.yh; diff --git a/embeddings.cpp b/embeddings.cpp index f0d58cb4..0a591644 100644 --- a/embeddings.cpp +++ b/embeddings.cpp @@ -236,6 +236,7 @@ EX } virtual hyperpoint logical_to_base(hyperpoint h) = 0; virtual ld anim_center_z() { return center_z(); } virtual hyperpoint anim_tile_center(); + virtual void logical_fix(transmatrix&) = 0; virtual bool is_euc_in_product() { return false; } virtual bool is_product_embedding() { return false; } @@ -380,6 +381,17 @@ struct emb_none : embedding_method { } return h; } + + void logical_fix(transmatrix& T) { + if(nonisotropic) { + hyperpoint h = tC0(T); + transmatrix rot = gpushxto0(h) * T; + fix_rotation(rot); + T = rgpushxto0(h) * rot; + } + else fixmatrix(T); + fixelliptic(T); + } }; /** embeddings methods that are not emb_none */ @@ -407,14 +419,27 @@ struct emb_actual : embedding_method { h[3] = 1; return h; } + + void logical_fix(transmatrix& T) { + hyperpoint a = T * tile_center(); + hyperpoint i0 = actual_to_intermediate(a); + auto l0 = intermediate_to_logical * i0; + auto l = l0; l[2] = 0; + auto i = logical_to_intermediate * l; + auto rot0= inverse(intermediate_to_actual_translation(i0)) * T ; + auto rot = intermediate_to_logical_scaled * rot0 * logical_scaled_to_intermediate; + ld alpha = atan2(rot[0][1], rot[0][0]); + T = intermediate_to_actual_translation(i) * spin(alpha); + fixelliptic(T); + } }; /** embed in the 3D variant of the same geometry */ struct emb_same_in_same : emb_actual { virtual bool is_same_in_same() { return true; } - transmatrix intermediate_to_actual_translation(hyperpoint i) override { return rgpushxto0(i); } - hyperpoint actual_to_intermediate(hyperpoint a) override { return a; } + transmatrix intermediate_to_actual_translation(hyperpoint i) override { return rgpushxto0(logical_to_actual(i)); } + hyperpoint actual_to_intermediate(hyperpoint a) override { return actual_to_logical(a); } hyperpoint orthogonal_move(const hyperpoint& h, ld z) override { ld u = 1; if(h[2]) z += asin_auto(h[2]), u /= cos_auto(asin_auto(h[2])); @@ -478,6 +503,12 @@ struct emb_same_in_same : emb_actual { return hpxy3(h[0] * u, h[1] * u, 0); } + void logical_fix(transmatrix& T) override { + // optimization + for(int i=0; i<4; i++) T[i][2] = T[2][i] = i == 2; + fixmatrix(T); + fixelliptic(T); + } }; /** embed in the product geometry */ @@ -539,7 +570,7 @@ struct emb_sphere_in_low : emb_actual { bool is_sph_in_low() override { return true; } bool is_depth_limited() override { return true; } transmatrix intermediate_to_actual_translation(hyperpoint i) override { - return map_relative_push(logical_to_actual(i)); + return map_relative_push(logical_to_actual(i)) * zpush(-1); } hyperpoint actual_to_intermediate(hyperpoint a) override { return actual_to_logical(a); } ld center_z() { return 1; } @@ -596,6 +627,12 @@ struct emb_sphere_in_low : emb_actual { h[2] = z; h[3] = 1; return h; } + + void logical_fix(transmatrix& T) { + fix4(T); + fixmatrix(T); + fixelliptic(T); + } }; /** abstract class for embeddings of Euclidean plane; these embeddings are not isotropic */ diff --git a/hyperpoint.cpp b/hyperpoint.cpp index abf847c8..e08d1eda 100644 --- a/hyperpoint.cpp +++ b/hyperpoint.cpp @@ -1449,28 +1449,27 @@ EX transmatrix shift_object(transmatrix Position, const transmatrix& ori, const } case smEmbedded: { - if(cgi.emb->is_euc_in_hyp() || cgi.emb->is_sph_in_low()) { - geom3::light_flip(true); - transmatrix T = rgpushxto0(direct_exp(direction)); - geom3::light_flip(false); - return Position * cgi.emb->base_to_actual(T); + // optimization -- should work without it + if(cgi.emb->is_same_in_same()) return Position * rgpushxto0(direct_exp(direction)); + + if(gproduct) { + return Position * cgi.emb->intermediate_to_actual_translation(ori * cgi.emb->logical_to_intermediate * direction); } - if(cgi.emb->is_euc_in_sph()) Position = inverse(View) * Position; + auto P = inverse_shift(ggmatrix(cwt.at), shiftless(Position)); - transmatrix rot = inverse(cgi.emb->map_relative_push(Position * tile_center())) * Position; - ld z = cgi.emb->center_z(); - if(z) rot = rot * lzpush(z); - transmatrix urot = unswap_spin(rot); + hyperpoint a = P * tile_center(); + hyperpoint i0 = cgi.emb->actual_to_intermediate(a); + auto l0 = cgi.emb->intermediate_to_logical * i0; + auto l = l0; l[2] = 0; + auto i = cgi.emb->logical_to_intermediate * l; + auto rot1 = inverse(cgi.emb->intermediate_to_actual_translation(i)) * P; + auto rot = cgi.emb->intermediate_to_logical_scaled * rot1 * cgi.emb->logical_scaled_to_intermediate; - geom3::light_flip(true); - transmatrix T = rgpushxto0(direct_exp(urot * direction)); - geom3::light_flip(false); - T = cgi.emb->base_to_actual(T); - auto res = Position * inverse(rot) * T * rot; - - if(cgi.emb->is_euc_in_sph()) res = View * res; - return res; + auto par = cgi.emb->logical_to_intermediate * rot * direction; + if(cgi.emb->is_sph_in_low()) par = cspin180(0, 1) * par; // reason unknown + auto tra = cgi.emb->intermediate_to_actual_translation(par); + return Position * inverse(rot1) * tra * rot1; } default: throw hr_exception("unknown shift method in shift_object"); } diff --git a/hypgraph.cpp b/hypgraph.cpp index b845aa09..495df093 100644 --- a/hypgraph.cpp +++ b/hypgraph.cpp @@ -2025,14 +2025,9 @@ EX void adjust_eye(transmatrix& T, cell *c, ld sign) { T = T * lzpush(sign * (cgi.SLEV[sl] - cgi.FLOOR - vid.eye + i)); } -/** achieve top-down perspective */ -EX transmatrix default_spin() { - return cspin90(0, 1) * cgi.emb->intermediate_to_logical_scaled; - } - EX bool shmup_inverted() { if(!embedded_plane) return false; - return (vid.wall_height < 0) ^ (cgi.emb->is_euc_in_nil() || cgi.emb->is_euc_in_sl2()); + return vid.wall_height < 0; } EX void centerpc(ld aspd) { @@ -2056,8 +2051,8 @@ EX void centerpc(ld aspd) { centerover = pc->base; transmatrix T = pc->at; - if(nonisotropic) { - transmatrix rot = inverse(rgpushxto0(T * C0)) * T; + if(embedded_plane) { + transmatrix rot = inverse(cgi.emb->intermediate_to_actual_translation(cgi.emb->actual_to_intermediate(T * tile_center()))) * T; T = T * inverse(rot); adjust_eye(T, pc->base, +1); T = T * rot; @@ -2069,11 +2064,13 @@ EX void centerpc(ld aspd) { View = iview_inverse(T); if(gproduct) NLP = ortho_inverse(pc->ori); if(WDIM == 2) { + // already taken into account in gproduct + rotate_view(cgi.emb->intermediate_to_logical_scaled); + // right => upwards + rotate_view(cspin90(0, 1)); + // apply playerturny if(shmup_inverted()) rotate_view(cspin180(2, 1)); - if(gproduct) - rotate_view( cspin(2, 1, -90._deg - shmup::playerturny[id]) * cspin90(0, 1)); - else - rotate_view( cspin(2, 1, -90._deg - shmup::playerturny[id]) * default_spin()); + rotate_view( cspin(2, 1, -90._deg - shmup::playerturny[id])); } return; } diff --git a/shmup.cpp b/shmup.cpp index 4fd613cd..924252d2 100644 --- a/shmup.cpp +++ b/shmup.cpp @@ -105,7 +105,6 @@ void monster::reset() { stunoff = 0; blowoff = 0; fragoff = 0; footphase = 0; inertia = Hypc; ori = Id; vel = 0; swordangle = 0; - if(cgip && cgi.emb && cgi.emb->is_euc_in_product()) ori = cgi.emb->intermediate_to_logical_scaled; } using namespace multi; @@ -185,51 +184,11 @@ cell *monster::findbase(const shiftmatrix& T, int maxsteps) { else return findbaseAround(T, base, maxsteps); } -/** fix the matrix, including the appropriate fixes for nonisotropic, embedded_plane, and elliptic space */ -void full_fix(transmatrix& T) { - if(embedded_plane) { - if(cgi.emb->is_sph_in_low()) { - for(int i=0; i<4; i++) T[i][3] = 0, T[3][i] = 0; - T[3][3] = 1; - fixmatrix(T); - } - else if(cgi.emb->is_same_in_same()) { - for(int i=0; i<4; i++) T[i][2] = 0, T[2][i] = 0; - T[2][2] = 1; - fixmatrix(T); - } - else if(gproduct) { - fixmatrix(T); - } - else { - hyperpoint h = T * tile_center(); - transmatrix rot = iso_inverse(cgi.emb->map_relative_push(h)) * T; - if(cgi.emb->is_euc_in_sph()) rot = rot * lzpush(1); - fix_rotation(rot); - if(cgi.emb->is_hyp_in_solnih()) h[0] = 0; - else if(cgi.emb->is_euc_in_nil()) h[1] = 0; - - T = cgi.emb->map_relative_push(h) * rot; - if(cgi.emb->is_euc_in_sph()) T = T * lzpush(-1); - fixmatrix(T); - } - } - else if(nonisotropic) { - hyperpoint h = tC0(T); - transmatrix rot = gpushxto0(h) * T; - fix_rotation(rot); - T = rgpushxto0(h) * rot; - } - else - fixmatrix(T); - fixelliptic(T); - } - void monster::rebasePat(const shiftmatrix& new_pat, cell *c2) { if(isVirtual) { at = new_pat.T; virtualRebase(this); - full_fix(at); + cgi.emb->logical_fix(at); pat = shiftless(at); if(multi::players == 1 && this == shmup::pc[0]) current_display->which_copy = back_to_view(ggmatrix(base)); @@ -239,7 +198,7 @@ void monster::rebasePat(const shiftmatrix& new_pat, cell *c2) { at = inverse_shift(gmatrix[base], new_pat); transmatrix old_at = at; virtualRebase(this); - full_fix(at); + cgi.emb->logical_fix(at); if(base != c2) { if(fake::split()) println(hlog, "fake error"); else { @@ -257,7 +216,7 @@ void monster::rebasePat(const shiftmatrix& new_pat, cell *c2) { pat = new_pat; base = c2; at = inverse_shift(gmatrix[c2], pat); - full_fix(at); + cgi.emb->logical_fix(at); } bool trackroute(monster *m, shiftmatrix goal, double spd) {