update to tesseract rev 2425

master
Daniel Kolesa 2020-09-11 02:54:14 +02:00
parent fa79385b82
commit 2be4ed0739
54 changed files with 1653 additions and 1035 deletions

16
README
View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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)])

View File

@ -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)
}
] [

View File

@ -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;

View File

@ -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);
}
]

View File

@ -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));
}
]
]

View File

@ -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;
]
]

View File

@ -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
]
]
]

View File

@ -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) [

View File

@ -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<animspec>[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<class MDL, class MESH> 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<class MDL, class MESH> 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<class MDL, class MESH> 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<class F> void modelcommand(F *fun, const char *suffix, const char *args)
@ -1882,6 +1908,7 @@ template<class MDL, class MESH> 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");

View File

@ -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;

View File

@ -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)));

View File

@ -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);
}

View File

@ -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();

View File

@ -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;

View File

@ -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<grassvert> 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();

View File

@ -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; \

View File

@ -238,7 +238,7 @@ struct iqm : skelloader<iqm>
}
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;

View File

@ -275,77 +275,6 @@ static void clearsurfaces(cube *c)
}
}
#define LIGHTCACHESIZE 1024
static struct lightcacheentry
{
int x, y;
vector<int> 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<int> &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<<lightcachesize, cx = x<<lightcachesize, cy = y<<lightcachesize;
const vector<extentity *> &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<extentity *> &ents = entities::getents();
const vector<int> &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

View File

@ -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<int> &checklightcache(int x, int y);
#endif

View File

@ -454,7 +454,7 @@ void textinput(bool on, int mask)
}
textinputmask |= mask;
}
else
else if(textinputmask)
{
textinputmask &= ~mask;
if(!textinputmask) SDL_StopTextInput();

View File

@ -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();

View File

@ -225,7 +225,7 @@ struct md2 : vertloader<md2>
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))

View File

@ -65,7 +65,7 @@ struct md3 : vertloader<md3>
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<md3>
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;
}
};

View File

@ -227,7 +227,7 @@ struct md5 : skelloader<md5>
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;
}

View File

@ -178,7 +178,7 @@ struct obj : vertloader<obj>
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;
}
};

View File

@ -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<octaentities *> mapmodels, decals;
vector<grasstri> grasstris;
int hasmerges, mergelevel;
int shadowmask;
int shadowmask, shadowtransparent;
};
struct cube;

View File

@ -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<<slot.sts[j].type;
if(slot.shader->type&SHADER_ENVMAP) va->texmask |= 1<<TEX_ENVMAP;
}
}
va->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<<slot.sts[j].type;
if(slot.shader->type&SHADER_ENVMAP) va->texmask |= 1<<TEX_ENVMAP;
}
va->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);

View File

@ -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<extentity *> &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<dist) dist = f;
}
return dist;
}
#define INITRAYCUBE \
float dist = 0, dent = radius > 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<<lshift;
@ -355,38 +339,6 @@ float raycube(const vec &o, const vec &ray, float radius, int mode, int size, ex
}
}
// optimized version for light shadowing... every cycle here counts!!!
float shadowray(const vec &o, const vec &ray, float radius, int mode, extentity *t)
{
INITRAYCUBE;
CHECKINSIDEWORLD;
int side = O_BOTTOM, x = int(v.x), y = int(v.y), z = int(v.z);
for(;;)
{
DOWNOCTREE(shadowent, );
cube &c = *lc;
ivec lo(x&(~0U<<lshift), y&(~0U<<lshift), z&(~0U<<lshift));
if(!isempty(c) && !(c.material&MAT_ALPHA))
{
if(isentirelysolid(c)) return c.texture[side]==DEFAULT_SKY && mode&RAY_SKIPSKY ? radius : dist;
const clipplanes &p = getclipplanes(c, lo, 1<<lshift);
INTERSECTPLANES(side = p.side[i], goto nextcube);
INTERSECTBOX(side = (i<<1) + 1 - lsizemask[i], goto nextcube);
if(exitdist >= 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)

View File

@ -14,7 +14,6 @@ extern physent *collideplayer;
bool overlapsdynent(const vec &o, float radius);
void rotatebb(vec &center, 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);

View File

@ -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 };

View File

@ -4,7 +4,7 @@
#include <shared/gl.hh>
#include <shared/geom.hh>
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;

View File

@ -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<<aonoise), aoh/float(1<<aonoise));
if(aoderiv >= 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<shadowcachekey, shadowcacheval>
@ -1718,15 +1755,42 @@ struct shadowcache : hashtable<shadowcachekey, shadowcacheval>
}
};
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<uint> 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<<side)) return false;
shadowcolorclears.add(idx * 6 + side);
return true;
}
static void rendershadowtransparent(int idx, int side, bool cullside = false)
{
const shadowmapinfo &sm = shadowmaps[idx];
int sidex = 0, sidey = 0;
if(sm.light >= 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<<side))
{
if(clearshadowtransparent(i, side)) continue;
matrix4 cubematrix(cubeshadowviewmatrix[side]);
cubematrix.scale(1.0f/l.radius);
cubematrix.translate(vec(l.o).neg());
shadowmatrix.mul(smprojmatrix, cubematrix);
GLOBALPARAM(shadowmatrix, shadowmatrix);
rendershadowtransparent(i, side, (side & 1) ^ (side >> 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();

View File

@ -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;

View File

@ -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];

View File

@ -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;

View File

@ -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);

View File

@ -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<vtxarray *> &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<vtxarray *> &vas, bool transparent)
{
loopv(vas)
{
@ -949,29 +956,46 @@ static void findshadowvas(vector<vtxarray *> &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<vtxarray *> &vas)
static void findcsmshadowvas(vector<vtxarray *> &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<vtxarray *> &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<vtxarray *> &vas)
}
}
static void findspotshadowvas(vector<vtxarray *> &vas)
static void findspotshadowvas(vector<vtxarray *> &vas, bool transparent)
{
loopv(vas)
{
@ -1002,31 +1025,42 @@ static void findspotshadowvas(vector<vtxarray *> &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<<TEX_ALPHA)) smalphaworldshader->setvariant(0, slot.texmask&(1<<TEX_NORMAL) ? 1 : 0, slot, vslot);
else smalphaworldshader->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<<shadowside) && va->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<<shadowside) && va->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();

View File

@ -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);

View File

@ -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");

View File

@ -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); }

View File

@ -1657,11 +1657,11 @@ template<class MDL> struct skelcommands : modelcommands<MDL, struct MDL::skelmes
static void loadpart(char *meshfile, char *skelname, float *smooth)
{
if(!MDL::loading) { conoutf("not loading an %s", MDL::formatname()); return; }
if(!MDL::loading) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
defformatstring(filename, "%s/%s", MDL::dir, meshfile);
part &mdl = MDL::loading->addpart();
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<class MDL> struct skelcommands : modelcommands<MDL, struct MDL::skelmes
static void settag(char *name, 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();
int i = mdl.meshes ? ((meshgroup *)mdl.meshes)->skel->findbone(name) : -1;
if(i >= 0)
@ -1685,12 +1685,12 @@ template<class MDL> struct skelcommands : modelcommands<MDL, struct MDL::skelmes
((meshgroup *)mdl.meshes)->skel->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<class MDL> struct skelcommands : modelcommands<MDL, struct MDL::skelmes
}
return;
}
conoutf("could not find bone %s to pitch", name);
conoutf(CON_ERROR, "could not find bone %s to pitch", name);
return;
}
@ -1733,17 +1733,17 @@ template<class MDL> struct skelcommands : modelcommands<MDL, struct MDL::skelmes
static void setpitchtarget(char *name, char *animfile, int *frameoffset, float *pitchmin, float *pitchmax)
{
if(!MDL::loading || MDL::loading->parts.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<class MDL> struct skelcommands : modelcommands<MDL, struct MDL::skelmes
static void setpitchcorrect(char *name, char *targetname, float *scale, float *pitchmin, float *pitchmax)
{
if(!MDL::loading || MDL::loading->parts.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<class MDL> struct skelcommands : modelcommands<MDL, struct MDL::skelmes
if(targetbone >= 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<class MDL> struct skelcommands : modelcommands<MDL, struct MDL::skelmes
static void setanim(char *anim, char *animfile, float *speed, int *priority, int *startoffset, int *endoffset)
{
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; }
vector<int> 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<class MDL> struct skelcommands : modelcommands<MDL, struct MDL::skelmes
static void setanimpart(char *maskstr)
{
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 *p = (part *)MDL::loading->parts.last();
@ -1825,32 +1825,32 @@ template<class MDL> struct skelcommands : modelcommands<MDL, struct MDL::skelmes
{
char *bonestr = bonestrs[i];
int bone = p->meshes ? ((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<class MDL> struct skelcommands : modelcommands<MDL, struct MDL::skelmes
{
char *bonestr = bonestrs[i];
int bone = p->meshes ? ((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();

View File

@ -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<Texture> 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();

View File

@ -457,18 +457,18 @@ template<class MDL> struct vertcommands : modelcommands<MDL, struct MDL::vertmes
static void loadpart(char *model, float *smooth)
{
if(!MDL::loading) { conoutf("not loading an %s", MDL::formatname()); return; }
if(!MDL::loading) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
defformatstring(filename, "%s/%s", MDL::dir, model);
part &mdl = MDL::loading->addpart();
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<class MDL> struct vertcommands : modelcommands<MDL, struct MDL::vertmes
static void setpitch(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 = *MDL::loading->parts.last();
mdl.pitchscale = *pitchscale;
@ -499,10 +499,10 @@ template<class MDL> struct vertcommands : modelcommands<MDL, struct MDL::vertmes
static void setanim(char *anim, int *frame, int *range, float *speed, int *priority)
{
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; }
vector<int> 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);

View File

@ -12,7 +12,7 @@
#include "texture.hh"
#include "world.hh"
extern const vec matnormals[6];
extern const bvec4 matnormals[6];
extern vector<materialsurface> 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<waterstrip> 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; x += wsubdiv)
{
vertw(x, wy1, wz);
vertw(x+wsubdiv, wy1, wz);
for(int y = wy1; y<wy2; y += wsubdiv)
{
vertw(x, y+wsubdiv, wz);
vertw(x+wsubdiv, y+wsubdiv, wz);
}
x += wsubdiv;
if(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; x<wx2; x += wsubdiv) for(int y = wy1; y<wy2; y += wsubdiv)
{
vertwq(x, y, wz);
vertwq(x+wsubdiv, y, wz);
vertwq(x+wsubdiv, y+wsubdiv, wz);
vertwq(x, y+wsubdiv, wz);
}
}
else waterstrips.add().save();
wsize = 0;
}
VERTW(vertl, {
gle::deftexcoord0();
}, {
gle::attribf(wxscale*(v1+wscroll), wyscale*(v2+wscroll));
})
VERTWN(vertln, {
gle::deftexcoord0();
}, {
gle::attribf(wxscale*(v1+wscroll), wyscale*(v2+wscroll));
})
#define renderwaterstrips(vertw, z) { \
def##vertw(); \
gle::begin(GL_TRIANGLE_STRIP, 2*(wy2-wy1 + 1)*(wx2-wx1)/subdiv); \
for(int x = wx1; x<wx2; x += subdiv) \
{ \
vertw(x, wy1, z); \
vertw(x+subdiv, wy1, z); \
for(int y = wy1; y<wy2; y += subdiv) \
{ \
vertw(x, y+subdiv, z); \
vertw(x+subdiv, y+subdiv, z); \
} \
gle::multidraw(); \
} \
xtraverts += gle::end(); \
if(force)
{
if(gle::attribbuf.length()) xtraverts += gle::end();
if(waterstrips.length()) flushwaterstrips();
}
}
static void rendervertwater(int subdiv, int xo, int yo, int z, int size, int mat)
{
if(wsize == size && wsubdiv == subdiv && wz == z)
{
if(wx2 == xo)
{
if(wy1 == yo && wy2 == yo + size) { wx2 += size; return; }
}
else if(wy2 == yo && wx1 == xo && wx2 == xo + size) { wy2 += size; return; }
}
flushwater(mat, false);
wx1 = xo;
wy1 = yo;
wx2 = wx1 + size,
wy2 = wy1 + size;
wx2 = xo + size,
wy2 = yo + size;
wz = z;
wsize = size;
whscale = 59.0f/(23.0f*wsize*wsize)/(2*M_PI);
wsubdiv = subdiv;
assert((wx1 & (subdiv - 1)) == 0);
assert((wy1 & (subdiv - 1)) == 0);
switch(mat)
{
case MAT_WATER:
{
whoffset = fmod(float(lastmillis/600.0f/(2*M_PI)), 1.0f);
renderwaterstrips(vertwt, z);
break;
}
case MAT_LAVA:
{
whoffset = fmod(float(lastmillis/2000.0f/(2*M_PI)), 1.0f);
renderwaterstrips(vertl, z);
break;
}
}
}
static int calcwatersubdiv(int x, int y, int z, int size)
@ -382,28 +424,13 @@ static int renderwaterlod(int x, int y, int z, int size, int mat)
}
}
#define renderwaterquad(vertwn, z) \
{ \
if(gle::attribbuf.empty()) { def##vertwn(); gle::begin(GL_QUADS); } \
vertwn(x, y, z); \
vertwn(x+rsize, y, z); \
vertwn(x+rsize, y+csize, z); \
vertwn(x, y+csize, z); \
xtraverts += 4; \
}
static void renderflatwater(int x, int y, int z, int rsize, int csize, int mat)
{
switch(mat)
{
case MAT_WATER:
renderwaterquad(vertwtn, z);
break;
case MAT_LAVA:
renderwaterquad(vertln, z);
break;
}
if(gle::attribbuf.empty()) { gle::defvertex(); gle::begin(GL_QUADS); }
vertwn(x, y, z);
vertwn(x+rsize, y, z);
vertwn(x+rsize, y+csize, z);
vertwn(x, y+csize, z);
}
static inline void renderwater(const materialsurface &m, int mat = MAT_WATER)
@ -442,54 +469,33 @@ void preloadwatershaders(bool force)
useshaderbyname("waterminimap");
}
static float wfwave = 0.0f, wfscroll = 0.0f, wfxscale = 1.0f, wfyscale = 1.0f;
static float wfwave = 0.0f;
static void renderwaterfall(const materialsurface &m, float offset, const vec *normal = nullptr)
static void renderwaterfall(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, zmin = m.o.z, zmax = zmin;
if(m.ends&1) zmin += -WATER_OFFSET-WATER_AMPLITUDE;
if(m.ends&2) zmax += wfwave;
int csize = m.csize, rsize = m.rsize;
#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) \
{ \
vec v(mx sx, my sy, mz sz); \
gle::attribf(v.x, v.y, v.z); \
GENFACENORMAL \
gle::attribf(wfxscale*v.y, -wfyscale*(v.z+wfscroll)); \
}
#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(wfxscale*v.x, -wfyscale*(v.z+wfscroll)); \
}
#define GENFACENORMAL gle::attribf(n.x, n.y, n.z);
if(normal)
switch(m.orient)
{
vec n = *normal;
switch(m.orient) { GENFACEVERTSXY(x, x, y, y, zmin, zmax, /**/, + csize, /**/, + rsize, + offset, - offset) }
#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); \
gle::attrib(matnormals[orient]); \
}
GENFACEVERTSXY(x, x, y, y, zmin, zmax, /**/, + csize, /**/, + rsize, + offset, - offset)
#undef GENFACEORIENT
#undef GENFACEVERT
}
#undef GENFACENORMAL
#define GENFACENORMAL
else switch(m.orient) { GENFACEVERTSXY(x, x, y, y, zmin, zmax, /**/, + csize, /**/, + rsize, + offset, - offset) }
#undef GENFACENORMAL
#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)
}
void renderlava()
@ -513,9 +519,13 @@ void renderlava()
if(lavasurfs[k].length())
{
Texture *tex = lslot.sts.inrange(0) ? lslot.sts[0].t: notexture;
wxscale = TEX_SCALE/(tex->xs*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<materialsurface> &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();
}
}
}

View File

@ -220,7 +220,7 @@ static void modifyoctaentity(int flags, int id, extentity &e, cube *c, const ive
}
vector<int> 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)

View File

@ -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);

View File

@ -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
{

View File

@ -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();

View File

@ -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