diff --git a/config.cpp b/config.cpp index 9d66c0e4..ab8d6b0d 100644 --- a/config.cpp +++ b/config.cpp @@ -1504,6 +1504,28 @@ EX void add_edit_wall_quality(char c) { }); } +EX void edit_levellines(char c) { + if(levellines) + dialog::addSelItem(XLAT("level lines"), fts(levellines), c); + else + dialog::addBoolItem(XLAT("level lines"), false, c); + dialog::add_action([] { + dialog::editNumber(levellines, 0, 100, 0.5, 0, XLAT("level lines"), + XLAT( + "This feature superimposes level lines on the rendered screen. These lines depend on the Z coordinate. In 3D hyperbolic the Z coordinate is taken from the Klein model. " + "Level lines can be used to observe the curvature: circles correspond to positive curvature, while hyperbolas correspond to negative. See e.g. the Hypersian Rug mode.") + ); + dialog::reaction = ray::reset_raycaster; + dialog::extra_options = [] { + dialog::addBoolItem(XLAT("disable textures"), disable_texture, 'T'); + dialog::add_action([] { ray::reset_raycaster(); disable_texture = !disable_texture; }); + dialog::addItem(XLAT("disable level lines"), 'D'); + dialog::add_action([] { ray::reset_raycaster(); levellines = 0; popScreen(); }); + }; + dialog::bound_low(0); + }); + } + EX void show3D() { cmode = sm::SIDE | sm::MAYDARK; gamescreen(0); @@ -1590,7 +1612,9 @@ EX void show3D() { dialog::addItem(XLAT("configure raycasting"), 'A'); dialog::add_action_push(ray::configure); } - + + edit_levellines('L'); + if(WDIM == 3 || (GDIM == 3 && euclid)) { dialog::addSelItem(XLAT("radar range"), fts(vid.radarrange), 'R'); dialog::add_action([] () { @@ -2362,6 +2386,15 @@ EX int read_config_args() { else if(argis("-msmoff")) { PHASEFROM(2); memory_saving_mode = false; } + else if(argis("-levellines")) { + PHASEFROM(2); shift_arg_formula(levellines); + } + else if(argis("-level-notexture")) { + PHASEFROM(2); disable_texture = true; + } + else if(argis("-level-texture")) { + PHASEFROM(2); disable_texture = false; + } else if(argis("-msens")) { PHASEFROM(2); shift_arg_formula(mouseaim_sensitivity); } @@ -2496,6 +2529,7 @@ EX unordered_map params = { {"mgrid", vid.multiplier_grid}, {"mring", vid.multiplier_ring}, {"collignon", vid.collignon_parameter}, + {"levellines", levellines}, #endif }; diff --git a/drawing.cpp b/drawing.cpp index 39988404..c1098e92 100644 --- a/drawing.cpp +++ b/drawing.cpp @@ -1743,6 +1743,7 @@ EX void sortquickqueue() { } EX void quickqueue() { + current_display->next_shader_flags = 0; spherespecial = 0; reset_projection(); current_display->set_all(0); int siz = isize(ptds); @@ -1932,6 +1933,7 @@ EX hookset *hooks_vr_draw_all; EX void drawqueue() { callhooks(hook_drawqueue); + current_display->next_shader_flags = 0; reset_projection(); // reset_projection() is not sufficient here, because we need to know shaderside_projection diff --git a/floorshapes.cpp b/floorshapes.cpp index 1ff087b1..6b9173fa 100644 --- a/floorshapes.cpp +++ b/floorshapes.cpp @@ -1078,6 +1078,7 @@ EX void make_floor_textures() { dynamicval a3(vid.always3, false); dynamicval hq(inHighQual, true); dynamicval hd(darken, 0); + dynamicval hll(levellines, 0); dynamicval gd(vid.depth, 1); dynamicval gc(vid.camera, 1); dynamicval dcgip(cgip, cgip); diff --git a/glhr.cpp b/glhr.cpp index f975b75f..994b9d1b 100644 --- a/glhr.cpp +++ b/glhr.cpp @@ -253,7 +253,7 @@ struct GLprogram { GLuint vertShader, fragShader; GLint uFog, uFogColor, uColor, tTexture, tInvExpTable, uMV, uProjection, uAlpha, uFogBase, uPP; - GLint uPRECX, uPRECY, uPRECZ, uIndexSL, uIterations; + GLint uPRECX, uPRECY, uPRECZ, uIndexSL, uIterations, uLevelLines; flagtype shader_flags; @@ -318,7 +318,9 @@ GLprogram::GLprogram(string vsh, string fsh) { uPRECX = -1; uIterations = -1; uAlpha = -1; - return; + uLevelLines = -1; + uFogColor = -1; + return; } _vsh = vsh; _fsh = fsh; @@ -375,6 +377,7 @@ GLprogram::GLprogram(string vsh, string fsh) { uPRECZ = glGetUniformLocation(_program, "PRECZ"); uIndexSL = glGetUniformLocation(_program, "uIndexSL"); uIterations = glGetUniformLocation(_program, "uIterations"); + uLevelLines = glGetUniformLocation(_program, "uLevelLines"); } GLprogram::~GLprogram() { @@ -581,11 +584,14 @@ EX void full_enable(shared_ptr p) { EX void fog_max(ld fogmax, color_t fogcolor) { WITHSHADER({ - glUniform1f(current_glprogram->uFog, 1 / fogmax); + if(current_glprogram->uFog != -1) + glUniform1f(current_glprogram->uFog, 1 / fogmax); - GLfloat cols[4]; - for(int i=0; i<4; i++) cols[i] = part(fogcolor, 3-i) / 255.0; - glUniform4f(current_glprogram->uFogColor, cols[0], cols[1], cols[2], cols[3]); + if(current_glprogram->uFogColor != -1) { + GLfloat cols[4]; + for(int i=0; i<4; i++) cols[i] = part(fogcolor, 3-i) / 255.0; + glUniform4f(current_glprogram->uFogColor, cols[0], cols[1], cols[2], cols[3]); + } }, { glFogf(GL_FOG_END, fogmax); }) diff --git a/raycaster.cpp b/raycaster.cpp index 2061a761..e32d7cb2 100644 --- a/raycaster.cpp +++ b/raycaster.cpp @@ -123,7 +123,7 @@ void enable_raycaster() { last_geometry = geometry; deg = S7; if(prod) deg += 2; if(!our_raycaster) { - bool use_reflect = reflect_val && !nil; + bool use_reflect = reflect_val && !nil && !levellines; string vsh = "attribute vec4 aPosition;\n" @@ -556,14 +556,18 @@ void enable_raycaster() { fmain += " vec2 u = cid + vec2(float(which) / float(uLength), 0);\n" " vec4 col = texture2D(tWallcolor, u);\n" - " if(col[3] > 0.0) {\n" + " if(col[3] > 0.0) {\n"; + + if(!(levellines && disable_texture)) fmain += " vec2 inface = map_texture(position, which);\n" " vec3 tmap = texture2D(tTextureMap, u).rgb;\n" " if(tmap.z == 0.) col.xyz *= min(1., (1.-inface.x)/ tmap.x);\n" " else {\n" " vec2 inface2 = tmap.xy + tmap.z * inface;\n" " col.xyz *= texture2D(tTexture, inface2).rgb;\n" - " }\n" + " }\n"; + + fmain += " float d = max(1. - go / uLinearSightRange, uExpStart * exp(-go / uExpDecay));\n" " col.xyz = col.xyz * d + uFogColor.xyz * (1.-d);\n"; @@ -594,6 +598,14 @@ void enable_raycaster() { " float z = at0.z * go;\n" " float w = 1.;\n"; + if(levellines) { + if(hyperbolic) + fmain += "gl_FragColor.xyz *= 0.5 + 0.5 * cos(z/cosh(go) * uLevelLines * 2. * PI);\n"; + else + fmain += "gl_FragColor.xyz *= 0.5 + 0.5 * cos(z * uLevelLines * 2. * PI);\n"; + fsh += "uniform float uLevelLines;\n"; + } + fmain += " gl_FragDepth = (-float("+fts(vnear+vfar)+")+w*float("+fts(2*vnear*vfar)+")/z)/float("+fts(vnear-vfar)+");\n" " gl_FragDepth = (gl_FragDepth + 1.) / 2.;\n"; @@ -866,6 +878,8 @@ EX void cast() { glUniform4fv(o->uWallX, isize(wallx), &wallx[0][0]); glUniform4fv(o->uWallY, isize(wally), &wally[0][0]); + if(o->uLevelLines != -1) + glUniform1f(o->uLevelLines, levellines); if(o->uBinaryWidth != -1) glUniform1f(o->uBinaryWidth, vid.binary_width/2 * (nih?1:log(2))); if(o->uPLevel != -1) @@ -980,6 +994,8 @@ EX void configure() { }); }; }); + + edit_levellines('L'); dialog::addBack(); dialog::display(); diff --git a/rug.cpp b/rug.cpp index a53360dd..d9596444 100644 --- a/rug.cpp +++ b/rug.cpp @@ -1365,6 +1365,9 @@ EX void drawRugScene() { current_display->set_all(ed); eyewidth_translate(ed); + if(glhr::current_glprogram->uLevelLines != -1) + glUniform1f(glhr::current_glprogram->uLevelLines, levellines); + if(vid.stereo_mode == sODS) { glhr::projection_multiply(glhr::ortho(M_PI, M_PI, 100)); // 2*M_PI)); } @@ -1406,6 +1409,7 @@ EX void drawRugScene() { 100, darkena(backcolor, 0, 0xFF) ); + GLERR("fog_max"); for(int t=0; t 0) { glhr::prepare(cp_array); glLineWidth(lwidth); glDrawArrays(GL_LINES, 0, isize(cp_array)); } + GLERR("rugt"); current_display->set_mask(0); + GLERR("afterrug"); } glEnable(GL_BLEND); @@ -1885,6 +1892,7 @@ EX void show() { dialog::addSelItem(XLAT("anti-crossing"), fts(anticusp_factor), 'A'); dialog::addBoolItem(XLAT("3D monsters/walls on the surface"), spatial_rug, 'S'); dialog::add_action([] () { spatial_rug = !spatial_rug; }); + edit_levellines('L'); #if CAP_SURFACE if(hyperbolic) { diff --git a/shaders.cpp b/shaders.cpp index 3b1c261e..c2a464ed 100644 --- a/shaders.cpp +++ b/shaders.cpp @@ -8,10 +8,14 @@ #include "hyper.h" namespace hr { +EX ld levellines; +EX bool disable_texture; + #if HDR constexpr flagtype GF_TEXTURE = 1; constexpr flagtype GF_VARCOLOR = 2; constexpr flagtype GF_LIGHTFOG = 4; +constexpr flagtype GF_LEVELS = 8; constexpr flagtype GF_which = 15; @@ -56,14 +60,19 @@ shared_ptr write_shader(flagtype shader_flags) { vsh += "attribute mediump vec4 aPosition;\n"; varying += "varying mediump vec4 vColor;\n"; + fmain += "gl_FragColor = vColor;\n"; if(shader_flags & GF_TEXTURE) { vsh += "attribute mediump vec2 aTexture;\n"; varying += "varying mediump vec2 vTexCoord;\n"; fsh += "uniform mediump sampler2D tTexture;\n"; vmain += "vTexCoord = aTexture;\n", - fmain += "gl_FragColor = vColor * texture2D(tTexture, vTexCoord);\n"; + fmain += "gl_FragColor *= texture2D(tTexture, vTexCoord);\n"; + } + if(shader_flags & GF_LEVELS) { + fsh += "uniform mediump float uLevelLines;\n"; + varying += "varying mediump vec4 vPos;\n"; + fmain += "gl_FragColor.rgb *= 0.5 + 0.5 * cos(vPos.z/vPos.w * uLevelLines * 2. * PI);\n"; } - else fmain += "gl_FragColor = vColor;\n"; if(shader_flags & GF_VARCOLOR) { vsh += "attribute mediump vec4 aColor;\n"; @@ -77,7 +86,7 @@ shared_ptr write_shader(flagtype shader_flags) { if(shader_flags & GF_LIGHTFOG) { vmain += "float fogx = clamp(1.0 + aPosition.z * uFog, 0.0, 1.0); vColor = vColor * fogx + uFogColor * (1.0-fogx);\n"; vsh += "uniform mediump float uFog;\n"; - vsh += "uniform mediump float uFogColor;\n"; + vsh += "uniform mediump vec4 uFogColor;\n"; } string coordinator; @@ -90,12 +99,18 @@ shared_ptr write_shader(flagtype shader_flags) { bool skip_t = false; if(pmodel == mdPixel) { - vmain += "vec4 pos = aPosition; pos[3] = 1.0; gl_Position = uP * uMV * pos;\n"; + vmain += "vec4 pos = aPosition; pos[3] = 1.0;\n"; + vmain += "pos = uMV * pos;\n"; + if(shader_flags & GF_LEVELS) vmain += "vPos = pos;\n"; + vmain += "gl_Position = uP * pos;\n"; skip_t = true; shader_flags |= SF_PIXELS | SF_DIRECT; } else if(pmodel == mdManual) { - vmain += "gl_Position = uP * uMV * aPosition;\n"; + vmain += "vec4 pos = uMV * aPosition;\n"; + if(shader_flags & GF_LEVELS) + vmain += "vPos = pos;\n"; + vmain += "gl_Position = uP * pos;\n"; skip_t = true; shader_flags |= SF_DIRECT; } @@ -228,6 +243,7 @@ shared_ptr write_shader(flagtype shader_flags) { "uniform mediump float uFogBase;\n" "uniform mediump vec4 uFogColor;\n"; } + if(shader_flags & GF_LEVELS) vmain += "vPos = t;\n"; if(treset) vmain += "t[3] = 1.0;\n"; vmain += "gl_Position = uP * t;\n"; } @@ -269,6 +285,10 @@ void display_data::set_projection(int ed) { unsigned id; id = geometry; id <<= 6; id |= pmodel; + if(levellines && pmodel != mdPixel) { + shader_flags |= GF_LEVELS; + if(disable_texture) shader_flags &=~ GF_TEXTURE; + } id <<= 6; id |= shader_flags; id <<= 6; id |= spherephase; id <<= 1; if(vid.consider_shader_projection) id |= 1; @@ -397,6 +417,10 @@ void display_data::set_projection(int ed) { if(selected->uAlpha != -1) glhr::set_ualpha(vid.alpha); + if(selected->uLevelLines != -1) { + glUniform1f(selected->uLevelLines, levellines); + } + if(selected->shader_flags & SF_ORIENT) glhr::projection_multiply(model_orientation_gl());