From 94cac21716d0dd92900ef771acd5f3ba67f7bc99 Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Fri, 24 Jul 2020 23:39:30 +0200 Subject: [PATCH] primitive-based rendering of the Berger sphere (very poor) --- config.cpp | 12 +++++ drawing.cpp | 84 ++++++++++++++++++++++++++++++-- hypgraph.cpp | 7 ++- nonisotropic.cpp | 123 +++++++++++++++++++++++++++++++++++++++++++++++ shaders.cpp | 2 +- 5 files changed, 221 insertions(+), 7 deletions(-) diff --git a/config.cpp b/config.cpp index 654dd832..6d4bde76 100644 --- a/config.cpp +++ b/config.cpp @@ -673,6 +673,8 @@ EX void initConfig() { addsaver(bright, "bright"); addsaver(cblind, "cblind"); + addsaver(berger_limit, "berger_limit"); + addsaverenum(centering, "centering"); callhooks(hooks_configfile); @@ -1729,6 +1731,16 @@ EX void show3D() { }); } + if(WDIM == 3 && sphere && stretch::factor) { + dialog::addItem(XLAT("Berger sphere limit"), berger_limit); + dialog::add_action([] () { + dialog::editNumber(berger_limit, 0, 10, 1, 2, "", + XLAT("Primitive-based rendering of Berger sphere is currently very slow and low quality. " + "Here you can choose how many images to draw.") + ); + }); + } + #if CAP_RAY if(GDIM == 3) { dialog::addItem(XLAT("configure raycasting"), 'A'); diff --git a/drawing.cpp b/drawing.cpp index d4cf2b5c..fe419723 100644 --- a/drawing.cpp +++ b/drawing.cpp @@ -1255,6 +1255,80 @@ void draw_s2xe0(dqi_poly *p) { #endif EX } +EX int berger_limit = 2; + +void draw_stretch(dqi_poly *p) { + + if(!(p->flags & POLY_TRIANGLES)) return; + + dqi_poly npoly = *p; + + npoly.offset = 0; + npoly.tab = &glcoords; + npoly.V = Id; + npoly.flags &= ~(POLY_INVERSE | POLY_FORCE_INVERTED); + + transmatrix T2 = stretch::translate( tC0(inverse(View)) ); + transmatrix U = View * T2; + + transmatrix iUV = inverse(U) * p->V; + + vector hs; + vector ths; + hs.resize(p->cnt); + ths.resize(p->cnt); + for(int i=0; icnt; i++) + hs[i] = iUV * glhr::gltopoint( (*p->tab)[p->offset+i] ); + + vector > results; + results.resize(p->cnt); + + auto& stinf = s2xe::stinf; + + if(p->tinf) { + npoly.tinf = &stinf; + npoly.offset_texture = 0; + stinf.texture_id = p->tinf->texture_id; + stinf.tvertices.clear(); + } + else { + npoly.tinf = NULL; + } + npoly.V = Id; + set_width(1); + glcoords.clear(); + + for(int i=0; icnt; i++) results[i] = stretch::inverse_exp_all(hs[i], berger_limit); + + for(int i=0; icnt; i+=3) { + auto &la = results[i]; + auto &lb = results[i+1]; + auto &lc = results[i+2]; + + int ia = 0, ib = 0, ic = 0; + + auto test = [] (hyperpoint a, hyperpoint b) -> bool { + return sqhypot_d(3, a-b) < 2; + }; + + for(auto& ha: la) for(auto& hb: lb) if(test(ha, hb)) + for(auto& hc: lc) if(test(ha, hc) && test(hb, hc)) { + + glcoords.push_back(glhr::pointtogl(U * ha)); + glcoords.push_back(glhr::pointtogl(U * hb)); + glcoords.push_back(glhr::pointtogl(U * hc)); + if(p->tinf) + for(int j=0; j<3; j++) + stinf.tvertices.push_back(p->tinf->tvertices[p->offset_texture+i+j]); + ia++; ib++; ic++; + } + } + + npoly.cnt = isize(glcoords); + + npoly.gldraw(); + } + EX namespace ods { #if CAP_ODS @@ -1522,6 +1596,11 @@ void dqi_poly::draw() { return; } */ + if(vid.usingGL && (current_display->set_all(global_projection), get_shader_flags() & SF_DIRECT) && sphere && (stretch::factor || ray::in_use)) { + draw_stretch(this); + return; + } + #if CAP_GL if(vid.usingGL && (current_display->set_all(global_projection), get_shader_flags() & SF_DIRECT)) { if(sl2 && pmodel == mdGeodesic && hybrid::csteps) { @@ -1997,14 +2076,11 @@ EX void reverse_transparent_walls() { EX void draw_main() { DEBBI(DF_GRAPH, ("draw_main")); - if(sphere && GDIM == 3 && pmodel == mdPerspective) { + if(sphere && GDIM == 3 && pmodel == mdPerspective && !stretch::in() && !ray::in_use) { if(ray::in_use && !ray::comparison_mode) { ray::cast(); reset_projection(); - /* currently incompatible with primitive-based renderer */ - /* also not implemented in stretch */ - if(stretch::factor) return; } #if CAP_GL diff --git a/hypgraph.cpp b/hypgraph.cpp index d897a7ff..84398a4a 100644 --- a/hypgraph.cpp +++ b/hypgraph.cpp @@ -1573,10 +1573,13 @@ EX void enable_flat_model() { #if HDR /** \brief enable the 'flat' model for drawing HUD. Use RAII so it will be switched back later */ +namespace stretch { extern ld factor; } + struct flat_model_enabler { projection_configuration bak; - flat_model_enabler() { bak = pconf; enable_flat_model(); } - ~flat_model_enabler() { pconf = bak; calcparam(); } + ld sf; + flat_model_enabler() { bak = pconf; sf = stretch::factor; stretch::factor = 0; enable_flat_model(); } + ~flat_model_enabler() { pconf = bak; stretch::factor = sf; calcparam(); } }; #endif diff --git a/nonisotropic.cpp b/nonisotropic.cpp index 60f0ed0c..bf5508dc 100644 --- a/nonisotropic.cpp +++ b/nonisotropic.cpp @@ -2404,6 +2404,129 @@ EX namespace stretch { h = itranslate(at) * h; return h[0] * h[0] + h[1] * h[1] + h[2] * h[2]; } + + EX vector inverse_exp_all(hyperpoint h, int generations) { + + vector res; + + if(stretch::factor == 0) { + ld d = hypot_d(3, h); + if(h[3] >= 1 || h[3] <= -1|| d == 0) return res; + ld a = acos(h[3]); + + res.push_back(point31(h[0] * a / d, h[1] * a / d, h[2] * a / d)); + + a = a - 2 * M_PI; + + res.push_back(point31(h[0] * a / d, h[1] * a / d, h[2] * a / d)); + + return res; + } + + ld xy = hypot_d(2, h); + ld SV = stretch::not_squared(); + + ld base_min_a = asin(xy); + ld base_max_a = M_PI - base_min_a; + + ld seek = M_PI/2-atan2(h[3], h[2]); + + auto ang = [&] (ld a) { + ld rp = xy / sin(a); + ld co = abs(rp) >= 1 ? 0 : sqrt(1-rp*rp); + + return atan2(co * sin(a), cos(a)) - co * (1 - 1/SV/SV) * a; + + // while(a0 > M_PI) a0 -= 2 * M_PI; + // while(a0 < -M_PI) a0 += 2 * M_PI; + }; + + for(int shift=-generations; shift ", vmin, " to ", vmax); + + int cmin = ceil((vmin - seek) / 2 / M_PI); + int cmax = floor((vmax - seek) / 2 / M_PI); + for(int c = cmin; c <= cmax; c++) { + ld cseek = seek + c * 2 * M_PI; + + for(int it=0; it<40; it++) { + + ld a = (mmin + mmax) / 2; + + ld cros = ang(a); + if(cros > cseek) mmax = a; else mmin = a; + } + + ld a = (mmin + mmax) / 2; + + ld r = asin_clamp( xy / sin(a) ); + + ld z_part = 1; + ld x_part = SV * tan(r); + + ld db = hypot(x_part, z_part); + x_part /= db; + z_part /= db; + + ld alpha = atan2(-h[1], h[0]); + + ld z = cos(r) * (1 - 1/SV/SV); + ld u = z * a; + + ld r_angle = alpha + u; + + ld len = a * hypot(sin_auto(r), cos_auto(r)/SV); + + auto answer = point3(cos(r_angle) * x_part * len, -sin(r_angle) * x_part * len, z_part * len); + + // int id = (shift << 10) + (mi << 9) + (t << 8) + c; + + /* + auto f = formula_exp(answer); + + ld err = sqhypot_d(4, f - h); + + println(hlog, "************************* ", answer, ": error = ", err, " id = ", id, " params = ", tie(shift, mi, t, c)); + */ + + res.emplace_back(answer); + } + } + } + } + + return res; + } + + EX } EX namespace nisot { diff --git a/shaders.cpp b/shaders.cpp index 79add50a..4b6e62b0 100644 --- a/shaders.cpp +++ b/shaders.cpp @@ -248,7 +248,7 @@ shared_ptr write_shader(flagtype shader_flags) { shader_flags |= SF_PERS3 | SF_DIRECT; if(hyperbolic) distfun = "acosh(t[3])", treset = true; - else if(euclid || nonisotropic) + else if(euclid || nonisotropic || stretch::in() || (sphere && ray::in_use)) distfun = "length(t.xyz)", treset = true; else { if(spherephase & 4) coordinator += "t = -t;\n";