diff --git a/classes.cpp b/classes.cpp index bb45f1fe..fd8a367a 100644 --- a/classes.cpp +++ b/classes.cpp @@ -995,7 +995,7 @@ enum eModel : int { /** list of available models (i.e., projections) */ EX vector mdinf = { {"disk/Gans", "general perspective", "general perspective", mf::azimuthal | mf::conformal, DEFAULTS}, - {"half-plane", "inversion", "half-plane", mf::conformal, DEFAULTS}, + {"half-plane", "inversion", "stereographic projection [VR]", mf::conformal, DEFAULTS}, {"band", "band", "Mercator", mf::band | mf::conformal, DEFAULTS}, {X3("polygonal"), mf::conformal, DEFAULTS}, {X3("formula"), 0, DEFAULTS}, diff --git a/drawing.cpp b/drawing.cpp index 2fdaca31..c757ed3a 100644 --- a/drawing.cpp +++ b/drawing.cpp @@ -161,6 +161,9 @@ EX unsigned char& part(color_t& col, int i) { #endif } +bool in_vr_sphere; +hyperpoint vr_sphere_center; + bool fatborder; EX color_t poly_outline; @@ -277,6 +280,7 @@ int axial_sign() { bool is_behind(const hyperpoint& H) { if(pmodel == mdAxial && sphere) return axial_sign() * H[2] <= BEHIND_LIMIT; + if(in_vr_sphere) return false; return pmodel == mdDisk && (hyperbolic ? H[2] >= 0 : true) && (nonisotropic ? false : pconf.alpha + H[2] <= BEHIND_LIMIT); } @@ -304,6 +308,7 @@ EX bool two_sided_model() { constexpr bool in_vr = false; #endif if(GDIM == 3) return false; + if(in_vr_sphere) return true; if(pmodel == mdHyperboloid) return !euclid && !in_vr; // if(pmodel == mdHemisphere) return true; if(pmodel == mdDisk) return sphere; @@ -316,6 +321,16 @@ EX bool two_sided_model() { } EX int get_side(const hyperpoint& H) { + if(in_vr_sphere) { + hyperpoint Hscr; + applymodel(shiftless(H), Hscr); + Hscr[3] = 1; + E4; + hyperpoint actual = vrhr::hmd_mv * Hscr; + ld val = 0; + for(int i=0; i<3; i++) val += (vr_sphere_center[i] - actual[i]) * actual[i]; + return val > 0 ? -1 : 1; + } if(pmodel == mdDisk && sphere) { double curnorm = H[0]*H[0]+H[1]*H[1]+H[2]*H[2]; double horizon = curnorm / pconf.alpha; @@ -951,7 +966,10 @@ void compute_side_by_centerin(dqi_poly *p, bool& nofill) { else nofill = true; } - applymodel(h1, hscr); hscr[0] *= current_display->radius; hscr[1] *= current_display->radius * pconf.stretch; + applymodel(h1, hscr); + if(vrhr::state != 2) { + hscr[0] *= current_display->radius; hscr[1] *= current_display->radius * pconf.stretch; + } for(int i=0; i 0 || equi)) can_have_inverse = true; + if(vrhr::state == 2) can_have_inverse = false; if(sphere && among(pmodel, mdEquidistant, mdEquiarea)) can_have_inverse = true; if(pmodel == mdJoukowsky) can_have_inverse = true; if(pmodel == mdJoukowskyInverted && pconf.skiprope) can_have_inverse = true; @@ -2234,6 +2253,21 @@ EX void reverse_transparent_walls() { EX void draw_main() { DEBBI(DF_GRAPH, ("draw_main")); + + in_vr_sphere = false; + #if CAP_VR + in_vr_sphere = vrhr::state == 2 && among(pmodel, mdDisk, mdBall, mdHyperboloid, mdHalfplane, mdHemisphere); + if(in_vr_sphere) { + hyperpoint a, b; + applymodel(shiftless(point3(0, 0, 1)), a); + applymodel(shiftless(point3(0, 0, -1)), b); + vr_sphere_center = (a + b) / 2; + vr_sphere_center[3] = 1; + E4; + vr_sphere_center = vrhr::hmd_mv * vr_sphere_center; + } + #endif + if(sphere && GDIM == 3 && pmodel == mdPerspective && !stretch::in() && !ray::in_use) { if(ray::in_use && !ray::comparison_mode) { diff --git a/hypgraph.cpp b/hypgraph.cpp index 323013d9..ad03f8ae 100644 --- a/hypgraph.cpp +++ b/hypgraph.cpp @@ -376,6 +376,49 @@ EX void applymodel(shiftpoint H_orig, hyperpoint& ret) { apply_other_model(H_orig, ret, pmodel); } +EX void vr_sphere(hyperpoint& ret, hyperpoint& H, eModel md) { + ret = H; + int flip = 1; + if(md == mdHalfplane) flip = -flip; + if(pconf.alpha < 1) flip = -flip; + ret *= pow(sqhypot_d(3, H), (flip * pconf.depth_scaling-1) / 2); + ret[2] += pconf.alpha; + if(md == mdHalfplane) { + ld d = sqhypot_d(3, ret); + ret /= abs(d); + } + models::apply_vr(ret[2], ret[1]); + } + +void vr_disk(hyperpoint& ret, hyperpoint& H) { + if(euclid) { + ret = H; + ret[2] = vid.depth * (1 - (ret[2] - 1) * pconf.depth_scaling) + pconf.alpha + vid.camera; + } + else if(sphere) { + vr_sphere(ret, H, mdDisk); + return; + } + else { + ld zlev = find_zlev(H); + ld zl = vid.depth-geom3::factor_to_lev(zlev) * pconf.depth_scaling; + + ld d = hdist0(H); + ld dd = hypot_d(2, H); + + hyperpoint H1 = ypush(vid.camera) * xpush(d) * ypush0(zl); + ld tzh = pconf.alpha + H1[2]; + ld ax = H1[0] / tzh; + ld ay = H1[1] / tzh; + + ret[0] = ax * H[0] / dd; + ret[1] = ax * H[1] / dd; + ret[2] = ay; + } + + models::apply_vr(ret[2], ret[1]); + } + EX void apply_other_model(shiftpoint H_orig, hyperpoint& ret, eModel md) { hyperpoint H = H_orig.h; @@ -409,6 +452,10 @@ EX void apply_other_model(shiftpoint H_orig, hyperpoint& ret, eModel md) { return; case mdBall: { + if(vrhr::state == 2) { + vr_disk(ret, H); + return; + } ld zlev = find_zlev(H); ld zl = vid.depth-geom3::factor_to_lev(zlev) * pconf.depth_scaling; @@ -435,22 +482,7 @@ EX void apply_other_model(shiftpoint H_orig, hyperpoint& ret, eModel md) { break; } if(vrhr::state == 2) { - ld zlev = find_zlev(H); - ld zl = vid.depth-geom3::factor_to_lev(zlev) * pconf.depth_scaling; - - ld d = hdist0(H); - ld dd = hypot_d(2, H); - - hyperpoint H1 = ypush(vid.camera) * xpush(d) * ypush0(zl); - ld tzh = pconf.alpha + H1[2]; - ld ax = H1[0] / tzh; - ld ay = H1[1] / tzh; - - ret[0] = ax * H[0] / dd; - ret[1] = ax * H[1] / dd; - ret[2] = ay; - - models::apply_vr(ret[2], ret[1]); + vr_disk(ret, H); return; } ld tz = get_tz(H); @@ -486,6 +518,10 @@ EX void apply_other_model(shiftpoint H_orig, hyperpoint& ret, eModel md) { } case mdHalfplane: { + if(sphere && vrhr::state == 2) { + vr_sphere(ret, H, md); + return; + } // Poincare to half-plane ld zlev = find_zlev(H); @@ -661,6 +697,7 @@ EX void apply_other_model(shiftpoint H_orig, hyperpoint& ret, eModel md) { } case gcSphere: { + if(vrhr::state == 2) { vr_sphere(ret, H, md); return; } ret = H; if(pconf.depth_scaling != 1) { ld v = intval(H, Hypc); @@ -699,12 +736,13 @@ EX void apply_other_model(shiftpoint H_orig, hyperpoint& ret, eModel md) { #if CAP_VR if(vrhr::state == 2) { + if(sphere) { vr_sphere(ret, H, md); return; } ret[0] = H[0] * pconf.hyperboloid_scaling; ret[1] = H[1] * pconf.hyperboloid_scaling; ret[2] = (pconf.alpha + H[2]); if(pconf.depth_scaling != 1) { ld v = intval(H, Hypc); - ret *= pow(v, (pconf.depth_scaling*(sphere?-1:1)-1) / 2); + ret *= pow(v, (pconf.depth_scaling-1) / 2); } models::apply_vr(ret[2], ret[1]); break; diff --git a/vr.cpp b/vr.cpp index fa92d6a6..5ca0b413 100644 --- a/vr.cpp +++ b/vr.cpp @@ -143,8 +143,10 @@ EX string error_msg; /** 0 = not loaded, 1 = loaded but not currently rendering, 2 = currently rendering the VR screen, 3 = currently rendering the computer screen */ EX int state = 0; +#if HDR // use E4 when working with real-world matrices to ensure that inverses, multiplications, etc. are computed correctly #define E4 dynamicval g(geometry, gCubeTiling) +#endif #define IN_E4(x) [&]{ E4; return x; }() @@ -198,7 +200,7 @@ EX bool first = true; EX transmatrix hmd_at = Id; EX transmatrix hmd_ref_at = Id; -EX transmatrix hmd_mvp, hmd_pre; +EX transmatrix hmd_mvp, hmd_pre, hmd_mv; EX transmatrix sm; @@ -794,6 +796,7 @@ EX void render() { if(eyes == eEyes::equidistant) { hmd_mvp = inverse(vrdata.eyepos[i]) * hmd_mvp; } + hmd_mv = hmd_mvp; hmd_mvp = vrdata.proj[i] * hmd_mvp; } eyeproj = vrdata.iproj[i];