From 2be4ed0739f416ded2444067f143862588626967 Mon Sep 17 00:00:00 2001 From: Daniel Kolesa Date: Fri, 11 Sep 2020 02:54:14 +0200 Subject: [PATCH] update to tesseract rev 2425 --- README | 16 ++ README.md | 10 +- config/default_map_settings.cfg | 20 +- config/glsl.cfg | 1 + config/glsl/deferred.cfg | 170 +++++------- config/glsl/grass.cfg | 14 +- config/glsl/material.cfg | 50 ++-- config/glsl/model.cfg | 14 +- config/glsl/sky.cfg | 82 +++--- config/glsl/smfilter.cfg | 172 ++++++++++++ config/glsl/tonemap.cfg | 17 +- config/glsl/volumetric.cfg | 26 +- config/glsl/world.cfg | 111 +++++++- src/engine/animmodel.hh | 45 +++- src/engine/bih.cc | 21 +- src/engine/command.cc | 7 +- src/engine/dynlight.cc | 46 ---- src/engine/dynlight.hh | 3 +- src/engine/ents.hh | 4 +- src/engine/grass.cc | 29 ++- src/engine/hitzone.hh | 3 +- src/engine/iqm.hh | 2 +- src/engine/light.cc | 136 ---------- src/engine/light.hh | 3 - src/engine/main.cc | 2 +- src/engine/material.cc | 144 ++++------ src/engine/md2.hh | 2 +- src/engine/md3.hh | 4 +- src/engine/md5.hh | 2 +- src/engine/obj.hh | 2 +- src/engine/octa.hh | 4 +- src/engine/octarender.cc | 56 ++-- src/engine/physics.cc | 57 +--- src/engine/physics.hh | 3 +- src/engine/rendergl.cc | 35 ++- src/engine/rendergl.hh | 2 +- src/engine/renderlights.cc | 447 +++++++++++++++++++++++++++----- src/engine/renderlights.hh | 2 +- src/engine/rendermodel.cc | 9 +- src/engine/rendermodel.hh | 3 +- src/engine/rendersky.cc | 89 ++++--- src/engine/renderva.cc | 170 +++++++++--- src/engine/renderva.hh | 8 +- src/engine/shader.cc | 2 + src/engine/shader.hh | 2 +- src/engine/skelmodel.hh | 46 ++-- src/engine/texture.cc | 185 +++++++++---- src/engine/vertmodel.hh | 12 +- src/engine/water.cc | 343 ++++++++++++------------ src/engine/world.cc | 12 +- src/shared/geom.hh | 8 + src/shared/glemu.cc | 10 +- src/shared/glemu.hh | 2 +- src/shared/glexts.hh | 23 +- 54 files changed, 1653 insertions(+), 1035 deletions(-) create mode 100644 config/glsl/smfilter.cfg diff --git a/README b/README index 05cbae7..d10188d 100644 --- a/README +++ b/README @@ -18,6 +18,7 @@ It provides a bunch of new rendering features such as: * HDR rendering with tonemapping and bloom * real-time diffuse global illumination for sunlight (radiance hints) * volumetric lighting +* transparent shadows * screen-space ambient occlusion * screen-space reflections and refractions for water and glass (use as many water planes as you want now!) * screen-space refractive alpha cubes @@ -81,3 +82,18 @@ Set attribute 5 of a light entity to 4. The intensity of the volumetric lighting medium can be controlled by volscale and the color can by controlled by volcolour. +To make transparent shadows: + +This is toggled on a per-map basis by setting "alphashadow" to 0 (off), 1 (sunlight), or 2 (sunlight and point lights). +If set to 1, you must set attribute 5 of a light entity to 16 to enable transparent shadows for that point light. +If set to 2, all point lights automatically cast transparent shadows. +If a light is volumetric, regardless of "alphashadow" setting, the light entity's attribute 5 must be set to 20 (4 | 16) for it +to cast a volumetric transparent shadow. + +To make a cube cast transparent shadows, use the "alpha" material. The texalpha/valpha commands can be then used to control the alpha +of the cube, which then is used to determine the intensity of the transparent shadow. This intensity can be further scaled by setting +the global map variable "alphashadowscale" (0..2). + +If a texture slot has an alpha texture (slot "a"), this can be used to mask the alpha of the cube. This masked alpha will then be used +to further mask the transparent shadow. + diff --git a/README.md b/README.md index 23b84e8..db919bc 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # OctaCore -This is the upcoming engine part of the newly revived OctaForge project. For -now there is nothing much to see here, besides a plain Tesseract codebase -import. This is going to change in near future; stay tuned. +**Last Tesseract SVN import:** 2425 + +This is the work in progress engine part of the newly revived OctaForge +project. + +Nothing much to see here yet, besides a stripped down, partially refactored +Tesseract codebase. diff --git a/config/default_map_settings.cfg b/config/default_map_settings.cfg index 948c54d..896816c 100644 --- a/config/default_map_settings.cfg +++ b/config/default_map_settings.cfg @@ -16,17 +16,15 @@ setdefaultenv = [ if $emptymap [setdefaultenv] materialreset -loop+ i 1 4 [ - texture [glass@i] "mat_glass/nieb/scratch2.png" // Glass Normals - texture [water@i] "mat_water/nieb/water.png" // Water (Unused) - texture 1 "mat_water/appleflap/water_normal.png" // Water Normals - texture 1 "mat_water/nieb/waterfall.png" // Waterfall Diffuse - texture 1 "mat_water/nieb/waterfall_normal.png" // Waterfall Normals - texture [lava@i] "mat_lava/nieb/lava.png" // Lava Diffuse - texture 1 "mat_lava/nieb/lava_normal.png" // Lava Normals - texture 1 "mat_lava/nieb/lava.png" // Lavafall Diffuse - texture 1 "mat_lava/nieb/lava_normal.png" // Lavafall Normals -] +texture glass "mat_glass/nieb/scratch2.png" // Glass Normals +texture water "mat_water/nieb/water.png" // Water (Unused) +texture 1 "mat_water/appleflap/water_normal.png" // Water Normals +texture 1 "mat_water/nieb/waterfall.png" // Waterfall Diffuse +texture 1 "mat_water/nieb/waterfall_normal.png" // Waterfall Normals +texture lava "mat_lava/nieb/lava.png" // Lava Diffuse +texture 1 "mat_lava/nieb/lava_normal.png" // Lava Normals +texture 1 "mat_lava/nieb/lava.png" // Lavafall Diffuse +texture 1 "mat_lava/nieb/lava_normal.png" // Lavafall Normals decalreset diff --git a/config/glsl.cfg b/config/glsl.cfg index cbb17b0..406a0b0 100644 --- a/config/glsl.cfg +++ b/config/glsl.cfg @@ -10,6 +10,7 @@ exec "config/glsl/gi.cfg" exec "config/glsl/particle.cfg" exec "config/glsl/stain.cfg" exec "config/glsl/material.cfg" +exec "config/glsl/smfilter.cfg" exec "config/glsl/deferred.cfg" exec "config/glsl/tonemap.cfg" exec "config/glsl/volumetric.cfg" diff --git a/config/glsl/deferred.cfg b/config/glsl/deferred.cfg index b1d432e..4023fd1 100644 --- a/config/glsl/deferred.cfg +++ b/config/glsl/deferred.cfg @@ -55,7 +55,9 @@ lazyshader 0 msaaedgedetect [ // deferredlighttype: // p -> point-light shadow (default cubemap) +// P -> point-light color shadow // c -> CSM +// C -> color CSM // a -> AO // A -> AO sun // r -> radiance hints @@ -64,6 +66,7 @@ lazyshader 0 msaaedgedetect [ // E -> 5x5 weighted bilinear filter // F -> 3x3 weighted bilinear filter // f -> 4x rotated grid filter +// N -> no filtering // m -> minimap // M -> multisampled // O -> sample 1 @@ -116,8 +119,10 @@ deferredlightvariantshader = [ numlights = (+ $arg6 0) baselight = (< (mod $arg2 4) 2) spotlight = (>= (mod $arg2 8) 4) - transparent = (<= 8 $arg2 16) - avatar = (<= 17 $arg2 31) + transparent = (<= 8 $arg2 15) + avatar = (<= 24 $arg2 31) + local colorshadow + colorshadow = (|| (? (<= 16 $arg2 23) 2) [&& (dlopt "C") [! $transparent] [! $avatar]]) variantshader 0 $arg1 $arg2 (? (< $arg2 0) [ attribute vec4 vvertex; uniform mat4 lightmatrix; @@ -173,6 +178,22 @@ deferredlightvariantshader = [ uniform sampler3D tex6, tex7, tex8, tex9; ]]) ]]) + @(? $colorshadow [ + uniform sampler2DRect tex10; + #define sunshadowtype vec3 + #define filtersunshadow(tc) (filtershadow(tc) * filtercolorshadow(tex10, tc)) + ] [ + #define sunshadowtype float + #define filtersunshadow filtershadow + ]) + @(? (> $colorshadow 1) [ + uniform sampler2DRect tex11; + #define lightshadowtype vec3 + #define filterlightshadow(tc) (filtershadow(tc) * filtercolorshadow(tex11, tc)) + ] [ + #define lightshadowtype float + #define filterlightshadow filtershadow + ]) uniform vec3 camera; uniform mat4 worldmatrix; uniform vec4 fogdir; @@ -207,104 +228,19 @@ deferredlightvariantshader = [ ]) @(if (|| (dlopt "p") [dlopt "c"]) [ - cond [dlopt "G"] [result [ - @(? (> $usetexgather 1) [ - #define shadowgather(center, xoff, yoff) textureGatherOffset(tex4, center, shadowtc.z, ivec2(xoff, yoff)) - ] [ - #define shadowgather(center, xoff, yoff) step(shadowtc.z, textureGatherOffset(tex4, center, ivec2(xoff, yoff))) - ]) - float filtershadow(vec3 shadowtc) - { - vec2 offset = fract(shadowtc.xy - 0.5), center = (shadowtc.xy - offset)*shadowatlasscale; - vec4 group1 = shadowgather(center, -2, -2); - vec4 group2 = shadowgather(center, 0, -2); - vec4 group3 = shadowgather(center, 2, -2); - vec4 group4 = shadowgather(center, -2, 0); - vec4 group5 = shadowgather(center, 0, 0); - vec4 group6 = shadowgather(center, 2, 0); - vec4 group7 = shadowgather(center, -2, 2); - vec4 group8 = shadowgather(center, 0, 2); - vec4 group9 = shadowgather(center, 2, 2); - vec4 locols = vec4(group1.ab, group3.ab); - vec4 hicols = vec4(group7.rg, group9.rg); - locols.yz += group2.ab; - hicols.yz += group8.rg; - vec4 midcols = vec4(group1.rg, group3.rg) + vec4(group7.ab, group9.ab) + - vec4(group4.rg, group6.rg) + vec4(group4.ab, group6.ab) + - mix(locols, hicols, offset.y); - vec4 cols = group5 + vec4(group2.rg, group8.ab); - cols.xyz += mix(midcols.xyz, midcols.yzw, offset.x); - return dot(cols, vec4(1.0/25.0)); - } - ]] [dlopt "g"] [result [ - @(? (> $usetexgather 1) [ - #define shadowgather(center, xoff, yoff) textureGatherOffset(tex4, center, shadowtc.z, ivec2(xoff, yoff)) - ] [ - #define shadowgather(center, xoff, yoff) step(shadowtc.z, textureGatherOffset(tex4, center, ivec2(xoff, yoff))) - ]) - float filtershadow(vec3 shadowtc) - { - vec2 offset = fract(shadowtc.xy - 0.5), center = (shadowtc.xy - offset)*shadowatlasscale; - vec4 group1 = shadowgather(center, -1, -1); - vec4 group2 = shadowgather(center, 1, -1); - vec4 group3 = shadowgather(center, -1, 1); - vec4 group4 = shadowgather(center, 1, 1); - vec4 cols = vec4(group1.rg, group2.rg) + vec4(group3.ab, group4.ab) + mix(vec4(group1.ab, group2.ab), vec4(group3.rg, group4.rg), offset.y); - return dot(mix(cols.xyz, cols.yzw, offset.x), vec3(1.0/9.0)); - } - ]] [dlopt "E"] [result [ - #define shadowval(xy, xoff, yoff) float(shadow2DRect(tex4, vec3(xy + vec2(xoff, yoff), shadowtc.z))) - float filtershadow(vec3 shadowtc) - { - vec2 offset = fract(shadowtc.xy - 0.5); - vec4 center = vec4(shadowtc.xy - offset + 0.5, shadowtc.xy - offset*0.5); - vec4 size = vec4(offset + 1.0, 2.0 - offset); - return (1.0/25.0)*dot(size.zxzx*size.wwyy, - vec4(shadowval(center.zw, -1.5, -1.5), - shadowval(center.zw, 2.0, -1.5), - shadowval(center.zw, -1.5, 2.0), - shadowval(center.zw, 2.0, 2.0))) + - (2.0/25.0)*dot(size, - vec4(shadowval(center.zy, 2.0, 0.0), - shadowval(center.xw, 0.0, 2.0), - shadowval(center.zy, -1.5, 0.0), - shadowval(center.xw, 0.0, -1.5))) + - (4.0/25.0)*shadowval(center.xy, 0.0, 0.0); - } - ]] [dlopt "F"] [result [ - #define shadowval(center, xoff, yoff) float(shadow2DRect(tex4, center + vec3(xoff, yoff, 0.0))) - float filtershadow(vec3 shadowtc) - { - vec2 offset = fract(shadowtc.xy - 0.5); - vec3 center = shadowtc; - //center.xy -= offset; - //vec4 size = vec4(offset + 1.0, 2.0 - offset), weight = vec4(2.0 - 1.0 / size.xy, 1.0 / size.zw - 1.0); - //return (1.0/9.0)*dot(size.zxzx*size.wwyy, - // vec4(shadowval(center, weight.zw), - // shadowval(center, weight.xw), - // shadowval(center, weight.zy), - // shadowval(center, weight.xy))); - center.xy -= offset*0.5; - vec4 size = vec4(offset + 1.0, 2.0 - offset); - return (1.0/9.0)*dot(size.zxzx*size.wwyy, - vec4(shadowval(center, -0.5, -0.5), - shadowval(center, 1.0, -0.5), - shadowval(center, -0.5, 1.0), - shadowval(center, 1.0, 1.0))); - } - ]] [dlopt "f"] [result [ - #define shadowval(center, xoff, yoff) float(shadow2DRect(tex4, center + vec3(xoff, yoff, 0.0))) - float filtershadow(vec3 shadowtc) - { - return dot(vec4(0.25), - vec4(shadowval(shadowtc, -0.4, 1.0), - shadowval(shadowtc, -1.0, -0.4), - shadowval(shadowtc, 0.4, -1.0), - shadowval(shadowtc, 1.0, 0.4))); - } - ]] [result [ - #define filtershadow(shadowtc) float(shadow2DRect(tex4, shadowtc)) - ]] + cond [dlopt "G"] [ + smfilterg5 $colorshadow + ] [dlopt "g"] [ + smfilterg3 $colorshadow + ] [dlopt "E"] [ + smfilterb5 $colorshadow + ] [dlopt "F"] [ + smfilterb3 $colorshadow + ] [dlopt "f"] [ + smfilterrg $colorshadow + ] [ + smfilternone $colorshadow + ] ]) @(if (dlopt "c") [result [ @@ -495,7 +431,7 @@ deferredlightvariantshader = [ { @(if (= (+ (? (dlopt "p") $numlights) (dlopt "c")) 1) [unpackdistbias]) vec3 csmtc = getcsmtc(pos.xyz, distbias); - float sunoccluded = filtershadow(csmtc); + sunshadowtype sunoccluded = filtersunshadow(csmtc); @(if (dlopt "m") [result [ light += diffuse.rgb*sunfacing * sunlightcolor * sunoccluded; ]] [result [ @@ -555,14 +491,16 @@ deferredlightvariantshader = [ @(if $spotlight [ if (dlopt "p") [result [ vec3 spot@[j]tc = getspottc(light@[j]dir, spot@[j]dist, spotparams[@@j], shadowparams[@@j], shadowoffset[@@j], distbias * lightpos[@@j].w); - light@[j]atten *= spot@[j]atten * filtershadow(spot@[j]tc); + lightshadowtype light@[j]shadow = light@[j]atten * spot@[j]atten * filterlightshadow(spot@[j]tc); ]] [result [ - light@[j]atten *= spot@[j]atten; + float light@[j]shadow = light@[j]atten * spot@[j]atten; ]] ] [ if (dlopt "p") [result [ vec3 shadow@[j]tc = getshadowtc(light@[j]dir, shadowparams[@@j], shadowoffset[@@j], distbias * lightpos[@@j].w); - light@[j]atten *= filtershadow(shadow@[j]tc); + lightshadowtype light@[j]shadow = light@[j]atten * filterlightshadow(shadow@[j]tc); + ]] [result [ + #define light@[j]shadow light@[j]atten ]] ]) @(if (= (+ $numlights $baselight) 1) [result [ @@ -574,14 +512,14 @@ deferredlightvariantshader = [ ]]) light@[j]facing *= light@[j]invdist; @(if (dlopt "m") [result [ - light += diffuse.rgb*light@[j]facing * lightcolor[@@j].rgb * light@[j]atten; + light += diffuse.rgb*light@[j]facing * lightcolor[@@j].rgb * light@[j]shadow; ]] [result [ @(if (= (+ $numlights (dlopt "c")) 1) [unpackspec]) float light@[j]spec = pow(clamp(light@[j]facing*facing - light@[j]invdist*dot(camdir, light@[j]dir), 0.0, 1.0), gloss) * specscale; @(if (dlopt "z") [result [ light@[j]spec *= lightcolor[@@j].a; ]]) - light += (diffuse.rgb*light@[j]facing + light@[j]spec) * lightcolor[@@j].rgb * light@[j]atten; + light += (diffuse.rgb*light@[j]facing + light@[j]spec) * lightcolor[@@j].rgb * light@[j]shadow; @(? (= (+ $numlights $baselight) 1) [ float foglerp = clamp(exp2(fogcoord*fogdensity.x)*fogdensity.y, 0.0, 1.0); light *= foglerp; @@ -629,12 +567,14 @@ deferredlightshader = [ maxvariants = $basevariants if (dlopt "t") [maxvariants = (+ $maxvariants $basevariants 1)] if (dlopt "d") [maxvariants = (+ $maxvariants $basevariants 1)] - deferredlightvariantshader $shadername -1 (concatword $arg1 $arg3) $arg4 $arg5 0 $maxvariants // base shader, no points lights, sunlight + if (dlopt "P") [maxvariants = (+ $maxvariants (* (max $arg6 1) (? (dlopt "s") 2 1)))] + deferredlightvariantshader $shadername -1 $arg1 $arg4 $arg5 0 $maxvariants // null base shader + deferredlightvariantshader $shadername 0 (concatword $arg1 $arg3) $arg4 $arg5 0 $maxvariants // row 0, base shader, sunlight if (dlopt "t") [ - deferredlightvariantshader $shadername 16 (concatword $arg1 $arg3) $arg4 $arg5 0 $maxvariants // row 16, trasparency, base shader, no points lights, sunlight + deferredlightvariantshader $shadername 8 (concatword $arg1 $arg3) $arg4 $arg5 0 $maxvariants // row 8, transparency, base shader, sunlight ] if (dlopt "d") [ - deferredlightvariantshader $shadername 17 (concatword $arg1 $arg3) $arg4 $arg5 0 $maxvariants // row 17, avatar, base shader, no points lights, sunlight + deferredlightvariantshader $shadername 24 (concatword $arg1 $arg3) $arg4 $arg5 0 $maxvariants // row 24, avatar, base shader, sunlight ] loop+ i 1 (max $arg6 1) [ if (dlopt "b") [ @@ -667,6 +607,18 @@ deferredlightshader = [ deferredlightvariantshader $shadername 15 (concatword $arg1 $arg2) $arg4 $arg5 $i $maxvariants // row 15, transparent, shadowed spot lights ] ] + if (dlopt "P") [ + if (dlopt "b") [ + deferredlightvariantshader $shadername 17 (concatword $arg1 $arg2 $arg3) $arg4 $arg5 $i $maxvariants // row 17, color shadow, shadowed point lights, sunlight + ] + deferredlightvariantshader $shadername 19 (concatword $arg1 $arg2) $arg4 $arg5 $i $maxvariants // row 19, color shadow, shadowed point lights + if (dlopt "s") [ + if (dlopt "b") [ + deferredlightvariantshader $shadername 21 (concatword $arg1 $arg2 $arg3) $arg4 $arg5 $i $maxvariants // row 21, color shadow, shadowed spot lights, sunlight + ] + deferredlightvariantshader $shadername 23 (concatword $arg1 $arg2) $arg4 $arg5 $i $maxvariants // row 23, color shadow, shadowed spot lights + ] + ] if (dlopt "d") [ if (dlopt "b") [ deferredlightvariantshader $shadername 24 (concatword $arg1 $arg3) $arg4 $arg5 $i $maxvariants // row 24, avatar, point lights, sunlight diff --git a/config/glsl/grass.cfg b/config/glsl/grass.cfg index 1a56b71..a80dcbf 100644 --- a/config/glsl/grass.cfg +++ b/config/glsl/grass.cfg @@ -15,16 +15,22 @@ grassvariantshader = [ variantshader 0 $arg1 (? (grassopt "b") 0 -1) [ attribute vec4 vvertex, vcolor; attribute vec2 vtexcoord0; + attribute vec4 vtangent; uniform mat4 camprojmatrix; + uniform vec3 camera; + uniform vec3 grassmargin; @(ginterpvert) varying vec2 texcoord0; varying vec4 colorscale; + varying vec2 bounds; @(? (grassopt "b") [uniform vec4 blendmapparams; varying vec2 texcoord1;]) void main(void) { gl_Position = camprojmatrix * vvertex; colorscale = vcolor; - texcoord0 = vtexcoord0; + texcoord0 = vtexcoord0; + vec2 camdir = (camera.xy - vvertex.xy) * grassmargin.y; + bounds = vec2(dot(camdir, vtangent.xy), dot(camdir, vtangent.zw)) + grassmargin.z; @(? (grassopt "b") [ texcoord1 = (vvertex.xy - blendmapparams.xy)*blendmapparams.zw; ]) @@ -36,14 +42,18 @@ grassvariantshader = [ @(ginterpfrag) varying vec2 texcoord0; varying vec4 colorscale; + varying vec2 bounds; @(? (grassopt "b") [uniform sampler2D tex1; varying vec2 texcoord1;]) void main(void) { vec4 color = texture2D(tex0, texcoord0) * colorscale; + color.a *= clamp(min(bounds.x, bounds.y), 0.0, 1.0); @(? (grassopt "b") [ color.a *= texture2D(tex1, texcoord1).r; ]) - if(color.a <= grasstest) + vec2 coords = fract((gl_FragCoord.xy - 0.5)*0.5); + float dither = coords.x + 1.5*coords.y - 4.0*coords.x*coords.y + 0.25; + if(color.a <= grasstest * dither) discard; gcolor = vec4(color.rgb, 0.0); @(gnormpack [vec3(0.5, 0.5, 1.0)]) diff --git a/config/glsl/material.cfg b/config/glsl/material.cfg index f1ced84..6c8180b 100644 --- a/config/glsl/material.cfg +++ b/config/glsl/material.cfg @@ -51,10 +51,11 @@ lazyshader 0 "waterminimap" [ watershader = [ lazyshader 0 $arg1 [ attribute vec4 vvertex; - attribute vec2 vtexcoord0; uniform mat4 camprojmatrix; uniform vec3 camera; - varying vec2 texcoord0, texcoord1; + uniform vec2 watertexgen; + uniform float millis; + varying vec2 texcoord0, texcoord1, texcoord2, texcoord3; varying vec3 surface; @(? (>= (strstr $arg1 "reflect") 0) [ uniform mat4 raymatrix; @@ -68,16 +69,20 @@ watershader = [ @(? (>= (strstr $arg1 "reflect") 0) [ esurface = (raymatrix * vvertex).xyz; ]) - texcoord0 = vtexcoord0 * 0.18; - texcoord1 = vtexcoord0 * 0.08; + + vec2 tc = vvertex.xy * watertexgen; + texcoord0 = tc * 0.18 + millis*vec2( 0.25, 0.75)*0.1250; + texcoord1 = tc * 0.18 + millis*vec2(-0.75, -0.25)*0.1450; + texcoord2 = tc * 0.08 + millis*vec2(-0.50, 0.50)*0.0805; + texcoord3 = tc * 0.08 + millis*vec2( 0.25, -0.75)*0.0825; + @(gdepthpackvert 1) } ] [ @(gfetchdefs [tex7 tex8 tex9]) - uniform float millis; uniform vec3 camera; uniform mat4 linearworldmatrix; - varying vec2 texcoord0, texcoord1; + varying vec2 texcoord0, texcoord1, texcoord2, texcoord3; varying vec3 surface; uniform sampler2D tex0, tex1; uniform vec4 viewsize; @@ -102,10 +107,10 @@ watershader = [ void main(void) { vec3 camdir = camera - surface, camvec = normalize(camdir); - vec3 bump = texture2D(tex1, texcoord0 + millis*vec2( 0.25, 0.75)*0.1250).rgb; - vec3 bump2 = texture2D(tex1, texcoord0 + millis*vec2(-0.75, -0.25)*0.1450).rgb; - vec3 bump3 = texture2D(tex1, texcoord1 + millis*vec2(-0.50, 0.50)*0.0805).rgb; - vec3 bump4 = texture2D(tex1, texcoord1 + millis*vec2( 0.25, -0.75)*0.0825).rgb; + vec3 bump = texture2D(tex1, texcoord0).rgb; + vec3 bump2 = texture2D(tex1, texcoord1).rgb; + vec3 bump3 = texture2D(tex1, texcoord2).rgb; + vec3 bump4 = texture2D(tex1, texcoord3).rgb; bump = normalize(bump + bump2 + bump3 + bump4 - 2.0); vec2 rtc = bump.xy * waterrefract.w; @@ -248,18 +253,19 @@ waterfogshader waterfog lazyshader 0 "lava" [ attribute vec4 vvertex; attribute vec3 vnormal; - attribute vec2 vtexcoord0; uniform mat4 camprojmatrix; varying mat3 world; + uniform vec4 lavatexgen; varying vec2 texcoord0; @(ginterpvert) void main(void) { gl_Position = camprojmatrix * vvertex; - texcoord0 = vtexcoord0; vec3 tangent = mix(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), abs(vnormal.x)); vec3 bitangent = mix(vec3(0.0, 0.0, -1.0), vec3(0.0, 1.0, 0.0), abs(vnormal.z)); world = mat3(tangent, bitangent, vnormal); + vec2 tc = vec2(dot(vvertex.xy, tangent.xy), dot(vvertex.yz, bitangent.yz)); + texcoord0 = (tc + lavatexgen.zw) * lavatexgen.xy; @(gdepthpackvert) } ] [ @@ -285,21 +291,22 @@ lazyshader 0 "lava" [ lazyshader 0 "waterfallenv" [ attribute vec4 vvertex; attribute vec3 vnormal; - attribute vec2 vtexcoord0; uniform mat4 camprojmatrix; uniform vec4 camera; varying vec3 camdir; varying mat3 world; + uniform vec4 waterfalltexgen; varying vec2 texcoord0; @(ginterpvert 1) void main(void) { gl_Position = camprojmatrix * vvertex; - texcoord0 = vtexcoord0; camdir = camera.xyz - vvertex.xyz; vec3 tangent = mix(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), abs(vnormal.x)); vec3 bitangent = mix(vec3(0.0, 0.0, -1.0), vec3(0.0, 1.0, 0.0), abs(vnormal.z)); world = mat3(tangent, bitangent, vnormal); + vec2 tc = vec2(dot(vvertex.xy, tangent.xy), dot(vvertex.yz, bitangent.yz)); + texcoord0 = (tc + waterfalltexgen.zw) * waterfalltexgen.xy; @(gdepthpackvert 1) } ] [ @@ -342,18 +349,19 @@ lazyshader 0 "waterfallenv" [ lazyshader 0 "waterfall" [ attribute vec4 vvertex; attribute vec3 vnormal; - attribute vec2 vtexcoord0; uniform mat4 camprojmatrix; varying mat3 world; + uniform vec4 waterfalltexgen; varying vec2 texcoord0; @(ginterpvert 1) void main(void) { gl_Position = camprojmatrix * vvertex; - texcoord0 = vtexcoord0; vec3 tangent = mix(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), abs(vnormal.x)); vec3 bitangent = mix(vec3(0.0, 0.0, -1.0), vec3(0.0, 1.0, 0.0), abs(vnormal.z)); world = mat3(tangent, bitangent, vnormal); + vec2 tc = vec2(dot(vvertex.xy, tangent.xy), dot(vvertex.yz, bitangent.yz)); + texcoord0 = (tc + waterfalltexgen.zw) * waterfalltexgen.xy; @(gdepthpackvert 1) } ] [ @@ -389,21 +397,22 @@ lazyshader 0 "waterfall" [ lazyshader 0 "glassenv" [ attribute vec4 vvertex; attribute vec3 vnormal; - attribute vec2 vtexcoord0; uniform mat4 camprojmatrix; uniform vec4 camera; varying vec3 camdir; varying mat3 world; + uniform vec2 glasstexgen; varying vec2 texcoord0; @(ginterpvert 1) void main(void) { gl_Position = camprojmatrix * vvertex; - texcoord0 = vtexcoord0; camdir = camera.xyz - vvertex.xyz; vec3 tangent = mix(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), abs(vnormal.x)); vec3 bitangent = mix(vec3(0.0, 0.0, -1.0), vec3(0.0, 1.0, 0.0), abs(vnormal.z)); world = mat3(tangent, bitangent, vnormal); + vec2 tc = vec2(dot(vvertex.xy, tangent.xy), dot(vvertex.yz, bitangent.yz)); + texcoord0 = tc * glasstexgen; @(gdepthpackvert 1) } ] [ @@ -445,18 +454,19 @@ lazyshader 0 "glassenv" [ lazyshader 0 "glass" [ attribute vec4 vvertex; attribute vec3 vnormal; - attribute vec2 vtexcoord0; uniform mat4 camprojmatrix; varying mat3 world; + uniform vec2 glasstexgen; varying vec2 texcoord0; @(ginterpvert 1) void main(void) { gl_Position = camprojmatrix * vvertex; - texcoord0 = vtexcoord0; vec3 tangent = mix(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), abs(vnormal.x)); vec3 bitangent = mix(vec3(0.0, 0.0, -1.0), vec3(0.0, 1.0, 0.0), abs(vnormal.z)); world = mat3(tangent, bitangent, vnormal); + vec2 tc = vec2(dot(vvertex.xy, tangent.xy), dot(vvertex.yz, bitangent.yz)); + texcoord0 = tc * glasstexgen; @(gdepthpackvert 1) } ] [ diff --git a/config/glsl/model.cfg b/config/glsl/model.cfg index 1c9748d..e325b5e 100644 --- a/config/glsl/model.cfg +++ b/config/glsl/model.cfg @@ -139,6 +139,7 @@ shadowmodelshader "alphashadowmodel" "a" // b -> dual-quat skeletal animation // c -> disable cullface // t -> transparent +// u -> dither modelvertexshader = [ local modeltype @@ -226,9 +227,16 @@ modelfragmentshader = [ { vec4 diffuse = texture2D(tex0, texcoord0); - @(? (mdlopt "a") [ - if(diffuse.a <= alphatest) - discard; + @(if (mdlopt "a") [ + ? (mdlopt "u") [ + vec2 coords = fract((gl_FragCoord.xy - 0.5)*0.5); + float dither = coords.x + 1.5*coords.y - 4.0*coords.x*coords.y + 0.25; + if(diffuse.a <= alphatest * dither) + discard; + ] [ + if(diffuse.a <= alphatest) + discard; + ] ]) gcolor.rgb = diffuse.rgb*colorscale.rgb; diff --git a/config/glsl/sky.cfg b/config/glsl/sky.cfg index b66a09b..afb69d3 100644 --- a/config/glsl/sky.cfg +++ b/config/glsl/sky.cfg @@ -71,73 +71,55 @@ shader 0 "atmosphere" [ camvec = p.xyz / p.w; } ] [ - // adapted from http://blog.cloudparty.com/2013/09/25/stunning-procedural-skies-in-webgl-part-2/ - uniform vec3 sunlight; + uniform vec4 sunlight; uniform vec3 sundir; - uniform vec3 sundiskparams; - uniform vec3 atmoradius; - uniform float gm; - uniform vec3 betar, betam, betarm; + uniform vec3 sunweight; + uniform vec3 sundiskcolor; + uniform vec2 sundiskparams; + uniform vec4 opticaldepthparams; + uniform vec3 mieparams; + uniform vec3 betarayleigh, betamie, betaozone; uniform vec2 hdrgamma; - uniform float atmoalpha; varying vec3 camvec; fragdata(0) vec4 fragcolor; - vec3 calcextinction(float dist) - { - return exp2(-dist * betarm); - } - - vec3 calcscatter(float costheta) - { - float rphase = 1.0 + costheta*costheta; - float mphase = pow(1.0 + gm*(gm - 2.0*costheta), -1.5); - return betar*rphase + betam*mphase; - } - - float baseopticaldepth(vec3 ray) - { - float a = atmoradius.x * max(ray.z, min(sundir.z, 0.0)); - return sqrt(a*a + atmoradius.z) - a; - } - - float opticaldepth(vec3 pos, vec3 ray) - { - pos.z = max(pos.z, 0.0) + atmoradius.x; - float a = dot(pos, ray); - return sqrt(a*a + atmoradius.y - dot(pos, pos)) - a; - } - void main(void) { vec3 camdir = normalize(camvec); float costheta = dot(camdir, sundir); + // sun disk + float edgeoffset = max(1.0 - (1.0 - max(costheta, 0.0)*costheta)*sundiskparams.x, 0.0); + // limb darken with distance to edge + vec3 limbdarken = pow(vec3(edgeoffset), vec3(0.397, 0.503, 0.64)); + // lighten edges for corona, but limit it to not interfere with limb darkening + float corona = min(edgeoffset * sundiskparams.y, 1.0); + corona = max(0.1725 / (1.15 - corona * corona) - 0.15, 0.0); + // apply limb darkening and corona to clamped sunlight color + vec3 sundisk = sundiskcolor * limbdarken * corona; + // optical depth along view ray - float raydist = baseopticaldepth(camdir); + float offset = camdir.z*opticaldepthparams.w; + vec3 depth = sqrt(offset*offset + opticaldepthparams.xyz) - offset; + vec3 rayleighweight = betarayleigh * depth.x; + vec3 mieweight = betamie * depth.y; + vec3 ozoneweight = betaozone * (depth.z - depth.x); // extinction of light along view ray - vec3 extinction = calcextinction(raydist); + vec3 viewweight = sunweight - (rayleighweight + mieweight + ozoneweight); + vec3 extinction = (exp2(viewweight) - 1.0) / viewweight; + + // calculate in-scattering + float rphase = (1.5 + 0.5*costheta*costheta) * (3.0 / (16.0 * 3.14159265)); + float mphase = inversesqrt(mieparams.x + mieparams.y*min(costheta, mieparams.z)); + vec3 scatter = rayleighweight * rphase + mieweight * (mphase * mphase * mphase); - // optical depth for incoming light hitting the view ray - float lightraydist = opticaldepth(camdir * (raydist * max(0.15 + 0.75 * sundir.z, 0.0)), sundir); + // combine scattering and extinction with sundisk + vec3 inscatter = (sunlight.rgb * scatter + sundisk) * extinction; - // cast a ray towards the sun and calculate the incoming extincted light - vec3 incominglight = calcextinction(lightraydist); - - // calculate the in-scattering - vec3 scattering = calcscatter(costheta) * (1.0 - extinction); - - // combine - vec3 inscatter = incominglight * scattering; - - // sun disk - vec3 sundisk = sundiskparams.z * extinction * pow(clamp(costheta*sundiskparams.x + sundiskparams.y, 0.0, 1.0), 8.0); - - inscatter += sundisk; @(hdrgammaencode inscatter) - fragcolor = vec4(sunlight * inscatter, atmoalpha); + fragcolor = vec4(inscatter, sunlight.a); } ] diff --git a/config/glsl/smfilter.cfg b/config/glsl/smfilter.cfg new file mode 100644 index 0000000..c5dfb2a --- /dev/null +++ b/config/glsl/smfilter.cfg @@ -0,0 +1,172 @@ +// shadow map filters, arg1 enables color sampling + +smfiltercolor = [result [ + #define filtercolorshadow(tex, tc) texture2DRect(tex, tc.xy @(if $numargs [result [* @arg1]])).rgb +]] + +smfilterg5 = [result [ + @(? (> $usetexgather 1) [ + #define shadowgather(center, xoff, yoff) textureGatherOffset(tex4, center, shadowtc.z, ivec2(xoff, yoff)) + ] [ + #define shadowgather(center, xoff, yoff) step(shadowtc.z, textureGatherOffset(tex4, center, ivec2(xoff, yoff))) + ]) + float filtershadow(vec3 shadowtc) + { + vec2 offset = fract(shadowtc.xy - 0.5); + vec2 center = (shadowtc.xy - offset) * shadowatlasscale; + vec4 group1 = shadowgather(center, -2, -2); + vec4 group2 = shadowgather(center, 0, -2); + vec4 group3 = shadowgather(center, 2, -2); + vec4 group4 = shadowgather(center, -2, 0); + vec4 group5 = shadowgather(center, 0, 0); + vec4 group6 = shadowgather(center, 2, 0); + vec4 group7 = shadowgather(center, -2, 2); + vec4 group8 = shadowgather(center, 0, 2); + vec4 group9 = shadowgather(center, 2, 2); + vec4 locols = vec4(group1.ab, group3.ab); + vec4 hicols = vec4(group7.rg, group9.rg); + locols.yz += group2.ab; + hicols.yz += group8.rg; + vec4 midcols = vec4(group1.rg, group3.rg) + vec4(group7.ab, group9.ab) + + vec4(group4.rg, group6.rg) + vec4(group4.ab, group6.ab) + + mix(locols, hicols, offset.y); + vec4 cols = group5 + vec4(group2.rg, group8.ab); + cols.xyz += mix(midcols.xyz, midcols.yzw, offset.x); + return dot(cols, vec4(1.0/25.0)); + } + @(if $arg1 [smfiltercolor 0.5]) +]] + +smfilterg3 = [result [ + @(? (> $usetexgather 1) [ + #define shadowgather(center, xoff, yoff) textureGatherOffset(tex4, center, shadowtc.z, ivec2(xoff, yoff)) + ] [ + #define shadowgather(center, xoff, yoff) step(shadowtc.z, textureGatherOffset(tex4, center, ivec2(xoff, yoff))) + ]) + float filtershadow(vec3 shadowtc) + { + vec2 offset = fract(shadowtc.xy - 0.5); + vec2 center = (shadowtc.xy - offset) * shadowatlasscale; + vec4 group1 = shadowgather(center, -1, -1); + vec4 group2 = shadowgather(center, 1, -1); + vec4 group3 = shadowgather(center, -1, 1); + vec4 group4 = shadowgather(center, 1, 1); + vec4 cols = vec4(group1.rg, group2.rg) + vec4(group3.ab, group4.ab) + mix(vec4(group1.ab, group2.ab), vec4(group3.rg, group4.rg), offset.y); + return dot(mix(cols.xyz, cols.yzw, offset.x), vec3(1.0/9.0)); + } + @(if $arg1 [smfiltercolor 0.5]) +]] + +smfilterb5 = [result [ + #define shadowval(xy, xoff, yoff) float(shadow2DRect(tex4, vec3(xy + vec2(xoff, yoff), shadowtc.z))) + float filtershadow(vec3 shadowtc) + { + vec2 offset = fract(shadowtc.xy - 0.5); + vec4 center = vec4(shadowtc.xy - offset + 0.5, shadowtc.xy - offset*0.5); + vec4 size = vec4(offset + 1.0, 2.0 - offset); + return (1.0/25.0)*dot(size.zxzx*size.wwyy, + vec4(shadowval(center.zw, -1.5, -1.5), + shadowval(center.zw, 2.0, -1.5), + shadowval(center.zw, -1.5, 2.0), + shadowval(center.zw, 2.0, 2.0))) + + (2.0/25.0)*dot(size, + vec4(shadowval(center.zy, 2.0, 0.0), + shadowval(center.xw, 0.0, 2.0), + shadowval(center.zy, -1.5, 0.0), + shadowval(center.xw, 0.0, -1.5))) + + (4.0/25.0)*shadowval(center.xy, 0.0, 0.0); + } + @(if $arg1 [smfiltercolor 0.5]) +]] + +smfilterb3 = [result [ + #define shadowval(center, xoff, yoff) float(shadow2DRect(tex4, center + vec3(xoff, yoff, 0.0))) + float filtershadow(vec3 shadowtc) + { + vec2 offset = fract(shadowtc.xy - 0.5); + vec3 center = shadowtc; + //center.xy -= offset; + //vec4 size = vec4(offset + 1.0, 2.0 - offset), weight = vec4(2.0 - 1.0 / size.xy, 1.0 / size.zw - 1.0); + //return (1.0/9.0)*dot(size.zxzx*size.wwyy, + // vec4(shadowval(center, weight.zw), + // shadowval(center, weight.xw), + // shadowval(center, weight.zy), + // shadowval(center, weight.xy))); + center.xy -= offset*0.5; + vec4 size = vec4(offset + 1.0, 2.0 - offset); + return (1.0/9.0)*dot(size.zxzx*size.wwyy, + vec4(shadowval(center, -0.5, -0.5), + shadowval(center, 1.0, -0.5), + shadowval(center, -0.5, 1.0), + shadowval(center, 1.0, 1.0))); + } + @(if $arg1 [smfiltercolor 0.5]) +]] + +smfilterrg = [result [ + #define shadowval(center, xoff, yoff) float(shadow2DRect(tex4, center + vec3(xoff, yoff, 0.0))) + float filtershadow(vec3 shadowtc) + { + return dot(vec4(0.25), + vec4(shadowval(shadowtc, -0.4, 1.0), + shadowval(shadowtc, -1.0, -0.4), + shadowval(shadowtc, 0.4, -1.0), + shadowval(shadowtc, 1.0, 0.4))); + } + @(if $arg1 [smfiltercolor 0.5]) +]] + +smfilternone = [result [ + float filtershadow(vec3 shadowtc) + { + return float(shadow2DRect(tex4, shadowtc)); + } + @(if $arg1 [smfiltercolor]) +]] + +lazyshader 0 "smalphaclear" [ + attribute vec4 vvertex; + uniform vec2 shadowatlasscale; + void main(void) + { + gl_Position = vec4(vvertex.xy * shadowatlasscale * 2.0 - 1.0, 0.0, 1.0); + } +] [ + fragdata(0) vec4 fragcolor; + void main(void) + { + fragcolor = vec4(1.0); + } +] + +loop i 2 [ + lazyshader 0 (? $i "smalphablur2d" "smalphablurrect") [ + attribute vec4 vvertex; + attribute vec4 vtexcoord0; + uniform vec2 shadowatlasscale; + flat varying vec4 smbounds; + varying vec4 texcoord0; + void main(void) + { + gl_Position = vec4(vvertex.xy * shadowatlasscale * 2.0 - 1.0, 0.0, 1.0); + smbounds = (vtexcoord0 + vec4(0.5, 0.5, -0.5, -0.5)) @(? $i [* shadowatlasscale.xyxy]); + texcoord0 = (vvertex.xyxy + vec4(-1.0, -1.0, 1.0, 1.0)) @(? $i [* shadowatlasscale.xyxy]); + } + ] [ + @(? $i [ + uniform sampler2D tex0; + #define texval(tc) texture2D(tex0, tc) + ] [ + uniform sampler2DRect tex0; + #define texval(tc) texture2DRect(tex0, tc) + ]) + flat varying vec4 smbounds; + varying vec4 texcoord0; + fragdata(0) vec4 fragcolor; + void main(void) + { + vec4 tc = vec4(max(texcoord0.xy, smbounds.xy), min(texcoord0.zw, smbounds.zw)); + fragcolor = 0.25 * (texval(tc.xy) + texval(tc.zy) + texval(tc.xw) + texval(tc.zw)); + } + ] +] diff --git a/config/glsl/tonemap.cfg b/config/glsl/tonemap.cfg index 96c21c5..06c1b5a 100644 --- a/config/glsl/tonemap.cfg +++ b/config/glsl/tonemap.cfg @@ -275,7 +275,7 @@ shader 0 "hdrbloom" [ gl_Position = vvertex; texcoord0 = vtexcoord0; float avglum = 4.0 * @(? (>= $hwvtexunits 4) [texture2D(tex2, vec2(0.5, 0.5)).r] [vcolor]); - lumscale = hdrparams.x * -log2(1.0 - clamp(avglum, 0.03, 0.3))/(avglum + 1e-4); + lumscale = hdrparams.x * -log2(1.0 - clamp(avglum, @hdrminexposure, @hdrmaxexposure))/(avglum + 1e-4); lumthreshold = -log2(1.0 - hdrparams.z); } ] [ @@ -307,15 +307,16 @@ hdrtonemapvertexshader = [ ]) uniform vec4 hdrparams; varying vec2 texcoord0, texcoord1; - flat varying float lumscale, lumsaturate; + flat varying float lumscale; + flat varying vec2 lumsaturate; void main(void) { gl_Position = vvertex; texcoord0 = vtexcoord0; texcoord1 = vtexcoord1; float avglum = 4.0 * @(? (>= $hwvtexunits 4) [texture2D(tex2, vec2(0.5, 0.5)).r] [vcolor]); - lumscale = hdrparams.x * -log2(1.0 - clamp(avglum, 0.03, 0.3))/(avglum + 1e-4); - lumsaturate = -log2(1.0 - hdrparams.y) / lumscale; + lumscale = hdrparams.x * -log2(1.0 - clamp(avglum, @hdrminexposure, @hdrmaxexposure))/(avglum + 1e-4); + lumsaturate = vec2(1.0, -hdrparams.y) * 2.0 / max(1.0 - hdrparams.y, 1e-4f); } ] ] @@ -323,9 +324,8 @@ hdrtonemapvertexshader = [ hdrtonemapfrag = [ result [{ // color = 1.0 - exp2(-color*lumscale); - float lum = dot(@arg1, vec3(@lumweights)); - @arg1 = min(@arg1, lumsaturate); - @arg1 *= (1.0 - exp2(-lum*lumscale)) / (dot(@arg1, vec3(@lumweights)) + 1e-4); + float lum = dot(@arg1, vec3(@lumweights)), target = 1.0 - exp2(-lum*lumscale), excess = max(target*lumsaturate.x + lumsaturate.y, 0.0); + @arg1 = (@arg1 + excess) * target / (lum + excess + 1e-4); }] ] @@ -334,7 +334,8 @@ hdrtonemapdefs = [ uniform vec4 hdrparams; uniform vec2 hdrgamma; varying vec2 texcoord0, texcoord1; - flat varying float lumscale, lumsaturate; + flat varying float lumscale; + flat varying vec2 lumsaturate; ] ] diff --git a/config/glsl/volumetric.cfg b/config/glsl/volumetric.cfg index 10ec443..1d59f9c 100644 --- a/config/glsl/volumetric.cfg +++ b/config/glsl/volumetric.cfg @@ -4,7 +4,8 @@ volumetricvariantshader = [ local volumetrictype volumetrictype = $arg3 maxsteps = $arg4 - spotlight = (>= $arg2 1) + spotlight = (>= $arg2 2) + colorshadow = (>= (mod (+ $arg2 1) 3) 2) variantshader 0 $arg1 $arg2 (? (< $arg2 0) [ attribute vec4 vvertex; uniform mat4 lightmatrix; @@ -23,6 +24,13 @@ volumetricvariantshader = [ uniform sampler2DRectShadow tex4; ]] ]) + @(if $colorshadow [result [ + uniform sampler2DRect tex11; + #define filtercolorshadow(tc) texture2DRect(tex11, tc.xy @(? (! (volopt "N")) [* 0.5])).rgb + #define lightshadowtype vec3 + ]] [result [ + #define lightshadowtype float + ]]) uniform vec4 lightpos; uniform vec3 lightcolor; @(? $spotlight [ @@ -92,7 +100,7 @@ volumetricvariantshader = [ ray *= invdist; vec3 camlight = lightpos.xyz - camera * lightpos.w; float camlight2 = dot(camlight, camlight), v = dot(camlight, ray), d = v*v + 1.0 - camlight2; - float light = 0.0; + lightshadowtype light = lightshadowtype(0.0); if(d > 0) { d = sqrt(d); @@ -136,7 +144,7 @@ volumetricvariantshader = [ vec3 spottc = getspottc(lightdir, spotdist); lightatten *= filtershadow(spottc); ]) - light += lightatten; + light += lightatten @(? $colorshadow [* filtercolorshadow(spottc)]); } ]] [result [ float lightatten = clamp(1.0 - length(lightdir), 0.0, 1.0); @@ -144,7 +152,7 @@ volumetricvariantshader = [ vec3 shadowtc = getshadowtc(lightdir); lightatten *= filtershadow(shadowtc); ]) - light += lightatten; + light += lightatten @(? $colorshadow [* filtercolorshadow(shadowtc)]); ]]) space -= stepdist; if(space <= 0) break; @@ -172,9 +180,15 @@ volumetricshader = [ shadername = (concatword "volumetric" $volumetrictype $arg3) volumetricvariantshader $shadername -1 $arg1 $arg3 volumetricvariantshader $shadername 0 (concatword $arg1 $arg2) $arg3 + if (volopt "P") [ + volumetricvariantshader $shadername 1 (concatword $arg1 $arg2) $arg3 + ] if (volopt "s") [ - volumetricvariantshader $shadername 1 $arg1 $arg3 - volumetricvariantshader $shadername 2 (concatword $arg1 $arg2) $arg3 + volumetricvariantshader $shadername 2 $arg1 $arg3 + volumetricvariantshader $shadername 3 (concatword $arg1 $arg2) $arg3 + if (volopt "P") [ + volumetricvariantshader $shadername 4 (concatword $arg1 $arg2) $arg3 + ] ] ] diff --git a/config/glsl/world.cfg b/config/glsl/world.cfg index af539dd..4d96331 100644 --- a/config/glsl/world.cfg +++ b/config/glsl/world.cfg @@ -178,15 +178,20 @@ worldvariantshader = [ ]]) @(if (wtopt "a") [ - ? (wtopt "A") [ + if (wtopt "A") [result [ vec3 rlight = gfetch(refractlight, gl_FragCoord.xy).rgb; - gglow.rgb += rlight * refractparams.xyz; - ] (? (wtopt "m") [ + @(? (wtopt "m") [ + gcolor.rgb *= diffuse.a; + gglow.rgb += rlight * refractparams.xyz * (1.0 - colorparams.a * diffuse.a); + ] [ + gglow.rgb += rlight * refractparams.xyz * (1.0 - colorparams.a); + ]) + ]] [? (wtopt "m") [ gcolor.rgb *= diffuse.a; #define packnorm diffuse.a * colorparams.a ] [ #define packnorm colorparams.a - ]) + ]] ]) @(gnormpackdef normal packnorm) @@ -443,7 +448,7 @@ bumpvariantshader = [ vec4 diffuse = texture2D(diffusemap, dtc); - @(? (&& (btopt "a") [! (btopt "A")] [btopt "m"]) [ + @(? (&& (btopt "a") [btopt "m"]) [ vec4 normal = texture2D(normalmap, dtc); #define bump normal.rgb ] [ @@ -485,17 +490,22 @@ bumpvariantshader = [ ]]) @(if (btopt "a") [ - ? (btopt "A") [ + if (btopt "A") [result [ vec2 rtc = bump.xy*refractparams.w; float rmask = clamp(refractdepth*(lineardepth - dot(gfetch(refractmask, gl_FragCoord.xy + rtc).rgb, gdepthunpackparams)), 0.0, 1.0); vec3 rlight = gfetch(refractlight, gl_FragCoord.xy + rtc*rmask).rgb; - gglow.rgb += rlight * refractparams.xyz; - ] (? (btopt "m") [ + @(? (btopt "m") [ + gcolor.rgb *= normal.a; + gglow.rgb += rlight * refractparams.xyz * (1.0 - colorparams.a * normal.a); + ] [ + gglow.rgb += rlight * refractparams.xyz * (1.0 - colorparams.a); + ]) + ]] [? (btopt "m") [ gcolor.rgb *= normal.a; #define packnorm normal.a * colorparams.a ] [ #define packnorm colorparams.a - ]) + ]] ]) @(gnormpackdef bumpw packnorm) @@ -596,7 +606,7 @@ bumpshader "triplanardetailbumpparallaxworld" "Tdp" bumpshader "triplanardetailbumpspecparallaxworld" "Tdps" bumpshader "triplanardetailbumpspecmapparallaxworld" "TdpsS" -shader 0 shadowmapworld [ +shader 0 "smworld" [ attribute vec4 vvertex; uniform mat4 shadowmatrix; void main(void) @@ -609,6 +619,87 @@ shader 0 shadowmapworld [ } ] +// shadowmaptype: +// a -> transparent +// m -> alpha-mask +// n -> normalmap + +smtopt = [ >= (strstr $shadowmaptype $arg1) 0 ] + +shadowmapworldvariantshader = [ + local shadowmaptype + shadowmaptype = $arg2 + srow = -1 + if (smtopt "m") [ + srow = 0 + ] + if (smtopt "n") [ + srow = 1 + ] + variantshader 1 $arg1 $srow [ + attribute vec4 vvertex; + + attribute vec2 vtexcoord0; + uniform vec2 texgenscroll; + uniform vec4 colorparams; + varying vec2 texcoord0; + + uniform mat4 shadowmatrix; + + void main(void) + { + gl_Position = shadowmatrix * vvertex; + + texcoord0 = vtexcoord0 + texgenscroll; + } + ] [ + uniform vec4 colorparams; + uniform sampler2D diffusemap; + varying vec2 texcoord0; + + @(? (smtopt "n") [ + uniform sampler2D normalmap; + ]) + + fragdata(0) vec4 gcolor; + + void main(void) + { + vec4 diffuse = texture2D(diffusemap, texcoord0); + + @(if (smtopt "a") [result [ + float alpha = colorparams.a; + @(if (smtopt "m") [result [ + @(? (smtopt "n") [ + alpha *= texture2D(normalmap, texcoord0).a; + ] [ + alpha *= diffuse.a; + ]) + #define mask alpha + ]] [result [ + #define mask 0.0 + ]]) + ]] [result [ + #define alpha 1.0 + #define mask 1.0 + ]]) + + gcolor.rgb = mix(vec3(1.0), diffuse.rgb*colorparams.rgb, alpha) * (1.0 - mask); + gcolor.a = alpha; + } + ] +] + +shadowmapshader = [ + defershader 1 $arg1 [ + shadowmapworldvariantshader @arg1 @arg2 + shadowmapworldvariantshader @arg1 @(concatword $arg2 "m") + shadowmapworldvariantshader @arg1 @(concatword $arg2 "mn") + ] +] + +shadowmapshader "smalphaworld" "a" + defershader 1 "rsmworld" [ loop i 2 [ variantshader 1 "rsmworld" (- $i 1) [ diff --git a/src/engine/animmodel.hh b/src/engine/animmodel.hh index 38d38c8..7d5ba8d 100644 --- a/src/engine/animmodel.hh +++ b/src/engine/animmodel.hh @@ -110,18 +110,24 @@ struct animmodel : model struct skin : shaderparams { + enum + { + DITHER = 1<<0 + }; + part *owner; Texture *tex, *decal, *masks, *envmap, *normalmap; Shader *shader, *rsmshader; - int cullface; + int cullface, flags; shaderparamskey *key; - skin() : owner(0), tex(notexture), decal(nullptr), masks(notexture), envmap(nullptr), normalmap(nullptr), shader(nullptr), rsmshader(nullptr), cullface(1), key(nullptr) {} + skin() : owner(0), tex(notexture), decal(nullptr), masks(notexture), envmap(nullptr), normalmap(nullptr), shader(nullptr), rsmshader(nullptr), cullface(1), flags(0), key(nullptr) {} bool masked() const { return masks != notexture; } bool envmapped() const { return envmapmax>0; } bool bumpmapped() const { return normalmap != nullptr; } bool alphatested() const { return alphatest > 0 && tex->type&Texture::ALPHA; } + bool dithered() const { return (flags&DITHER) != 0; } bool decaled() const { return decal != nullptr; } void setkey() @@ -187,7 +193,11 @@ struct animmodel : model string opts; int optslen = 0; - if(alphatested()) opts[optslen++] = 'a'; + if(alphatested()) + { + opts[optslen++] = 'a'; + if(dithered()) opts[optslen++] = 'u'; + } if(decaled()) opts[optslen++] = decal->type&Texture::ALPHA ? 'D' : 'd'; if(bumpmapped()) opts[optslen++] = 'n'; if(envmapped()) { opts[optslen++] = 'm'; opts[optslen++] = 'e'; } @@ -1060,7 +1070,7 @@ struct animmodel : model if(animpart<0 || animpart>=MAXANIMPARTS || num<0 || num>=NUM_ANIMS) return; if(frame<0 || range<=0 || !meshes || !meshes->hasframes(frame, range)) { - conoutf("invalid frame %d, range %d in model %s", frame, range, model->name); + conoutf(CON_ERROR, "invalid frame %d, range %d in model %s", frame, range, model->name); return; } if(!anims[animpart]) anims[animpart] = new vector[NUM_ANIMS]; @@ -1564,6 +1574,17 @@ struct animmodel : model loopv(parts) loopvj(parts[i]->skins) parts[i]->skins[j].alphatest = alphatest; } + void setdither(bool val) + { + if(parts.empty()) loaddefaultparts(); + loopv(parts) loopvj(parts[i]->skins) + { + skin &s = parts[i]->skins[j]; + if(val) s.flags |= skin::DITHER; + else s.flags &= ~skin::DITHER; + } + } + void setfullbright(float fullbright) { if(parts.empty()) loaddefaultparts(); @@ -1745,12 +1766,12 @@ template struct modelcommands static void setdir(char *name) { - if(!MDL::loading) { conoutf("not loading an %s", MDL::formatname()); return; } + if(!MDL::loading) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; } formatstring(MDL::dir, "media/model/%s", name); } #define loopmeshes(meshname, m, body) do { \ - if(!MDL::loading || MDL::loading->parts.empty()) { conoutf("not loading an %s", MDL::formatname()); return; } \ + if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; } \ part &mdl = *MDL::loading->parts.last(); \ if(!mdl.meshes) return; \ loopv(mdl.meshes->meshes) \ @@ -1801,6 +1822,11 @@ template struct modelcommands loopskins(meshname, s, s.alphatest = std::max(0.0f, std::min(1.0f, *cutoff))); } + static void setdither(char *meshname, int *dither) + { + loopskins(meshname, s, { if(*dither) s.flags |= skin::DITHER; else s.flags &= ~skin::DITHER; }); + } + static void setcullface(char *meshname, int *cullface) { loopskins(meshname, s, s.cullface = *cullface); @@ -1861,9 +1887,9 @@ template struct modelcommands static void setlink(int *parent, int *child, char *tagname, float *x, float *y, float *z) { - if(!MDL::loading) { conoutf("not loading an %s", MDL::formatname()); return; } - if(!MDL::loading->parts.inrange(*parent) || !MDL::loading->parts.inrange(*child)) { conoutf("no models loaded to link"); return; } - if(!MDL::loading->parts[*parent]->link(MDL::loading->parts[*child], tagname, vec(*x, *y, *z))) conoutf("could not link model %s", MDL::loading->name); + if(!MDL::loading) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; } + if(!MDL::loading->parts.inrange(*parent) || !MDL::loading->parts.inrange(*child)) { conoutf(CON_ERROR, "no models loaded to link"); return; } + if(!MDL::loading->parts[*parent]->link(MDL::loading->parts[*child], tagname, vec(*x, *y, *z))) conoutf(CON_ERROR, "could not link model %s", MDL::loading->name); } template void modelcommand(F *fun, const char *suffix, const char *args) @@ -1882,6 +1908,7 @@ template struct modelcommands modelcommand(setgloss, "gloss", "si"); modelcommand(setglow, "glow", "sfff"); modelcommand(setalphatest, "alphatest", "sf"); + modelcommand(setdither, "dither", "si"); modelcommand(setcullface, "cullface", "si"); modelcommand(setcolor, "color", "sfff"); modelcommand(setenvmap, "envmap", "ss"); diff --git a/src/engine/bih.cc b/src/engine/bih.cc index ebf1c9c..b00fb16 100644 --- a/src/engine/bih.cc +++ b/src/engine/bih.cc @@ -16,7 +16,7 @@ bool BIH::triintersect(const mesh &m, int tidx, const vec &mo, const vec &mray, float det = mray.dot(n), v, w, f; if(det >= 0) { - if(!(mode&RAY_SHADOW) && m.flags&MESH_CULLFACE) return false; + if(m.flags&MESH_CULLFACE) return false; v = e.dot(c); if(v < 0 || v > det) return false; w = -e.dot(b); @@ -42,7 +42,7 @@ bool BIH::triintersect(const mesh &m, int tidx, const vec &mo, const vec &mray, ti = std::clamp(int(m.tex->ys * at.y), 0, m.tex->ys-1); if(!(m.tex->alphamask[ti*((m.tex->xs+7)/8) + si/8] & (1<<(si%8)))) return false; } - if(!(mode&RAY_SHADOW)) hitsurface = m.xformnorm.transform(n).normalize(); + hitsurface = m.xformnorm.transform(n).normalize(); dist = f*invdet; return true; } @@ -134,7 +134,7 @@ inline bool BIH::traverse(const vec &o, const vec &ray, float maxdist, float &di loopi(nummeshes) { mesh &m = meshes[i]; - if(!(m.flags&MESH_RENDER) || (!(mode&RAY_SHADOW) && m.flags&MESH_NOCLIP)) continue; + if(!(m.flags&MESH_RENDER) || m.flags&MESH_NOCLIP) continue; float t1 = (m.bbmin.x - o.x)*invray.x, t2 = (m.bbmax.x - o.x)*invray.x, tmin, tmax; @@ -318,11 +318,7 @@ bool mmintersect(const extentity &e, const vec &o, const vec &ray, float maxdist { model *m = loadmapmodel(e.attr1); if(!m) return false; - if(mode&RAY_SHADOW) - { - if(!m->shadow || e.flags&EF_NOSHADOW) return false; - } - else if((mode&RAY_ENTS)!=RAY_ENTS && (!m->collide || e.flags&EF_NOCOLLIDE)) return false; + if((mode&RAY_ENTS)!=RAY_ENTS && (!m->collide || e.flags&EF_NOCOLLIDE)) return false; if(!m->bih && !m->setBIH()) return false; float scale = e.attr5 ? 100.0f/e.attr5 : 1.0f; vec mo = vec(o).sub(e.o).mul(scale), mray(ray); @@ -350,12 +346,9 @@ bool mmintersect(const extentity &e, const vec &o, const vec &ray, float maxdist if(m->bih->traverse(mo, mray, maxdist ? maxdist*scale : 1e16f, dist, mode)) { dist /= scale; - if(!(mode&RAY_SHADOW)) - { - if(roll != 0) hitsurface.rotate_around_y(sincosmod360(-roll)); - if(pitch != 0) hitsurface.rotate_around_x(sincosmod360(pitch)); - if(yaw != 0) hitsurface.rotate_around_z(sincosmod360(yaw)); - } + if(roll != 0) hitsurface.rotate_around_y(sincosmod360(-roll)); + if(pitch != 0) hitsurface.rotate_around_x(sincosmod360(pitch)); + if(yaw != 0) hitsurface.rotate_around_z(sincosmod360(yaw)); return true; } return false; diff --git a/src/engine/command.cc b/src/engine/command.cc index d7ba469..2f5acb6 100644 --- a/src/engine/command.cc +++ b/src/engine/command.cc @@ -691,6 +691,11 @@ int getvarmax(const char *name) GETVAR(id, name, 0); return id->maxval; } +float getfvar(const char *name) +{ + _GETVAR(id, ID_FVAR, name, 0); + return *id->storage.f; +} float getfvarmin(const char *name) { _GETVAR(id, ID_FVAR, name, 0); @@ -4200,7 +4205,7 @@ CMPSCMD(>s, >); CMPSCMD(<=s, <=); CMPSCMD(>=s, >=); -ICOMMAND(echo, "C", (char *s), conoutf("\f1%s", s)); +ICOMMAND(echo, "C", (char *s), conoutf(CON_ECHO, "\f1%s", s)); ICOMMAND(error, "C", (char *s), conoutf(CON_ERROR, "%s", s)); ICOMMAND(strstr, "ss", (char *a, char *b), { char *s = strstr(a, b); intret(s ? s-a : -1); }); ICOMMAND(strlen, "s", (char *s), intret(strlen(s))); diff --git a/src/engine/dynlight.cc b/src/engine/dynlight.cc index 17b0004..55b796c 100644 --- a/src/engine/dynlight.cc +++ b/src/engine/dynlight.cc @@ -147,49 +147,3 @@ bool getdynlight(int n, vec &o, float &radius, vec &color, vec &dir, int &spot, flags = d.flags & 0xFF; return true; } - -void dynlightreaching(const vec &target, vec &color, vec &dir, bool hud) -{ - vec dyncolor(0, 0, 0);//, dyndir(0, 0, 0); - loopv(dynlights) - { - dynlight &d = dynlights[i]; - if(d.curradius<=0) continue; - - vec ray(target); - ray.sub(hud ? d.hud : d.o); - float mag = ray.squaredlen(); - if(mag >= d.curradius*d.curradius) continue; - mag = sqrtf(mag); - - float intensity = 1 - mag/d.curradius; - if(d.spot > 0 && mag > 1e-4f) - { - float spotatten = 1 - (1 - ray.dot(d.dir)/mag) / (1 - cos360(d.spot)); - if(spotatten <= 0) continue; - intensity *= spotatten; - } - - vec color = d.curcolor; - color.mul(intensity); - dyncolor.add(color); - //dyndir.add(ray.mul(intensity/mag)); - } -#if 0 - if(!dyndir.iszero()) - { - dyndir.normalize(); - float x = dyncolor.magnitude(), y = color.magnitude(); - if(x+y>0) - { - dir.mul(x); - dyndir.mul(y); - dir.add(dyndir).div(x+y); - if(dir.iszero()) dir = vec(0, 0, 1); - else dir.normalize(); - } - } -#endif - color.add(dyncolor); -} - diff --git a/src/engine/dynlight.hh b/src/engine/dynlight.hh index b6f8d07..2d78973 100644 --- a/src/engine/dynlight.hh +++ b/src/engine/dynlight.hh @@ -12,8 +12,7 @@ enum DL_FLASH = 1<<10 }; -void adddynlight(const vec &o, float radius, const vec &color, int fade = 0, int peak = 0, int flags = 0, float initradius = 0, const vec &initcolor = vec(0, 0, 0), physent *owner = NULL, const vec &dir = vec(0, 0, 0), int spot = 0); -void dynlightreaching(const vec &target, vec &color, vec &dir, bool hud = false); +void adddynlight(const vec &o, float radius, const vec &color, int fade = 0, int peak = 0, int flags = 0, float initradius = 0, const vec &initcolor = vec(0, 0, 0), physent *owner = nullptr, const vec &dir = vec(0, 0, 0), int spot = 0); void removetrackeddynlights(physent *owner = nullptr); void updatedynlights(); diff --git a/src/engine/ents.hh b/src/engine/ents.hh index 210feda..081c032 100644 --- a/src/engine/ents.hh +++ b/src/engine/ents.hh @@ -72,12 +72,12 @@ struct physent // base entity type, can be affe vec deltapos, newpos; // movement interpolation float yaw, pitch, roll; float maxspeed; // cubes per second, 100 for player - int timeinair; float radius, eyeheight, maxheight, aboveeye; // bounding box size float xradius, yradius, zmargin; vec floor; // the normal of floor the dynent is on - int inwater; + ushort timeinair; + uchar inwater; bool jumping; signed char move, strafe, crouching; diff --git a/src/engine/grass.cc b/src/engine/grass.cc index 05594b5..33cc127 100644 --- a/src/engine/grass.cc +++ b/src/engine/grass.cc @@ -11,8 +11,10 @@ VARP(grass, 0, 1, 1); VAR(dbggrass, 0, 0, 1); VARP(grassdist, 0, 256, 10000); FVARP(grasstaper, 0, 0.2, 1); -FVARP(grassstep, 0.5, 2, 8); +FVARP(grassstep, 0.5, 3, 8); VARP(grassheight, 1, 4, 64); +VARP(grassmargin, 0, 8, 32); +FVAR(grassmarginfade, 0, 1, 1); #define NUMGRASSWEDGES 8 @@ -20,6 +22,7 @@ static struct grasswedge { vec dir, across, edge1, edge2; plane bound1, bound2; + bvec4 vertbounds; grasswedge(int i) : dir(2*M_PI*(i+0.5f)/float(NUMGRASSWEDGES), 0), @@ -30,6 +33,10 @@ static struct grasswedge bound2(vec(2*M_PI*((i+1)/float(NUMGRASSWEDGES) + 0.25f), 0), 0) { across.div(-across.dot(bound1)); + + bvec vertbound1(bound1), vertbound2(bound2); + vertbounds = bvec4(vertbound1.x, vertbound1.y, vertbound2.x, vertbound2.y); + vertbounds.flip(); } } grasswedges[NUMGRASSWEDGES] = { 0, 1, 2, 3, 4, 5, 6, 7 }; @@ -38,6 +45,7 @@ struct grassvert vec pos; bvec4 color; vec2 tc; + bvec4 bounds; }; static vector grassverts; @@ -148,15 +156,15 @@ static void gengrassquads(grassgroup *&group, const grasswedge &w, const grasstr rightdb = w.bound2.dot(rightdir); } vec p1 = leftp, p2 = rightp; - if(leftb > 0) + if(leftb > grassmargin) { - if(w.bound1.dist(p2) >= 0) continue; - p1.add(vec(across).mul(leftb)); + if(w.bound1.dist(p2) >= grassmargin) continue; + p1.add(vec(across).mul(leftb - grassmargin)); } - if(rightb > 0) + if(rightb > grassmargin) { - if(w.bound2.dist(p1) >= 0) continue; - p2.sub(vec(across).mul(rightb)); + if(w.bound2.dist(p1) >= grassmargin) continue; + p2.sub(vec(across).mul(rightb - grassmargin)); } if(grassverts.length() >= 4*maxgrass) break; @@ -185,6 +193,7 @@ static void gengrassquads(grassgroup *&group, const grasswedge &w, const grasstr gv.pos = p##n; \ gv.color = color; \ gv.tc = vec2(tc##n, tcv); \ + gv.bounds = w.vertbounds; \ modify; \ } @@ -215,7 +224,7 @@ static void gengrassquads(vtxarray *va) loopi(NUMGRASSWEDGES) { grasswedge &w = grasswedges[i]; - if(w.bound1.dist(g.center) > g.radius || w.bound2.dist(g.center) > g.radius) continue; + if(w.bound1.dist(g.center) > g.radius + grassmargin || w.bound2.dist(g.center) > g.radius + grassmargin) continue; gengrassquads(group, w, g, s.grasstex); } } @@ -291,12 +300,15 @@ void rendergrass() gle::vertexpointer(sizeof(grassvert), ptr->pos.v); gle::colorpointer(sizeof(grassvert), ptr->color.v); gle::texcoord0pointer(sizeof(grassvert), ptr->tc.v); + gle::tangentpointer(sizeof(grassvert), ptr->bounds.v, GL_BYTE); gle::enablevertex(); gle::enablecolor(); gle::enabletexcoord0(); + gle::enabletangent(); gle::enablequads(); GLOBALPARAMF(grasstest, grasstest); + GLOBALPARAMF(grassmargin, grassmargin, grassmargin ? grassmarginfade / grassmargin : 0.0f, grassmargin ? grassmarginfade : 1.0f); int texid = -1, blend = -1; loopv(grassgroups) @@ -330,6 +342,7 @@ void rendergrass() gle::disablevertex(); gle::disablecolor(); gle::disabletexcoord0(); + gle::disabletangent(); gle::clearvbo(); diff --git a/src/engine/hitzone.hh b/src/engine/hitzone.hh index b9dcdcb..09367b9 100644 --- a/src/engine/hitzone.hh +++ b/src/engine/hitzone.hh @@ -60,8 +60,7 @@ struct skelbih float v = ray.dot(q) / det; \ if(v < 0 || u + v > 1) return false; \ float f = ec.dot(q) / det; \ - if(f < 0 || f*skelmodel::intersectscale > skelmodel::intersectdist) return false; \ - if(!(skelmodel::intersectmode&RAY_SHADOW) && tm->noclip) return false; \ + if(f < 0 || f*skelmodel::intersectscale > skelmodel::intersectdist || tm->noclip) return false; \ if((skelmodel::intersectmode&RAY_ALPHAPOLY)==RAY_ALPHAPOLY) \ { \ Texture *tex = s[t.mesh].tex; \ diff --git a/src/engine/iqm.hh b/src/engine/iqm.hh index e687c00..9455bdc 100644 --- a/src/engine/iqm.hh +++ b/src/engine/iqm.hh @@ -238,7 +238,7 @@ struct iqm : skelloader } if(!m->numtris || !m->numverts) { - conoutf("empty mesh in %s", filename); + conoutf(CON_WARN, "empty mesh in %s", filename); meshes.removeobj(m); delete m; continue; diff --git a/src/engine/light.cc b/src/engine/light.cc index f345a23..8faa265 100644 --- a/src/engine/light.cc +++ b/src/engine/light.cc @@ -275,77 +275,6 @@ static void clearsurfaces(cube *c) } } -#define LIGHTCACHESIZE 1024 - -static struct lightcacheentry -{ - int x, y; - vector lights; -} lightcache[LIGHTCACHESIZE]; - -#define LIGHTCACHEHASH(x, y) (((((x)^(y))<<5) + (((x)^(y))>>5)) & (LIGHTCACHESIZE - 1)) - -VARF(lightcachesize, 4, 6, 12, clearlightcache()); - -void clearlightcache(int id) -{ - if(id >= 0) - { - const extentity &light = *entities::getents()[id]; - int radius = light.attr1; - if(radius <= 0) return; - for(int x = int(max(light.o.x-radius, 0.0f))>>lightcachesize, ex = int(min(light.o.x+radius, worldsize-1.0f))>>lightcachesize; x <= ex; x++) - for(int y = int(max(light.o.y-radius, 0.0f))>>lightcachesize, ey = int(min(light.o.y+radius, worldsize-1.0f))>>lightcachesize; y <= ey; y++) - { - lightcacheentry &lce = lightcache[LIGHTCACHEHASH(x, y)]; - if(lce.x != x || lce.y != y) continue; - lce.x = -1; - lce.lights.setsize(0); - } - return; - } - - for(lightcacheentry *lce = lightcache; lce < &lightcache[LIGHTCACHESIZE]; lce++) - { - lce->x = -1; - lce->lights.setsize(0); - } -} - -const vector &checklightcache(int x, int y) -{ - x >>= lightcachesize; - y >>= lightcachesize; - lightcacheentry &lce = lightcache[LIGHTCACHEHASH(x, y)]; - if(lce.x == x && lce.y == y) return lce.lights; - - lce.lights.setsize(0); - int csize = 1< &ents = entities::getents(); - loopv(ents) - { - const extentity &light = *ents[i]; - switch(light.type) - { - case ET_LIGHT: - { - int radius = light.attr1; - if(radius <= 0 || - light.o.x + radius < cx || light.o.x - radius > cx + csize || - light.o.y + radius < cy || light.o.y - radius > cy + csize) - continue; - break; - } - default: continue; - } - lce.lights.add(i); - } - - lce.x = x; - lce.y = y; - return lce.lights; -} - static uint lightprogress = 0; bool calclight_canceled = false; @@ -632,7 +561,6 @@ VAR(fullbrightlevel, 0, 160, 255); void clearlights() { - clearlightcache(); clearshadowcache(); cleardeferredlightshaders(); resetsmoothgroups(); @@ -640,70 +568,6 @@ void clearlights() void initlights() { - clearlightcache(); clearshadowcache(); loaddeferredlightshaders(); } - -#if 0 -static void lightreaching(const vec &target, vec &color, vec &dir, bool fast = false, extentity *t = nullptr, float minambient = 0.4f) -{ - if(fullbright && editmode) - { - color = vec(1, 1, 1); - dir = vec(0, 0, 1); - return; - } - - color = dir = vec(0, 0, 0); - const vector &ents = entities::getents(); - const vector &lights = checklightcache(int(target.x), int(target.y)); - loopv(lights) - { - extentity &e = *ents[lights[i]]; - if(e.type != ET_LIGHT || e.attr1 <= 0) - continue; - - vec ray(target); - ray.sub(e.o); - float mag = ray.magnitude(); - if(mag >= float(e.attr1)) - continue; - - if(mag < 1e-4f) ray = vec(0, 0, -1); - else - { - ray.div(mag); - if(shadowray(e.o, ray, mag, RAY_SHADOW | RAY_POLY, t) < mag) - continue; - } - - float intensity = 1 - mag / float(e.attr1); - if(e.attached && e.attached->type==ET_SPOTLIGHT) - { - vec spot = vec(e.attached->o).sub(e.o).normalize(); - float spotatten = 1 - (1 - ray.dot(spot)) / (1 - cos360(std::clamp(int(e.attached->attr1), 1, 89))); - if(spotatten <= 0) continue; - intensity *= spotatten; - } - - //if(target==player->o) - //{ - // conoutf(CON_DEBUG, "%d - %f %f", i, intensity, mag); - //} - - vec lightcol = vec(e.attr2, e.attr3, e.attr4).mul(1.0f/255).max(0); - color.add(vec(lightcol).mul(intensity)); - dir.add(vec(ray).mul(-intensity*lightcol.x*lightcol.y*lightcol.z)); - } - if(!sunlight.iszero() && shadowray(target, sunlightdir, 1e16f, RAY_SHADOW | RAY_POLY, t) > 1e15f) - { - vec lightcol = sunlight.tocolor().mul(sunlightscale); - color.add(lightcol); - dir.add(vec(sunlightdir).mul(lightcol.x*lightcol.y*lightcol.z)); - } - color.max(ambient.tocolor().max(minambient)).min(1.5f); - if(dir.iszero()) dir = vec(0, 0, 1); - else dir.normalize(); -} -#endif diff --git a/src/engine/light.hh b/src/engine/light.hh index 0f4ecaf..9b4b9c1 100644 --- a/src/engine/light.hh +++ b/src/engine/light.hh @@ -61,7 +61,6 @@ extern int fullbright, fullbrightlevel; extern void clearlights(); extern void initlights(); -extern void clearlightcache(int id = -1); extern void brightencube(cube &c); extern void setsurfaces(cube &c, const surfaceinfo *surfs, const vertinfo *verts, int numverts); extern void setsurface(cube &c, int orient, const surfaceinfo &surf, const vertinfo *verts, int numverts); @@ -92,6 +91,4 @@ extern volatile bool check_calclight_progress; extern void check_calclight_canceled(); -extern const vector &checklightcache(int x, int y); - #endif diff --git a/src/engine/main.cc b/src/engine/main.cc index b24625e..cca2260 100644 --- a/src/engine/main.cc +++ b/src/engine/main.cc @@ -454,7 +454,7 @@ void textinput(bool on, int mask) } textinputmask |= mask; } - else + else if(textinputmask) { textinputmask &= ~mask; if(!textinputmask) SDL_StopTextInput(); diff --git a/src/engine/material.cc b/src/engine/material.cc index 6ef9e67..7807406 100644 --- a/src/engine/material.cc +++ b/src/engine/material.cc @@ -86,23 +86,27 @@ struct QuadNode } }; -static void drawmaterial(const materialsurface &m, float offset) +static void drawmaterial(const materialsurface &m, float offset, const bvec4 &color = bvec4(0, 0, 0, 0)) { if(gle::attribbuf.empty()) { gle::defvertex(); + gle::defcolor(4, GL_UNSIGNED_BYTE); gle::begin(GL_QUADS); } float x = m.o.x, y = m.o.y, z = m.o.z, csize = m.csize, rsize = m.rsize; switch(m.orient) { -#define GENFACEORIENT(orient, v0, v1, v2, v3) \ + #define GENFACEORIENT(orient, v0, v1, v2, v3) \ case orient: v0 v1 v2 v3 break; -#define GENFACEVERT(orient, vert, mx,my,mz, sx,sy,sz) \ - gle::attribf(mx sx, my sy, mz sz); + #define GENFACEVERT(orient, vert, mx,my,mz, sx,sy,sz) \ + { \ + gle::attribf(mx sx, my sy, mz sz); \ + gle::attrib(color); \ + } GENFACEVERTS(x, x, y, y, z, z, /**/, + csize, /**/, + rsize, + offset, - offset) -#undef GENFACEORIENT -#undef GENFACEVERT + #undef GENFACEORIENT + #undef GENFACEVERT } } @@ -490,91 +494,66 @@ static void rendermatgrid() enablepolygonoffset(GL_POLYGON_OFFSET_LINE); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); int lastmat = -1; + bvec4 color(0, 0, 0, 0); loopvrev(editsurfs) { materialsurface &m = editsurfs[i]; if(m.material != lastmat) { - xtraverts += gle::end(); - bvec color; switch(m.material&~MATF_INDEX) { - case MAT_WATER: color = bvec( 0, 0, 85); break; // blue - case MAT_CLIP: color = bvec(85, 0, 0); break; // red - case MAT_GLASS: color = bvec( 0, 85, 85); break; // cyan - case MAT_NOCLIP: color = bvec( 0, 85, 0); break; // green - case MAT_LAVA: color = bvec(85, 40, 0); break; // orange - case MAT_GAMECLIP: color = bvec(85, 85, 0); break; // yellow - case MAT_DEATH: color = bvec(40, 40, 40); break; // black - case MAT_NOGI: color = bvec(40, 30, 0); break; // brown - case MAT_ALPHA: color = bvec(85, 0, 85); break; // pink + case MAT_WATER: color = bvec4( 0, 0, 85, 255); break; // blue + case MAT_CLIP: color = bvec4(85, 0, 0, 255); break; // red + case MAT_GLASS: color = bvec4( 0, 85, 85, 255); break; // cyan + case MAT_NOCLIP: color = bvec4( 0, 85, 0, 255); break; // green + case MAT_LAVA: color = bvec4(85, 40, 0, 255); break; // orange + case MAT_GAMECLIP: color = bvec4(85, 85, 0, 255); break; // yellow + case MAT_DEATH: color = bvec4(40, 40, 40, 255); break; // black + case MAT_NOGI: color = bvec4(40, 30, 0, 255); break; // brown + case MAT_ALPHA: color = bvec4(85, 0, 85, 255); break; // pink default: continue; } - gle::colorf(color.x*ldrscaleb, color.y*ldrscaleb, color.z*ldrscaleb); lastmat = m.material; } - drawmaterial(m, -0.1f); + drawmaterial(m, -0.1f, color); } xtraverts += gle::end(); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); disablepolygonoffset(GL_POLYGON_OFFSET_LINE); } -static float glassxscale = 0, glassyscale = 0; +extern const bvec4 matnormals[6] = +{ + bvec4(0x80, 0, 0), + bvec4(0x7F, 0, 0), + bvec4(0, 0x80, 0), + bvec4(0, 0x7F, 0), + bvec4(0, 0, 0x80), + bvec4(0, 0, 0x7F) +}; -static void drawglass(const materialsurface &m, float offset, const vec *normal = nullptr) +static void drawglass(const materialsurface &m, float offset) { if(gle::attribbuf.empty()) { gle::defvertex(); - if(normal) gle::defnormal(); - gle::deftexcoord0(); + gle::defnormal(4, GL_BYTE); gle::begin(GL_QUADS); } + float x = m.o.x, y = m.o.y, z = m.o.z, csize = m.csize, rsize = m.rsize; + switch(m.orient) + { #define GENFACEORIENT(orient, v0, v1, v2, v3) \ case orient: v0 v1 v2 v3 break; - #undef GENFACEVERTX - #define GENFACEVERTX(orient, vert, mx,my,mz, sx,sy,sz) \ + #define GENFACEVERT(orient, vert, mx,my,mz, sx,sy,sz) \ { \ - vec v(mx sx, my sy, mz sz); \ - gle::attribf(v.x, v.y, v.z); \ - GENFACENORMAL \ - gle::attribf(glassxscale*v.y, -glassyscale*v.z); \ + gle::attribf(mx sx, my sy, mz sz); \ + gle::attrib(matnormals[orient]); \ } - #undef GENFACEVERTY - #define GENFACEVERTY(orient, vert, mx,my,mz, sx,sy,sz) \ - { \ - vec v(mx sx, my sy, mz sz); \ - gle::attribf(v.x, v.y, v.z); \ - GENFACENORMAL \ - gle::attribf(glassxscale*v.x, -glassyscale*v.z); \ - } - #undef GENFACEVERTZ - #define GENFACEVERTZ(orient, vert, mx,my,mz, sx,sy,sz) \ - { \ - vec v(mx sx, my sy, mz sz); \ - gle::attribf(v.x, v.y, v.z); \ - GENFACENORMAL \ - gle::attribf(glassxscale*v.x, glassyscale*v.y); \ - } - #define GENFACENORMAL gle::attribf(n.x, n.y, n.z); - float x = m.o.x, y = m.o.y, z = m.o.z, csize = m.csize, rsize = m.rsize; - if(normal) - { - vec n = *normal; - switch(m.orient) { GENFACEVERTS(x, x, y, y, z, z, /**/, + csize, /**/, + rsize, + offset, - offset) } - } - #undef GENFACENORMAL - #define GENFACENORMAL - else switch(m.orient) { GENFACEVERTS(x, x, y, y, z, z, /**/, + csize, /**/, + rsize, + offset, - offset) } - #undef GENFACENORMAL + GENFACEVERTS(x, x, y, y, z, z, /**/, + csize, /**/, + rsize, + offset, - offset) #undef GENFACEORIENT - #undef GENFACEVERTX - #define GENFACEVERTX(o,n, x,y,z, xv,yv,zv) GENFACEVERT(o,n, x,y,z, xv,yv,zv) - #undef GENFACEVERTY - #define GENFACEVERTY(o,n, x,y,z, xv,yv,zv) GENFACEVERT(o,n, x,y,z, xv,yv,zv) - #undef GENFACEVERTZ - #define GENFACEVERTZ(o,n, x,y,z, xv,yv,zv) GENFACEVERT(o,n, x,y,z, xv,yv,zv) + #undef GENFACEVERT + } } float matliquidsx1 = -1, matliquidsy1 = -1, matliquidsx2 = 1, matliquidsy2 = 1; @@ -679,16 +658,6 @@ void rendermaterialmask() glEnable(GL_CULL_FACE); } -extern const vec matnormals[6] = -{ - vec(-1, 0, 0), - vec( 1, 0, 0), - vec(0, -1, 0), - vec(0, 1, 0), - vec(0, 0, -1), - vec(0, 0, 1) -}; - #define GLASSVARS(name) \ CVAR0R(name##colour, 0xB0D8FF); \ FVARR(name##refract, 0, 0.1f, 1e3f); \ @@ -715,8 +684,9 @@ static void renderglass() MatSlot &gslot = lookupmaterialslot(MAT_GLASS+k); Texture *tex = gslot.sts.inrange(0) ? gslot.sts[0].t : notexture; - glassxscale = TEX_SCALE/(tex->xs*gslot.scale); - glassyscale = TEX_SCALE/(tex->ys*gslot.scale); + float xscale = TEX_SCALE/(tex->xs*gslot.scale); + float yscale = TEX_SCALE/(tex->ys*gslot.scale); + GLOBALPARAMF(glasstexgen, xscale, yscale); glActiveTexture_(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, tex->id); @@ -742,7 +712,7 @@ static void renderglass() glBindTexture(GL_TEXTURE_CUBE_MAP, lookupenvmap(m.envmap)); envmap = m.envmap; } - drawglass(m, 0.1f, &matnormals[m.orient]); + drawglass(m, 0.1f); } xtraverts += gle::end(); } @@ -784,30 +754,28 @@ void rendereditmaterials() glEnable(GL_BLEND); int lastmat = -1; + bvec4 color(0, 0, 0, 0); loopv(editsurfs) { const materialsurface &m = editsurfs[i]; if(lastmat!=m.material) { - xtraverts += gle::end(); - bvec color; switch(m.material&~MATF_INDEX) { - case MAT_WATER: color = bvec(255, 128, 0); break; // blue - case MAT_CLIP: color = bvec( 0, 255, 255); break; // red - case MAT_GLASS: color = bvec(255, 0, 0); break; // cyan - case MAT_NOCLIP: color = bvec(255, 0, 255); break; // green - case MAT_LAVA: color = bvec( 0, 128, 255); break; // orange - case MAT_GAMECLIP: color = bvec( 0, 0, 255); break; // yellow - case MAT_DEATH: color = bvec(192, 192, 192); break; // black - case MAT_NOGI: color = bvec(128, 160, 255); break; // brown - case MAT_ALPHA: color = bvec( 0, 255, 0); break; // pink + case MAT_WATER: color = bvec4(255, 128, 0, 255); break; // blue + case MAT_CLIP: color = bvec4( 0, 255, 255, 255); break; // red + case MAT_GLASS: color = bvec4(255, 0, 0, 255); break; // cyan + case MAT_NOCLIP: color = bvec4(255, 0, 255, 255); break; // green + case MAT_LAVA: color = bvec4( 0, 128, 255, 255); break; // orange + case MAT_GAMECLIP: color = bvec4( 0, 0, 255, 255); break; // yellow + case MAT_DEATH: color = bvec4(192, 192, 192, 255); break; // black + case MAT_NOGI: color = bvec4(128, 160, 255, 255); break; // brown + case MAT_ALPHA: color = bvec4( 0, 255, 0, 255); break; // pink default: continue; } - gle::color(color); lastmat = m.material; } - drawmaterial(m, -0.1f); + drawmaterial(m, -0.1f, color); } xtraverts += gle::end(); diff --git a/src/engine/md2.hh b/src/engine/md2.hh index 47a6911..aa44d13 100644 --- a/src/engine/md2.hh +++ b/src/engine/md2.hh @@ -225,7 +225,7 @@ struct md2 : vertloader Texture *tex, *masks; loadskin(name, pname, tex, masks); mdl.initskins(tex, masks); - if(tex==notexture) conoutf("could not load model skin for %s", name1); + if(tex==notexture) conoutf(CON_ERROR, "could not load model skin for %s", name1); identflags &= ~IDF_PERSIST; defformatstring(name3, "media/model/%s/md2.cfg", name); if(!execfile(name3, false)) diff --git a/src/engine/md3.hh b/src/engine/md3.hh index 2e61093..991d1df 100644 --- a/src/engine/md3.hh +++ b/src/engine/md3.hh @@ -65,7 +65,7 @@ struct md3 : vertloader if(strncmp(header.id, "IDP3", 4) != 0 || header.version != 15) // header check { delete f; - conoutf("md3: corrupted header"); + conoutf(CON_ERROR, "md3: corrupted header"); return false; } @@ -171,7 +171,7 @@ struct md3 : vertloader Texture *tex, *masks; loadskin(name, pname, tex, masks); mdl.initskins(tex, masks); - if(tex==notexture) conoutf("could not load model skin for %s", name1); + if(tex==notexture) conoutf(CON_ERROR, "could not load model skin for %s", name1); return true; } }; diff --git a/src/engine/md5.hh b/src/engine/md5.hh index b56c6a8..51033df 100644 --- a/src/engine/md5.hh +++ b/src/engine/md5.hh @@ -227,7 +227,7 @@ struct md5 : skelloader m->load(f, buf, sizeof(buf)); if(!m->numtris || !m->numverts) { - conoutf("empty mesh in %s", filename); + conoutf(CON_WARN, "empty mesh in %s", filename); meshes.removeobj(m); delete m; } diff --git a/src/engine/obj.hh b/src/engine/obj.hh index 15d0ff6..15355ec 100644 --- a/src/engine/obj.hh +++ b/src/engine/obj.hh @@ -178,7 +178,7 @@ struct obj : vertloader Texture *tex, *masks; loadskin(name, pname, tex, masks); mdl.initskins(tex, masks); - if(tex==notexture) conoutf("could not load model skin for %s", name1); + if(tex==notexture) conoutf(CON_ERROR, "could not load model skin for %s", name1); return true; } }; diff --git a/src/engine/octa.hh b/src/engine/octa.hh index 643dafe..74e2e4b 100644 --- a/src/engine/octa.hh +++ b/src/engine/octa.hh @@ -168,7 +168,7 @@ struct vtxarray ushort minvert, maxvert; // DRE info elementset *texelems, *decalelems; // List of element indices sets (range) per texture materialsurface *matbuf; // buffer of material surfaces - int verts, tris, texs, blendtris, blends, alphabacktris, alphaback, alphafronttris, alphafront, refracttris, refract, texmask, sky, matsurfs, matmask, distance, rdistance, dyntexs, decaltris, decaltexs; + int verts, tris, texs, blendtris, blends, alphabacktris, alphaback, alphafronttris, alphafront, refracttris, refract, alphatris, texmask, sky, matsurfs, matmask, distance, rdistance, dyntexs, dynalphatexs, decaltris, decaltexs; ivec o; int size; // location and size of cube. ivec geommin, geommax; // BB of geom @@ -185,7 +185,7 @@ struct vtxarray vector mapmodels, decals; vector grasstris; int hasmerges, mergelevel; - int shadowmask; + int shadowmask, shadowtransparent; }; struct cube; diff --git a/src/engine/octarender.cc b/src/engine/octarender.cc index 5710d63..2a10525 100644 --- a/src/engine/octarender.cc +++ b/src/engine/octarender.cc @@ -538,6 +538,9 @@ struct vacollect : verthash va->ebuf = 0; va->edata = 0; va->eoffset = 0; + va->texmask = 0; + va->dyntexs = 0; + va->dynalphatexs = 0; if(va->texs) { va->texelems = new elementset[va->texs]; @@ -574,19 +577,20 @@ struct vacollect : verthash else if(k.alpha==ALPHA_BACK) { va->texs--; va->tris -= e.length/3; va->alphaback++; va->alphabacktris += e.length/3; } else if(k.alpha==ALPHA_FRONT) { va->texs--; va->tris -= e.length/3; va->alphafront++; va->alphafronttris += e.length/3; } else if(k.alpha==ALPHA_REFRACT) { va->texs--; va->tris -= e.length/3; va->refract++; va->refracttris += e.length/3; } + + VSlot &vslot = lookupvslot(k.tex, false); + if(vslot.isdynamic()) + { + va->dyntexs++; + if(k.alpha) va->dynalphatexs++; + } + Slot &slot = *vslot.slot; + loopvj(slot.sts) va->texmask |= 1<type&SHADER_ENVMAP) va->texmask |= 1<texmask = 0; - va->dyntexs = 0; - loopi(va->texs+va->blends+va->alphaback+va->alphafront+va->refract) - { - VSlot &vslot = lookupvslot(va->texelems[i].texture, false); - if(vslot.isdynamic()) va->dyntexs++; - Slot &slot = *vslot.slot; - loopvj(slot.sts) va->texmask |= 1<type&SHADER_ENVMAP) va->texmask |= 1<alphatris = va->alphabacktris + va->alphafronttris + va->refracttris; va->decalbuf = 0; va->decaldata = 0; @@ -960,7 +964,7 @@ struct edgegroup static inline uint hthash(const edgegroup &g) { - return g.slope.x^g.slope.y^g.slope.z^g.origin.x^g.origin.y^g.origin.z; + return g.slope.x^(g.slope.y<<2)^(g.slope.z<<4)^g.origin.x^g.origin.y^g.origin.z; } static inline bool htcmp(const edgegroup &x, const edgegroup &y) @@ -1052,21 +1056,21 @@ static void gencubeedges(cube &c, const ivec &co, int size) while(cur >= 0) { cubeedge &p = cubeedges[cur]; - if(p.flags&CE_DUP ? - ce.offset>=p.offset && ce.offset+ce.size<=p.offset+p.size : - ce.offset==p.offset && ce.size==p.size) - { - p.flags |= CE_DUP; - insert = false; - break; - } - else if(ce.offset >= p.offset) + if(ce.offset <= p.offset+p.size) { + if(ce.offset < p.offset) break; + if(p.flags&CE_DUP ? + ce.offset+ce.size <= p.offset+p.size : + ce.offset==p.offset && ce.size==p.size) + { + p.flags |= CE_DUP; + insert = false; + break; + } if(ce.offset == p.offset+p.size) ce.flags &= ~CE_START; - prev = cur; - cur = p.next; } - else break; + prev = cur; + cur = p.next; } if(insert) { @@ -1178,7 +1182,7 @@ static vtxarray *newva(const ivec &o, int size) vc.setupdata(va); - if(va->alphafronttris || va->alphabacktris || va->refracttris) + if(va->alphatris) { va->alphamin = ivec(vec(vc.alphamin).mul(8)).shr(3); va->alphamax = ivec(vec(vc.alphamax).mul(8)).add(7).shr(3); @@ -1200,7 +1204,7 @@ static vtxarray *newva(const ivec &o, int size) va->nogimax = vc.nogimax; wverts += va->verts; - wtris += va->tris + va->blends + va->alphabacktris + va->alphafronttris + va->refracttris + va->decaltris; + wtris += va->tris + va->blends + va->alphatris + va->decaltris; allocva++; valist.add(va); @@ -1210,7 +1214,7 @@ static vtxarray *newva(const ivec &o, int size) void destroyva(vtxarray *va, bool reparent) { wverts -= va->verts; - wtris -= va->tris + va->blends + va->alphabacktris + va->alphafronttris + va->refracttris + va->decaltris; + wtris -= va->tris + va->blends + va->alphatris + va->decaltris; allocva--; valist.removeobj(va); if(!va->parent) varoot.removeobj(va); diff --git a/src/engine/physics.cc b/src/engine/physics.cc index f3fd733..000b108 100644 --- a/src/engine/physics.cc +++ b/src/engine/physics.cc @@ -207,21 +207,6 @@ static float disttooutsideent(const vec &o, const vec &ray, float radius, int mo return dist; } -// optimized shadow version -static float shadowent(octaentities *oc, const vec &o, const vec &ray, float radius, int mode, extentity *t) -{ - float dist = radius, f = 0.0f; - const vector &ents = entities::getents(); - loopv(oc->mapmodels) - { - extentity &e = *ents[oc->mapmodels[i]]; - if(!(e.flags&EF_OCTA) || &e==t) continue; - if(!mmintersect(e, o, ray, radius, mode, f)) continue; - if(f>0 && f 0 ? radius : 1e16f; \ vec v(o), invray(ray.x ? 1/ray.x : 1e16f, ray.y ? 1/ray.y : 1e16f, ray.z ? 1/ray.z : 1e16f); \ @@ -251,7 +236,7 @@ static float shadowent(octaentities *oc, const vec &o, const vec &ray, float rad dist += disttoworld; \ } -#define DOWNOCTREE(disttoent, earlyexit) \ +#define DOWNOCTREE(disttoent) \ cube *lc = levels[lshift]; \ for(;;) \ { \ @@ -262,7 +247,6 @@ static float shadowent(octaentities *oc, const vec &o, const vec &ray, float rad float edist = disttoent(lc->ext->ents, o, ray, dent, mode, t); \ if(edist < dent) \ { \ - earlyexit return min(edist, dist); \ elvl = lshift; \ dent = min(dent, edist); \ } \ @@ -308,7 +292,7 @@ float raycube(const vec &o, const vec &ray, float radius, int mode, int size, ex int closest = -1, x = int(v.x), y = int(v.y), z = int(v.z); for(;;) { - DOWNOCTREE(disttoent, if(mode&RAY_SHADOW)); + DOWNOCTREE(disttoent); int lsize = 1<= 0) return c.texture[side]==DEFAULT_SKY && mode&RAY_SKIPSKY ? radius : dist+max(enterdist+0.1f, 0.0f); - } - - nextcube: - FINDCLOSEST(side = O_RIGHT - lsizemask.x, side = O_FRONT - lsizemask.y, side = O_TOP - lsizemask.z); - - if(dist>=radius) return dist; - - UPOCTREE(return radius); - } -} - float rayent(const vec &o, const vec &ray, float radius, int mode, int size, int &orient, int &ent) { hitent = -1; @@ -834,8 +786,7 @@ static bool mmcollide(physent *d, const vec &dir, float cutoff, octaentities &oc vec center, radius; float rejectradius = m->collisionbox(center, radius), scale = e.attr5 > 0 ? e.attr5/100.0f : 1; - center.mul(scale); - if(d->o.reject(vec(e.o).add(center), d->radius + rejectradius*scale)) continue; + if(d->o.reject(e.o, d->radius + rejectradius*scale)) continue; int yaw = e.attr2, pitch = e.attr3, roll = e.attr4; if(mcol == COLLIDE_TRI || testtricol) @@ -1776,7 +1727,7 @@ static void modifyvelocity(physent *pl, bool local, bool water, bool floating, i game::physicstrigger(pl, local, 1, 0); } } - if(!floating && pl->physstate == PHYS_FALL) pl->timeinair += curtime; + if(!floating && pl->physstate == PHYS_FALL) pl->timeinair = min(pl->timeinair + curtime, 1000); vec m(0.0f, 0.0f, 0.0f); if((pl->move || pl->strafe) && allowmove) diff --git a/src/engine/physics.hh b/src/engine/physics.hh index 31c052f..526a92f 100644 --- a/src/engine/physics.hh +++ b/src/engine/physics.hh @@ -14,7 +14,6 @@ extern physent *collideplayer; bool overlapsdynent(const vec &o, float radius); void rotatebb(vec ¢er, vec &radius, int yaw, int pitch, int roll = 0); -float shadowray(const vec &o, const vec &ray, float radius, int mode, extentity *t = nullptr); void moveplayer(physent *pl, int moveres, bool local); bool moveplayer(physent *pl, int moveres, bool local, int curtime); @@ -35,7 +34,7 @@ void cleardynentcache(); void updatedynentcache(physent *d); bool entinmap(dynent *d, bool avoidplayers = false); -enum { RAY_BB = 1, RAY_POLY = 3, RAY_ALPHAPOLY = 7, RAY_ENTS = 9, RAY_CLIPMAT = 16, RAY_SKIPFIRST = 32, RAY_EDITMAT = 64, RAY_SHADOW = 128, RAY_PASS = 256, RAY_SKIPSKY = 512 }; +enum { RAY_BB = 1, RAY_POLY = 3, RAY_ALPHAPOLY = 7, RAY_ENTS = 9, RAY_CLIPMAT = 16, RAY_SKIPFIRST = 32, RAY_EDITMAT = 64, RAY_PASS = 128 }; float raycube (const vec &o, const vec &ray, float radius = 0, int mode = RAY_CLIPMAT, int size = 0, extentity *t = 0); float raycubepos(const vec &o, const vec &ray, vec &hit, float radius = 0, int mode = RAY_CLIPMAT, int size = 0); diff --git a/src/engine/rendergl.cc b/src/engine/rendergl.cc index cf9ce71..5ab2211 100644 --- a/src/engine/rendergl.cc +++ b/src/engine/rendergl.cc @@ -31,7 +31,7 @@ static vec minimapcenter(0, 0, 0), minimapradius(0, 0, 0), minimapscale(0, 0, 0); -bool hasVAO = false, hasTR = false, hasTSW = false, hasPBO = false, hasFBO = false, hasAFBO = false, hasDS = false, hasTF = false, hasCBF = false, hasS3TC = false, hasFXT1 = false, hasLATC = false, hasRGTC = false, hasAF = false, hasFBB = false, hasFBMS = false, hasTMS = false, hasMSS = false, hasFBMSBS = false, hasUBO = false, hasMBR = false, hasDB2 = false, hasDBB = false, hasTG = false, hasTQ = false, hasPF = false, hasTRG = false, hasTI = false, hasHFV = false, hasHFP = false, hasDBT = false, hasDC = false, hasDBGO = false, hasEGPU4 = false, hasGPU4 = false, hasGPU5 = false, hasBFE = false, hasEAL = false, hasCR = false, hasOQ2 = false, hasES3 = false, hasCB = false, hasCI = false; +bool hasVAO = false, hasTR = false, hasTSW = false, hasPBO = false, hasFBO = false, hasAFBO = false, hasDS = false, hasTF = false, hasCBF = false, hasS3TC = false, hasFXT1 = false, hasLATC = false, hasRGTC = false, hasAF = false, hasFBB = false, hasFBMS = false, hasTMS = false, hasMSS = false, hasFBMSBS = false, hasUBO = false, hasMBR = false, hasDB2 = false, hasDBB = false, hasTG = false, hasTQ = false, hasPF = false, hasTRG = false, hasTI = false, hasHFV = false, hasHFP = false, hasDBT = false, hasDC = false, hasDBGO = false, hasEGPU4 = false, hasGPU4 = false, hasGPU5 = false, hasBFE = false, hasEAL = false, hasCR = false, hasOQ2 = false, hasES2 = false, hasES3 = false, hasCB = false, hasCI = false, hasTS = false; bool mesa = false, intel = false, amd = false, nvidia = false; int hasstencil = 0; @@ -54,7 +54,6 @@ PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus_ = nullptr; PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer_ = nullptr; PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers_ = nullptr; PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers_ = nullptr; -PFNGLFRAMEBUFFERTEXTURE1DPROC glFramebufferTexture1D_ = nullptr; PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D_ = nullptr; PFNGLFRAMEBUFFERTEXTURE3DPROC glFramebufferTexture3D_ = nullptr; PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer_ = nullptr; @@ -94,10 +93,8 @@ PFNGLCOPYTEXSUBIMAGE3DPROC glCopyTexSubImage3D_ = nullptr; PFNGLCOMPRESSEDTEXIMAGE3DPROC glCompressedTexImage3D_ = nullptr; PFNGLCOMPRESSEDTEXIMAGE2DPROC glCompressedTexImage2D_ = nullptr; -PFNGLCOMPRESSEDTEXIMAGE1DPROC glCompressedTexImage1D_ = nullptr; PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glCompressedTexSubImage3D_ = nullptr; PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glCompressedTexSubImage2D_ = nullptr; -PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glCompressedTexSubImage1D_ = nullptr; PFNGLGETCOMPRESSEDTEXIMAGEPROC glGetCompressedTexImage_ = nullptr; PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements_ = nullptr; @@ -274,6 +271,9 @@ PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glBindFragDataLocationIndexed_ = nullptr; // GL_ARB_copy_image PFNGLCOPYIMAGESUBDATAPROC glCopyImageSubData_ = nullptr; +// GL_ARB_texture_storage +PFNGLTEXSTORAGE2DPROC glTexStorage2D_ = nullptr; + static inline void *getprocaddress(const char *name) { return SDL_GL_GetProcAddress(name); @@ -439,10 +439,8 @@ void gl_checkextensions() glCompressedTexImage3D_ = (PFNGLCOMPRESSEDTEXIMAGE3DPROC) getprocaddress("glCompressedTexImage3D"); glCompressedTexImage2D_ = (PFNGLCOMPRESSEDTEXIMAGE2DPROC) getprocaddress("glCompressedTexImage2D"); - glCompressedTexImage1D_ = (PFNGLCOMPRESSEDTEXIMAGE1DPROC) getprocaddress("glCompressedTexImage1D"); glCompressedTexSubImage3D_ = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) getprocaddress("glCompressedTexSubImage3D"); glCompressedTexSubImage2D_ = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) getprocaddress("glCompressedTexSubImage2D"); - glCompressedTexSubImage1D_ = (PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) getprocaddress("glCompressedTexSubImage1D"); glGetCompressedTexImage_ = (PFNGLGETCOMPRESSEDTEXIMAGEPROC) getprocaddress("glGetCompressedTexImage"); glDrawRangeElements_ = (PFNGLDRAWRANGEELEMENTSPROC) getprocaddress("glDrawRangeElements"); @@ -749,7 +747,6 @@ void gl_checkextensions() glBindFramebuffer_ = (PFNGLBINDFRAMEBUFFERPROC) getprocaddress("glBindFramebuffer"); glDeleteFramebuffers_ = (PFNGLDELETEFRAMEBUFFERSPROC) getprocaddress("glDeleteFramebuffers"); glGenFramebuffers_ = (PFNGLGENFRAMEBUFFERSPROC) getprocaddress("glGenFramebuffers"); - glFramebufferTexture1D_ = (PFNGLFRAMEBUFFERTEXTURE1DPROC) getprocaddress("glFramebufferTexture1D"); glFramebufferTexture2D_ = (PFNGLFRAMEBUFFERTEXTURE2DPROC) getprocaddress("glFramebufferTexture2D"); glFramebufferTexture3D_ = (PFNGLFRAMEBUFFERTEXTURE3DPROC) getprocaddress("glFramebufferTexture3D"); glFramebufferRenderbuffer_ = (PFNGLFRAMEBUFFERRENDERBUFFERPROC) getprocaddress("glFramebufferRenderbuffer"); @@ -771,7 +768,6 @@ void gl_checkextensions() glBindFramebuffer_ = (PFNGLBINDFRAMEBUFFERPROC) getprocaddress("glBindFramebufferEXT"); glDeleteFramebuffers_ = (PFNGLDELETEFRAMEBUFFERSPROC) getprocaddress("glDeleteFramebuffersEXT"); glGenFramebuffers_ = (PFNGLGENFRAMEBUFFERSPROC) getprocaddress("glGenFramebuffersEXT"); - glFramebufferTexture1D_ = (PFNGLFRAMEBUFFERTEXTURE1DPROC) getprocaddress("glFramebufferTexture1DEXT"); glFramebufferTexture2D_ = (PFNGLFRAMEBUFFERTEXTURE2DPROC) getprocaddress("glFramebufferTexture2DEXT"); glFramebufferTexture3D_ = (PFNGLFRAMEBUFFERTEXTURE3DPROC) getprocaddress("glFramebufferTexture3DEXT"); glFramebufferRenderbuffer_ = (PFNGLFRAMEBUFFERRENDERBUFFERPROC) getprocaddress("glFramebufferRenderbufferEXT"); @@ -1001,6 +997,12 @@ void gl_checkextensions() } if(hasTG) usetexgather = hasGPU5 && !intel && !nvidia ? 2 : 1; + if(glversion >= 410 || hasext("GL_ARB_ES2_compatibility")) + { + hasES2 = true; + if(glversion < 410 && dbgexts) conoutf(CON_INIT, "Using GL_ARB_ES2_compatibility extension."); + } + if(glversion >= 430 || hasext("GL_ARB_ES3_compatibility")) { hasES3 = true; @@ -1043,6 +1045,19 @@ void gl_checkextensions() if(dbgexts) conoutf(CON_INIT, "Using GL_NV_copy_image extension."); } + if(glversion >= 420 || hasext("GL_ARB_texture_storage")) + { + glTexStorage2D_ = (PFNGLTEXSTORAGE2DPROC)getprocaddress("glTexStorage2D"); + hasTS = true; + if(glversion < 420 && dbgexts) conoutf(CON_INIT, "Using GL_ARB_texture_storage extension."); + } + else if(hasext("GL_EXT_texture_storage")) + { + glTexStorage2D_ = (PFNGLTEXSTORAGE2DPROC)getprocaddress("glTexStorage2DEXT"); + hasTS = true; + if(dbgexts) conoutf(CON_INIT, "Using GL_EXT_texture_storage extension."); + } + extern int gdepthstencil, gstencil, glineardepth, msaadepthstencil, msaalineardepth, batchsunlight, smgather, rhrect, tqaaresolvegather; if(amd) { @@ -1060,7 +1075,6 @@ void gl_checkextensions() } else if(intel) { - smgather = 1; // native shadow filter is slow if(mesa) { batchsunlight = 0; // causes massive slowdown in linux driver @@ -1070,6 +1084,7 @@ void gl_checkextensions() } else { + smgather = 1; // native shadow filter is slow // causes massive slowdown in windows driver if reading depth-stencil texture if(checkdepthtexstencilrb()) { @@ -2167,7 +2182,7 @@ void drawminimap() camera1 = oldcamera; drawtex = 0; - createtexture(minimaptex, size, size, nullptr, 3, 1, GL_RGB5, GL_TEXTURE_2D); + createtexture(minimaptex, size, size, nullptr, 3, 1, hasES2 ? GL_RGB565 : GL_RGB5, GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); GLfloat border[4] = { minimapcolour.x/255.0f, minimapcolour.y/255.0f, minimapcolour.z/255.0f, 1.0f }; diff --git a/src/engine/rendergl.hh b/src/engine/rendergl.hh index 66829a8..cafda28 100644 --- a/src/engine/rendergl.hh +++ b/src/engine/rendergl.hh @@ -4,7 +4,7 @@ #include #include -extern bool hasVAO, hasTR, hasTSW, hasPBO, hasFBO, hasAFBO, hasDS, hasTF, hasCBF, hasS3TC, hasFXT1, hasLATC, hasRGTC, hasAF, hasFBB, hasFBMS, hasTMS, hasMSS, hasFBMSBS, hasUBO, hasMBR, hasDB2, hasDBB, hasTG, hasTQ, hasPF, hasTRG, hasTI, hasHFV, hasHFP, hasDBT, hasDC, hasDBGO, hasEGPU4, hasGPU4, hasGPU5, hasBFE, hasEAL, hasCR, hasOQ2, hasES3, hasCB, hasCI; +extern bool hasVAO, hasTR, hasTSW, hasPBO, hasFBO, hasAFBO, hasDS, hasTF, hasCBF, hasS3TC, hasFXT1, hasLATC, hasRGTC, hasAF, hasFBB, hasFBMS, hasTMS, hasMSS, hasFBMSBS, hasUBO, hasMBR, hasDB2, hasDBB, hasTG, hasTQ, hasPF, hasTRG, hasTI, hasHFV, hasHFP, hasDBT, hasDC, hasDBGO, hasEGPU4, hasGPU4, hasGPU5, hasBFE, hasEAL, hasCR, hasOQ2, hasES2, hasES3, hasCB, hasCI, hasTS; extern int xtraverts, xtravertsva; diff --git a/src/engine/renderlights.cc b/src/engine/renderlights.cc index 7b91e2a..18d2794 100644 --- a/src/engine/renderlights.cc +++ b/src/engine/renderlights.cc @@ -283,6 +283,7 @@ VARFP(aobilateralupscale, 0, 0, 1, cleanupao()); VARF(aopackdepth, 0, 1, 1, cleanupao()); VARFP(aotaps, 1, 5, 12, cleanupao()); VARF(aoderivnormal, 0, 0, 1, cleanupao()); +VAR(aoderiv, -1, 1, 1); VAR(debugao, 0, 0, 1); void initao() @@ -349,7 +350,10 @@ void renderao() LOCALPARAMF(contrastparams, (2.0f*aodark)/aotaps, aosharp); LOCALPARAMF(offsetscale, xscale/eyematrix.d.z, yscale/eyematrix.d.z, eyematrix.d.x/eyematrix.d.z, eyematrix.d.y/eyematrix.d.z); LOCALPARAMF(prefilterdepth, aoprefilterdepth); + + if(aoderiv >= 0) glHint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, aoderiv ? GL_NICEST : GL_FASTEST); screenquad(vieww, viewh, aow/float(1<= 0) glHint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, GL_DONT_CARE); if(aobilateral) { @@ -965,7 +969,7 @@ void resolvemsaacolor(int w = vieww, int h = viewh) endtimer(resolvetimer); } -FVAR(bloomthreshold, 1e-3f, 0.8f, 1e3f); +FVAR(bloomthreshold, 1e-3f, 0.85f, 1e3f); FVARP(bloomscale, 0, 1.0f, 1e3f); VARP(bloomblur, 0, 7, 7); VARP(bloomiter, 0, 0, 4); @@ -977,7 +981,9 @@ VAR(hdrreduce, 0, 2, 2); VARFP(hdrprec, 0, 2, 3, cleanupgbuffer()); FVARFP(hdrgamma, 1e-3f, 2, 1e3f, initwarning("HDR setup", INIT_LOAD, CHANGE_SHADERS)); FVARR(hdrbright, 1e-4f, 1.0f, 1e4f); -FVAR(hdrsaturate, 1e-3f, 0.8f, 1e3f); +FVAR(hdrsaturate, 1e-3f, 0.85f, 1e3f); +FVAR(hdrminexposure, 0, 0.03f, 1); +FVAR(hdrmaxexposure, 0, 0.3f, 1); VARFP(gscale, 25, 100, 100, cleanupgbuffer()); VARFP(gscalecubic, 0, 0, 1, cleanupgbuffer()); VARFP(gscalenearest, 0, 0, 1, cleanupgbuffer()); @@ -1331,6 +1337,33 @@ done: endtimer(hdrtimer); } +static void getavglum(int *numargs, ident *id) +{ + if(!bloomfbo[4]) return; + glBindFramebuffer_(GL_FRAMEBUFFER, bloomfbo[4]); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + float avglum = -1; + glReadPixels(0, 0, 1, 1, GL_RED, GL_FLOAT, &avglum); + glBindFramebuffer_(GL_FRAMEBUFFER, 0); + if(avglum < 0) return; + avglum *= 4; + if(*numargs < 0) floatret(avglum); + else printfvar(id, avglum); +} + +COMMANDN(avglum, getavglum, "N$"); + +VAR(debugbloom, 0, 0, 1); + +static void viewbloom() +{ + int w = min(hudw, hudh)/2, h = (w*hudh)/hudw; + SETSHADER(hudrect); + gle::colorf(1, 1, 1); + glBindTexture(GL_TEXTURE_RECTANGLE, bloomtex[3]); + debugquad(0, 0, w, h, 0, 0, bloomw, bloomh); +} + VAR(debugdepth, 0, 0, 1); void viewdepth() @@ -1581,7 +1614,8 @@ extern int smminradius; struct lightinfo { - int ent, shadowmap, flags; + int ent, shadowmap; + ushort flags, batched; vec o, color; float radius, dist; vec dir, spotx, spoty; @@ -1590,8 +1624,8 @@ struct lightinfo occludequery *query; lightinfo() {} - lightinfo(const vec &o, const vec &color, float radius, int flags = 0, const vec &dir = vec(0, 0, 0), int spot = 0) - : ent(-1), shadowmap(-1), flags(flags), + lightinfo(const vec &o, const vec &color, float radius, ushort flags = 0, const vec &dir = vec(0, 0, 0), int spot = 0) + : ent(-1), shadowmap(-1), flags(flags), batched(~0), o(o), color(color), radius(radius), dist(camera1->o.dist(o)), dir(dir), spot(spot), query(nullptr) { @@ -1599,7 +1633,7 @@ struct lightinfo calcscissor(); } lightinfo(int i, const extentity &e) - : ent(i), shadowmap(-1), flags(e.attr5), + : ent(i), shadowmap(-1), flags(e.attr5), batched(~0), o(e.o), color(vec(e.attr2, e.attr3, e.attr4).max(0)), radius(e.attr1), dist(camera1->o.dist(e.o)), dir(0, 0, 0), spot(0), query(nullptr) { @@ -1622,6 +1656,7 @@ struct lightinfo bool noshadow() const { return flags&L_NOSHADOW || radius <= smminradius; } bool nospec() const { return (flags&L_NOSPEC) != 0; } bool volumetric() const { return (flags&L_VOLUMETRIC) != 0; } + bool colorshadow() const { return (flags&L_SMALPHA) != 0; } void addscissor(float &dx1, float &dy1, float &dx2, float &dy2) const { @@ -1695,17 +1730,19 @@ struct shadowcacheval; struct shadowmapinfo { - ushort x, y, size, sidemask; + ushort x, y, size; + uchar sidemask, transparent; int light; shadowcacheval *cached; }; struct shadowcacheval { - ushort x, y, size, sidemask; + ushort x, y, size; + uchar sidemask, transparent; shadowcacheval() {} - shadowcacheval(const shadowmapinfo &sm) : x(sm.x), y(sm.y), size(sm.size), sidemask(sm.sidemask) {} + shadowcacheval(const shadowmapinfo &sm) : x(sm.x), y(sm.y), size(sm.size), sidemask(sm.sidemask), transparent(sm.transparent) {} }; struct shadowcache : hashtable @@ -1718,15 +1755,42 @@ struct shadowcache : hashtable } }; -extern int smcache, smfilter, smgather; +extern int smcache, smfilter, smgather, smalpha, smalphaprec, alphashadow; #define SHADOWCACHE_EVICT 2 GLuint shadowatlastex = 0, shadowatlasfbo = 0; +GLuint shadowcolortex = 0, shadowblanktex = 0; +GLuint shadowfiltertex = 0, shadowfilterfbo = 0; GLenum shadowatlastarget = GL_NONE; +vector shadowcolorclears, shadowcolorblurs; +int smalign = 0; shadowcache shadowcache; bool shadowcachefull = false; int evictshadowcache = 0; +Shader *smalphaworldshader = nullptr; + +extern int usetexgather; +static inline bool usegatherforsm() { return smfilter > 1 && smgather && hasTG && usetexgather; } +static inline bool usesmcomparemode() { return !usegatherforsm() || (hasTG && hasGPU5 && usetexgather > 1); } + +static void loadsmshaders() +{ + if(smalpha && alphashadow) + { + smalphaworldshader = useshaderbyname("smalphaworld"); + if(smfilter) + { + useshaderbyname("smalphaclear"); + useshaderbyname(usegatherforsm() ? "smalphablur2d" : "smalphablurrect"); + } + } +} + +static void clearsmshaders() +{ + smalphaworldshader = nullptr; +} static inline void setsmnoncomparemode() // use texture gather { @@ -1742,28 +1806,34 @@ static inline void setsmcomparemode() // use embedded shadow cmp glTexParameteri(shadowatlastarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } -extern int usetexgather; -static inline bool usegatherforsm() { return smfilter > 1 && smgather && hasTG && usetexgather; } -static inline bool usesmcomparemode() { return !usegatherforsm() || (hasTG && hasGPU5 && usetexgather > 1); } - +VAR(debugshadowatlas, 0, 0, 3); void viewshadowatlas() { + bool color = debugshadowatlas > 1 && shadowcolortex && (debugshadowatlas <= 2 || shadowfiltertex); int w = min(hudw, hudh)/2, h = (w*hudh)/hudw, x = hudw-w, y = hudh-h; float tw = 1, th = 1; - if(shadowatlastarget == GL_TEXTURE_RECTANGLE) + if((color && debugshadowatlas > 2) || shadowatlastarget == GL_TEXTURE_RECTANGLE) { tw = shadowatlaspacker.w; th = shadowatlaspacker.h; + if(debugshadowatlas > 2) { tw /= 2; th /= 2; } SETSHADER(hudrect); } else hudshader->set(); gle::colorf(1, 1, 1); - glBindTexture(shadowatlastarget, shadowatlastex); - if(usesmcomparemode()) setsmnoncomparemode(); + if(color) + { + if(debugshadowatlas > 2) glBindTexture(GL_TEXTURE_RECTANGLE, shadowcolortex); + else glBindTexture(shadowatlastarget, shadowcolortex); + } + else + { + glBindTexture(shadowatlastarget, shadowatlastex); + if(usesmcomparemode()) setsmnoncomparemode(); + } debugquad(x, y, w, h, 0, 0, tw, th); - if(usesmcomparemode()) setsmcomparemode(); + if(!color && usesmcomparemode()) setsmcomparemode(); } -VAR(debugshadowatlas, 0, 0, 1); extern int smdepthprec, smsize; @@ -1779,26 +1849,62 @@ void setupshadowatlas() glTexParameteri(shadowatlastarget, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); glTexParameteri(shadowatlastarget, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + smalign = 0; + if(smalpha && alphashadow) + { + if(!shadowcolortex) glGenTextures(1, &shadowcolortex); + + GLenum colcomp = smalphaprec > 1 ? GL_RGB10 : (smalphaprec ? (hasES2 ? GL_RGB565 : GL_RGB5) : GL_R3_G3_B2); + createtexture(shadowcolortex, shadowatlaspacker.w, shadowatlaspacker.h, nullptr, 3, 1, colcomp, shadowatlastarget); + + if(!shadowblanktex) glGenTextures(1, &shadowblanktex); + static const uchar blank[4] = {255, 255, 255, 255}; + createtexture(shadowblanktex, 1, 1, blank, 3, 1, GL_RGB, GL_TEXTURE_RECTANGLE); + + if(smfilter) + { + smalign = 1; + + if(!shadowfiltertex) glGenTextures(1, &shadowfiltertex); + createtexture(shadowfiltertex, shadowatlaspacker.w/2, shadowatlaspacker.h/2, nullptr, 3, 1, colcomp, GL_TEXTURE_RECTANGLE); + + if(!shadowfilterfbo) glGenFramebuffers_(1, &shadowfilterfbo); + glBindFramebuffer_(GL_FRAMEBUFFER, shadowfilterfbo); + glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, shadowfiltertex, 0); + + if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + fatal("Failed allocating shadow filter buffer!"); + } + } + if(!shadowatlasfbo) glGenFramebuffers_(1, &shadowatlasfbo); glBindFramebuffer_(GL_FRAMEBUFFER, shadowatlasfbo); - glDrawBuffer(GL_NONE); - glReadBuffer(GL_NONE); - + if(shadowcolortex) glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, shadowatlastarget, shadowcolortex, 0); glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, shadowatlastarget, shadowatlastex, 0); + if(!shadowcolortex) glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); + if(glCheckFramebufferStatus_(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) fatal("failed allocating shadow atlas!"); glBindFramebuffer_(GL_FRAMEBUFFER, 0); + + loadsmshaders(); } void cleanupshadowatlas() { if(shadowatlastex) { glDeleteTextures(1, &shadowatlastex); shadowatlastex = 0; } + if(shadowcolortex) { glDeleteTextures(1, &shadowcolortex); shadowcolortex = 0; } + if(shadowblanktex) { glDeleteTextures(1, &shadowblanktex); shadowblanktex = 0; } if(shadowatlasfbo) { glDeleteFramebuffers_(1, &shadowatlasfbo); shadowatlasfbo = 0; } + if(shadowfiltertex) { glDeleteTextures(1, &shadowfiltertex); shadowfiltertex = 0; } + if(shadowfilterfbo) { glDeleteFramebuffers_(1, &shadowfilterfbo); shadowfilterfbo = 0; } clearshadowcache(); + clearsmshaders(); } const matrix4 cubeshadowviewmatrix[6] = @@ -1840,6 +1946,8 @@ VARFP(smfilter, 0, 2, 3, { cleardeferredlightshaders(); cleanupshadowatlas(); cl VARFP(smgather, 0, 0, 1, { cleardeferredlightshaders(); cleanupshadowatlas(); cleanupvolumetric(); }); VAR(smnoshadow, 0, 0, 1); VAR(smdynshadow, 0, 1, 1); +VARF(smalpha, 0, 2, 2, { cleardeferredlightshaders(); cleanupshadowatlas(); }); +VARFP(smalphaprec, 0, 0, 2, cleanupshadowatlas()); VAR(lightpassesused, 1, 0, 0); VAR(lightsvisible, 1, 0, 0); VAR(lightsoccluded, 1, 0, 0); @@ -1847,6 +1955,9 @@ VARN(lightbatches, lightbatchesused, 1, 0, 0); VARN(lightbatchrects, lightbatchrectsused, 1, 0, 0); VARN(lightbatchstacks, lightbatchstacksused, 1, 0, 0); +VARFR(alphashadow, 0, 0, 2, { cleardeferredlightshaders(); cleanupshadowatlas(); }); +FVARFR(alphashadowscale, 0, 1, 2, clearshadowcache()); + enum { MAXLIGHTTILEBATCH = 8 @@ -1900,7 +2011,8 @@ enum { BF_SPOTLIGHT = 1<<0, BF_NOSHADOW = 1<<1, - BF_NOSUN = 1<<2 + BF_SMALPHA = 1<<2, + BF_NOSUN = 1<<3 }; struct lightbatchkey @@ -1968,6 +2080,7 @@ static shadowmapinfo *addshadowmap(ushort x, ushort y, int size, int &idx, int l sm->size = size; sm->light = light; sm->sidemask = 0; + sm->transparent = 0; sm->cached = cached; return sm; } @@ -1995,6 +2108,7 @@ struct cascadedshadowmap matrix4 model; // model view is shared by all splits splitinfo splits[CSM_MAXSPLITS]; // per-split parameters vec lightview; // view vector for light + int rendered; void setup(); // insert shadowmaps for each split frustum if there is sunlight void updatesplitdist(); // compute split frustum distances void getmodelmatrix(); // compute the shared model matrix @@ -2005,7 +2119,7 @@ struct cascadedshadowmap void cascadedshadowmap::setup() { - int size = (csmmaxsize * shadowatlaspacker.w) / SHADOWATLAS_SIZE; + int size = ((csmmaxsize * shadowatlaspacker.w) / SHADOWATLAS_SIZE + smalign)&~smalign; loopi(csmsplits) { ushort smx = USHRT_MAX, smy = USHRT_MAX; @@ -2078,10 +2192,10 @@ void cascadedshadowmap::getprojmatrix() vec tc; model.transform(c, tc); int border = smfilter > 2 ? smborder2 : smborder; - const float pradius = ceil(radius * csmpradiustweak), step = (2*pradius) / (sm.size - 2*border); - vec2 offset = vec2(tc).sub(pradius).div(step); - offset.x = floor(offset.x); - offset.y = floor(offset.y); + const float pradius = ceil(radius * csmpradiustweak) + smalign, step = (2*pradius) / (sm.size - 2*border); + vec2 offset = vec2(tc).sub(pradius).div(step*(1+smalign)); + offset.x = floor(offset.x)*(1+smalign); + offset.y = floor(offset.y)*(1+smalign); split.center = vec(vec2(offset).mul(step).add(pradius), -0.5f*(minz + maxz)); split.bounds = vec(pradius, pradius, 0.5f*(maxz - minz)); @@ -2425,7 +2539,7 @@ void disableavatarmask() VAR(forcespotlights, 1, 0, 0); -extern int spotlights; +extern int spotlights, volumetricsmalphalights; static Shader *volumetricshader = nullptr, *volumetricbilateralshader[2] = { nullptr, nullptr }; @@ -2445,10 +2559,12 @@ Shader *loadvolumetricshader() if(usegatherforsm()) common[commonlen++] = smfilter > 2 ? 'G' : 'g'; else if(smfilter) common[commonlen++] = smfilter > 2 ? 'E' : (smfilter > 1 ? 'F' : 'f'); + else common[commonlen++] = 'N'; if(spotlights || forcespotlights) common[commonlen++] = 's'; common[commonlen] = '\0'; shadow[shadowlen++] = 'p'; + if(smalpha > 1 && alphashadow && volumetricsmalphalights) shadow[shadowlen++] = 'P'; shadow[shadowlen] = '\0'; defformatstring(name, "volumetric%s%s%d", common, shadow, volsteps); @@ -2514,6 +2630,7 @@ FVAR(volprefilter, 0, 4, 1e3f); FVAR(voldistclamp, 0, 0.99f, 2); CVAR1R(volcolour, 0x808080); FVARR(volscale, 0, 1, 16); +VAR(volderiv, -1, 1, 1); static Shader *deferredlightshader = nullptr, *deferredminimapshader = nullptr, *deferredmsaapixelshader = nullptr, *deferredmsaasampleshader = nullptr; @@ -2525,7 +2642,7 @@ void cleardeferredlightshaders() deferredmsaasampleshader = nullptr; } -extern int nospeclights; +extern int nospeclights, smalphalights; Shader *loaddeferredlightshader(const char *type = nullptr) { @@ -2553,11 +2670,13 @@ Shader *loaddeferredlightshader(const char *type = nullptr) } if(usegatherforsm()) common[commonlen++] = smfilter > 2 ? 'G' : 'g'; else if(smfilter) common[commonlen++] = smfilter > 2 ? 'E' : (smfilter > 1 ? 'F' : 'f'); + else common[commonlen++] = 'N'; if(spotlights || forcespotlights) common[commonlen++] = 's'; if(nospeclights) common[commonlen++] = 'z'; common[commonlen] = '\0'; shadow[shadowlen++] = 'p'; + if(smalpha > 1 && alphashadow > (smalphalights ? 0 : 1)) shadow[shadowlen++] = 'P'; shadow[shadowlen] = '\0'; int usecsm = 0, userh = 0; @@ -2565,6 +2684,7 @@ Shader *loaddeferredlightshader(const char *type = nullptr) { usecsm = csmsplits; sun[sunlen++] = 'c'; + if(smalpha && alphashadow) sun[sunlen++] = 'C'; sun[sunlen++] = '0' + csmsplits; if(!minimap) { @@ -2845,6 +2965,13 @@ static void bindlighttexs(int msaapass = 0, bool transparent = false) glActiveTexture_(GL_TEXTURE6 + i); glBindTexture(GL_TEXTURE_3D, rhtex[i]); } + if(smalpha && alphashadow) + { + glActiveTexture_(GL_TEXTURE10); + glBindTexture(GL_TEXTURE_RECTANGLE, csm.rendered > 1 ? (smfilter ? shadowfiltertex : shadowcolortex) : shadowblanktex); + glActiveTexture_(GL_TEXTURE11); + glBindTexture(GL_TEXTURE_RECTANGLE, smfilter ? shadowfiltertex : shadowcolortex); + } glActiveTexture_(GL_TEXTURE0); } @@ -2870,7 +2997,7 @@ static inline void setlightglobals(bool transparent = false) else GLOBALPARAMF(lightscale, ambient.x*lightscale*ambientscale, ambient.y*lightscale*ambientscale, ambient.z*lightscale*ambientscale, 255*lightscale); - if(!sunlight.iszero() && csmshadowmap) + if(csm.rendered) { csm.bindparams(); rh.bindparams(); @@ -2930,9 +3057,10 @@ static inline void setlightparams(int i, const lightinfo &l) } } -static inline void setlightshader(Shader *s, int n, bool baselight, bool shadowmap, bool spotlight, bool transparent = false, bool avatar = false) +static inline void setlightshader(Shader *s, int n, bool baselight, bool shadowmap, bool spotlight, bool transparent = false, bool colorshadow = false, bool avatar = false) { - s->setvariant(n-1, (shadowmap ? 1 : 0) + (baselight ? 0 : 2) + (spotlight ? 4 : 0) + (transparent ? 8 : 0) + (avatar ? 24 : 0)); + int variant = (shadowmap ? 1 : 0) + (baselight ? 0 : 2) + (spotlight ? 4 : 0) + (transparent ? 8 : (avatar ? 24 : (colorshadow ? 16 : 0))); + s->setvariant(n - (variant&7 ? 1 : 0), variant); lightpos.setv(lightposv, n); lightcolor.setv(lightcolorv, n); if(spotlight) spotparams.setv(spotparamsv, n); @@ -2954,7 +3082,7 @@ static void rendersunpass(Shader *s, int stencilref, bool transparent, float bsx int tx1 = max(int(floor((bsx1*0.5f+0.5f)*vieww)), 0), ty1 = max(int(floor((bsy1*0.5f+0.5f)*viewh)), 0), tx2 = min(int(ceil((bsx2*0.5f+0.5f)*vieww)), vieww), ty2 = min(int(ceil((bsy2*0.5f+0.5f)*viewh)), viewh); - s->setvariant(transparent ? 0 : -1, 16); + s->setvariant(0, transparent ? 8 : 0); lightquad(-1, (tx1*2.0f)/vieww-1.0f, (ty1*2.0f)/viewh-1.0f, (tx2*2.0f)/vieww-1.0f, (ty2*2.0f)/viewh-1.0f, tilemask); lightpassesused++; @@ -2962,7 +3090,7 @@ static void rendersunpass(Shader *s, int stencilref, bool transparent, float bsx { setavatarstencil(stencilref, true); - s->setvariant(0, 17); + s->setvariant(0, 24); lightquad(-1, (tx1*2.0f)/vieww-1.0f, (ty1*2.0f)/viewh-1.0f, (tx2*2.0f)/vieww-1.0f, (ty2*2.0f)/viewh-1.0f, tilemask); lightpassesused++; @@ -2994,7 +3122,9 @@ static void renderlightsnobatch(Shader *s, int stencilref, bool transparent, flo GLOBALPARAM(lightmatrix, lightmatrix); setlightparams(0, l); - setlightshader(s, 1, false, l.shadowmap >= 0, l.spot > 0, transparent, avatarpass > 0); + + bool colorshadow = shadowmaps.inrange(l.shadowmap) && shadowmaps[l.shadowmap].transparent; + setlightshader(s, 1, false, l.shadowmap >= 0, l.spot > 0, transparent, colorshadow, avatarpass > 0); int tx1 = int(floor((sx1*0.5f+0.5f)*vieww)), ty1 = int(floor((sy1*0.5f+0.5f)*viewh)), tx2 = int(ceil((sx2*0.5f+0.5f)*vieww)), ty2 = int(ceil((sy2*0.5f+0.5f)*viewh)); @@ -3038,9 +3168,8 @@ static void renderlightsnobatch(Shader *s, int stencilref, bool transparent, flo lightsphere::disable(); } -static void renderlightbatches(Shader *s, int stencilref, bool transparent, float bsx1, float bsy1, float bsx2, float bsy2, const uint *tilemask) +static void renderlightbatches(Shader *s, int stencilref, bool transparent, float bsx1, float bsy1, float bsx2, float bsy2, const uint *tilemask, bool sunpass) { - bool sunpass = !sunlight.iszero() && csmshadowmap && batchsunlight <= (gi && giscale && gidist ? 1 : 0); int btx1, bty1, btx2, bty2; calctilebounds(bsx1, bsy1, bsx2, bsy2, btx1, bty1, btx2, bty2); loopv(lightbatches) @@ -3067,10 +3196,10 @@ static void renderlightbatches(Shader *s, int stencilref, bool transparent, floa if(n) { - bool shadowmap = !(batch.flags & BF_NOSHADOW), spotlight = (batch.flags & BF_SPOTLIGHT) != 0; - setlightshader(s, n, baselight, shadowmap, spotlight, transparent); + bool shadowmap = !(batch.flags & BF_NOSHADOW), spotlight = (batch.flags & BF_SPOTLIGHT) != 0, colorshadow = (batch.flags & BF_SMALPHA) != 0; + setlightshader(s, n, baselight, shadowmap, spotlight, transparent, colorshadow); } - else s->setvariant(transparent ? 0 : -1, 16); + else s->setvariant(0, transparent ? 8 : 0); lightpassesused++; @@ -3117,8 +3246,8 @@ static void renderlightbatches(Shader *s, int stencilref, bool transparent, floa if(sx1 >= sx2 || sy1 >= sy2 || sz1 >= sz2) continue; } - if(n) setlightshader(s, n, baselight, shadowmap, spotlight, false, true); - else s->setvariant(0, 17); + if(n) setlightshader(s, n, baselight, shadowmap, spotlight, false, false, true); + else s->setvariant(0, 24); if(hasDBT && depthtestlights > 1) glDepthBounds_(sz1*0.5f + 0.5f, min(sz2*0.5f + 0.5f, depthtestlightsclamp)); lightquad(sz1, sx1, sy1, sx2, sy2, tilemask); @@ -3186,7 +3315,7 @@ void renderlights(float bsx1 = -1, float bsy1 = -1, float bsx2 = 1, float bsy2 = if(hasDBT && depthtestlights > 1) glEnable(GL_DEPTH_BOUNDS_TEST_EXT); - bool sunpass = !lighttilebatch || drawtex == DRAWTEX_MINIMAP || (!sunlight.iszero() && csmshadowmap && batchsunlight <= (gi && giscale && gidist ? 1 : 0)); + bool sunpass = !lighttilebatch || drawtex == DRAWTEX_MINIMAP || (csm.rendered && batchsunlight <= (gi && giscale && gidist ? 1 : 0)); if(sunpass) { if(depthtestlights && depth) { glDisable(GL_DEPTH_TEST); depth = false; } @@ -3201,7 +3330,7 @@ void renderlights(float bsx1 = -1, float bsy1 = -1, float bsx2 = 1, float bsy2 = } else { - renderlightbatches(s, stencilref, transparent, bsx1, bsy1, bsx2, bsy2, tilemask); + renderlightbatches(s, stencilref, transparent, bsx1, bsy1, bsx2, bsy2, tilemask, sunpass); } if(msaapass == 1 && ghasstencil) @@ -3255,6 +3384,11 @@ void rendervolumetric() glActiveTexture_(GL_TEXTURE4); glBindTexture(shadowatlastarget, shadowatlastex); if(usesmcomparemode()) setsmcomparemode(); else setsmnoncomparemode(); + if(smalpha > 1 && alphashadow && volumetricsmalphalights) + { + glActiveTexture_(GL_TEXTURE11); + glBindTexture(GL_TEXTURE_RECTANGLE, smfilter ? shadowfiltertex : shadowcolortex); + } glActiveTexture_(GL_TEXTURE0); GLOBALPARAMF(shadowatlasscale, 1.0f/shadowatlaspacker.w, 1.0f/shadowatlaspacker.h); @@ -3273,6 +3407,8 @@ void rendervolumetric() glEnable(GL_SCISSOR_TEST); + if(volderiv >= 0) glHint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, volderiv ? GL_NICEST : GL_FASTEST); + bool outside = true; loopv(lightorder) { @@ -3284,12 +3420,13 @@ void rendervolumetric() lightmatrix.scale(l.radius*lightradiustweak); GLOBALPARAM(lightmatrix, lightmatrix); + bool colorshadow = l.colorshadow() && l.shadowmap >= 0 && shadowmaps[l.shadowmap].transparent; if(l.spot > 0) { - volumetricshader->setvariant(0, l.shadowmap >= 0 ? 2 : 1); + volumetricshader->setvariant(0, l.shadowmap >= 0 ? (colorshadow ? 4 : 3) : 2); LOCALPARAM(spotparams, vec4(l.dir, 1/(1 - cos360(l.spot)))); } - else if(l.shadowmap >= 0) volumetricshader->setvariant(0, 0); + else if(l.shadowmap >= 0) volumetricshader->setvariant(0, colorshadow ? 1 : 0); else volumetricshader->set(); LOCALPARAM(lightpos, vec4(l.o, 1).div(l.radius)); @@ -3352,6 +3489,8 @@ void rendervolumetric() lightsphere::disable(); + if(volderiv >= 0) glHint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, GL_DONT_CARE); + if(depthtestlights) { glDepthMask(GL_TRUE); @@ -3558,7 +3697,7 @@ void collectlights() if(l.spot) { w = 1; h = 1; prec *= tan360(l.spot); lod = smspotprec; } else { w = 3; h = 2; lod = smcubeprec; } lod *= std::clamp(l.radius * prec / sqrtf(max(1.0f, l.dist/l.radius)), float(smminsize), float(smmaxsize)); - int size = std::clamp(int(ceil((lod * shadowatlaspacker.w) / SHADOWATLAS_SIZE)), 1, shadowatlaspacker.w / w); + int size = (std::clamp(int(ceil((lod * shadowatlaspacker.w) / SHADOWATLAS_SIZE)), 1, shadowatlaspacker.w / w) + smalign)&~smalign; w *= size; h *= size; @@ -3601,7 +3740,7 @@ struct batchrect : lightrect batchrect() {} batchrect(const lightinfo &l, ushort idx) : lightrect(l), - group((l.shadowmap < 0 ? BF_NOSHADOW : 0) | (l.spot > 0 ? BF_SPOTLIGHT : 0)), + group((l.shadowmap < 0 ? BF_NOSHADOW : (shadowmaps[l.shadowmap].transparent ? BF_SMALPHA : 0)) | (l.spot > 0 ? BF_SPOTLIGHT : 0)), idx(idx) {} }; @@ -3735,6 +3874,7 @@ void packlights() shadowmaps[l.shadowmap].light = -1; l.shadowmap = -1; } + l.batched = ~0; lightsoccluded++; continue; } @@ -3746,7 +3886,7 @@ void packlights() if(l.spot) { w = 1; h = 1; prec *= tan360(l.spot); lod = smspotprec; } else { w = 3; h = 2; lod = smcubeprec; } lod *= std::clamp(l.radius * prec / sqrtf(max(1.0f, l.dist/l.radius)), float(smminsize), float(smmaxsize)); - int size = std::clamp(int(ceil((lod * shadowatlaspacker.w) / SHADOWATLAS_SIZE)), 1, shadowatlaspacker.w / w); + int size = (std::clamp(int(ceil((lod * shadowatlaspacker.w) / SHADOWATLAS_SIZE)), 1, shadowatlaspacker.w / w) + smalign)&~smalign; w *= size; h *= size; ushort x = USHRT_MAX, y = USHRT_MAX; @@ -3758,12 +3898,11 @@ void packlights() else if(smcache) shadowcachefull = true; } + l.batched = batchrects.length(); batchrects.add(batchrect(l, i)); } lightsvisible = lightorder.length() - lightsoccluded; - - batchlights(); } static inline void nogiquad(int x, int y, int w, int h) @@ -4248,11 +4387,62 @@ void renderradiancehints() endtimer(rhcputimer); } +static void setupshadowtransparent() +{ + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glClearColor(1, 1, 1, 1); + + glDepthMask(GL_FALSE); + + glEnable(GL_BLEND); + glBlendFunc(GL_ZERO, GL_SRC_COLOR); +} + +static void cleanupshadowtransparent() +{ + glDisable(GL_BLEND); + glDepthMask(GL_TRUE); + + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); +} + +static inline bool clearshadowtransparent(int idx, int side) +{ + if(shadowtransparent&(1<= 0 && side > 0) + { + sidex = (side>>1)*sm.size; + sidey = (side&1)*sm.size; + } + glViewport(sm.x + sidex, sm.y + sidey, sm.size, sm.size); + glScissor(sm.x + sidex, sm.y + sidey, sm.size, sm.size); + glClear(GL_COLOR_BUFFER_BIT); + + shadowside = side; + + renderalphashadow(cullside); + + if(smfilter) shadowcolorblurs.add(idx * 6 + side); +} + void rendercsmshadowmaps() { if(csminoq && !debugshadowatlas && !inoq && shouldworkinoq()) return; + + csm.rendered = 0; + if(sunlight.iszero() || !csmshadowmap) return; + csm.rendered = 1; + if(inoq) { glBindFramebuffer_(GL_FRAMEBUFFER, shadowatlasfbo); @@ -4277,7 +4467,8 @@ void rendercsmshadowmaps() glEnable(GL_SCISSOR_TEST); - findshadowvas(); + findshadowvas(smalpha && alphashadow); + if(shadowtransparent) csm.rendered = 2; findshadowmms(); shadowmaskbatchedmodels(smdynshadow!=0); @@ -4285,9 +4476,10 @@ void rendercsmshadowmaps() loopi(csmsplits) if(csm.splits[i].idx >= 0) { - const shadowmapinfo &sm = shadowmaps[csm.splits[i].idx]; + const cascadedshadowmap::splitinfo &split = csm.splits[i]; + const shadowmapinfo &sm = shadowmaps[split.idx]; - shadowmatrix.mul(csm.splits[i].proj, csm.model); + shadowmatrix.mul(split.proj, csm.model); GLOBALPARAM(shadowmatrix, shadowmatrix); glViewport(sm.x, sm.y, sm.size, sm.size); @@ -4300,6 +4492,22 @@ void rendercsmshadowmaps() rendershadowmodelbatches(); } + if(shadowtransparent) + { + setupshadowtransparent(); + loopi(csmsplits) if(csm.splits[i].idx >= 0) + { + const cascadedshadowmap::splitinfo &split = csm.splits[i]; + if(clearshadowtransparent(split.idx, i)) continue; + + shadowmatrix.mul(split.proj, csm.model); + GLOBALPARAM(shadowmatrix, shadowmatrix); + + rendershadowtransparent(split.idx, i); + } + cleanupshadowtransparent(); + } + clearbatchedmapmodels(); glDisable(GL_SCISSOR_TEST); @@ -4345,7 +4553,7 @@ int calcshadowinfo(const extentity &e, vec &origin, float &radius, vec &spotloc, } lod *= smminsize; - int size = std::clamp(int(ceil((lod * shadowatlaspacker.w) / SHADOWATLAS_SIZE)), 1, shadowatlaspacker.w / w); + int size = (std::clamp(int(ceil((lod * shadowatlaspacker.w) / SHADOWATLAS_SIZE)), 1, shadowatlaspacker.w / w) + smalign)&~smalign; bias = border / float(size - border); return type; @@ -4409,7 +4617,12 @@ void rendershadowmaps(int offset = 0) shadowmesh *mesh = e ? findshadowmesh(l.ent, *e) : nullptr; - findshadowvas(); + findshadowvas(smalpha > 1 && alphashadow > (l.colorshadow() ? 0 : 1)); + if(shadowtransparent) + { + sm.transparent = shadowtransparent; + if(batchrects.inrange(l.batched)) batchrects[l.batched].group |= BF_SMALPHA; + } findshadowmms(); shadowmaskbatchedmodels(!(l.flags&L_NODYNSHADOW) && smdynshadow); @@ -4419,7 +4632,7 @@ void rendershadowmaps(int offset = 0) int cachemask = 0; if(smcache) { - int dynmask = smcache <= 1 ? batcheddynamicmodels() : 0; + int dynmask = smcache <= 1 ? (batcheddynamicmodels() | dynamicshadowvas()) : 0; cached = sm.cached; if(cached) { @@ -4450,12 +4663,19 @@ void rendershadowmaps(int offset = 0) shadowmatrix.mul(smprojmatrix, spotmatrix); GLOBALPARAM(shadowmatrix, shadowmatrix); - glCullFace((l.dir.z >= 0) == (smcullside != 0) ? GL_BACK : GL_FRONT); + glCullFace((l.dir.z >= 0) == !smcullside ? GL_FRONT : GL_BACK); shadowside = 0; if(mesh) rendershadowmesh(mesh); else rendershadowmapworld(); rendershadowmodelbatches(); + + if(shadowtransparent) + { + setupshadowtransparent(); + rendershadowtransparent(i, 0, (l.dir.z >= 0) == !smcullside); + cleanupshadowtransparent(); + } } else { @@ -4488,6 +4708,23 @@ void rendershadowmaps(int offset = 0) if(mesh) rendershadowmesh(mesh); else rendershadowmapworld(); rendershadowmodelbatches(); } + if(shadowtransparent) + { + setupshadowtransparent(); + loop(side, 6) if(sidemask&(1<> 2) ^ smcullside); + } + cleanupshadowtransparent(); + } } clearbatchedmapmodels(); @@ -4509,6 +4746,84 @@ void rendershadowmaps(int offset = 0) } } +static void filtershadowcolors() +{ + if(shadowfilterfbo) + { + glBindFramebuffer_(GL_FRAMEBUFFER, shadowfilterfbo); + glViewport(0, 0, shadowatlaspacker.w/2, shadowatlaspacker.h/2); + } + else glViewport(0, 0, shadowatlaspacker.w, shadowatlaspacker.h); + + glDepthMask(GL_FALSE); + + GLOBALPARAMF(shadowatlasscale, 1.0f/shadowatlaspacker.w, 1.0f/shadowatlaspacker.h); + + if(shadowcolorclears.length()) + { + SETSHADER(smalphaclear); + gle::defvertex(2); + gle::begin(GL_QUADS); + loopv(shadowcolorclears) + { + shadowmapinfo &sm = shadowmaps[shadowcolorclears[i] / 6]; + int side = shadowcolorclears[i] % 6; + + int x = sm.x, y = sm.y; + if(sm.light >= 0 && side > 0) + { + x += (side>>1)*sm.size; + y += (side&1)*sm.size; + } + + gle::attribf(x, y); + gle::attribf(x, y+sm.size); + gle::attribf(x+sm.size, y+sm.size); + gle::attribf(x+sm.size, y); + } + shadowcolorclears.setsize(0); + xtraverts += gle::end(); + } + + if(shadowcolorblurs.length()) + { + if(usegatherforsm()) + { + SETSHADER(smalphablur2d); + glBindTexture(GL_TEXTURE_2D, shadowcolortex); + } + else + { + SETSHADER(smalphablurrect); + glBindTexture(GL_TEXTURE_RECTANGLE, shadowcolortex); + } + gle::defvertex(2); + gle::deftexcoord0(4); + gle::begin(GL_QUADS); + loopv(shadowcolorblurs) + { + shadowmapinfo &sm = shadowmaps[shadowcolorblurs[i] / 6]; + int side = shadowcolorblurs[i] % 6; + + int x = sm.x, y = sm.y; + if(sm.light >= 0 && side > 0) + { + x += (side>>1)*sm.size; + y += (side&1)*sm.size; + } + + gle::attribf(x, y); gle::attribf(x, y, x+sm.size, y+sm.size); + gle::attribf(x, y+sm.size); gle::attribf(x, y, x+sm.size, y+sm.size); + gle::attribf(x+sm.size, y+sm.size); gle::attribf(x, y, x+sm.size, y+sm.size); + gle::attribf(x+sm.size, y); gle::attribf(x, y, x+sm.size, y+sm.size); + } + shadowcolorblurs.setsize(0); + xtraverts += gle::end(); + } + + glDepthMask(GL_TRUE); +} + void rendershadowatlas() { timer *smcputimer = begintimer("shadow map", false); @@ -4520,7 +4835,14 @@ void rendershadowatlas() if(debugshadowatlas) { glClearDepth(0); - glClear(GL_DEPTH_BUFFER_BIT); + if(shadowcolortex) + { + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glClearColor(0, 0, 0, 0); + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + } + else glClear(GL_DEPTH_BUFFER_BIT); glClearDepth(1); } @@ -4536,6 +4858,10 @@ void rendershadowatlas() glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + if(shadowcolorclears.length() || shadowcolorblurs.length()) filtershadowcolors(); + + batchlights(); + endtimer(smtimer); endtimer(smcputimer); } @@ -5003,6 +5329,7 @@ bool debuglights() { if(debugshadowatlas) viewshadowatlas(); else if(debugao) viewao(); + else if(debugbloom) viewbloom(); else if(debugdepth) viewdepth(); else if(debugstencil) viewstencil(); else if(debugrefract) viewrefract(); diff --git a/src/engine/renderlights.hh b/src/engine/renderlights.hh index f6d8b17..5bddab2 100644 --- a/src/engine/renderlights.hh +++ b/src/engine/renderlights.hh @@ -10,7 +10,7 @@ #define LIGHTTILE_MAXW 16 #define LIGHTTILE_MAXH 16 -enum { L_NOSHADOW = 1<<0, L_NODYNSHADOW = 1<<1, L_VOLUMETRIC = 1<<2, L_NOSPEC = 1<<3 }; +enum { L_NOSHADOW = 1<<0, L_NODYNSHADOW = 1<<1, L_VOLUMETRIC = 1<<2, L_NOSPEC = 1<<3, L_SMALPHA = 1<<4 }; /* only for the inlines below */ extern int lighttilealignw, lighttilealignh, lighttilevieww, lighttileviewh, lighttilew, lighttileh; diff --git a/src/engine/rendermodel.cc b/src/engine/rendermodel.cc index b65780d..0830415 100644 --- a/src/engine/rendermodel.cc +++ b/src/engine/rendermodel.cc @@ -130,6 +130,13 @@ static void mdlalphatest(float *cutoff) } COMMAND(mdlalphatest, "f"); +static void mdldither(int *dither) +{ + checkmdl; + loadingmodel->setdither(*dither != 0); +} +COMMAND(mdldither, "i"); + static void mdldepthoffset(int *offset) { checkmdl; @@ -483,7 +490,7 @@ void cleanupmodels() static void clearmodel(char *name) { model *m = models.find(name, nullptr); - if(!m) { conoutf("model %s is not loaded", name); return; } + if(!m) { conoutf(CON_WARN, "model %s is not loaded", name); return; } loopv(mapmodels) { mapmodelinfo &mmi = mapmodels[i]; diff --git a/src/engine/rendermodel.hh b/src/engine/rendermodel.hh index ad71884..d02e7fd 100644 --- a/src/engine/rendermodel.hh +++ b/src/engine/rendermodel.hh @@ -56,6 +56,7 @@ struct model virtual void setgloss(int gloss) {} virtual void setglow(float glow, float glowdelta, float glowpulse) {} virtual void setalphatest(float alpha) {} + virtual void setdither(bool dither) {} virtual void setfullbright(float fullbright) {} virtual void setcullface(int cullface) {} virtual void setcolor(const vec &color) {} @@ -94,7 +95,7 @@ struct model { collidecenter.z = collideradius.z = collideheight/2; } - rejectradius = collideradius.magnitude(); + rejectradius = vec(collidecenter).abs().add(collideradius).magnitude(); } center = collidecenter; radius = collideradius; diff --git a/src/engine/rendersky.cc b/src/engine/rendersky.cc index c77fa27..ad91990 100644 --- a/src/engine/rendersky.cc +++ b/src/engine/rendersky.cc @@ -380,18 +380,18 @@ void cleanupsky() } VARR(atmo, 0, 0, 1); -FVARR(atmoplanetsize, 1e-3f, 8, 1e3f); +FVARR(atmoplanetsize, 1e-3f, 1, 1e3f); FVARR(atmoheight, 1e-3f, 1, 1e3f); -FVARR(atmobright, 0, 4, 16); +FVARR(atmobright, 0, 1, 16); CVAR1R(atmosunlight, 0); FVARR(atmosunlightscale, 0, 1, 16); -FVARR(atmosundisksize, 0, 1, 10); +CVAR1R(atmosundisk, 0); +FVARR(atmosundisksize, 0, 12, 90); +FVARR(atmosundiskcorona, 0, 0.4f, 1); FVARR(atmosundiskbright, 0, 1, 16); -FVARR(atmohaze, 0, 0.03f, 1); -CVAR0R(atmohazefade, 0xAEACA9); -FVARR(atmohazefadescale, 0, 1, 1); -FVARR(atmoclarity, 0, 0.2f, 10); -FVARR(atmodensity, 1e-3f, 0.99f, 10); +FVARR(atmohaze, 0, 0.1f, 16); +FVARR(atmodensity, 0, 1, 16); +FVARR(atmoozone, 0, 1, 16); FVARR(atmoalpha, 0, 1, 1); static void drawatmosphere() @@ -403,34 +403,57 @@ static void drawatmosphere() sunmatrix.mul(invprojmatrix); LOCALPARAM(sunmatrix, sunmatrix); - LOCALPARAM(sunlight, (!atmosunlight.iszero() ? atmosunlight.tocolor().mul(atmosunlightscale) : sunlight.tocolor().mul(sunlightscale)).mul(atmobright*ldrscale)); + // optical depth scales for 3 different shells of atmosphere - air, haze, ozone + const float earthradius = 6371e3f, earthairheight = 8.4e3f, earthhazeheight = 1.25e3f, earthozoneheight = 50e3f; + float planetradius = earthradius*atmoplanetsize; + vec atmoshells = vec(earthairheight, earthhazeheight, earthozoneheight).mul(atmoheight).add(planetradius).square().sub(planetradius*planetradius); + LOCALPARAM(opticaldepthparams, vec4(atmoshells, planetradius)); + + // Henyey-Greenstein approximation, 1/(4pi) * (1 - g^2)/(1 + g^2 - 2gcos)]^1.5 + // Hoffman-Preetham variation uses (1-g)^2 instead of 1-g^2 which avoids excessive glare + // clamp values near 0 angle to avoid spotlight artifact inside sundisk + float gm = max(0.95f - 0.2f*atmohaze, 0.65f), miescale = pow((1-gm)*(1-gm)/(4*M_PI), -2.0f/3.0f); + LOCALPARAMF(mieparams, miescale*(1 + gm*gm), miescale*-2*gm, 1 - (1 - cosf(0.5f*atmosundisksize*(1 - atmosundiskcorona)*RAD))); + + static const vec lambda(680e-9f, 550e-9f, 450e-9f), + k(0.686f, 0.678f, 0.666f), + ozone(3.426f, 8.298f, 0.356f); + vec betar = vec(lambda).square().square().recip().mul(1.241e-30f/M_LN2 * atmodensity), + betam = vec(lambda).recip().square().mul(k).mul(9.072e-17f/M_LN2 * atmohaze), + betao = vec(ozone).mul(1.5e-7f/M_LN2 * atmoozone); + LOCALPARAM(betarayleigh, betar); + LOCALPARAM(betamie, betam); + LOCALPARAM(betaozone, betao); + + // extinction in direction of sun + float sunoffset = sunlightdir.z*planetradius; + vec sundepth = vec(atmoshells).add(sunoffset*sunoffset).sqrt().sub(sunoffset); + vec sunweight = vec(betar).mul(sundepth.x).madd(betam, sundepth.y).madd(betao, sundepth.z - sundepth.x); + vec sunextinction = vec(sunweight).neg().exp2(); + vec suncolor = !atmosunlight.iszero() ? atmosunlight.tocolor().mul(atmosunlightscale) : sunlight.tocolor().mul(sunlightscale); + // assume sunlight color is gamma encoded, so decode to linear light, then apply extinction + extern float hdrgamma; + vec sunscale = vec(suncolor).mul(ldrscale).pow(hdrgamma).mul(atmobright * 16).mul(sunextinction); + float maxsunweight = max(max(sunweight.x, sunweight.y), sunweight.z); + if(maxsunweight > 127) sunweight.mul(127/maxsunweight); + sunweight.add(1e-4f); + LOCALPARAM(sunweight, sunweight); + LOCALPARAM(sunlight, vec4(sunscale, atmoalpha)); LOCALPARAM(sundir, sunlightdir); - vec sundiskparams; - sundiskparams.y = -(1 - 0.0075f * atmosundisksize); - sundiskparams.x = 1/(1 + sundiskparams.y); - sundiskparams.y *= sundiskparams.x; - sundiskparams.z = atmosundiskbright; - LOCALPARAM(sundiskparams, sundiskparams); + // invert extinction at zenith to get an approximation of how bright the sun disk should be + vec zenithdepth = vec(atmoshells).add(planetradius*planetradius).sqrt().sub(planetradius); + vec zenithweight = vec(betar).mul(zenithdepth.x).madd(betam, zenithdepth.y).madd(betao, zenithdepth.z - zenithdepth.x); + vec zenithextinction = vec(zenithweight).sub(sunweight).exp2(); + vec diskcolor = (!atmosundisk.iszero() ? atmosundisk.tocolor() : suncolor).mul(ldrscale).pow(hdrgamma).mul(zenithextinction).mul(atmosundiskbright * 4); + LOCALPARAM(sundiskcolor, diskcolor); - const float earthradius = 6.371e6f, earthatmoheight = 0.1e6f; - float planetradius = earthradius*atmoplanetsize, atmoradius = planetradius + earthatmoheight*atmoheight; - LOCALPARAMF(atmoradius, planetradius, atmoradius*atmoradius, atmoradius*atmoradius - planetradius*planetradius); - - float gm = (1 - atmohaze)*0.2f + 0.75f; - LOCALPARAMF(gm, gm); - - vec lambda(680e-9f, 550e-9f, 450e-9f), - betar = vec(lambda).square().square().recip().mul(1.86e-31f / atmodensity), - betam = vec(lambda).recip().mul(2*M_PI).square().mul(atmohazefade.tocolor().mul(atmohazefadescale)).mul(1.36e-19f * max(atmohaze, 1e-3f)), - betarm = vec(betar).div(1+atmoclarity).add(betam); - betar.div(betarm).mul(3/(16*M_PI)); - betam.div(betarm).mul((1-gm)*(1-gm)/(4*M_PI)); - LOCALPARAM(betar, betar); - LOCALPARAM(betam, betam); - LOCALPARAM(betarm, betarm.div(M_LN2)); - - LOCALPARAMF(atmoalpha, atmoalpha); + // convert from view cosine into mu^2 for limb darkening, where mu = sqrt(1 - sin^2) and sin^2 = 1 - cos^2, thus mu^2 = 1 - (1 - cos^2*scale) + // convert corona offset into scale for mu^2, where sin = (1-corona) and thus mu^2 = 1 - (1-corona^2) + float sundiskscale = sinf(0.5f*atmosundisksize*RAD); + float coronamu = 1 - (1-atmosundiskcorona)*(1-atmosundiskcorona); + if(sundiskscale > 0) LOCALPARAMF(sundiskparams, 1.0f/(sundiskscale*sundiskscale), 1.0f/max(coronamu, 1e-3f)); + else LOCALPARAMF(sundiskparams, 0, 0); gle::defvertex(); gle::begin(GL_TRIANGLE_STRIP); diff --git a/src/engine/renderva.cc b/src/engine/renderva.cc index df508e6..29b1fd3 100644 --- a/src/engine/renderva.cc +++ b/src/engine/renderva.cc @@ -667,10 +667,10 @@ void renderoutline() drawvatris(va, 3*va->tris, 0); xtravertsva += va->verts; } - if(va->alphaback || va->alphafront || va->refract) + if(va->alphatris) { - drawvatris(va, 3*(va->alphabacktris + va->alphafronttris + va->refracttris), 3*(va->tris + va->blendtris)); - xtravertsva += 3*(va->alphabacktris + va->alphafronttris + va->refracttris); + drawvatris(va, 3*va->alphatris, 3*(va->tris + va->blendtris)); + xtravertsva += 3*va->alphatris; } prev = va; @@ -907,7 +907,7 @@ VAR(smnodraw, 0, 0, 1); vec shadoworigin(0, 0, 0), shadowdir(0, 0, 0); float shadowradius = 0, shadowbias = 0; -int shadowside = 0, shadowspot = 0; +int shadowside = 0, shadowspot = 0, shadowtransparent = 0; static vtxarray *shadowva = nullptr; @@ -941,7 +941,14 @@ static void sortshadowvas() } } -static void findshadowvas(vector &vas) +static inline void getshadowvabb(vtxarray &v, ivec &bbmin, ivec &bbmax, bool transparent = false) +{ + if(v.children.length() || v.mapmodels.length()) { bbmin = v.bbmin; bbmax = v.bbmax; } + else { bbmin = v.geommin; bbmax = v.geommax; } + if(transparent && v.alphatris) { bbmin.min(v.alphamin); bbmax.max(v.alphamax); } +} + +static void findshadowvas(vector &vas, bool transparent) { loopv(vas) { @@ -949,29 +956,46 @@ static void findshadowvas(vector &vas) float dist = vadist(&v, shadoworigin); if(dist < shadowradius || !smdistcull) { - v.shadowmask = !smbbcull ? 0x3F : (v.children.length() || v.mapmodels.length() ? - calcbbsidemask(v.bbmin, v.bbmax, shadoworigin, shadowradius, shadowbias) : - calcbbsidemask(v.geommin, v.geommax, shadoworigin, shadowradius, shadowbias)); + ivec bbmin, bbmax; + getshadowvabb(v, bbmin, bbmax, transparent); + v.shadowmask = smbbcull ? 0x3F : calcbbsidemask(bbmin, bbmax, shadoworigin, shadowradius, shadowbias); + if(transparent) + { + if(v.alphatris) + { + v.shadowtransparent = v.shadowmask & calcbbsidemask(v.alphamin, v.alphamax, shadoworigin, shadowradius, shadowbias); + shadowtransparent |= v.shadowtransparent; + } + else v.shadowtransparent = 0; + } addshadowva(&v, dist); - if(v.children.length()) findshadowvas(v.children); + if(v.children.length()) findshadowvas(v.children, transparent); } } } -static void findcsmshadowvas(vector &vas) +static void findcsmshadowvas(vector &vas, bool transparent) { loopv(vas) { vtxarray &v = *vas[i]; ivec bbmin, bbmax; - if(v.children.length() || v.mapmodels.length()) { bbmin = v.bbmin; bbmax = v.bbmax; } - else { bbmin = v.geommin; bbmax = v.geommax; } + getshadowvabb(v, bbmin, bbmax, transparent); v.shadowmask = calcbbcsmsplits(bbmin, bbmax); if(v.shadowmask) { + if(transparent) + { + if(v.alphatris) + { + v.shadowtransparent = v.shadowmask & calcbbcsmsplits(v.alphamin, v.alphamax); + shadowtransparent |= v.shadowtransparent; + } + else v.shadowtransparent = 0; + } float dist = shadowdir.project_bb(bbmin, bbmax) - shadowbias; addshadowva(&v, dist); - if(v.children.length()) findcsmshadowvas(v.children); + if(v.children.length()) findcsmshadowvas(v.children, transparent); } } } @@ -982,8 +1006,7 @@ static void findrsmshadowvas(vector &vas) { vtxarray &v = *vas[i]; ivec bbmin, bbmax; - if(v.children.length() || v.mapmodels.length()) { bbmin = v.bbmin; bbmax = v.bbmax; } - else { bbmin = v.geommin; bbmax = v.geommax; } + getshadowvabb(v, bbmin, bbmax); v.shadowmask = calcbbrsmsplits(bbmin, bbmax); if(v.shadowmask) { @@ -994,7 +1017,7 @@ static void findrsmshadowvas(vector &vas) } } -static void findspotshadowvas(vector &vas) +static void findspotshadowvas(vector &vas, bool transparent) { loopv(vas) { @@ -1002,31 +1025,42 @@ static void findspotshadowvas(vector &vas) float dist = vadist(&v, shadoworigin); if(dist < shadowradius || !smdistcull) { - v.shadowmask = !smbbcull || (v.children.length() || v.mapmodels.length() ? - bbinsidespot(shadoworigin, shadowdir, shadowspot, v.bbmin, v.bbmax) : - bbinsidespot(shadoworigin, shadowdir, shadowspot, v.geommin, v.geommax)) ? 1 : 0; + ivec bbmin, bbmax; + getshadowvabb(v, bbmin, bbmax, transparent); + bool insidespot = bbinsidespot(shadoworigin, shadowdir, shadowspot, bbmin, bbmax); + v.shadowmask = !smbbcull || insidespot ? 1 : 0; + if(transparent) + { + if(v.alphatris) + { + v.shadowtransparent = v.shadowmask && bbinsidespot(shadoworigin, shadowdir, shadowspot, v.alphamin, v.alphamax) ? 1 : 0; + shadowtransparent |= v.shadowtransparent; + } + else v.shadowtransparent = 0; + } addshadowva(&v, dist); - if(v.children.length()) findspotshadowvas(v.children); + if(v.children.length()) findspotshadowvas(v.children, transparent); } } } -void findshadowvas() +void findshadowvas(bool transparent) { + shadowtransparent = 0; memset(vasort, 0, sizeof(vasort)); switch(shadowmapping) { case SM_REFLECT: findrsmshadowvas(varoot); break; - case SM_CUBEMAP: findshadowvas(varoot); break; - case SM_CASCADE: findcsmshadowvas(varoot); break; - case SM_SPOT: findspotshadowvas(varoot); break; + case SM_CUBEMAP: findshadowvas(varoot, transparent); break; + case SM_CASCADE: findcsmshadowvas(varoot, transparent); break; + case SM_SPOT: findspotshadowvas(varoot, transparent); break; } sortshadowvas(); } void rendershadowmapworld() { - SETSHADER(shadowmapworld); + SETSHADER(smworld); gle::enablevertex(); @@ -1133,6 +1167,7 @@ struct renderstate { bool colormask, depthmask; int alphaing; + bool shadowing; GLuint vbuf; bool vattribs, vquery; vec colorscale; @@ -1193,6 +1228,7 @@ enum RENDERPASS_Z, RENDERPASS_CAUSTICS, RENDERPASS_GBUFFER_BLEND, + RENDERPASS_SMALPHA, RENDERPASS_RSM, RENDERPASS_RSM_BLEND }; @@ -1351,7 +1387,7 @@ static void changevbuf(renderstate &cur, int pass, vtxarray *va) vertex *vdata = (vertex *)0; gle::vertexpointer(sizeof(vertex), vdata->pos.v); - if(pass==RENDERPASS_GBUFFER || pass==RENDERPASS_RSM) + if(pass==RENDERPASS_GBUFFER || pass==RENDERPASS_RSM || pass==RENDERPASS_SMALPHA) { gle::normalpointer(sizeof(vertex), vdata->norm.v, GL_BYTE); gle::texcoord0pointer(sizeof(vertex), vdata->tc.v); @@ -1361,7 +1397,7 @@ static void changevbuf(renderstate &cur, int pass, vtxarray *va) static void changebatchtmus(renderstate &cur, int pass, geombatch &b) { - if(b.vslot.slot->shader->type&SHADER_ENVMAP && b.es.envmap!=EMID_CUSTOM) + if(b.vslot.slot->shader && b.vslot.slot->shader->type&SHADER_ENVMAP && b.es.envmap!=EMID_CUSTOM) { GLuint emtex = lookupenvmap(b.es.envmap); if(cur.textures[TEX_ENVMAP]!=emtex) @@ -1416,7 +1452,7 @@ static inline void bindslottex(renderstate &cur, int type, Texture *tex, GLenum static void changeslottmus(renderstate &cur, int pass, Slot &slot, VSlot &vslot) { - if(pass==RENDERPASS_GBUFFER || pass==RENDERPASS_RSM) + if(pass==RENDERPASS_GBUFFER || pass==RENDERPASS_RSM || pass==RENDERPASS_SMALPHA) { Texture *diffuse = slot.sts.empty() ? notexture : slot.sts[0].t; bindslottex(cur, TEX_DIFFUSE, diffuse); @@ -1436,23 +1472,22 @@ static void changeslottmus(renderstate &cur, int pass, Slot &slot, VSlot &vslot) if(cur.alphaing) { float alpha = cur.alphaing > 1 ? vslot.alphafront : vslot.alphaback; - if(cur.alphascale != alpha) + if(cur.colorscale != vslot.colorscale || cur.alphascale != alpha) { - cur.alphascale = alpha; - cur.refractscale = 0; - goto changecolorparams; - } - if(cur.colorscale != vslot.colorscale) - { - changecolorparams: cur.colorscale = vslot.colorscale; - GLOBALPARAMF(colorparams, alpha*vslot.colorscale.x, alpha*vslot.colorscale.y, alpha*vslot.colorscale.z, alpha); + cur.alphascale = alpha; + if(pass == RENDERPASS_SMALPHA) + { + extern float alphashadowscale; + GLOBALPARAMF(colorparams, alphashadowscale*vslot.colorscale.x, alphashadowscale*vslot.colorscale.y, alphashadowscale*vslot.colorscale.z, alpha); + } + else GLOBALPARAMF(colorparams, alpha*vslot.colorscale.x, alpha*vslot.colorscale.y, alpha*vslot.colorscale.z, alpha); } if(cur.alphaing > 1 && vslot.refractscale > 0 && (cur.refractscale != vslot.refractscale || cur.refractcolor != vslot.refractcolor)) { cur.refractscale = vslot.refractscale; cur.refractcolor = vslot.refractcolor; - float refractscale = 0.5f/ldrscale*(1-alpha); + float refractscale = 0.5f/ldrscale; GLOBALPARAMF(refractparams, vslot.refractcolor.x*refractscale, vslot.refractcolor.y*refractscale, vslot.refractcolor.z*refractscale, vslot.refractscale*viewh); } } @@ -1547,7 +1582,13 @@ static inline void changeshader(renderstate &cur, int pass, geombatch &b) { VSlot &vslot = b.vslot; Slot &slot = *vslot.slot; - if(pass == RENDERPASS_RSM) + if(pass == RENDERPASS_SMALPHA) + { + extern Shader *smalphaworldshader; + if(slot.texmask&(1<setvariant(0, slot.texmask&(1<set(slot, vslot); + } + else if(pass == RENDERPASS_RSM) { extern Shader *rsmworldshader; if(b.es.layer&LAYER_BOTTOM) rsmworldshader->setvariant(0, 0, slot, vslot); @@ -1612,7 +1653,8 @@ static void renderbatches(renderstate &cur, int pass) curbatch = b.next; if(cur.vbuf != b.va->vbuf) changevbuf(cur, pass, b.va); - if(pass == RENDERPASS_GBUFFER || pass == RENDERPASS_RSM) changebatchtmus(cur, pass, b); + if(pass == RENDERPASS_GBUFFER || pass == RENDERPASS_RSM || pass == RENDERPASS_SMALPHA) + changebatchtmus(cur, pass, b); if(cur.vslot != &b.vslot) { changeslottmus(cur, pass, *b.vslot.slot, b.vslot); @@ -1647,7 +1689,7 @@ static void renderzpass(renderstate &cur, vtxarray *va) { firsttex += va->texs + va->blends; offset += 3*(va->tris + va->blendtris); - numtris = va->alphabacktris + va->alphafronttris + va->refracttris; + numtris = va->alphatris; xtravertsva += 3*numtris; } else xtravertsva += va->verts; @@ -1708,6 +1750,11 @@ static void renderva(renderstate &cur, vtxarray *va, int pass = RENDERPASS_GBUFF if(doquery) endvaquery(va, ); break; + case RENDERPASS_SMALPHA: + mergetexs(cur, va); + if(!batchgeom && geombatches.length()) renderbatches(cur, pass); + break; + case RENDERPASS_RSM: mergetexs(cur, va); if(!batchgeom && geombatches.length()) renderbatches(cur, pass); @@ -1879,6 +1926,13 @@ void rendergeom() } } +int dynamicshadowvas() +{ + int vis = 0; + for(vtxarray *va = shadowva; va; va = va->rnext) if(va->dynalphatexs) vis |= va->shadowtransparent; + return vis; +} + int dynamicshadowvabounds(int mask, vec &bbmin, vec &bbmax) { int vis = 0; @@ -1974,7 +2028,7 @@ int findalphavas() alphafrontsx2 = alphafrontsy2 = alphabacksx2 = alphabacksy2 = alpharefractsx2 = alpharefractsy2 = -1; alphabackvas = alpharefractvas = 0; memset(alphatiles, 0, sizeof(alphatiles)); - for(vtxarray *va = visibleva; va; va = va->next) if(va->alphabacktris || va->alphafronttris || va->refracttris) + for(vtxarray *va = visibleva; va; va = va->next) if(va->alphatris) { if(va->occluded >= OCCLUDE_BB) continue; if(va->occluded >= OCCLUDE_GEOM && pvsoccluded(va->alphamin, va->alphamax)) continue; @@ -2063,6 +2117,36 @@ void renderalphageom(int side) cleanupgeom(cur); } +void renderalphashadow(bool cullside) +{ + resetbatches(); + + renderstate cur; + cur.alphascale = -1; + + setupgeom(cur); + + glCullFace(cullside ? GL_FRONT : GL_BACK); + + cur.alphaing = 1; + for(vtxarray *va = shadowva; va; va = va->rnext) + if(va->shadowtransparent&(1<alphabacktris) + renderva(cur, va, RENDERPASS_SMALPHA); + if(geombatches.length()) renderbatches(cur, RENDERPASS_SMALPHA); + + glCullFace(cullside ? GL_BACK : GL_FRONT); + + cur.alphaing = 2; + for(vtxarray *va = shadowva; va; va = va->rnext) + if(va->shadowtransparent&(1<alphatris) + renderva(cur, va, RENDERPASS_SMALPHA); + if(geombatches.length()) renderbatches(cur, RENDERPASS_SMALPHA); + + glCullFace(GL_BACK); + + cleanupgeom(cur); +} + CVARP(explicitskycolour, 0x800080); bool renderexplicitsky(bool outline) @@ -2738,7 +2822,7 @@ void rendershadowmesh(shadowmesh *m) int draw = m->draws[shadowside]; if(draw < 0) return; - SETSHADER(shadowmapworld); + SETSHADER(smworld); gle::enablevertex(); diff --git a/src/engine/renderva.hh b/src/engine/renderva.hh index 3e69d13..e776776 100644 --- a/src/engine/renderva.hh +++ b/src/engine/renderva.hh @@ -38,11 +38,12 @@ int cullfrustumsides(const vec &lightpos, float lightradius, float size, float b extern vec shadoworigin, shadowdir; extern float shadowradius, shadowbias; -extern int shadowside, shadowspot; +extern int shadowside, shadowspot, shadowtransparent; -extern void findshadowvas(); -extern void findshadowmms(); +void findshadowvas(bool transparent = false); +void findshadowmms(); +int dynamicshadowvas(); int dynamicshadowvabounds(int mask, vec &bbmin, vec &bbmax); void rendershadowmapworld(); @@ -64,6 +65,7 @@ extern uint alphatiles[LIGHTTILE_MAXH]; int findalphavas(); void renderrefractmask(); void renderalphageom(int side); +void renderalphashadow(bool cullside = false); bool renderexplicitsky(bool outline = false); diff --git a/src/engine/shader.cc b/src/engine/shader.cc index f7cb2c0..d42bbb8 100644 --- a/src/engine/shader.cc +++ b/src/engine/shader.cc @@ -765,6 +765,8 @@ void Shader::cleanup(bool full) owner = nullptr; } +bool Shader::isnull(const Shader *s) { return !s; } + static void genattriblocs(Shader &s, const char *vs, const char *ps, Shader *reusevs, Shader *reuseps) { static int len = strlen("//:attrib"); diff --git a/src/engine/shader.hh b/src/engine/shader.hh index 7cc6bb4..b7ed1ca 100644 --- a/src/engine/shader.hh +++ b/src/engine/shader.hh @@ -203,7 +203,7 @@ struct Shader bool isdynamic() const { return (type&SHADER_DYNAMIC)!=0; } - static inline bool isnull(const Shader *s) { return !s; } + static bool isnull(const Shader *s); bool isnull() const { return isnull(this); } diff --git a/src/engine/skelmodel.hh b/src/engine/skelmodel.hh index 0770e73..519d147 100644 --- a/src/engine/skelmodel.hh +++ b/src/engine/skelmodel.hh @@ -1657,11 +1657,11 @@ template struct skelcommands : modelcommandsaddpart(); mdl.meshes = MDL::loading->sharemeshes(path(filename), skelname[0] ? skelname : nullptr, *smooth > 0 ? cosf(std::clamp(*smooth, 0.0f, 180.0f)*RAD) : 2); - if(!mdl.meshes) conoutf("could not load %s", filename); + if(!mdl.meshes) conoutf(CON_ERROR, "could not load %s", filename); else { if(mdl.meshes && ((meshgroup *)mdl.meshes)->skel->numbones > 0) mdl.disablepitch(); @@ -1672,7 +1672,7 @@ template struct skelcommands : modelcommandsparts.empty()) { conoutf("not loading an %s", MDL::formatname()); return; } + if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; } part &mdl = *(part *)MDL::loading->parts.last(); int i = mdl.meshes ? ((meshgroup *)mdl.meshes)->skel->findbone(name) : -1; if(i >= 0) @@ -1685,12 +1685,12 @@ template struct skelcommands : modelcommandsskel->addtag(tagname, i, m); return; } - conoutf("could not find bone %s for tag %s", name, tagname); + conoutf(CON_ERROR, "could not find bone %s for tag %s", name, tagname); } static void setpitch(char *name, float *pitchscale, float *pitchoffset, float *pitchmin, float *pitchmax) { - if(!MDL::loading || MDL::loading->parts.empty()) { conoutf("not loading an %s", MDL::formatname()); return; } + if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; } part &mdl = *(part *)MDL::loading->parts.last(); if(name[0]) @@ -1713,7 +1713,7 @@ template struct skelcommands : modelcommands struct skelcommands : modelcommandsparts.empty()) { conoutf("\frnot loading an %s", MDL::formatname()); return; } + if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; } part &mdl = *(part *)MDL::loading->parts.last(); if(!mdl.meshes) return; defformatstring(filename, "%s/%s", MDL::dir, animfile); animspec *sa = ((meshgroup *)mdl.meshes)->loadanim(path(filename)); - if(!sa) { conoutf("\frcould not load %s anim file %s", MDL::formatname(), filename); return; } + if(!sa) { conoutf(CON_ERROR, "could not load %s anim file %s", MDL::formatname(), filename); return; } skeleton *skel = ((meshgroup *)mdl.meshes)->skel; int bone = skel ? skel->findbone(name) : -1; if(bone < 0) { - conoutf("\frcould not find bone %s to pitch target", name); + conoutf(CON_ERROR, "could not find bone %s to pitch target", name); return; } loopv(skel->pitchtargets) if(skel->pitchtargets[i].bone == bone) return; @@ -1756,14 +1756,14 @@ template struct skelcommands : modelcommandsparts.empty()) { conoutf("\frnot loading an %s", MDL::formatname()); return; } + if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; } part &mdl = *(part *)MDL::loading->parts.last(); if(!mdl.meshes) return; skeleton *skel = ((meshgroup *)mdl.meshes)->skel; int bone = skel ? skel->findbone(name) : -1; if(bone < 0) { - conoutf("\frcould not find bone %s to pitch correct", name); + conoutf(CON_ERROR, "could not find bone %s to pitch correct", name); return; } if(skel->findpitchcorrect(bone) >= 0) return; @@ -1771,7 +1771,7 @@ template struct skelcommands : modelcommands= 0) loopv(skel->pitchtargets) if(skel->pitchtargets[i].bone == targetbone) { target = i; break; } if(target < 0) { - conoutf("\frcould not find pitch target %s to pitch correct %s", targetname, name); + conoutf(CON_ERROR, "could not find pitch target %s to pitch correct %s", targetname, name); return; } pitchcorrect c; @@ -1787,18 +1787,18 @@ template struct skelcommands : modelcommandsparts.empty()) { conoutf("not loading an %s", MDL::formatname()); return; } + if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; } vector anims; //game::findanims(anim, anims); - if(anims.empty()) conoutf("could not find animation %s", anim); + if(anims.empty()) conoutf(CON_ERROR, "could not find animation %s", anim); else { part *p = (part *)MDL::loading->parts.last(); if(!p->meshes) return; defformatstring(filename, "%s/%s", MDL::dir, animfile); animspec *sa = ((meshgroup *)p->meshes)->loadanim(path(filename)); - if(!sa) conoutf("could not load %s anim file %s", MDL::formatname(), filename); + if(!sa) conoutf(CON_ERROR, "could not load %s anim file %s", MDL::formatname(), filename); else loopv(anims) { int start = sa->frame, end = sa->range; @@ -1814,7 +1814,7 @@ template struct skelcommands : modelcommandsparts.empty()) { conoutf("not loading an %s", MDL::formatname()); return; } + if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; } part *p = (part *)MDL::loading->parts.last(); @@ -1825,32 +1825,32 @@ template struct skelcommands : modelcommandsmeshes ? ((meshgroup *)p->meshes)->skel->findbone(bonestr[0]=='!' ? bonestr+1 : bonestr) : -1; - if(bone<0) { conoutf("could not find bone %s for anim part mask [%s]", bonestr, maskstr); bonestrs.deletearrays(); return; } + if(bone<0) { conoutf(CON_ERROR, "could not find bone %s for anim part mask [%s]", bonestr, maskstr); bonestrs.deletearrays(); return; } bonemask.add(bone | (bonestr[0]=='!' ? BONEMASK_NOT : 0)); } bonestrs.deletearrays(); bonemask.sort(); if(bonemask.length()) bonemask.add(BONEMASK_END); - if(!p->addanimpart(bonemask.getbuf())) conoutf("too many animation parts"); + if(!p->addanimpart(bonemask.getbuf())) conoutf(CON_ERROR, "too many animation parts"); } static void setadjust(char *name, float *yaw, float *pitch, float *roll, float *tx, float *ty, float *tz) { - if(!MDL::loading || MDL::loading->parts.empty()) { conoutf("not loading an %s", MDL::formatname()); return; } + if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; } part &mdl = *(part *)MDL::loading->parts.last(); if(!name[0]) return; int i = mdl.meshes ? ((meshgroup *)mdl.meshes)->skel->findbone(name) : -1; - if(i < 0) { conoutf("could not find bone %s to adjust", name); return; } + if(i < 0) { conoutf(CON_ERROR, "could not find bone %s to adjust", name); return; } while(!MDL::adjustments.inrange(i)) MDL::adjustments.add(skeladjustment(0, 0, 0, vec(0, 0, 0))); MDL::adjustments[i] = skeladjustment(*yaw, *pitch, *roll, vec(*tx/4, *ty/4, *tz/4)); } static void sethitzone(int *id, char *maskstr) { - if(!MDL::loading || MDL::loading->parts.empty()) { conoutf("not loading an %s", MDL::formatname()); return; } - if(*id >= 0x80) { conoutf("invalid hit zone id %d", *id); return; } + if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; } + if(*id >= 0x80) { conoutf(CON_ERROR, "invalid hit zone id %d", *id); return; } part *p = (part *)MDL::loading->parts.last(); meshgroup *m = (meshgroup *)p->meshes; @@ -1863,7 +1863,7 @@ template struct skelcommands : modelcommandsmeshes ? ((meshgroup *)p->meshes)->skel->findbone(bonestr[0]=='!' ? bonestr+1 : bonestr) : -1; - if(bone<0) { conoutf("could not find bone %s for hit zone mask [%s]", bonestr, maskstr); bonestrs.deletearrays(); return; } + if(bone<0) { conoutf(CON_ERROR, "could not find bone %s for hit zone mask [%s]", bonestr, maskstr); bonestrs.deletearrays(); return; } bonemask.add(bone | (bonestr[0]=='!' ? BONEMASK_NOT : 0)); } bonestrs.deletearrays(); diff --git a/src/engine/texture.cc b/src/engine/texture.cc index 3caee97..89dbeda 100644 --- a/src/engine/texture.cc +++ b/src/engine/texture.cc @@ -746,7 +746,9 @@ static GLenum compressedformat(GLenum format, int w, int h, int force = 0) { if(usetexcompress && texcompress && force >= 0 && (force || max(w, h) >= texcompress)) switch(format) { + case GL_R3_G3_B2: case GL_RGB5: + case GL_RGB565: case GL_RGB8: case GL_RGB: return usetexcompress > 1 ? GL_COMPRESSED_RGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB; case GL_RGB5_A1: return usetexcompress > 1 ? GL_COMPRESSED_RGBA_S3TC_DXT1_EXT : GL_COMPRESSED_RGBA; @@ -763,6 +765,36 @@ static GLenum compressedformat(GLenum format, int w, int h, int force = 0) return format; } +static GLenum uncompressedformat(GLenum format) +{ + switch(format) + { + case GL_COMPRESSED_ALPHA: + return GL_ALPHA; + case GL_COMPRESSED_LUMINANCE: + case GL_COMPRESSED_LUMINANCE_LATC1_EXT: + return GL_LUMINANCE; + case GL_COMPRESSED_LUMINANCE_ALPHA: + case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: + return GL_LUMINANCE_ALPHA; + case GL_COMPRESSED_RED: + case GL_COMPRESSED_RED_RGTC1: + return GL_RED; + case GL_COMPRESSED_RG: + case GL_COMPRESSED_RG_RGTC2: + return GL_RG; + case GL_COMPRESSED_RGB: + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + return GL_RGB; + case GL_COMPRESSED_RGBA: + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + return GL_RGBA; + } + return GL_FALSE; +} + static int formatsize(GLenum format) { switch(format) @@ -778,6 +810,18 @@ static int formatsize(GLenum format) } } +static GLenum sizedformat(GLenum format) +{ + switch(format) + { + case GL_RED: return GL_R8; + case GL_RG: return GL_RG8; + case GL_RGB: return GL_RGB8; + case GL_RGBA: return GL_RGBA8; + } + return format; +} + VARFP(usenp2, 0, 1, 1, initwarning("texture quality", INIT_LOAD)); static void resizetexture(int w, int h, bool mipmap, bool canreduce, GLenum target, int compress, int &tw, int &th) @@ -811,7 +855,16 @@ static void resizetexture(int w, int h, bool mipmap, bool canreduce, GLenum targ } } -static void uploadtexture(GLenum target, GLenum internal, int tw, int th, GLenum format, GLenum type, const void *pixels, int pw, int ph, int pitch, bool mipmap) +static GLuint mipmapfbo[2] = { 0, 0 }; + +void cleanupmipmaps() +{ + if(mipmapfbo[0]) { glDeleteFramebuffers_(2, mipmapfbo); memset(mipmapfbo, 0, sizeof(mipmapfbo)); } +} + +VARFP(gpumipmap, 0, 1, 1, cleanupmipmaps()); + +static void uploadtexture(int tnum, GLenum target, GLenum internal, int tw, int th, GLenum format, GLenum type, const void *pixels, int pw, int ph, int pitch, bool mipmap, bool prealloc) { int bpp = formatsize(format), row = 0, rowalign = 0; if(!pitch) pitch = pw*bpp; @@ -833,30 +886,57 @@ static void uploadtexture(GLenum target, GLenum internal, int tw, int th, GLenum loopi(th) memcpy(&buf[i*tw*bpp], &((uchar *)pixels)[i*pitch], tw*bpp); } } - for(int level = 0, align = 0;; level++) + bool shouldgpumipmap = pixels && mipmap && max(tw, th) > 1 && gpumipmap && hasFBB && !uncompressedformat(internal); + for(int level = 0, align = 0, mw = tw, mh = th;; level++) { uchar *src = buf ? buf : (uchar *)pixels; - if(buf) pitch = tw*bpp; + if(buf) pitch = mw*bpp; int srcalign = row > 0 ? rowalign : texalign(src, pitch, 1); if(align != srcalign) glPixelStorei(GL_UNPACK_ALIGNMENT, align = srcalign); if(row > 0) glPixelStorei(GL_UNPACK_ROW_LENGTH, row); - if(target==GL_TEXTURE_1D) glTexImage1D(target, level, internal, tw, 0, format, type, src); - else glTexImage2D(target, level, internal, tw, th, 0, format, type, src); + if(!prealloc) glTexImage2D(target, level, internal, mw, mh, 0, format, type, src); + else if(src) glTexSubImage2D(target, level, 0, 0, mw, mh, format, type, src); if(row > 0) glPixelStorei(GL_UNPACK_ROW_LENGTH, row = 0); - if(!mipmap || max(tw, th) <= 1) break; - int srcw = tw, srch = th; - if(tw > 1) tw /= 2; - if(th > 1) th /= 2; + if(!mipmap || shouldgpumipmap || max(mw, mh) <= 1) break; + int srcw = mw, srch = mh; + if(mw > 1) mw /= 2; + if(mh > 1) mh /= 2; if(src) { - if(!buf) buf = new uchar[tw*th*bpp]; - scaletexture(src, srcw, srch, bpp, pitch, buf, tw, th); + if(!buf) buf = new uchar[mw*mh*bpp]; + scaletexture(src, srcw, srch, bpp, pitch, buf, mw, mh); } } if(buf) delete[] buf; + if(shouldgpumipmap) + { + GLint fbo = 0; + if(!inbetweenframes || drawtex) glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo); + if(!prealloc) for(int level = 1, mw = tw, mh = th; max(mw, mh) > 1; level++) + { + if(mw > 1) mw /= 2; + if(mh > 1) mh /= 2; + glTexImage2D(target, level, internal, mw, mh, 0, format, type, nullptr); + } + if(!mipmapfbo[0]) glGenFramebuffers_(2, mipmapfbo); + glBindFramebuffer_(GL_READ_FRAMEBUFFER, mipmapfbo[0]); + glBindFramebuffer_(GL_DRAW_FRAMEBUFFER, mipmapfbo[1]); + for(int level = 1, mw = tw, mh = th; max(mw, mh) > 1; level++) + { + int srcw = mw, srch = mh; + if(mw > 1) mw /= 2; + if(mh > 1) mh /= 2; + glFramebufferTexture2D_(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, tnum, level - 1); + glFramebufferTexture2D_(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, tnum, level); + glBlitFramebuffer_(0, 0, srcw, srch, 0, 0, mw, mh, GL_COLOR_BUFFER_BIT, GL_LINEAR); + } + glFramebufferTexture2D_(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, 0, 0); + glFramebufferTexture2D_(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, 0, 0); + glBindFramebuffer_(GL_FRAMEBUFFER, fbo); + } } -static void uploadcompressedtexture(GLenum target, GLenum subtarget, GLenum format, int w, int h, const uchar *data, int align, int blocksize, int levels, bool mipmap) +static void uploadcompressedtexture(GLenum target, GLenum subtarget, GLenum format, int w, int h, const uchar *data, int align, int blocksize, int levels, bool mipmap, bool prealloc) { int hwlimit = target==GL_TEXTURE_CUBE_MAP ? hwcubetexsize : hwtexsize, sizelimit = levels > 1 && maxtexsize ? min(maxtexsize, hwlimit) : hwlimit; @@ -866,7 +946,7 @@ static void uploadcompressedtexture(GLenum target, GLenum subtarget, GLenum form int size = ((w + align-1)/align) * ((h + align-1)/align) * blocksize; if(w <= sizelimit && h <= sizelimit) { - if(target==GL_TEXTURE_1D) glCompressedTexImage1D_(subtarget, level, format, w, 0, size, data); + if(prealloc) glCompressedTexSubImage2D_(subtarget, level, 0, 0, w, h, format, size, data); else glCompressedTexImage2D_(subtarget, level, format, w, h, 0, size, data); level++; if(!mipmap) break; @@ -893,36 +973,6 @@ static GLenum textarget(GLenum subtarget) return subtarget; } -static GLenum uncompressedformat(GLenum format) -{ - switch(format) - { - case GL_COMPRESSED_ALPHA: - return GL_ALPHA; - case GL_COMPRESSED_LUMINANCE: - case GL_COMPRESSED_LUMINANCE_LATC1_EXT: - return GL_LUMINANCE; - case GL_COMPRESSED_LUMINANCE_ALPHA: - case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: - return GL_LUMINANCE_ALPHA; - case GL_COMPRESSED_RED: - case GL_COMPRESSED_RED_RGTC1: - return GL_RED; - case GL_COMPRESSED_RG: - case GL_COMPRESSED_RG_RGTC2: - return GL_RG; - case GL_COMPRESSED_RGB: - case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: - return GL_RGB; - case GL_COMPRESSED_RGBA: - case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: - case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: - case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: - return GL_RGBA; - } - return GL_FALSE; -} - static const GLint *swizzlemask(GLenum format) { static const GLint luminance[4] = { GL_RED, GL_RED, GL_RED, GL_ONE }; @@ -939,7 +989,7 @@ static void setuptexparameters(int tnum, const void *pixels, int clamp, int filt { glBindTexture(target, tnum); glTexParameteri(target, GL_TEXTURE_WRAP_S, clamp&1 ? GL_CLAMP_TO_EDGE : (clamp&0x100 ? GL_MIRRORED_REPEAT : GL_REPEAT)); - if(target!=GL_TEXTURE_1D) glTexParameteri(target, GL_TEXTURE_WRAP_T, clamp&2 ? GL_CLAMP_TO_EDGE : (clamp&0x200 ? GL_MIRRORED_REPEAT : GL_REPEAT)); + glTexParameteri(target, GL_TEXTURE_WRAP_T, clamp&2 ? GL_CLAMP_TO_EDGE : (clamp&0x200 ? GL_MIRRORED_REPEAT : GL_REPEAT)); if(target==GL_TEXTURE_3D) glTexParameteri(target, GL_TEXTURE_WRAP_R, clamp&4 ? GL_CLAMP_TO_EDGE : (clamp&0x400 ? GL_MIRRORED_REPEAT : GL_REPEAT)); if(target==GL_TEXTURE_2D && hasAF && min(aniso, hwmaxaniso) > 0 && filter > 1) glTexParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, min(aniso, hwmaxaniso)); glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter && bilinear ? GL_LINEAR : GL_NEAREST); @@ -1012,7 +1062,9 @@ static GLenum textype(GLenum &component, GLenum &format) if(!format) format = GL_RG; break; + case GL_R3_G3_B2: case GL_RGB5: + case GL_RGB565: case GL_RGB8: case GL_RGB16: case GL_RGB10: @@ -1021,6 +1073,7 @@ static GLenum textype(GLenum &component, GLenum &format) if(!format) format = GL_RGB; break; + case GL_RGBA4: case GL_RGB5_A1: case GL_RGBA8: case GL_RGBA16: @@ -1092,10 +1145,16 @@ static GLenum textype(GLenum &component, GLenum &format) return type; } +static int miplevels(int n) +{ + int levels = 1; + for(; n > 1; n /= 2) levels++; + return levels; +} + void createtexture(int tnum, int w, int h, const void *pixels, int clamp, int filter, GLenum component, GLenum subtarget, int pw, int ph, int pitch, bool resize, GLenum format, bool swizzle) { GLenum target = textarget(subtarget), type = textype(component, format); - if(tnum) setuptexparameters(tnum, pixels, clamp, filter, format, target, swizzle); if(!pw) pw = w; if(!ph) ph = h; int tw = w, th = h; @@ -1105,24 +1164,34 @@ void createtexture(int tnum, int w, int h, const void *pixels, int clamp, int fi resizetexture(w, h, mipmap, false, target, 0, tw, th); if(mipmap) component = compressedformat(component, tw, th); } - uploadtexture(subtarget, component, tw, th, format, type, pixels, pw, ph, pitch, mipmap); + bool prealloc = !resize && hasTS && hasTRG && hasTSW && !uncompressedformat(component); + if(filter >= 0 && clamp >= 0) + { + setuptexparameters(tnum, pixels, clamp, filter, format, target, swizzle); + if(prealloc) glTexStorage2D_(target, mipmap ? miplevels(max(tw, th)) : 1, sizedformat(component), tw, th); + } + uploadtexture(tnum, subtarget, component, tw, th, format, type, pixels, pw, ph, pitch, mipmap, prealloc); } static void createcompressedtexture(int tnum, int w, int h, const uchar *data, int align, int blocksize, int levels, int clamp, int filter, GLenum format, GLenum subtarget, bool swizzle = false) { GLenum target = textarget(subtarget); - if(tnum) setuptexparameters(tnum, data, clamp, filter, format, target, swizzle); - uploadcompressedtexture(target, subtarget, format, w, h, data, align, blocksize, levels, filter > 1); + bool mipmap = filter > 1, prealloc = hasTS && hasTRG && hasTSW; + if(filter >= 0 && clamp >= 0) + { + setuptexparameters(tnum, data, clamp, filter, format, target, swizzle); + if(prealloc) glTexStorage2D_(target, mipmap ? miplevels(max(w, h)) : 1, format, w, h); + } + uploadcompressedtexture(target, subtarget, format, w, h, data, align, blocksize, levels, mipmap, prealloc); } void create3dtexture(int tnum, int w, int h, int d, const void *pixels, int clamp, int filter, GLenum component, GLenum target, bool swizzle) { GLenum format = GL_FALSE, type = textype(component, format); - if(tnum) setuptexparameters(tnum, pixels, clamp, filter, format, target, swizzle); + if(filter >= 0 && clamp >= 0) setuptexparameters(tnum, pixels, clamp, filter, format, target, swizzle); glTexImage3D_(target, 0, component, w, h, d, 0, format, type, pixels); } - static hashnameset textures; Texture *notexture = nullptr; // used as default, ensured to be loaded @@ -2485,6 +2554,12 @@ static void mergespec(ImageData &c, ImageData &s) dst[3] = src[0]; ); } + else if(s.bpp == 3) + { + readwritergbatex(c, s, + dst[3] = (int(src[0]) + int(src[1]) + int(src[2]))/3; + ); + } else { readwritergbatex(c, s, @@ -2651,6 +2726,7 @@ void Slot::load() MatSlot &lookupmaterialslot(int index, bool load) { + if(materialslots[index].sts.empty() && index&MATF_INDEX) index &= ~MATF_INDEX; MatSlot &s = materialslots[index]; if(load && !s.linked) { @@ -2899,7 +2975,7 @@ static Texture *cubemaploadwildcard(Texture *t, const char *name, bool mipit, bo component = compressedformat(format, t->w, t->h, compress); switch(component) { - case GL_RGB: component = GL_RGB5; break; + case GL_RGB: component = hasES2 ? GL_RGB565 : GL_RGB5; break; } } glGenTextures(1, &t->id); @@ -2919,11 +2995,11 @@ static Texture *cubemaploadwildcard(Texture *t, const char *name, bool mipit, bo if(w > 1) w /= 2; if(h > 1) h /= 2; } - createcompressedtexture(!i ? t->id : 0, w, h, data, s.align, s.bpp, levels, 3, mipit ? 2 : 1, s.compressed, side.target, true); + createcompressedtexture(t->id, w, h, data, s.align, s.bpp, levels, i ? -1 : 3, mipit ? 2 : 1, s.compressed, side.target, true); } else { - createtexture(!i ? t->id : 0, t->w, t->h, s.data, 3, mipit ? 2 : 1, component, side.target, s.w, s.h, s.pitch, false, format, true); + createtexture(t->id, t->w, t->h, s.data, i ? -1 : 3, mipit ? 2 : 1, component, side.target, s.w, s.h, s.pitch, false, format, true); } } return t; @@ -3004,7 +3080,7 @@ static GLuint genenvmap(const vec &o, int envmapsize, int blur, bool onlysky) glGenTextures(1, &tex); // workaround for Catalyst bug: // all texture levels must be specified before glCopyTexSubImage2D is called, otherwise it crashes - loopi(6) createtexture(!i ? tex : 0, texsize, texsize, nullptr, 3, 2, GL_RGB5, cubemapsides[i].target); + loopi(6) createtexture(tex, texsize, texsize, nullptr, i ? -1 : 3, 2, hasES2 ? GL_RGB565 : GL_RGB5, cubemapsides[i].target); float yaw = 0, pitch = 0; loopi(6) { @@ -3189,6 +3265,7 @@ static void cleanuptexture(Texture *t) void cleanuptextures() { + cleanupmipmaps(); clearenvmaps(); loopv(slots) slots[i]->cleanup(); loopv(vslots) vslots[i]->cleanup(); diff --git a/src/engine/vertmodel.hh b/src/engine/vertmodel.hh index 50de376..30ab309 100644 --- a/src/engine/vertmodel.hh +++ b/src/engine/vertmodel.hh @@ -457,18 +457,18 @@ template struct vertcommands : modelcommandsaddpart(); if(mdl.index) mdl.disablepitch(); mdl.meshes = MDL::loading->sharemeshes(path(filename), *smooth > 0 ? cosf(std::clamp(*smooth, 0.0f, 180.0f)*RAD) : 2); - if(!mdl.meshes) conoutf("could not load %s", filename); + if(!mdl.meshes) conoutf(CON_ERROR, "could not load %s", filename); else mdl.initskins(); } static void settag(char *tagname, float *tx, float *ty, float *tz, float *rx, float *ry, float *rz) { - if(!MDL::loading || MDL::loading->parts.empty()) { conoutf("not loading an %s", MDL::formatname()); return; } + if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; } part &mdl = *(part *)MDL::loading->parts.last(); float cx = *rx ? cosf(*rx/2*RAD) : 1, sx = *rx ? sinf(*rx/2*RAD) : 0, cy = *ry ? cosf(*ry/2*RAD) : 1, sy = *ry ? sinf(*ry/2*RAD) : 0, @@ -480,7 +480,7 @@ template struct vertcommands : modelcommandsparts.empty()) { conoutf("not loading an %s", MDL::formatname()); return; } + if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; } part &mdl = *MDL::loading->parts.last(); mdl.pitchscale = *pitchscale; @@ -499,10 +499,10 @@ template struct vertcommands : modelcommandsparts.empty()) { conoutf("not loading an %s", MDL::formatname()); return; } + if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; } vector anims; //game::findanims(anim, anims); - if(anims.empty()) conoutf("could not find animation %s", anim); + if(anims.empty()) conoutf(CON_ERROR, "could not find animation %s", anim); else loopv(anims) { MDL::loading->parts.last()->setanim(0, anims[i], *frame, *range, *speed, *priority); diff --git a/src/engine/water.cc b/src/engine/water.cc index bfc7c42..650ab36 100644 --- a/src/engine/water.cc +++ b/src/engine/water.cc @@ -12,7 +12,7 @@ #include "texture.hh" #include "world.hh" -extern const vec matnormals[6]; +extern const bvec4 matnormals[6]; extern vector watersurfs[4], waterfallsurfs[4], lavasurfs[4], lavafallsurfs[4]; @@ -221,113 +221,155 @@ void renderwaterfog(int mat, float surface) VARP(watersubdiv, 0, 2, 3); VARP(waterlod, 0, 1, 3); -static int wx1, wy1, wx2, wy2, wsize; -static float whscale, whoffset; +static int wx1, wy1, wx2, wy2, wz, wsize, wsubdiv; +static float whscale, whoffset, whphase; -#define VERTW(vertw, defbody, body) \ - static inline void def##vertw() \ - { \ - gle::defvertex(); \ - defbody; \ - } \ - static inline void vertw(float v1, float v2, float v3) \ - { \ - float angle = (v1-wx1)*(v2-wy1)*(v1-wx2)*(v2-wy2)*whscale+whoffset; \ - float s = angle - int(angle) - 0.5f; \ - s *= 8 - fabs(s)*16; \ - float h = WATER_AMPLITUDE*s-WATER_OFFSET; \ - gle::attribf(v1, v2, v3+h); \ - body; \ +static inline float vertwangle(float v1, float v2) +{ + return (v1-wx1)*(v2-wy1)*(v1-wx2)*(v2-wy2)*whscale+whoffset; +} + +static inline float vertwphase(float angle) +{ + float s = angle - int(angle) - 0.5f; + s *= 8 - fabs(s)*16; + return WATER_AMPLITUDE*s-WATER_OFFSET; +} + +static inline void vertw(float v1, float v2, float v3) +{ + float h = vertwphase(vertwangle(v1, v2)); + gle::attribf(v1, v2, v3+h); +} + +static inline void vertwq(float v1, float v2, float v3) +{ + gle::attribf(v1, v2, v3+whphase); +} + +static inline void vertwn(float v1, float v2, float v3) +{ + float h = -WATER_OFFSET; + gle::attribf(v1, v2, v3+h); +} + +struct waterstrip +{ + int x1, y1, x2, y2, z; + ushort size, subdiv; + + int numverts() const { return 2*((y2-y1)/subdiv + 1)*((x2-x1)/subdiv); } + + void save() + { + x1 = wx1; + y1 = wy1; + x2 = wx2; + y2 = wy2; + z = wz; + size = wsize; + subdiv = wsubdiv; } -#define VERTWN(vertw, defbody, body) \ - static inline void def##vertw() \ - { \ - gle::defvertex(); \ - defbody; \ - } \ - static inline void vertw(float v1, float v2, float v3) \ - { \ - float h = -WATER_OFFSET; \ - gle::attribf(v1, v2, v3+h); \ - body; \ + + void restore() + { + wx1 = x1; + wy1 = y1; + wx2 = x2; + wy2 = y2; + wz = z; + wsize = size; + wsubdiv = subdiv; } -#define VERTWT(vertwt, defbody, body) \ - VERTW(vertwt, defbody, { \ - float v = angle - int(angle+0.25f) - 0.25f; \ - v *= 8 - fabs(v)*16; \ - float duv = 0.5f*v; \ - body; \ - }) +}; +static vector waterstrips; -static float wxscale = 1.0f, wyscale = 1.0f, wscroll = 0.0f; +static void flushwaterstrips() +{ + if(gle::attribbuf.length()) xtraverts += gle::end(); + gle::defvertex(); + int numverts = 0; + loopv(waterstrips) numverts += waterstrips[i].numverts(); + gle::begin(GL_TRIANGLE_STRIP, numverts); + loopv(waterstrips) + { + waterstrips[i].restore(); + whscale = 59.0f/(23.0f*wsize*wsize)/(2*M_PI); + for(int x = wx1; x=wx2) break; + vertw(x, wy2, wz); + vertw(x+wsubdiv, wy2, wz); + for(int y = wy2; y>wy1; y -= wsubdiv) + { + vertw(x, y-wsubdiv, wz); + vertw(x+wsubdiv, y-wsubdiv, wz); + } + } + gle::multidraw(); + } + waterstrips.setsize(0); + wsize = 0; + xtraverts += gle::end(); +} -VERTW(vertwt, { - gle::deftexcoord0(); -}, { - gle::attribf(wxscale*v1, wyscale*v2); -}) -VERTWN(vertwtn, { - gle::deftexcoord0(); -}, { - gle::attribf(wxscale*v1, wyscale*v2); -}) +static void flushwater(int mat = MAT_WATER, bool force = true) +{ + if(wsize) + { + if(wsubdiv == wsize && wx2-wx1 <= wsize*2 && wy2-wy1 <= wsize*2) + { + if(gle::attribbuf.empty()) { gle::defvertex(); gle::begin(GL_QUADS); } + for(int x = wx1; xxs*lslot.scale); - wyscale = TEX_SCALE/(tex->ys*lslot.scale); - wscroll = lastmillis/1000.0f; + float xscale = TEX_SCALE/(tex->xs*lslot.scale); + float yscale = TEX_SCALE/(tex->ys*lslot.scale); + float scroll = lastmillis/1000.0f; + LOCALPARAMF(lavatexgen, xscale, yscale, scroll, scroll); + + whoffset = fmod(float(lastmillis/2000.0f/(2*M_PI)), 1.0f); + whphase = vertwphase(whoffset); glBindTexture(GL_TEXTURE_2D, tex->id); glActiveTexture_(GL_TEXTURE1); @@ -526,7 +536,7 @@ void renderlava() vector &surfs = lavasurfs[k]; loopv(surfs) renderwater(surfs[i], MAT_LAVA); - xtraverts += gle::end(); + flushwater(MAT_LAVA); } if(drawtex != DRAWTEX_MINIMAP && lavafallsurfs[k].length()) @@ -536,9 +546,10 @@ void renderlava() s = angle - int(angle) - 0.5f; s *= 8 - fabs(s)*16; wfwave = vertwater ? WATER_AMPLITUDE*s-WATER_OFFSET : -WATER_OFFSET; - wfscroll = 16.0f*lastmillis/3000.0f; - wfxscale = TEX_SCALE/(tex->xs*lslot.scale); - wfyscale = TEX_SCALE/(tex->ys*lslot.scale); + float scroll = -16.0f*lastmillis/3000.0f; + float xscale = TEX_SCALE/(tex->xs*lslot.scale); + float yscale = TEX_SCALE/(tex->ys*lslot.scale); + LOCALPARAMF(lavatexgen, xscale, yscale, 0.0f, scroll); glBindTexture(GL_TEXTURE_2D, tex->id); glActiveTexture_(GL_TEXTURE1); @@ -549,7 +560,7 @@ void renderlava() loopv(surfs) { materialsurface &m = surfs[i]; - renderwaterfall(m, 0.1f, &matnormals[m.orient]); + renderwaterfall(m, 0.1f); } xtraverts += gle::end(); } @@ -570,9 +581,10 @@ void renderwaterfalls() s = angle - int(angle) - 0.5f; s *= 8 - fabs(s)*16; wfwave = vertwater ? WATER_AMPLITUDE*s-WATER_OFFSET : -WATER_OFFSET; - wfscroll = 16.0f*lastmillis/1000.0f; - wfxscale = TEX_SCALE/(tex->xs*wslot.scale); - wfyscale = TEX_SCALE/(tex->ys*wslot.scale); + float scroll = -16.0f*lastmillis/1000.0f; + float xscale = TEX_SCALE/(tex->xs*wslot.scale); + float yscale = TEX_SCALE/(tex->ys*wslot.scale); + GLOBALPARAMF(waterfalltexgen, xscale, yscale, 0.0f, scroll); bvec color = getwaterfallcolour(k), refractcolor = getwaterfallrefractcolour(k); if(color.iszero()) color = getwatercolour(k); @@ -600,7 +612,7 @@ void renderwaterfalls() loopv(surfs) { materialsurface &m = surfs[i]; - renderwaterfall(m, 0.1f, &matnormals[m.orient]); + renderwaterfall(m, 0.1f); } xtraverts += gle::end(); } @@ -616,9 +628,12 @@ void renderwater() MatSlot &wslot = lookupmaterialslot(MAT_WATER+k); Texture *tex = wslot.sts.inrange(0) ? wslot.sts[0].t: notexture; - wxscale = TEX_SCALE/(tex->xs*wslot.scale); - wyscale = TEX_SCALE/(tex->ys*wslot.scale); - wscroll = 0.0f; + float xscale = TEX_SCALE/(tex->xs*wslot.scale); + float yscale = TEX_SCALE/(tex->ys*wslot.scale); + GLOBALPARAMF(watertexgen, xscale, yscale); + + whoffset = fmod(float(lastmillis/600.0f/(2*M_PI)), 1.0f); + whphase = vertwphase(whoffset); glBindTexture(GL_TEXTURE_2D, tex->id); glActiveTexture_(GL_TEXTURE1); @@ -683,7 +698,7 @@ void renderwater() if(camera1->o.z < m.o.z - WATER_OFFSET) continue; renderwater(m); } - xtraverts += gle::end(); + flushwater(); if(belowshader) { @@ -694,7 +709,7 @@ void renderwater() if(camera1->o.z >= m.o.z - WATER_OFFSET) continue; renderwater(m); } - xtraverts += gle::end(); + flushwater(); } } } diff --git a/src/engine/world.cc b/src/engine/world.cc index f862717..b80b139 100644 --- a/src/engine/world.cc +++ b/src/engine/world.cc @@ -220,7 +220,7 @@ static void modifyoctaentity(int flags, int id, extentity &e, cube *c, const ive } vector outsideents; -int spotlights = 0, volumetriclights = 0, nospeclights = 0; +int spotlights = 0, volumetriclights = 0, nospeclights = 0, smalphalights = 0, volumetricsmalphalights = 0; static bool modifyoctaent(int flags, int id, extentity &e) { @@ -250,9 +250,13 @@ static bool modifyoctaent(int flags, int id, extentity &e) switch(e.type) { case ET_LIGHT: - clearlightcache(id); if(e.attr5&L_VOLUMETRIC) { if(flags&MODOE_ADD) volumetriclights++; else --volumetriclights; } if(e.attr5&L_NOSPEC) { if(!(flags&MODOE_ADD ? nospeclights++ : --nospeclights)) cleardeferredlightshaders(); } + if(e.attr5&L_SMALPHA) + { + if(!(flags&MODOE_ADD ? smalphalights++ : --smalphalights)) cleardeferredlightshaders(); + if(e.attr5&L_VOLUMETRIC) { if(!(flags&MODOE_ADD ? volumetricsmalphalights++ : --volumetricsmalphalights)) cleanupvolumetric(); } + } break; case ET_SPOTLIGHT: if(!(flags&MODOE_ADD ? spotlights++ : --spotlights)) { cleardeferredlightshaders(); cleanupvolumetric(); } break; case ET_PARTICLES: clearparticleemitters(); break; @@ -1074,7 +1078,7 @@ static extentity *newentity(bool local, const vec &o, int type, int v1, int v2, { idx = -1; for(int i = keepents; i < ents.length(); i++) if(ents[i]->type == ET_EMPTY) { idx = i; break; } - if(idx < 0 && ents.length() >= MAXENTS) { conoutf("too many entities"); return nullptr; } + if(idx < 0 && ents.length() >= MAXENTS) { conoutf(CON_ERROR, "too many entities"); return nullptr; } } else while(ents.length() < idx) ents.add(entities::newentity())->type = ET_EMPTY; extentity &e = *entities::newentity(); @@ -1330,6 +1334,8 @@ void resetmap() spotlights = 0; volumetriclights = 0; nospeclights = 0; + smalphalights = 0; + volumetricsmalphalights = 0; } void startmap(const char *name) diff --git a/src/shared/geom.hh b/src/shared/geom.hh index 24566f7..d271272 100644 --- a/src/shared/geom.hh +++ b/src/shared/geom.hh @@ -141,6 +141,10 @@ struct vec float squaredot(const vec &o) const { float k = dot(o); return k*k; } float absdot(const vec &o) const { return fabs(x*o.x) + fabs(y*o.y) + fabs(z*o.z); } float zdot(const vec &o) const { return z*o.z; } + vec &pow(float f) { x = ::pow(x, f); y = ::pow(y, f); z = ::pow(z, f); return *this; } + vec &exp() { x = ::exp(x); y = ::exp(y); z = ::exp(z); return *this; } + vec &exp2() { x = ::exp2(x); y = ::exp2(y); z = ::exp2(z); return *this; } + vec &sqrt() { x = sqrtf(x); y = sqrtf(y); z = sqrtf(z); return *this; } vec &mul(const vec &o) { x *= o.x; y *= o.y; z *= o.z; return *this; } vec &mul(float f) { x *= f; y *= f; z *= f; return *this; } vec &mul2(float f) { x *= f; y *= f; return *this; } @@ -935,6 +939,10 @@ struct matrix4x3 void translate(float x, float y, float z) { translate(vec(x, y, z)); } void translate(const vec &p, float scale) { translate(vec(p).mul(scale)); } + void posttranslate(const vec &p) { d.add(p); } + void posttranslate(float x, float y, float z) { posttranslate(vec(x, y, z)); } + void posttranslate(const vec &p, float scale) { d.madd(p, scale); } + void accumulate(const matrix4x3 &m, float k) { a.madd(m.a, k); diff --git a/src/shared/glemu.cc b/src/shared/glemu.cc index cfbbfde..4e86852 100644 --- a/src/shared/glemu.cc +++ b/src/shared/glemu.cc @@ -295,7 +295,15 @@ namespace gle if(primtype == GL_QUADS) { if(!quadsenabled) enablequads(); - drawquads(start/4, numvertexes/4); + for(;;) + { + int count = min(numvertexes/4, MAXQUADS); + drawquads(start/4, count); + numvertexes -= 4*count; + if(numvertexes < 4) break; + setattribs(buf + 4*count*vertexsize); + start = 0; + } } else { diff --git a/src/shared/glemu.hh b/src/shared/glemu.hh index 76a3aec..7f00bfd 100644 --- a/src/shared/glemu.hh +++ b/src/shared/glemu.hh @@ -29,7 +29,6 @@ namespace gle extern void begin(GLenum mode); extern void begin(GLenum mode, int numverts); - extern void multidraw(); extern void defattribs(const char *fmt); extern void defattrib(int type, int size, int format); @@ -175,6 +174,7 @@ namespace gle static inline void attrib(const bvec &b, uchar w) { attribub(b.x, b.y, b.z, w); } static inline void attrib(const bvec4 &b) { attribub(b.x, b.y, b.z, b.w); } + extern void multidraw(); extern int end(); extern void enablequads(); diff --git a/src/shared/glexts.hh b/src/shared/glexts.hh index 4aa119a..5255605 100644 --- a/src/shared/glexts.hh +++ b/src/shared/glexts.hh @@ -169,7 +169,6 @@ typedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC) (GLenum target, GLuint framebu typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC) (GLsizei n, const GLuint *framebuffers); typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target); -typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); @@ -190,7 +189,6 @@ extern PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus_; extern PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer_; extern PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers_; extern PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers_; -extern PFNGLFRAMEBUFFERTEXTURE1DPROC glFramebufferTexture1D_; extern PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D_; extern PFNGLFRAMEBUFFERTEXTURE3DPROC glFramebufferTexture3D_; extern PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer_; @@ -314,10 +312,8 @@ extern PFNGLFLUSHMAPPEDBUFFERRANGEPROC glFlushMappedBufferRange_; #define glCompressedTexImage3D_ glCompressedTexImage3D #define glCompressedTexImage2D_ glCompressedTexImage2D -#define glCompressedTexImage1D_ glCompressedTexImage1D #define glCompressedTexSubImage3D_ glCompressedTexSubImage3D #define glCompressedTexSubImage2D_ glCompressedTexSubImage2D -#define glCompressedTexSubImage1D_ glCompressedTexSubImage1D #define glGetCompressedTexImage_ glGetCompressedTexImage #define glDrawRangeElements_ glDrawRangeElements @@ -333,10 +329,8 @@ extern PFNGLCOPYTEXSUBIMAGE3DPROC glCopyTexSubImage3D_; extern PFNGLCOMPRESSEDTEXIMAGE3DPROC glCompressedTexImage3D_; extern PFNGLCOMPRESSEDTEXIMAGE2DPROC glCompressedTexImage2D_; -extern PFNGLCOMPRESSEDTEXIMAGE1DPROC glCompressedTexImage1D_; extern PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glCompressedTexSubImage3D_; extern PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glCompressedTexSubImage2D_; -extern PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glCompressedTexSubImage1D_; extern PFNGLGETCOMPRESSEDTEXIMAGEPROC glGetCompressedTexImage_; extern PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements_; @@ -535,6 +529,11 @@ extern PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer_; extern PFNGLDRAWBUFFERSPROC glDrawBuffers_; #endif +#ifndef GL_VERSION_2_0 +#define GL_VERSION_2_0 1 +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B +#endif + #ifndef GL_VERSION_2_1 #define GL_VERSION_2_1 1 #define GL_PIXEL_PACK_BUFFER 0x88EB @@ -793,6 +792,18 @@ extern PFNGLBLENDEQUATIONSEPARATEIPROC glBlendEquationSeparatei_; extern PFNGLBLENDFUNCIPROC glBlendFunci_; extern PFNGLBLENDFUNCSEPARATEIPROC glBlendFuncSeparatei_; +#ifndef GL_RGB565 +#define GL_RGB565 0x8D62 +#endif + +#ifndef GL_VERSION_4_2 +#define GL_VERSION_4_2 1 +typedef void (APIENTRYP PFNGLTEXSTORAGE2DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +#endif + +// GL_ARB_texture_storage +extern PFNGLTEXSTORAGE2DPROC glTexStorage2D_; + #ifndef GL_VERSION_4_3 #define GL_VERSION_4_3 1 #define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A