diff --git a/config.cpp b/config.cpp index f09d96cf..35868bac 100644 --- a/config.cpp +++ b/config.cpp @@ -1027,6 +1027,7 @@ EX void initConfig() { param_f(camera_rot_speed, "camrot", "camera-rot-speed", 1); param_f(panini_alpha, "panini_alpha", 0); + param_f(stereo_alpha, "stereo_alpha", 0); callhooks(hooks_configfile); @@ -1863,14 +1864,15 @@ EX void explain_detail() { } EX ld max_fov_angle() { - if(panini_alpha >= 1 || panini_alpha <= -1) return 360; - return acos(-panini_alpha) * 2 / degree; + auto& p = panini_alpha ? panini_alpha : stereo_alpha; + if(p >= 1 || p <= -1) return 360; + return acos(-p) * 2 / degree; } EX void add_edit_fov(char key IS('f'), bool pop IS(false)) { string sfov = fts(vid.fov) + "°"; - if(panini_alpha) { + if(panini_alpha || stereo_alpha) { sfov += " / " + fts(max_fov_angle()) + "°"; } dialog::addSelItem(XLAT("field of view"), sfov, key); @@ -1897,7 +1899,22 @@ EX void add_edit_fov(char key IS('f'), bool pop IS(false)) { "The Panini projection is an alternative perspective projection " "which allows very wide field-of-view values. HyperRogue uses " "a quick implementation, so parameter values too close to 1 may " - "be buggy; try e.g. 0.9 instead.") + "be buggy (outside of raycasting); try e.g. 0.9 instead.") + ); + dialog::reaction = reset_all_shaders; + dialog::extra_options = [] { + add_edit_fov('F', true); + }; + }); + dialog::addSelItem(XLAT("spherical perspective projection"), fts(stereo_alpha), 'S'); + dialog::add_action([] { + popScreen(); + dialog::editNumber(stereo_alpha, 0, 1, 0.1, 0, "spherical perspective parameter", + XLAT( + "Set to 1 to get stereographic projection, " + "which allows very wide field-of-view values. HyperRogue uses " + "a quick implementation, so parameter values too close to 1 may " + "be buggy (outside of raycasting); try e.g. 0.9 instead.") ); dialog::reaction = reset_all_shaders; dialog::extra_options = [] { diff --git a/graph.cpp b/graph.cpp index ec543865..2ad98459 100644 --- a/graph.cpp +++ b/graph.cpp @@ -23,6 +23,7 @@ EX bool wmspatial, wmescher, wmplain, wmblack, wmascii, wmascii3; EX bool mmspatial, mmhigh, mmmon, mmitem; EX ld panini_alpha = 0; +EX ld stereo_alpha = 0; EX int detaillevel = 0; @@ -3742,7 +3743,7 @@ EX bool clip_checked = false; void make_clipping_planes() { #if MAXMDIM >= 4 clip_checked = false; - if(!frustum_culling || PIU(sphere) || experimental || vid.stereo_mode == sODS || panini_alpha) return; + if(!frustum_culling || PIU(sphere) || experimental || vid.stereo_mode == sODS || panini_alpha || stereo_alpha) return; if(WDIM == 3 && pmodel == mdPerspective && !nonisotropic && !in_s2xe()) threshold = sin_auto(cgi.corner_bonus), xyz_threshold = 0, clip_checked = true; @@ -4980,7 +4981,7 @@ EX void calcparam() { cd->ycenter += cd->scrsize * pconf.yposition; ld fov = vid.fov * degree / 2; - cd->tanfov = sin(fov) / (cos(fov) + panini_alpha); + cd->tanfov = sin(fov) / (cos(fov) + (panini_alpha ? panini_alpha : stereo_alpha)); callhooks(hooks_calcparam); reset_projection(); diff --git a/models.cpp b/models.cpp index 75a91e04..d9cefde8 100644 --- a/models.cpp +++ b/models.cpp @@ -892,6 +892,10 @@ EX namespace models { PHASEFROM(2); shift_arg_formula(panini_alpha, reset_all_shaders); } + else if(argis("-salpha")) { + PHASEFROM(2); + shift_arg_formula(stereo_alpha, reset_all_shaders); + } else if(argis("-zoom")) { PHASEFROM(2); shift_arg_formula(vpconf.scale); } diff --git a/raycaster.cpp b/raycaster.cpp index 3ea3bf80..f28e1120 100644 --- a/raycaster.cpp +++ b/raycaster.cpp @@ -436,7 +436,23 @@ void enable_raycaster() { "at0.xyz *= hz+alpha;\n" "at0.z = hz;\n}" " else at0.z = 0.;\n" -"\n" + "\n" + ; + + else if(stereo_alpha) fmain += + "mediump float hr = at0.x*at0.x+at0.y*at0.y;\n" + "mediump float alpha = " + to_glsl(stereo_alpha) + ";\n" + "mediump float A = 1. + hr;\n" + "mediump float B = -2.*hr*alpha;\n" + "mediump float C = 1. - hr*alpha*alpha;\n" + "B /= A; C /= A;\n" + + "mediump float hz = B / 2. + sqrt(C + B*B/4.);\n" + "if(abs(hz) > 1e-3) {" + "at0.xyz *= hz+alpha;\n" + "at0.z = hz;\n}" + " else at0.z = 0.;\n" + "\n" ; fmain += @@ -1241,6 +1257,9 @@ void enable_raycaster() { if(panini_alpha) fmain += panini_shader(); + else if(stereo_alpha) + fmain += stereo_shader(); + #ifndef GLES_ONLY fmain += " gl_FragDepth = (" + to_glsl(-vnear-vfar)+"+t.w*" + to_glsl(2*vnear*vfar)+"/t.z)/" + to_glsl(vnear-vfar)+";\n" diff --git a/shaders.cpp b/shaders.cpp index 7a27194d..a9892ca3 100644 --- a/shaders.cpp +++ b/shaders.cpp @@ -77,6 +77,17 @@ EX string panini_shader() { "t.w = 1.;\n"; } +EX string stereo_shader() { + return + "t.w += 1.; t *= 2. / t.w; t.w -= 1.;\n" + "float s = t.z;\n" + "float l = length(t.xyz);\n" + "t /= max(l, 1e-2);\n" + "t.z += " + glhr::to_glsl(panini_alpha) + ";\n" + "t *= l;\n" + "t.w = 1.;\n"; + } + shared_ptr write_shader(flagtype shader_flags) { string varying, vsh, fsh, vmain = "void main() {\n", fmain = "void main() {\n"; @@ -415,6 +426,10 @@ shared_ptr write_shader(flagtype shader_flags) { vmain += panini_shader(); shader_flags |= SF_ORIENT; } + else if((shader_flags & SF_PERS3) && stereo_alpha) { + vmain += "t = uPP * t;", vsh += "uniform mediump mat4 uPP;"; + vmain += stereo_shader(); + } vmain += "gl_Position = uP * t;\n"; }