From 6513b3a87ef0701a8996902b296a63251794c820 Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Sun, 1 Nov 2020 21:20:54 +0100 Subject: [PATCH] Panini projection --- config.cpp | 45 +++++++++++++++++++++++++++++++++++++++------ glhr.cpp | 2 +- graph.cpp | 5 ++++- models.cpp | 5 +++++ raycaster.cpp | 37 +++++++++++++++++++++++++++++-------- shaders.cpp | 27 ++++++++++++++++++++++++++- 6 files changed, 104 insertions(+), 17 deletions(-) diff --git a/config.cpp b/config.cpp index 66e8d784..e4d8a432 100644 --- a/config.cpp +++ b/config.cpp @@ -713,6 +713,8 @@ EX void initConfig() { addsaver(camera_speed, "camera-speed", 1); addsaver(camera_rot_speed, "camera-rot-speed", 1); + addsaver(panini_alpha, "panini_alpha", 0); + callhooks(hooks_configfile); #if CAP_CONFIG @@ -1551,17 +1553,48 @@ EX void explain_detail() { )); } -EX void add_edit_fov(char key IS('f')) { - dialog::addSelItem(XLAT("field of view"), fts(vid.fov) + "°", key); - dialog::add_action([] { - dialog::editNumber(vid.fov, 1, 170, 1, 45, "field of view", +EX ld max_fov_angle() { + return acos(-panini_alpha) * 2 / degree; + } + +EX void add_edit_fov(char key IS('f'), bool pop IS(false)) { + + string sfov = fts(vid.fov) + "°"; + if(panini_alpha) { + sfov += " / " + fts(max_fov_angle()) + "°"; + } + dialog::addSelItem(XLAT("field of view"), sfov, key); + dialog::add_action([=] { + if(pop) popScreen(); + dialog::editNumber(vid.fov, 1, max_fov_angle(), 1, 45, "field of view", XLAT( "Horizontal field of view, in angles. " "This affects the Hypersian Rug mode (even when stereo is OFF) " - "and non-disk models.") + "and non-disk models.") + "\n\n" + + XLAT( + "Must be less than %1°. Panini projection can be used to get higher values.", + fts(max_fov_angle()) + ) ); dialog::bound_low(1e-8); - dialog::bound_up(179); + dialog::bound_up(max_fov_angle() - 0.01); + dialog::extra_options = [] { + dialog::addSelItem(XLAT("Panini projection"), fts(panini_alpha), 'P'); + dialog::add_action([] { + popScreen(); + dialog::editNumber(panini_alpha, 0, 1, 0.1, 0, "Panini parameter", + XLAT( + "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.") + ); + dialog::reaction = ray::reset_raycaster; + dialog::extra_options = [] { + add_edit_fov('F', true); + }; + }); + }; }); } diff --git a/glhr.cpp b/glhr.cpp index d697ad1f..e5e2d03f 100644 --- a/glhr.cpp +++ b/glhr.cpp @@ -154,7 +154,7 @@ void display(const glmatrix& m) { printf("\n"); } -glmatrix operator * (glmatrix m1, glmatrix m2) { +EX glmatrix operator * (glmatrix m1, glmatrix m2) { glmatrix res; for(int i=0; i<4; i++) for(int j=0; j<4; j++) { diff --git a/graph.cpp b/graph.cpp index 22509867..7ea21314 100644 --- a/graph.cpp +++ b/graph.cpp @@ -22,6 +22,8 @@ EX bool spatial_graphics; EX bool wmspatial, wmescher, wmplain, wmblack, wmascii, wmascii3; EX bool mmspatial, mmhigh, mmmon, mmitem; +EX ld panini_alpha = 0; + EX int detaillevel = 0; EX bool first_cell_to_draw = true; @@ -4878,7 +4880,8 @@ EX void calcparam() { cd->xcenter += cd->scrsize * pconf.xposition; cd->ycenter += cd->scrsize * pconf.yposition; - cd->tanfov = tan(vid.fov * degree / 2); + ld fov = vid.fov * degree / 2; + cd->tanfov = sin(fov) / (cos(fov) + panini_alpha); callhooks(hooks_calcparam); reset_projection(); diff --git a/models.cpp b/models.cpp index c82dd7c8..7bed5ea0 100644 --- a/models.cpp +++ b/models.cpp @@ -208,6 +208,7 @@ EX namespace models { EX bool has_orientation(eModel m) { if(m == mdHorocyclic) return hyperbolic; + if((m == mdPerspective || m == mdGeodesic) && panini_alpha) return true; return among(m, mdHalfplane, mdPolynomial, mdPolygonal, mdTwoPoint, mdJoukowsky, mdJoukowskyInverted, mdSpiral, mdSimulatedPerspective, mdTwoHybrid, mdHorocyclic, mdAxial, mdAntiAxial, mdQuadrant, mdWerner, mdAitoff, mdHammer, mdLoximuthal, mdWinkelTripel) || mdBandAny(); @@ -868,6 +869,10 @@ EX namespace models { PHASEFROM(2); shift_arg_formula(vpconf.skiprope); } + else if(argis("-palpha")) { + PHASEFROM(2); + shift_arg_formula(panini_alpha, reset_all_shaders); + } else if(argis("-zoom")) { PHASEFROM(2); shift_arg_formula(vpconf.scale); } diff --git a/raycaster.cpp b/raycaster.cpp index f83bfbf0..950a0d21 100644 --- a/raycaster.cpp +++ b/raycaster.cpp @@ -370,14 +370,34 @@ void enable_raycaster() { " mediump mat4 vw = uStart * xzspin(-lambda) * xpush(eye) * yzspin(phi);\n" " mediump vec4 at0 = vec4(0., 0., 1., 0.);\n"; - else fmain += - " mediump mat4 vw = uStart;\n" - " mediump vec4 at0 = at;\n" - " gl_FragColor = vec4(0,0,0,1);\n" - " mediump float left = 1.;\n" - " at0.y = -at.y;\n" - " at0.w = 0.;\n" - " at0.xyz = at0.xyz / length(at0.xyz);\n"; + else { + fmain += + " mediump mat4 vw = uStart;\n" + " mediump vec4 at0 = at;\n" + " gl_FragColor = vec4(0,0,0,1);\n" + " mediump float left = 1.;\n" + " at0.y = -at.y;\n" + " at0.w = 0.;\n"; + + if(panini_alpha) fmain += + "mediump float hr = at0.x*at0.x;\n" + "mediump float alpha = " + to_glsl(panini_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 += + " at0.xyz = at0.xyz / length(at0.xyz);\n"; + } if(hyperbolic) fsh += " mediump float len(mediump vec4 x) { return x[3]; }\n"; else if(sphere && rotspace) fsh += " mediump float len(mediump vec4 x) { return 1.+x.x*x.x+x.y*x.y-x.z*x.z-x.w*x.w; }\n"; @@ -1091,6 +1111,7 @@ void enable_raycaster() { } #ifndef GLES_ONLY + /* todo: fix for Panini */ fmain += " gl_FragDepth = (" + to_glsl(-vnear-vfar)+"+w*" + to_glsl(2*vnear*vfar)+"/z)/" + to_glsl(vnear-vfar)+";\n" " gl_FragDepth = (gl_FragDepth + 1.) / 2.;\n"; diff --git a/shaders.cpp b/shaders.cpp index 67d2be1b..2ff71a25 100644 --- a/shaders.cpp +++ b/shaders.cpp @@ -60,6 +60,12 @@ glhr::glmatrix model_orientation_gl() { return s; } +EX void reset_all_shaders() { + ray::reset_raycaster(); + compiled_programs.clear(); + matched_programs.clear(); + } + shared_ptr write_shader(flagtype shader_flags) { string varying, vsh, fsh, vmain = "void main() {\n", fmain = "void main() {\n"; @@ -332,6 +338,21 @@ shared_ptr write_shader(flagtype shader_flags) { } if(shader_flags & GF_LEVELS) vmain += "vPos = t;\n"; if(treset) vmain += "t[3] = 1.0;\n"; + + if(WDIM == 3 && panini_alpha) { + vmain += "t = uPP * t;", vsh += "uniform mediump mat4 uPP;"; + /* panini */ + vmain += "t.w += 1.; t *= 2. / t.w; t.w -= 1.;\n"; + vmain += "float s = t.z;"; + vmain += "float l = length(t.xyz);"; + vmain += "t /= max(length(t.xz), 1e-2);\n"; + vmain += "t.z += " + glhr::to_glsl(panini_alpha) + ";\n"; + vmain += "t *= l;\n"; + vmain += "t.w = 1.;\n"; + frustum_culling = false; + shader_flags |= SF_ORIENT; + } + vmain += "gl_Position = uP * t;\n"; } @@ -485,7 +506,8 @@ void display_data::set_projection(int ed, ld shift) { for(int i=0; i<3; i++) NLP[3][i] = NLP[i][3] = 0; NLP[3][3] = 1; } - glhr::projection_multiply(glhr::tmtogl_transpose(NLP)); + if(!(shader_flags & SF_ORIENT)) + glhr::projection_multiply(glhr::tmtogl_transpose(NLP)); } if(ed) { glhr::using_eyeshift = true; @@ -520,6 +542,9 @@ void display_data::set_projection(int ed, ld shift) { if(get_shader_flags() & SF_USE_ALPHA) pp[3][2] = GLfloat(pconf.alpha); + if(nisot::local_perspective_used()) + pp = glhr::tmtogl_transpose(NLP) * pp; + if(get_shader_flags() & SF_ORIENT) { if(GDIM == 3) for(int a=0; a<4; a++) models::apply_orientation_yz(pp[a][1], pp[a][2]);