diff --git a/Game.Assets/hl2/shaders/common_flashlight_gl460.fs b/Game.Assets/hl2/shaders/common_flashlight_gl460.fs new file mode 100644 index 00000000..31ec76b0 --- /dev/null +++ b/Game.Assets/hl2/shaders/common_flashlight_gl460.fs @@ -0,0 +1,211 @@ +#ifndef COMMON_FLASHLIGHT_GL460_FS +#define COMMON_FLASHLIGHT_GL460_FS + +#include "common_gl460.fs" + +float DoShadowPoisson16Sample(sampler2DShadow DepthSampler, sampler2D DepthSamplerRaw, sampler2D RandomRotationSampler, vec3 vProjCoords, vec2 vScreenPos, vec4 vShadowTweaks, bool bNvidiaHardwarePCF, bool bFetch4) +{ + vec2 vPoissonOffset[8] = vec2[8]( vec2( 0.3475, 0.0042 ), + vec2( 0.8806, 0.3430 ), + vec2( -0.0041, -0.6197 ), + vec2( 0.0472, 0.4964 ), + vec2( -0.3730, 0.0874 ), + vec2( -0.9217, -0.3177 ), + vec2( -0.6289, 0.7388 ), + vec2( 0.5744, -0.7741 ) ); + + float flScaleOverMapSize = vShadowTweaks.x * 2.0; // Tweak parameters to shader + vec2 vNoiseOffset = vShadowTweaks.zw; + vec4 vLightDepths = vec4(0.0), accum = vec4(0.0); + vec2 rotOffset = vec2(0.0); + + vec2 shadowMapCenter = vProjCoords.xy; // Center of shadow filter + float objDepth = min(vProjCoords.z, 0.99999); // Object depth in shadow space + + // 2D Rotation Matrix setup + vec3 RMatTop = vec3(0.0), RMatBottom = vec3(0.0); + RMatTop.xy = texture(RandomRotationSampler, cFlashlightScreenScale.xy * (vScreenPos * 0.5 + 0.5) + vNoiseOffset).xy * 2.0 - 1.0; + RMatBottom.xy = vec2(-1.0, 1.0) * RMatTop.yx; // 2x2 rotation matrix in 4-tuple + + RMatTop *= flScaleOverMapSize; // Scale up kernel while accounting for texture resolution + RMatBottom *= flScaleOverMapSize; + + RMatTop.z = shadowMapCenter.x; // To be added in d2adds generated below + RMatBottom.z = shadowMapCenter.y; + + float fResult = 0.0; + + if (bNvidiaHardwarePCF) + { + for (int i = 0; i < 8; i++) + { + rotOffset.x = dot(RMatTop.xy, vPoissonOffset[i].xy) + RMatTop.z; + rotOffset.y = dot(RMatBottom.xy, vPoissonOffset[i].xy) + RMatBottom.z; + vLightDepths[i & 3] += texture(DepthSampler, vec3(rotOffset, objDepth)); + } + + fResult = dot(vLightDepths, vec4(0.25, 0.25, 0.25, 0.25)); + } + else if (bFetch4) + { + for (int i = 0; i < 8; i++) + { + rotOffset.x = dot(RMatTop.xy, vPoissonOffset[i].xy) + RMatTop.z; + rotOffset.y = dot(RMatBottom.xy, vPoissonOffset[i].xy) + RMatBottom.z; + vLightDepths = texture(DepthSamplerRaw, rotOffset.xy); + accum += vec4(greaterThan(vLightDepths, vec4(objDepth))); + } + + fResult = dot(accum, vec4(1.0 / 32.0, 1.0 / 32.0, 1.0 / 32.0, 1.0 / 32.0)); + } + else // ATI vanilla hardware shadow mapping + { + for (int i = 0; i < 2; i++) + { + rotOffset.x = dot(RMatTop.xy, vPoissonOffset[4 * i + 0].xy) + RMatTop.z; + rotOffset.y = dot(RMatBottom.xy, vPoissonOffset[4 * i + 0].xy) + RMatBottom.z; + vLightDepths.x = texture(DepthSamplerRaw, rotOffset.xy).x; + + rotOffset.x = dot(RMatTop.xy, vPoissonOffset[4 * i + 1].xy) + RMatTop.z; + rotOffset.y = dot(RMatBottom.xy, vPoissonOffset[4 * i + 1].xy) + RMatBottom.z; + vLightDepths.y = texture(DepthSamplerRaw, rotOffset.xy).x; + + rotOffset.x = dot(RMatTop.xy, vPoissonOffset[4 * i + 2].xy) + RMatTop.z; + rotOffset.y = dot(RMatBottom.xy, vPoissonOffset[4 * i + 2].xy) + RMatBottom.z; + vLightDepths.z = texture(DepthSamplerRaw, rotOffset.xy).x; + + rotOffset.x = dot(RMatTop.xy, vPoissonOffset[4 * i + 3].xy) + RMatTop.z; + rotOffset.y = dot(RMatBottom.xy, vPoissonOffset[4 * i + 3].xy) + RMatBottom.z; + vLightDepths.w = texture(DepthSamplerRaw, rotOffset.xy).x; + + accum += vec4(greaterThan(vLightDepths, vec4(objDepth))); + } + + fResult = dot(accum, vec4(0.125, 0.125, 0.125, 0.125)); + } + + return fResult; +} + +float DoFlashlightShadow(sampler2DShadow DepthSampler, sampler2D DepthSamplerRaw, sampler2D RandomRotationSampler, vec3 vProjCoords, vec2 vScreenPos, int nShadowLevel, vec4 vShadowTweaks, bool bAllowHighQuality) +{ + float flShadow = 1.0; + + if (nShadowLevel == NVIDIA_PCF_POISSON) + flShadow = DoShadowPoisson16Sample(DepthSampler, DepthSamplerRaw, RandomRotationSampler, vProjCoords, vScreenPos, vShadowTweaks, true, false); + else if (nShadowLevel == ATI_NOPCF) + flShadow = DoShadowPoisson16Sample(DepthSampler, DepthSamplerRaw, RandomRotationSampler, vProjCoords, vScreenPos, vShadowTweaks, false, false); + else if (nShadowLevel == ATI_NO_PCF_FETCH4) + flShadow = DoShadowPoisson16Sample(DepthSampler, DepthSamplerRaw, RandomRotationSampler, vProjCoords, vScreenPos, vShadowTweaks, false, true); + + return flShadow; +} + +vec3 SpecularLight(vec3 vWorldNormal, vec3 vLightDir, float fSpecularExponent, + vec3 vEyeDir, bool bDoSpecularWarp, sampler2D specularWarpSampler, float fFresnel) +{ + vec3 result = vec3(0.0, 0.0, 0.0); + + vec3 vReflect = 2.0 * vWorldNormal * dot(vWorldNormal, vEyeDir) - vEyeDir; // Reflect view through normal + vec3 vSpecular = vec3(clamp(dot(vReflect, vLightDir), 0.0, 1.0)); // L.R (use half-angle instead?) + vSpecular = vec3(pow(vSpecular.x, fSpecularExponent)); // Raise to specular power + + // Optionally warp as function of scalar specular and fresnel + if (bDoSpecularWarp) + vSpecular *= texture(specularWarpSampler, vec2(vSpecular.x, fFresnel)).xyz; // Sample at { (L.R)^k, fresnel } + + return vSpecular; +} + +void DoSpecularFlashlight(vec3 flashlightPos, vec3 worldPos, vec4 flashlightSpacePosition, vec3 worldNormal, + vec3 attenuationFactors, float farZ, sampler2D FlashlightSampler, sampler2DShadow FlashlightDepthSampler, sampler2D FlashlightDepthSamplerRaw, sampler2D RandomRotationSampler, + int nShadowLevel, bool bDoShadows, bool bAllowHighQuality, vec2 vScreenPos, float fSpecularExponent, vec3 vEyeDir, + bool bDoSpecularWarp, sampler2D specularWarpSampler, float fFresnel, vec4 vShadowTweaks, + + // Outputs of this shader...separate shadowed diffuse and specular from the flashlight + out vec3 diffuseLighting, out vec3 specularLighting) +{ + vec3 vProjCoords = flashlightSpacePosition.xyz / flashlightSpacePosition.w; + vec3 flashlightColor = texture(FlashlightSampler, vProjCoords.xy).xyz; + + flashlightColor *= cFlashlightColor.xyz; // Flashlight color + + vec3 delta = flashlightPos - worldPos; + vec3 L = normalize(delta); + float distSquared = dot(delta, delta); + float dist = sqrt(distSquared); + + float endFalloffFactor = RemapValClamped(dist, farZ, 0.6 * farZ, 0.0, 1.0); + + // Attenuation for light and to fade out shadow over distance + float fAtten = clamp(dot(attenuationFactors, vec3(1.0, 1.0 / dist, 1.0 / distSquared)), 0.0, 1.0); + + // Shadowing and coloring terms + if (bDoShadows) + { + float flShadow = DoFlashlightShadow(FlashlightDepthSampler, FlashlightDepthSamplerRaw, RandomRotationSampler, vProjCoords, vScreenPos, nShadowLevel, vShadowTweaks, bAllowHighQuality); + float flAttenuated = mix(flShadow, 1.0, vShadowTweaks.y); // Blend between fully attenuated and not attenuated + flShadow = clamp(mix(flAttenuated, flShadow, fAtten), 0.0, 1.0); // Blend between shadow and above, according to light attenuation + flashlightColor *= flShadow; // Shadow term + } + + diffuseLighting = vec3(fAtten); + diffuseLighting *= clamp(dot(L.xyz, worldNormal.xyz) + flFlashlightNoLambertValue, 0.0, 1.0); // Lambertian term + diffuseLighting *= flashlightColor; + diffuseLighting *= endFalloffFactor; + + // Specular term (masked by diffuse) + specularLighting = diffuseLighting * SpecularLight(worldNormal, L, fSpecularExponent, vEyeDir, bDoSpecularWarp, specularWarpSampler, fFresnel); +} + +// Diffuse only version +vec3 DoFlashlight(vec3 flashlightPos, vec3 worldPos, vec4 flashlightSpacePosition, vec3 worldNormal, + vec3 attenuationFactors, float farZ, sampler2D FlashlightSampler, sampler2DShadow FlashlightDepthSampler, sampler2D FlashlightDepthSamplerRaw, + sampler2D RandomRotationSampler, int nShadowLevel, bool bDoShadows, bool bAllowHighQuality, + vec2 vScreenPos, bool bClip, vec4 vShadowTweaks, bool bHasNormal) +{ + vec3 vProjCoords = flashlightSpacePosition.xyz / flashlightSpacePosition.w; + vec3 flashlightColor = texture(FlashlightSampler, vProjCoords.xy).xyz; + + flashlightColor *= cFlashlightColor.xyz; // Flashlight color + + vec3 delta = flashlightPos - worldPos; + vec3 L = normalize(delta); + float distSquared = dot(delta, delta); + float dist = sqrt(distSquared); + + float endFalloffFactor = RemapValClamped(dist, farZ, 0.6 * farZ, 0.0, 1.0); + + // Attenuation for light and to fade out shadow over distance + float fAtten = clamp(dot(attenuationFactors, vec3(1.0, 1.0 / dist, 1.0 / distSquared)), 0.0, 1.0); + + // Shadowing and coloring terms + if (bDoShadows) + { + float flShadow = DoFlashlightShadow(FlashlightDepthSampler, FlashlightDepthSamplerRaw, RandomRotationSampler, vProjCoords, vScreenPos, nShadowLevel, vShadowTweaks, bAllowHighQuality); + float flAttenuated = mix(flShadow, 1.0, vShadowTweaks.y); // Blend between fully attenuated and not attenuated + flShadow = clamp(mix(flAttenuated, flShadow, fAtten), 0.0, 1.0); // Blend between shadow and above, according to light attenuation + flashlightColor *= flShadow; // Shadow term + } + + vec3 diffuseLighting = vec3(fAtten); + + float flLDotWorldNormal; + if (bHasNormal) + { + flLDotWorldNormal = dot(L.xyz, worldNormal.xyz); + } + else + { + flLDotWorldNormal = 1.0; + } + + diffuseLighting *= clamp(flLDotWorldNormal + flFlashlightNoLambertValue, 0.0, 1.0); // Lambertian term + + diffuseLighting *= flashlightColor; + diffuseLighting *= endFalloffFactor; + + return diffuseLighting; +} + +#endif // COMMON_FLASHLIGHT_GL460_FS diff --git a/Game.Assets/hl2/shaders/common_gl460.fs b/Game.Assets/hl2/shaders/common_gl460.fs index ded80b5c..3591b696 100644 --- a/Game.Assets/hl2/shaders/common_gl460.fs +++ b/Game.Assets/hl2/shaders/common_gl460.fs @@ -3,6 +3,40 @@ #include "common_gl460.glsl" +// System defined pixel shader constants + +// NOTE: w == 1.0f / (Dest alpha compressed depth range). +#define g_LinearFogColor ps_const[29] +#define OO_DESTALPHA_DEPTH_RANGE (g_LinearFogColor.w) + +// Linear and gamma light scale values +#define cLightScale ps_const[30] +#define LINEAR_LIGHT_SCALE (cLightScale.x) +#define LIGHT_MAP_SCALE (cLightScale.y) +#define ENV_MAP_SCALE (cLightScale.z) +#define GAMMA_LIGHT_SCALE (cLightScale.w) + +// Flashlight constants +#define cFlashlightColor ps_const[28] +#define cFlashlightScreenScale ps_const[31] // .zw are currently unused +#define flFlashlightNoLambertValue cFlashlightColor.w // This is either 0.0 or 2.0 + +#define HDR_INPUT_MAP_SCALE 16.0 + +#define TONEMAP_SCALE_NONE 0 +#define TONEMAP_SCALE_LINEAR 1 +#define TONEMAP_SCALE_GAMMA 2 + +#define PIXEL_FOG_TYPE_NONE -1 //MATERIAL_FOG_NONE is handled by PIXEL_FOG_TYPE_RANGE, this is for explicitly disabling fog in the shader +#define PIXEL_FOG_TYPE_RANGE 0 //range+none packed together in ps2b. Simply none in ps20 (instruction limits) +#define PIXEL_FOG_TYPE_HEIGHT 1 +#define PIXEL_FOG_TYPE_RANGE_RADIAL 2 + +// If you change these, make the corresponding change in hardwareconfig.cpp +#define NVIDIA_PCF_POISSON 0 +#define ATI_NOPCF 1 +#define ATI_NO_PCF_FETCH4 2 + // texture combining modes for combining base and detail/basetexture2 #define TCOMBINE_RGB_EQUALS_BASE_x_DETAILx2 0 // original mode #define TCOMBINE_RGB_ADDITIVE 1 // base.rgb+detail.rgb*fblend @@ -71,4 +105,133 @@ vec3 TextureCombinePostLighting(vec3 lit_baseColor, vec4 detailColor, int combin return lit_baseColor; } +float CalcWaterFogAlpha(float flWaterZ, float flEyePosZ, float flWorldPosZ, float flProjPosZ, float flFogOORange) +{ + float flDepthFromWater = flWaterZ - flWorldPosZ; + + // Calculate the ratio of water fog to regular fog (ie. how much of the distance from the viewer + // to the vert is actually underwater. + float flDepthFromEye = flEyePosZ - flWorldPosZ; + float f = clamp(flDepthFromWater * (1.0 / flDepthFromEye), 0.0, 1.0); + + // $tmp.w is now the distance that we see through water. + return clamp(f * flProjPosZ * flFogOORange, 0.0, 1.0); +} + +float CalcRangeFog(float flProjPosZ, float flFogStartOverRange, float flFogMaxDensity, float flFogOORange) +{ + return clamp(min(flFogMaxDensity, (flProjPosZ * flFogOORange) - flFogStartOverRange), 0.0, 1.0); +} + +float CalcPixelFogFactor(int iPIXELFOGTYPE, vec4 fogParams, float flEyePosZ, float flWorldPosZ, float flProjPosZ) +{ + float retVal = 0.0; + if (iPIXELFOGTYPE == PIXEL_FOG_TYPE_NONE) + { + retVal = 0.0; + } + else if (iPIXELFOGTYPE == PIXEL_FOG_TYPE_RANGE) //range fog, or no fog depending on fog parameters + { + retVal = CalcRangeFog(flProjPosZ, fogParams.x, fogParams.z, fogParams.w); + } + else if (iPIXELFOGTYPE == PIXEL_FOG_TYPE_HEIGHT) //height fog + { + retVal = CalcWaterFogAlpha(fogParams.y, flEyePosZ, flWorldPosZ, flProjPosZ, fogParams.w); + } + + return retVal; +} + +//g_FogParams not defined by default, but this is the same layout for every shader that does define it +#define g_FogEndOverRange g_FogParams.x +#define g_WaterZ g_FogParams.y +#define g_FogMaxDensity g_FogParams.z +#define g_FogOORange g_FogParams.w + +vec3 BlendPixelFog(vec3 vShaderColor, float pixelFogFactor, vec3 vFogColor, int iPIXELFOGTYPE) +{ + if (iPIXELFOGTYPE == PIXEL_FOG_TYPE_RANGE || iPIXELFOGTYPE == PIXEL_FOG_TYPE_RANGE_RADIAL) //either range fog or no fog depending on fog parameters and whether this is ps20 or ps2b + { + pixelFogFactor = clamp(pixelFogFactor, 0.0, 1.0); + return mix(vShaderColor.rgb, vFogColor.rgb, pixelFogFactor * pixelFogFactor); //squaring the factor will get the middle range mixing closer to hardware fog + } + else if (iPIXELFOGTYPE == PIXEL_FOG_TYPE_HEIGHT) + { + return mix(vShaderColor.rgb, vFogColor.rgb, clamp(pixelFogFactor, 0.0, 1.0)); + } + return vShaderColor; +} + +// The framebuffer performs the linear->gamma conversion for us (GL_FRAMEBUFFER_SRGB), which is +// the equivalent of the CONVERT_TO_SRGB == 0 path. +vec3 SRGBOutput(vec3 vShaderColor) +{ + return vShaderColor; +} + +float SoftParticleDepth(float flDepth) +{ + return flDepth * OO_DESTALPHA_DEPTH_RANGE; +} + +float DepthToDestAlpha(float flProjZ) +{ + return SoftParticleDepth(flProjZ); +} + +vec4 FinalOutput(vec4 vShaderColor, float pixelFogFactor, int iPIXELFOGTYPE, int iTONEMAP_SCALE_TYPE, bool bWriteDepthToDestAlpha, float flProjZ) +{ + vec4 result; + if (iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_LINEAR) + { + result.rgb = vShaderColor.rgb * LINEAR_LIGHT_SCALE; + } + else if (iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_GAMMA) + { + result.rgb = vShaderColor.rgb * GAMMA_LIGHT_SCALE; + } + else if (iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_NONE) + { + result.rgb = vShaderColor.rgb; + } + + if (bWriteDepthToDestAlpha) + result.a = DepthToDestAlpha(flProjZ); + else + result.a = vShaderColor.a; + + // TODO: fog + // result.rgb = BlendPixelFog(result.rgb, pixelFogFactor, g_LinearFogColor.rgb, iPIXELFOGTYPE); + + result.rgb = SRGBOutput(result.rgb); //SRGB in pixel shader conversion + + return result; +} + +vec4 FinalOutput(vec4 vShaderColor, float pixelFogFactor, int iPIXELFOGTYPE, int iTONEMAP_SCALE_TYPE) +{ + return FinalOutput(vShaderColor, pixelFogFactor, iPIXELFOGTYPE, iTONEMAP_SCALE_TYPE, false, 1.0); +} + +float RemapValClamped(float val, float A, float B, float C, float D) +{ + float cVal = (val - A) / (B - A); + cVal = clamp(cVal, 0.0, 1.0); + + return C + (D - C) * cVal; +} + +float DepthFeathering(sampler2D DepthSampler, vec2 vScreenPos, float fProjZ, float fProjW, vec4 vDepthBlendConstants) +{ + float flFeatheredAlpha; + float flSceneDepth = texture(DepthSampler, vScreenPos).a; // PC uses dest alpha of the frame buffer + float flSpriteDepth = SoftParticleDepth(fProjZ); + + flFeatheredAlpha = abs(flSceneDepth - flSpriteDepth) * vDepthBlendConstants.x; + flFeatheredAlpha = max(smoothstep(0.75, 1.0, flSceneDepth), flFeatheredAlpha); //as the sprite approaches the edge of our compressed depth space, the math stops working. So as the sprite approaches the far depth, smoothly remove feathering. + flFeatheredAlpha = clamp(flFeatheredAlpha, 0.0, 1.0); + + return flFeatheredAlpha; +} + #endif // COMMON_GL460_FS diff --git a/Game.Assets/hl2/shaders/common_gl460.glsl b/Game.Assets/hl2/shaders/common_gl460.glsl index 4049a35d..d4b0cade 100644 --- a/Game.Assets/hl2/shaders/common_gl460.glsl +++ b/Game.Assets/hl2/shaders/common_gl460.glsl @@ -1,6 +1,30 @@ #ifndef COMMON_GL460_GLSL #define COMMON_GL460_GLSL +vec3 CalcReflectionVectorUnnormalized(vec3 normal, vec3 eyeVector) +{ + // FIXME: might be better of normalizing with a normalizing cube map and + // get rid of the dot( normal, normal ) + // compute reflection vector r = 2 * ((n dot v)/(n dot n)) n - v + // multiply all values through by N.N. uniformly scaling reflection vector won't affect result + // since it is used in a cubemap lookup + return (2.0 * (dot(normal, eyeVector)) * normal) - (dot(normal, normal) * eyeVector); +} + +vec3 Vec3TangentToWorld(vec3 iTangentVector, vec3 iWorldNormal, vec3 iWorldTangent, vec3 iWorldBinormal) +{ + vec3 vWorldVector; + vWorldVector.xyz = iTangentVector.x * iWorldTangent.xyz; + vWorldVector.xyz += iTangentVector.y * iWorldBinormal.xyz; + vWorldVector.xyz += iTangentVector.z * iWorldNormal.xyz; + return vWorldVector.xyz; // Return without normalizing +} + +vec3 Vec3TangentToWorldNormalized(vec3 iTangentVector, vec3 iWorldNormal, vec3 iWorldTangent, vec3 iWorldBinormal) +{ + return normalize(Vec3TangentToWorld(iTangentVector, iWorldNormal, iWorldTangent, iWorldBinormal)); +} + vec3 LinearToGamma(vec3 f3linear) { return pow(f3linear, vec3(1.0 / 2.2)); diff --git a/Game.Assets/hl2/shaders/common_gl460.vs b/Game.Assets/hl2/shaders/common_gl460.vs index b9919971..b3a5c68b 100644 --- a/Game.Assets/hl2/shaders/common_gl460.vs +++ b/Game.Assets/hl2/shaders/common_gl460.vs @@ -3,6 +3,27 @@ #include "common_gl460.glsl" +#define FOGTYPE_RANGE 0 +#define FOGTYPE_HEIGHT 1 +#define FOGTYPE_RANGE_RADIAL 2 + +#define cOverbright 2.0 +#define cOOOverbright (1.0 / 2.0) + +#define cEyePosWaterZ vs_const[VERTEX_SHADER_CAMERA_POS] +#define cEyePos cEyePosWaterZ.xyz + +#define cViewProjZ vs_const[13] + +#define cFogParams vs_const[16] +#define cFogEndOverFogRange cFogParams.x +#define cFogOne cFogParams.y +#define cFogMaxDensity cFogParams.z +#define cOOFogRange cFogParams.w + +#define g_bLightEnabled lightEnabled +#define g_nLightCount lightCount + struct LightInfo { vec4 color; @@ -12,8 +33,6 @@ struct LightInfo vec4 atten; }; -bool g_bLightEnabled[4]; - // Four lights x 5 constants each = 20 constants LightInfo cLightInfo[4]; @@ -40,6 +59,24 @@ vec3 AmbientLight(vec3 worldNormal) return color; } +float RangeFog(vec3 projPos) +{ + return max(cFogMaxDensity, (-projPos.z * cOOFogRange + cFogEndOverFogRange)); +} + +float CalcFog(vec3 worldPos, vec3 projPos, int fogType) +{ + if (fogType == FOGTYPE_RANGE) + { + return RangeFog(projPos); + } + else + { + // We do this work in the pixel shader in dx9, so don't do any fog here. + return 1.0; + } +} + // The following "internal" routines are called "privately" by other routines in this file which // handle the particular flavor of vs20 control flow appropriate to the original caller float VertexAttenInternal(vec3 worldPos, int lightNum) @@ -110,7 +147,7 @@ float GetVertexAttenForLight(vec3 worldPos, int lightNum, bool useStaticControlF if (useStaticControlFlow) { - if (g_bLightEnabled[lightNum]) + if (g_bLightEnabled[lightNum] != 0.0) result = VertexAttenInternal(worldPos, lightNum); } else @@ -126,6 +163,34 @@ vec3 DoLightInternal(vec3 worldPos, vec3 worldNormal, int lightNum, bool bHalfLa VertexAttenInternal(worldPos, lightNum); } +vec3 DoLighting(vec3 worldPos, vec3 worldNormal, + vec3 staticLightingColor, bool bStaticLight, + bool bDynamicLight, bool bHalfLambert) +{ + vec3 linearColor = vec3(0.0, 0.0, 0.0); + + if (bStaticLight) // Static light + { + vec3 col = staticLightingColor * cOverbright; + linearColor += GammaToLinear(col); + } + + if (bDynamicLight) // Dynamic light + { + for (int i = 0; i < g_nLightCount; i++) + { + linearColor += DoLightInternal(worldPos, worldNormal, i, bHalfLambert); + } + } + + if (bDynamicLight) + { + linearColor += AmbientLight(worldNormal); //ambient light is already remapped + } + + return linearColor; +} + vec3 DoLightingUnrolled(vec3 worldPos, vec3 worldNormal, vec3 staticLightingColor, bool bStaticLight, bool bDynamicLight, bool bHalfLambert, int nNumLights) @@ -153,4 +218,59 @@ vec3 DoLightingUnrolled(vec3 worldPos, vec3 worldNormal, return linearColor; } +void SkinPositionAndNormal(bool bSkinning, vec4 modelPos, vec3 modelNormal, + ivec4 boneIndices, vec2 boneWeights, + out vec3 worldPos, out vec3 worldNormal) +{ + if (!bSkinning || numBones == 0) + { + worldPos = (modelMatrix * modelPos).xyz; + worldNormal = mat3(modelMatrix) * modelNormal; + } + else // skinning - always three bones + { + vec3 weights; + weights[0] = boneWeights.x; + weights[1] = boneWeights.y; + weights[2] = 1.0 - (boneWeights.x + boneWeights.y); + + mat4 blendMatrix = bones[boneIndices[0]] * weights[0] + + bones[boneIndices[1]] * weights[1] + + bones[boneIndices[2]] * weights[2]; + + worldPos = (blendMatrix * modelPos).xyz; + worldNormal = mat3(blendMatrix) * modelNormal; + } +} + +void SkinPositionNormalAndTangentSpace(bool bSkinning, vec4 modelPos, vec3 modelNormal, vec4 modelTangentS, + ivec4 boneIndices, vec2 boneWeights, + out vec3 worldPos, out vec3 worldNormal, + out vec3 worldTangentS, out vec3 worldTangentT) +{ + if (!bSkinning || numBones == 0) + { + worldPos = (modelMatrix * modelPos).xyz; + worldNormal = mat3(modelMatrix) * modelNormal; + worldTangentS = mat3(modelMatrix) * modelTangentS.xyz; + } + else // skinning - always three bones + { + vec3 weights; + weights[0] = boneWeights.x; + weights[1] = boneWeights.y; + weights[2] = 1.0 - (boneWeights.x + boneWeights.y); + + mat4 blendMatrix = bones[boneIndices[0]] * weights[0] + + bones[boneIndices[1]] * weights[1] + + bones[boneIndices[2]] * weights[2]; + + worldPos = (blendMatrix * modelPos).xyz; + worldNormal = mat3(blendMatrix) * modelNormal; + worldTangentS = mat3(blendMatrix) * modelTangentS.xyz; + } + + worldTangentT = cross(worldNormal, worldTangentS) * modelTangentS.w; +} + #endif // COMMON_GL460_VS diff --git a/Game.Assets/hl2/shaders/common_vertexlitgeneric_gl460.fs b/Game.Assets/hl2/shaders/common_vertexlitgeneric_gl460.fs new file mode 100644 index 00000000..b4d04d43 --- /dev/null +++ b/Game.Assets/hl2/shaders/common_vertexlitgeneric_gl460.fs @@ -0,0 +1,377 @@ +#ifndef COMMON_VERTEXLITGENERIC_GL460_FS +#define COMMON_VERTEXLITGENERIC_GL460_FS + +#include "common_gl460.fs" + +// We store four light colors and positions in an +// array of three of these structures like so: +// +// x y z w +// +------+------+------+------+ +// | L0.rgb | | +// +------+------+------+ | +// | L0.pos | L3 | +// +------+------+------+ rgb | +// | L1.rgb | | +// +------+------+------+------+ +// | L1.pos | | +// +------+------+------+ | +// | L2.rgb | L3 | +// +------+------+------+ pos | +// | L2.pos | | +// +------+------+------+------+ +// +struct PixelShaderLightInfo +{ + vec4 color; + vec4 pos; +}; + +#define cOverbright 2.0 +#define cOOOverbright 0.5 + +#define LIGHTTYPE_NONE 0 +#define LIGHTTYPE_SPOT 1 +#define LIGHTTYPE_POINT 2 +#define LIGHTTYPE_DIRECTIONAL 3 + +// Better suited to Pixel shader models, 11 instructions in pixel shader +// ... actually, now only 9: mul, cmp, cmp, mul, mad, mad, mad, mad, mad +vec3 PixelShaderAmbientLight(vec3 worldNormal, vec3 cAmbientCube[6]) +{ + vec3 linearColor, nSquared = worldNormal * worldNormal; + vec3 isNegative = mix(nSquared, vec3(0.0), greaterThanEqual(worldNormal, vec3(0.0))); + vec3 isPositive = mix(vec3(0.0), nSquared, greaterThanEqual(worldNormal, vec3(0.0))); + linearColor = isPositive.x * cAmbientCube[0] + isNegative.x * cAmbientCube[1] + + isPositive.y * cAmbientCube[2] + isNegative.y * cAmbientCube[3] + + isPositive.z * cAmbientCube[4] + isNegative.z * cAmbientCube[5]; + return linearColor; +} + +vec3 AmbientLight(vec3 worldNormal, vec3 cAmbientCube[6]) +{ + // Pixel shader case + return PixelShaderAmbientLight(worldNormal, cAmbientCube); +} + +//----------------------------------------------------------------------------- +// Purpose: Compute scalar diffuse term with various optional tweaks such as +// Half Lambert and ambient occlusion +//----------------------------------------------------------------------------- +vec3 DiffuseTerm(bool bHalfLambert, vec3 worldNormal, vec3 lightDir, + bool bDoAmbientOcclusion, float fAmbientOcclusion, + bool bDoLightingWarp, sampler2D lightWarpSampler) +{ + float fResult; + + float NDotL = dot(worldNormal, lightDir); // Unsaturated dot (-1 to 1 range) + + if (bHalfLambert) + { + fResult = clamp(NDotL * 0.5 + 0.5, 0.0, 1.0); // Scale and bias to 0 to 1 range + + if (!bDoLightingWarp) + { + fResult *= fResult; // Square + } + } + else + { + fResult = clamp(NDotL, 0.0, 1.0); // Saturate pure Lambertian term + } + + if (bDoAmbientOcclusion) + { + // Raise to higher powers for darker AO values + fResult *= fAmbientOcclusion; + } + + vec3 fOut = vec3(fResult, fResult, fResult); + if (bDoLightingWarp) + { + fOut = 2.0 * texture(lightWarpSampler, vec2(fResult, 0.0)).xyz; + } + + return fOut; +} + +vec3 PixelShaderDoGeneralDiffuseLight(float fAtten, vec3 worldPos, vec3 worldNormal, + vec3 vPosition, vec3 vColor, bool bHalfLambert, + bool bDoAmbientOcclusion, float fAmbientOcclusion, + bool bDoLightingWarp, sampler2D lightWarpSampler) +{ + vec3 lightDir = normalize(vPosition - worldPos); + return vColor * fAtten * DiffuseTerm(bHalfLambert, worldNormal, lightDir, bDoAmbientOcclusion, fAmbientOcclusion, bDoLightingWarp, lightWarpSampler); +} + +vec3 PixelShaderGetLightVector(vec3 worldPos, PixelShaderLightInfo cLightInfo[3], int nLightIndex) +{ + if (nLightIndex == 3) + { + // Unpack light 3 from w components... + vec3 vLight3Pos = vec3(cLightInfo[1].pos.w, cLightInfo[2].color.w, cLightInfo[2].pos.w); + return normalize(vLight3Pos - worldPos); + } + else + { + vec4 world4Pos = vec4(worldPos.x, worldPos.y, worldPos.z, 0.0); + return normalize(cLightInfo[nLightIndex].pos - world4Pos).xyz; + } +} + +vec3 PixelShaderGetLightColor(PixelShaderLightInfo cLightInfo[3], int nLightIndex) +{ + if (nLightIndex == 3) + { + // Unpack light 3 from w components... + return vec3(cLightInfo[0].color.w, cLightInfo[0].pos.w, cLightInfo[1].color.w); + } + else + { + return cLightInfo[nLightIndex].color.rgb; + } +} + +void SpecularAndRimTerms(vec3 vWorldNormal, vec3 vLightDir, float fSpecularExponent, + vec3 vEyeDir, bool bDoAmbientOcclusion, float fAmbientOcclusion, + bool bDoSpecularWarp, sampler2D specularWarpSampler, float fFresnel, + vec3 color, bool bDoRimLighting, float fRimExponent, + + // Outputs + out vec3 specularLighting, out vec3 rimLighting) +{ + rimLighting = vec3(0.0, 0.0, 0.0); + + vec3 vReflect = 2.0 * vWorldNormal * dot(vWorldNormal, vEyeDir) - vEyeDir; // Reflect view through normal + float LdotR = clamp(dot(vReflect, vLightDir), 0.0, 1.0); // L.R (use half-angle instead?) + specularLighting = vec3(pow(LdotR, fSpecularExponent)); // Raise to specular exponent + + // Optionally warp as function of scalar specular and fresnel + if (bDoSpecularWarp) + specularLighting *= texture(specularWarpSampler, vec2(specularLighting.x, fFresnel)).xyz; // Sample at { (L.R)^k, fresnel } + + specularLighting *= clamp(dot(vWorldNormal, vLightDir), 0.0, 1.0); // Mask with N.L + specularLighting *= color; // Modulate with light color + + if (bDoAmbientOcclusion) // Optionally modulate with ambient occlusion + specularLighting *= fAmbientOcclusion; + + if (bDoRimLighting) // Optionally do rim lighting + { + rimLighting = vec3(pow(LdotR, fRimExponent)); // Raise to rim exponent + rimLighting *= clamp(dot(vWorldNormal, vLightDir), 0.0, 1.0); // Mask with N.L + rimLighting *= color; // Modulate with light color + } +} + +// Traditional fresnel term approximation +float Fresnel(vec3 vNormal, vec3 vEyeDir) +{ + float fresnel = clamp(1.0 - dot(vNormal, vEyeDir), 0.0, 1.0); // 1-(N.V) for Fresnel term + return fresnel * fresnel; // Square for a more subtle look +} + +// Traditional fresnel term approximation which uses 4th power (square twice) +float Fresnel4(vec3 vNormal, vec3 vEyeDir) +{ + float fresnel = clamp(1.0 - dot(vNormal, vEyeDir), 0.0, 1.0); // 1-(N.V) for Fresnel term + fresnel = fresnel * fresnel; // Square + return fresnel * fresnel; // Square again for a more subtle look +} + +// +// Custom Fresnel with low, mid and high parameters defining a piecewise continuous function +// with traditional fresnel (0 to 1 range) as input. The 0 to 0.5 range blends between +// low and mid while the 0.5 to 1 range blends between mid and high +// +// | +// | . M . . . H +// | . +// L +// | +// +---------------- +// 0 1 +// +float Fresnel(vec3 vNormal, vec3 vEyeDir, vec3 vRanges) +{ + // note: vRanges is now encoded as ((mid-min)*2, mid, (max-mid)*2) to optimize math + float f = clamp(1.0 - dot(vNormal, vEyeDir), 0.0, 1.0); + f = f * f - 0.5; + return vRanges.y + (f >= 0.0 ? vRanges.z : vRanges.x) * f; +} + +void PixelShaderDoSpecularLight(vec3 vWorldPos, vec3 vWorldNormal, float fSpecularExponent, vec3 vEyeDir, + float fAtten, vec3 vLightColor, vec3 vLightDir, + bool bDoAmbientOcclusion, float fAmbientOcclusion, + bool bDoSpecularWarp, sampler2D specularWarpSampler, float fFresnel, + bool bDoRimLighting, float fRimExponent, + + // Outputs + out vec3 specularLighting, out vec3 rimLighting) +{ + // Compute Specular and rim terms + SpecularAndRimTerms(vWorldNormal, vLightDir, fSpecularExponent, + vEyeDir, bDoAmbientOcclusion, fAmbientOcclusion, + bDoSpecularWarp, specularWarpSampler, fFresnel, vLightColor * fAtten, + bDoRimLighting, fRimExponent, specularLighting, rimLighting); +} + +vec3 PixelShaderDoLightingLinear(vec3 worldPos, vec3 worldNormal, + vec3 staticLightingColor, bool bStaticLight, + bool bAmbientLight, vec4 lightAtten, vec3 cAmbientCube[6], + int nNumLights, PixelShaderLightInfo cLightInfo[3], + bool bHalfLambert, bool bDoAmbientOcclusion, float fAmbientOcclusion, + bool bDoLightingWarp, sampler2D lightWarpSampler) +{ + vec3 linearColor = vec3(0.0); + + if (bStaticLight) + { + // The static lighting comes in in gamma space and has also been premultiplied by $cOOOverbright + // need to get it into + // linear space so that we can do adds. + linearColor += GammaToLinear(staticLightingColor * cOverbright); + } + + if (bAmbientLight) + { + vec3 ambient = AmbientLight(worldNormal, cAmbientCube); + + if (bDoAmbientOcclusion) + ambient *= fAmbientOcclusion * fAmbientOcclusion; // Note squaring... + + linearColor += ambient; + } + + if (nNumLights > 0) + { + linearColor += PixelShaderDoGeneralDiffuseLight(lightAtten.x, worldPos, worldNormal, + cLightInfo[0].pos.xyz, cLightInfo[0].color.xyz, bHalfLambert, + bDoAmbientOcclusion, fAmbientOcclusion, + bDoLightingWarp, lightWarpSampler); + if (nNumLights > 1) + { + linearColor += PixelShaderDoGeneralDiffuseLight(lightAtten.y, worldPos, worldNormal, + cLightInfo[1].pos.xyz, cLightInfo[1].color.xyz, bHalfLambert, + bDoAmbientOcclusion, fAmbientOcclusion, + bDoLightingWarp, lightWarpSampler); + if (nNumLights > 2) + { + linearColor += PixelShaderDoGeneralDiffuseLight(lightAtten.z, worldPos, worldNormal, + cLightInfo[2].pos.xyz, cLightInfo[2].color.xyz, bHalfLambert, + bDoAmbientOcclusion, fAmbientOcclusion, + bDoLightingWarp, lightWarpSampler); + if (nNumLights > 3) + { + // Unpack the 4th light's data from tight constant packing + vec3 vLight3Color = vec3(cLightInfo[0].color.w, cLightInfo[0].pos.w, cLightInfo[1].color.w); + vec3 vLight3Pos = vec3(cLightInfo[1].pos.w, cLightInfo[2].color.w, cLightInfo[2].pos.w); + linearColor += PixelShaderDoGeneralDiffuseLight(lightAtten.w, worldPos, worldNormal, + vLight3Pos, vLight3Color, bHalfLambert, + bDoAmbientOcclusion, fAmbientOcclusion, + bDoLightingWarp, lightWarpSampler); + } + } + } + } + + return linearColor; +} + +void PixelShaderDoSpecularLighting(vec3 worldPos, vec3 worldNormal, float fSpecularExponent, vec3 vEyeDir, + vec4 lightAtten, int nNumLights, PixelShaderLightInfo cLightInfo[3], + bool bDoAmbientOcclusion, float fAmbientOcclusion, + bool bDoSpecularWarp, sampler2D specularWarpSampler, float fFresnel, + bool bDoRimLighting, float fRimExponent, + + // Outputs + out vec3 specularLighting, out vec3 rimLighting) +{ + specularLighting = rimLighting = vec3(0.0, 0.0, 0.0); + vec3 localSpecularTerm, localRimTerm; + + if (nNumLights > 0) + { + PixelShaderDoSpecularLight(worldPos, worldNormal, fSpecularExponent, vEyeDir, + lightAtten.x, PixelShaderGetLightColor(cLightInfo, 0), + PixelShaderGetLightVector(worldPos, cLightInfo, 0), + bDoAmbientOcclusion, fAmbientOcclusion, + bDoSpecularWarp, specularWarpSampler, fFresnel, + bDoRimLighting, fRimExponent, + localSpecularTerm, localRimTerm); + + specularLighting += localSpecularTerm; // Accumulate specular and rim terms + rimLighting += localRimTerm; + } + + if (nNumLights > 1) + { + PixelShaderDoSpecularLight(worldPos, worldNormal, fSpecularExponent, vEyeDir, + lightAtten.y, PixelShaderGetLightColor(cLightInfo, 1), + PixelShaderGetLightVector(worldPos, cLightInfo, 1), + bDoAmbientOcclusion, fAmbientOcclusion, + bDoSpecularWarp, specularWarpSampler, fFresnel, + bDoRimLighting, fRimExponent, + localSpecularTerm, localRimTerm); + + specularLighting += localSpecularTerm; // Accumulate specular and rim terms + rimLighting += localRimTerm; + } + + if (nNumLights > 2) + { + PixelShaderDoSpecularLight(worldPos, worldNormal, fSpecularExponent, vEyeDir, + lightAtten.z, PixelShaderGetLightColor(cLightInfo, 2), + PixelShaderGetLightVector(worldPos, cLightInfo, 2), + bDoAmbientOcclusion, fAmbientOcclusion, + bDoSpecularWarp, specularWarpSampler, fFresnel, + bDoRimLighting, fRimExponent, + localSpecularTerm, localRimTerm); + + specularLighting += localSpecularTerm; // Accumulate specular and rim terms + rimLighting += localRimTerm; + } + + if (nNumLights > 3) + { + PixelShaderDoSpecularLight(worldPos, worldNormal, fSpecularExponent, vEyeDir, + lightAtten.w, PixelShaderGetLightColor(cLightInfo, 3), + PixelShaderGetLightVector(worldPos, cLightInfo, 3), + bDoAmbientOcclusion, fAmbientOcclusion, + bDoSpecularWarp, specularWarpSampler, fFresnel, + bDoRimLighting, fRimExponent, + localSpecularTerm, localRimTerm); + + specularLighting += localSpecularTerm; // Accumulate specular and rim terms + rimLighting += localRimTerm; + } +} + +vec3 PixelShaderDoRimLighting(vec3 worldNormal, vec3 vEyeDir, vec3 cAmbientCube[6], float fFresnel) +{ + vec3 vReflect = reflect(-vEyeDir, worldNormal); // Reflect view through normal + + return fFresnel * PixelShaderAmbientLight(vEyeDir, cAmbientCube); +} + +// Called directly by newer shaders or through the following wrapper for older shaders +vec3 PixelShaderDoLighting(vec3 worldPos, vec3 worldNormal, + vec3 staticLightingColor, bool bStaticLight, + bool bAmbientLight, vec4 lightAtten, vec3 cAmbientCube[6], + int nNumLights, PixelShaderLightInfo cLightInfo[3], + bool bHalfLambert, + + // New optional/experimental parameters + bool bDoAmbientOcclusion, float fAmbientOcclusion, + bool bDoLightingWarp, sampler2D lightWarpSampler) +{ + vec3 linearColor = PixelShaderDoLightingLinear(worldPos, worldNormal, staticLightingColor, + bStaticLight, bAmbientLight, lightAtten, + cAmbientCube, nNumLights, cLightInfo, bHalfLambert, + bDoAmbientOcclusion, fAmbientOcclusion, + bDoLightingWarp, lightWarpSampler); + + return linearColor; +} + +#endif // COMMON_VERTEXLITGENERIC_GL460_FS diff --git a/Game.Assets/hl2/shaders/skin_gl460.fs b/Game.Assets/hl2/shaders/skin_gl460.fs new file mode 100644 index 00000000..2a9862f9 --- /dev/null +++ b/Game.Assets/hl2/shaders/skin_gl460.fs @@ -0,0 +1,327 @@ +#version 460 +// STATIC: "CONVERT_TO_SRGB" "0..0" +// STATIC: "CUBEMAP" "0..1" +// STATIC: "SELFILLUM" "0..1" +// STATIC: "SELFILLUMFRESNEL" "0..1" +// STATIC: "FLASHLIGHT" "0..1" +// STATIC: "LIGHTWARPTEXTURE" "0..1" +// STATIC: "PHONGWARPTEXTURE" "0..1" +// STATIC: "WRINKLEMAP" "0..1" +// STATIC: "DETAIL_BLEND_MODE" "0..6" +// STATIC: "DETAILTEXTURE" "0..1" +// STATIC: "RIMLIGHT" "0..1" +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" +// STATIC: "FASTPATH_NOBUMP" "0..1" +// STATIC: "BLENDTINTBYBASEALPHA" "0..1" + +// DYNAMIC: "WRITEWATERFOGTODESTALPHA" "0..1" +// DYNAMIC: "PIXELFOGTYPE" "0..1" +// DYNAMIC: "NUM_LIGHTS" "0..4" +// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" + +in vec4 vs_BaseTexCoord; // xy=base zw=detail +in vec3 vs_LightAtten; // Scalar light attenuation factors for FOUR lights +in vec3 vs_WorldVertToEyeVector; +in mat3 vs_TangentSpaceTranspose; +in vec4 vs_WorldPos_Atten3; +in vec4 vs_ProjPos_WrinkleWeight; + +layout(std140, binding = 6) uniform source_ps_constants { + vec4 ps_const[256]; +}; + +out vec4 fragColor; + +#include "common_flashlight_gl460.fs" +#include "common_vertexlitgeneric_gl460.fs" + +#define g_SelfIllumTint_and_DetailBlendFactor ps_const[0] +#define g_SelfIllumScaleBiasExpBrightness ps_const[3] +#define g_DiffuseModulation ps_const[1] +#define g_EnvmapTint_ShadowTweaks ps_const[2] // w controls spec mask +#define g_EnvMapFresnel ps_const[10] // x is envmap fresnel ... w is selfillummask control +#define g_EyePos_SpecExponent ps_const[11] +#define g_FogParams ps_const[12] +#define g_FlashlightAttenuationFactors_RimMask ps_const[13] // On non-flashlight pass, x has rim mask control +#define g_FlashlightPos_RimBoost ps_const[14] +#define g_FlashlightWorldToTexture mat4(ps_const[15], ps_const[16], ps_const[17], ps_const[18]) +#define g_FresnelSpecParams ps_const[19] // xyz are fresnel, w is specular boost +#define g_SpecularRimParams ps_const[26] // xyz are specular tint color, w is rim power + +// TODO: give this a better name. For now, I don't want to touch shader_constant_register_map.h since I don't want to trigger a recompile of everything... +#define g_ShaderControls ps_const[27] // x is basemap alpgha phong mask, y is 1 - blendtintbybasealpha, z is tint overlay amount, w controls "INVERTPHONGMASK" +#define g_FlashlightPos g_FlashlightPos_RimBoost.xyz +#define g_fRimBoost g_FlashlightPos_RimBoost.w +#define g_FresnelRanges g_FresnelSpecParams.xyz +#define g_SpecularBoost g_FresnelSpecParams.w +#define g_SpecularTint g_SpecularRimParams.xyz +#define g_RimExponent g_SpecularRimParams.w +#define g_FlashlightAttenuationFactors g_FlashlightAttenuationFactors_RimMask +#define g_RimMaskControl g_FlashlightAttenuationFactors_RimMask.x +#define g_SelfIllumMaskControl g_EnvMapFresnel.w +#define g_fBaseMapAlphaPhongMask g_ShaderControls.x +#define g_fTintReplacementControl g_ShaderControls.z +#define g_fInvertPhongMask g_ShaderControls.w + +layout(binding = 0) uniform sampler2D BaseTextureSampler; // Base map, selfillum in alpha +layout(binding = 1) uniform sampler2D SpecularWarpSampler; // Specular warp sampler (for iridescence etc) +layout(binding = 2) uniform sampler2D DiffuseWarpSampler; // Lighting warp sampler (1D texture for diffuse lighting modification) +layout(binding = 3) uniform sampler2D NormalMapSampler; // Normal map, specular mask in alpha +layout(binding = 4) uniform sampler2DShadow ShadowDepthSampler; // Flashlight shadow depth map sampler +layout(binding = 4) uniform sampler2D ShadowDepthSamplerRaw; +layout(binding = 5) uniform sampler2D NormalizeRandRotSampler; // Normalization / RandomRotation samplers +layout(binding = 6) uniform sampler2D FlashlightSampler; // Flashlight cookie +layout(binding = 7) uniform sampler2D SpecExponentSampler; // Specular exponent map +layout(binding = 8) uniform samplerCube EnvmapSampler; // Cubic environment map + +#if WRINKLEMAP +layout(binding = 9) uniform sampler2D WrinkleSampler; // Compression base +layout(binding = 10) uniform sampler2D StretchSampler; // Expansion base +layout(binding = 11) uniform sampler2D NormalWrinkleSampler; // Compression base +layout(binding = 12) uniform sampler2D NormalStretchSampler; // Expansion base +#endif + +#if DETAILTEXTURE +layout(binding = 13) uniform sampler2D DetailSampler; // detail texture +#endif + +layout(binding = 14) uniform sampler2D SelfIllumMaskSampler; // selfillummask + +void main() +{ + bool bWrinkleMap = WRINKLEMAP != 0; + bool bDoDiffuseWarp = LIGHTWARPTEXTURE != 0; + bool bDoSpecularWarp = PHONGWARPTEXTURE != 0; + bool bDoAmbientOcclusion = false; + bool bFlashlight = FLASHLIGHT != 0; + bool bSelfIllum = SELFILLUM != 0; + bool bDoRimLighting = RIMLIGHT != 0; + bool bCubemap = CUBEMAP != 0; + bool bBlendTintByBaseAlpha = BLENDTINTBYBASEALPHA != 0; + int nNumLights = NUM_LIGHTS; + + vec3 cAmbientCube[6] = vec3[6](ps_const[4].xyz, ps_const[5].xyz, ps_const[6].xyz, + ps_const[7].xyz, ps_const[8].xyz, ps_const[9].xyz); + + // 2 registers each - 6 registers total (4th light spread across w's) + PixelShaderLightInfo cLightInfo[3] = PixelShaderLightInfo[3]( + PixelShaderLightInfo(ps_const[20], ps_const[21]), + PixelShaderLightInfo(ps_const[22], ps_const[23]), + PixelShaderLightInfo(ps_const[24], ps_const[25])); + + // Unpacking for convenience + float fWrinkleWeight = vs_ProjPos_WrinkleWeight.w; + vec3 vProjPos = vs_ProjPos_WrinkleWeight.xyz; + vec3 vWorldPos = vs_WorldPos_Atten3.xyz; + float atten3 = vs_WorldPos_Atten3.w; + + vec4 vLightAtten = vec4(vs_LightAtten, atten3); + +#if WRINKLEMAP + float flWrinkleAmount = clamp(-fWrinkleWeight, 0.0, 1.0); // One of these two is zero + float flStretchAmount = clamp( fWrinkleWeight, 0.0, 1.0); // while the other is in the 0..1 range + + float flTextureAmount = 1.0 - flWrinkleAmount - flStretchAmount; // These should sum to one +#endif + + vec4 baseColor = texture(BaseTextureSampler, vs_BaseTexCoord.xy); +#if WRINKLEMAP + vec4 wrinkleColor = texture(WrinkleSampler, vs_BaseTexCoord.xy); + vec4 stretchColor = texture(StretchSampler, vs_BaseTexCoord.xy); + + // Apply wrinkle blend to only RGB. Alpha comes from the base texture + baseColor.rgb = (flTextureAmount * baseColor + flWrinkleAmount * wrinkleColor + flStretchAmount * stretchColor).rgb; +#endif + +#if DETAILTEXTURE + vec4 detailColor = texture(DetailSampler, vs_BaseTexCoord.zw); + baseColor = TextureCombine(baseColor, detailColor, DETAIL_BLEND_MODE, g_SelfIllumTint_and_DetailBlendFactor.w); +#endif + + float fogFactor = CalcPixelFogFactor(PIXELFOGTYPE, g_FogParams, g_EyePos_SpecExponent.z, vWorldPos.z, vProjPos.z); + + vec3 vEyeDir = normalize(vs_WorldVertToEyeVector.xyz); + vec3 vRimAmbientCubeColor = PixelShaderAmbientLight(vEyeDir, cAmbientCube); + + vec3 worldSpaceNormal, tangentSpaceNormal; + float fSpecMask = 1.0; + vec4 normalTexel = texture(NormalMapSampler, vs_BaseTexCoord.xy); + +#if WRINKLEMAP + vec4 wrinkleNormal = texture(NormalWrinkleSampler, vs_BaseTexCoord.xy); + vec4 stretchNormal = texture(NormalStretchSampler, vs_BaseTexCoord.xy); + normalTexel = flTextureAmount * normalTexel + flWrinkleAmount * wrinkleNormal + flStretchAmount * stretchNormal; +#endif + +#if (FASTPATH_NOBUMP == 0) + tangentSpaceNormal = mix(2.0 * normalTexel.xyz - 1.0, vec3(0, 0, 1), g_fBaseMapAlphaPhongMask); + fSpecMask = mix(normalTexel.a, baseColor.a, g_fBaseMapAlphaPhongMask); +#else + tangentSpaceNormal = vec3(0, 0, 1); + fSpecMask = baseColor.a; +#endif + + // We need a normal if we're doing any lighting + worldSpaceNormal = normalize(tangentSpaceNormal * vs_TangentSpaceTranspose); + + float fFresnelRanges = Fresnel(worldSpaceNormal, vEyeDir, g_FresnelRanges); + float fRimFresnel = Fresnel4(worldSpaceNormal, vEyeDir); + + // Break down reflect so that we can share dot(worldSpaceNormal,vEyeDir) with fresnel terms + vec3 vReflect = 2.0 * worldSpaceNormal * dot(worldSpaceNormal, vEyeDir) - vEyeDir; + + vec3 diffuseLighting = vec3(1.0, 1.0, 1.0); + vec3 envMapColor = vec3(0.0, 0.0, 0.0); + if (!bFlashlight) + { + // Summation of diffuse illumination from all local lights + diffuseLighting = PixelShaderDoLighting(vWorldPos, worldSpaceNormal, + vec3(0.0, 0.0, 0.0), false, true, vLightAtten, + cAmbientCube, nNumLights, cLightInfo, true, + + // These parameters aren't passed by generic shaders: + false, 1.0, + bDoDiffuseWarp, DiffuseWarpSampler); + + if (bCubemap) + { + // Mask is either normal map alpha or base map alpha +#if (SELFILLUMFRESNEL == 1) // This is to match the 2.0 version of vertexlitgeneric + float fEnvMapMask = mix(baseColor.a, g_fInvertPhongMask, g_EnvmapTint_ShadowTweaks.w); +#else + float fEnvMapMask = mix(baseColor.a, fSpecMask, g_EnvmapTint_ShadowTweaks.w); +#endif + + envMapColor = (ENV_MAP_SCALE * + mix(1.0, fFresnelRanges, g_EnvMapFresnel.x) * + mix(fEnvMapMask, 1.0 - fEnvMapMask, g_fInvertPhongMask)) * + texture(EnvmapSampler, vReflect).xyz * + g_EnvmapTint_ShadowTweaks.xyz; + } + } + + vec3 specularLighting = vec3(0.0, 0.0, 0.0); + vec3 rimLighting = vec3(0.0, 0.0, 0.0); + + vec3 vSpecularTint = vec3(1.0); + float fRimMask = 0.0; + float fSpecExp = 1.0; + +#if (FASTPATH_NOBUMP == 0) + vec4 vSpecExpMap = texture(SpecExponentSampler, vs_BaseTexCoord.xy); + + if (!bFlashlight) + { + fRimMask = mix(1.0, vSpecExpMap.a, g_RimMaskControl); // Select rim mask + } + + // If the exponent passed in as a constant is zero, use the value from the map as the exponent + fSpecExp = (g_EyePos_SpecExponent.w >= 0.0) ? g_EyePos_SpecExponent.w : (1.0 + 149.0 * vSpecExpMap.r); + + // If constant tint is negative, tint with albedo, based upon scalar tint map + vSpecularTint = mix(vec3(1.0, 1.0, 1.0), baseColor.rgb, vSpecExpMap.g); + vSpecularTint = (g_SpecularTint.r >= 0.0) ? g_SpecularTint.rgb : vSpecularTint; + +#else + fSpecExp = max(g_EyePos_SpecExponent.w, 0.0); +#endif + + vec3 albedo = baseColor.rgb; + + if (!bFlashlight) + { + // Summation of specular from all local lights besides the flashlight + PixelShaderDoSpecularLighting(vWorldPos, worldSpaceNormal, + fSpecExp, vEyeDir, vLightAtten, + nNumLights, cLightInfo, false, 1.0, bDoSpecularWarp, + SpecularWarpSampler, fFresnelRanges, bDoRimLighting, g_RimExponent, + + // Outputs + specularLighting, rimLighting); + } + else + { + vec4 flashlightSpacePosition = g_FlashlightWorldToTexture * vec4(vWorldPos, 1.0); + + DoSpecularFlashlight(g_FlashlightPos, vWorldPos, flashlightSpacePosition, worldSpaceNormal, + g_FlashlightAttenuationFactors.xyz, g_FlashlightAttenuationFactors.w, + FlashlightSampler, ShadowDepthSampler, ShadowDepthSamplerRaw, NormalizeRandRotSampler, FLASHLIGHTDEPTHFILTERMODE, FLASHLIGHTSHADOWS != 0, true, vProjPos.xy / vProjPos.z, + fSpecExp, vEyeDir, bDoSpecularWarp, SpecularWarpSampler, fFresnelRanges, g_EnvmapTint_ShadowTweaks, + + // These two values are output + diffuseLighting, specularLighting); + } + + // If we didn't already apply Fresnel to specular warp, modulate the specular + if (!bDoSpecularWarp) + fSpecMask *= fFresnelRanges; + + // Modulate with spec mask, boost and tint + specularLighting *= fSpecMask * g_SpecularBoost; + + if (bBlendTintByBaseAlpha) + { + vec3 tintedColor = albedo * g_DiffuseModulation.rgb; + tintedColor = mix(tintedColor, g_DiffuseModulation.rgb, g_fTintReplacementControl); + albedo = mix(albedo, tintedColor, baseColor.a); + } + else + { + albedo = albedo * g_DiffuseModulation.rgb; + } + + vec3 diffuseComponent = albedo * diffuseLighting; + if (bSelfIllum && !bFlashlight) + { +#if (SELFILLUMFRESNEL == 1) // To free up the constant register...see top of file + // This will apply a Fresnel term based on the vertex normal (not the per-pixel normal!) to help fake and internal glow look + vec3 vVertexNormal = normalize(vec3(vs_TangentSpaceTranspose[0].z, vs_TangentSpaceTranspose[1].z, vs_TangentSpaceTranspose[2].z)); + float flSelfIllumFresnel = (pow(clamp(dot(vVertexNormal.xyz, vEyeDir.xyz), 0.0, 1.0), g_SelfIllumScaleBiasExpBrightness.z) * g_SelfIllumScaleBiasExpBrightness.x) + g_SelfIllumScaleBiasExpBrightness.y; + diffuseComponent = mix(diffuseComponent, g_SelfIllumTint_and_DetailBlendFactor.rgb * albedo * g_SelfIllumScaleBiasExpBrightness.w, baseColor.a * clamp(flSelfIllumFresnel, 0.0, 1.0)); +#else + vec3 vSelfIllumMask = texture(SelfIllumMaskSampler, vs_BaseTexCoord.xy).xyz; + vSelfIllumMask = mix(baseColor.aaa, vSelfIllumMask, g_SelfIllumMaskControl); + diffuseComponent = mix(diffuseComponent, g_SelfIllumTint_and_DetailBlendFactor.rgb * albedo, vSelfIllumMask); +#endif + + diffuseComponent = max(vec3(0.0), diffuseComponent); + } + +#if DETAILTEXTURE + diffuseComponent = TextureCombinePostLighting(diffuseComponent, detailColor, + DETAIL_BLEND_MODE, g_SelfIllumTint_and_DetailBlendFactor.w); +#endif + + if (bDoRimLighting && !bFlashlight) + { + float fRimMultiply = fRimMask * fRimFresnel; // both unit range: [0, 1] + + // Add in rim light modulated with tint, mask and traditional Fresnel (not using Fresnel ranges) + rimLighting *= fRimMultiply; + + // Fold rim lighting into specular term by using the max so that we don't really add light twice... + specularLighting = max(specularLighting, rimLighting); + + // Add in view-ray lookup from ambient cube + specularLighting += (vRimAmbientCubeColor * g_fRimBoost) * clamp(fRimMultiply * worldSpaceNormal.z, 0.0, 1.0); + } + + vec3 result = specularLighting * vSpecularTint + envMapColor + diffuseComponent; + +#if WRITEWATERFOGTODESTALPHA && (PIXELFOGTYPE == PIXEL_FOG_TYPE_HEIGHT) + float alpha = fogFactor; +#else + float alpha = g_DiffuseModulation.a; + if (!bSelfIllum && !bBlendTintByBaseAlpha) + { + alpha = mix(baseColor.a * alpha, alpha, g_fBaseMapAlphaPhongMask); + } +#endif + + bool bWriteDepthToAlpha = (WRITE_DEPTH_TO_DESTALPHA != 0) && (WRITEWATERFOGTODESTALPHA == 0); + + //FIXME: need to take dowaterfog into consideration + fragColor = FinalOutput(vec4(result, alpha), fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_LINEAR, bWriteDepthToAlpha, vProjPos.z); + +} diff --git a/Game.Assets/hl2/shaders/skin_gl460.vs b/Game.Assets/hl2/shaders/skin_gl460.vs new file mode 100644 index 00000000..e35cfa06 --- /dev/null +++ b/Game.Assets/hl2/shaders/skin_gl460.vs @@ -0,0 +1,137 @@ +#version 460 + +// STATIC: "USE_STATIC_CONTROL_FLOW" "0..1" + +// DYNAMIC: "COMPRESSED_VERTS" "0..1" +// DYNAMIC: "DOWATERFOG" "0..1" +// DYNAMIC: "SKINNING" "0..1" +// DYNAMIC: "LIGHTING_PREVIEW" "0..1" +// DYNAMIC: "NUM_LIGHTS" "0..2" + +layout(location = 0) in vec3 v_Position; +layout(location = 1) in vec3 v_Normal; +layout(location = 2) in vec4 v_Color; +layout(location = 3) in vec4 v_Specular; +layout(location = 7) in ivec4 v_BoneIndex; +layout(location = 8) in vec2 v_BoneWeights; +layout(location = 9) in vec4 v_UserData; +layout(location = 10) in vec4 v_TexCoord0; +layout(location = 11) in vec4 v_TexCoord1; + +layout(std140, binding = 0) uniform source_matrices { + mat4 viewMatrix; + mat4 projectionMatrix; + mat4 modelMatrix; +}; + +layout(std140, binding = 2) uniform source_vertex_sharedUBO { + int numBones; + int lightCount; + int vertexSharedPad0; + int vertexSharedPad1; + vec4 lightEnabled; +}; + +layout(std140, binding = 4) uniform source_bone_matrices { + mat4 bones[256]; +}; + +layout(std140, binding = 5) uniform source_vs_constants { + vec4 vs_const[256]; +}; + +const int VERTEX_SHADER_CAMERA_POS = 2; +const int VERTEX_SHADER_AMBIENT_LIGHT = 21; +const int VERTEX_SHADER_LIGHT_INFO = 27; +const int SHADER_SPECIFIC_CONST_0 = 48; +const int SHADER_SPECIFIC_CONST_4 = 52; + +#include "common_gl460.vs" + +#define cBaseTexCoordTransform0 vs_const[SHADER_SPECIFIC_CONST_0 + 0] +#define cBaseTexCoordTransform1 vs_const[SHADER_SPECIFIC_CONST_0 + 1] +#define cDetailTexCoordTransform0 vs_const[SHADER_SPECIFIC_CONST_4 + 0] +#define cDetailTexCoordTransform1 vs_const[SHADER_SPECIFIC_CONST_4 + 1] + +const bool g_bSkinning = SKINNING != 0; +const int g_FogType = DOWATERFOG; + +out vec4 vs_BaseTexCoord; // includes detail tex coord +out vec3 vs_LightAtten; +out vec3 vs_WorldVertToEyeVector; +out mat3 vs_TangentSpaceTranspose; +out vec4 vs_WorldPos_Atten3; +out vec4 vs_ProjPos_WrinkleWeight; + +//----------------------------------------------------------------------------- +// Main shader entry point +//----------------------------------------------------------------------------- +void main() +{ + vec4 vPosition = vec4(v_Position, 1.0); + vec3 vNormal = v_Normal; + vec4 vTangent = v_UserData; + + // Perform skinning + vec3 worldNormal, worldPos, worldTangentS, worldTangentT; + SkinPositionNormalAndTangentSpace(g_bSkinning, vPosition, vNormal, vTangent, + v_BoneIndex, v_BoneWeights, worldPos, + worldNormal, worldTangentS, worldTangentT); + + // Always normalize since flex path is controlled by runtime + // constant not a shader combo and will always generate the normalization + worldNormal = normalize(worldNormal); + worldTangentS = normalize(worldTangentS); + worldTangentT = normalize(worldTangentT); + + // Transform into projection space + vec4 vProjPos = projectionMatrix * viewMatrix * vec4(worldPos, 1.0); + gl_Position = vProjPos; + + vs_ProjPos_WrinkleWeight.xyz = vProjPos.xyz; + vs_ProjPos_WrinkleWeight.w = 0.0; + + // Needed for water fog alpha and diffuse lighting + // FIXME: we shouldn't have to compute this all the time. + vs_WorldPos_Atten3.xyz = worldPos; + + // Needed for specular + vs_WorldVertToEyeVector = cEyePos - worldPos; + + InitLightInfo(); + + // Compute bumped lighting + // FIXME: We shouldn't have to compute this for unlit materials +#if !USE_STATIC_CONTROL_FLOW + vs_LightAtten.xyz = vec3(0, 0, 0); + vs_WorldPos_Atten3.w = 0.0; +#if (NUM_LIGHTS > 0) + vs_LightAtten.x = GetVertexAttenForLight(worldPos, 0, false); +#endif +#if (NUM_LIGHTS > 1) + vs_LightAtten.y = GetVertexAttenForLight(worldPos, 1, false); +#endif +#if (NUM_LIGHTS > 2) + vs_LightAtten.z = GetVertexAttenForLight(worldPos, 2, false); +#endif +#if (NUM_LIGHTS > 3) + vs_WorldPos_Atten3.w = GetVertexAttenForLight(worldPos, 3, false); +#endif +#else + vs_LightAtten.x = GetVertexAttenForLight(worldPos, 0, true); + vs_LightAtten.y = GetVertexAttenForLight(worldPos, 1, true); + vs_LightAtten.z = GetVertexAttenForLight(worldPos, 2, true); + vs_WorldPos_Atten3.w = GetVertexAttenForLight(worldPos, 3, true); +#endif + + // Base texture coordinate transform + vs_BaseTexCoord.x = dot(v_TexCoord0, cBaseTexCoordTransform0); + vs_BaseTexCoord.y = dot(v_TexCoord0, cBaseTexCoordTransform1); + vs_BaseTexCoord.z = dot(v_TexCoord0, cDetailTexCoordTransform0); + vs_BaseTexCoord.w = dot(v_TexCoord0, cDetailTexCoordTransform1); + + // Tangent space transform + vs_TangentSpaceTranspose[0] = vec3(worldTangentS.x, worldTangentT.x, worldNormal.x); + vs_TangentSpaceTranspose[1] = vec3(worldTangentS.y, worldTangentT.y, worldNormal.y); + vs_TangentSpaceTranspose[2] = vec3(worldTangentS.z, worldTangentT.z, worldNormal.z); +} diff --git a/Game.Assets/hl2/shaders/unlitgeneric_gl460.fs b/Game.Assets/hl2/shaders/unlitgeneric_gl460.fs index e05ff584..cf30090e 100644 --- a/Game.Assets/hl2/shaders/unlitgeneric_gl460.fs +++ b/Game.Assets/hl2/shaders/unlitgeneric_gl460.fs @@ -22,13 +22,13 @@ void main() vec4 texelColor = texture(basetexture, vs_TexCoord); if(isAlphaTesting){ switch(alphaTestFunc){ + case 0: discard; break; case 1: if(texelColor.a >= alphaTestRef){ discard; } break; case 2: if(texelColor.a != alphaTestRef){ discard; } break; case 3: if(texelColor.a > alphaTestRef){ discard; } break; case 4: if(texelColor.a <= alphaTestRef){ discard; } break; case 5: if(texelColor.a == alphaTestRef){ discard; } break; case 6: if(texelColor.a < alphaTestRef){ discard; } break; - case 7: discard; break; } } diff --git a/Game.Assets/hl2/shaders/vertexlitgeneric_bump_gl460.fs b/Game.Assets/hl2/shaders/vertexlitgeneric_bump_gl460.fs new file mode 100644 index 00000000..881af1fd --- /dev/null +++ b/Game.Assets/hl2/shaders/vertexlitgeneric_bump_gl460.fs @@ -0,0 +1,261 @@ +#version 460 + +// STATIC: "CUBEMAP" "0..1" +// STATIC: "DIFFUSELIGHTING" "0..1" +// STATIC: "LIGHTWARPTEXTURE" "0..1" +// STATIC: "SELFILLUM" "0..1" +// STATIC: "SELFILLUMFRESNEL" "0..1" +// STATIC: "NORMALMAPALPHAENVMAPMASK" "0..1" +// STATIC: "HALFLAMBERT" "0..1" +// STATIC: "FLASHLIGHT" "0..1" +// STATIC: "DETAILTEXTURE" "0..1" +// STATIC: "DETAIL_BLEND_MODE" "0..6" +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" +// STATIC: "BLENDTINTBYBASEALPHA" "0..1" + +// DYNAMIC: "NUM_LIGHTS" "0..4" +// DYNAMIC: "AMBIENT_LIGHT" "0..1" +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" + +in vec4 vs_BaseTexCoord2_TangentSpaceVertToEyeVectorXY; +in vec3 vs_LightAtten; +in vec4 vs_WorldVertToEyeVectorXYZ_TangentSpaceVertToEyeVectorZ; +in vec3 vs_WorldNormal; // World-space normal +in vec4 vs_WorldTangent; +in vec4 vs_ProjPos; +in vec4 vs_WorldPos_ProjPosZ; +in vec3 vs_DetailTexCoord_Atten3; +in vec4 vs_FogFactorW; + +layout(std140, binding = 6) uniform source_ps_constants { + vec4 ps_const[256]; +}; + +out vec4 fragColor; + +#include "common_flashlight_gl460.fs" +#include "common_vertexlitgeneric_gl460.fs" + +#define g_EnvmapTint_TintReplaceFactor ps_const[0] +#define g_DiffuseModulation ps_const[1] +#define g_EnvmapContrast_ShadowTweaks ps_const[2] +#define g_EnvmapSaturation ps_const[3].xyz +#define g_SelfIllumTint_and_BlendFactor ps_const[4] +#define g_SelfIllumTint (g_SelfIllumTint_and_BlendFactor.rgb) +#define g_DetailBlendFactor (g_SelfIllumTint_and_BlendFactor.w) + +// 11, 12 not used? +#define g_SelfIllumScaleBiasExpBrightness ps_const[11] + +#define g_ShaderControls ps_const[12] +#define g_fPixelFogType g_ShaderControls.x +#define g_fWriteDepthToAlpha g_ShaderControls.y +#define g_fWriteWaterFogToDestAlpha g_ShaderControls.z + +#define g_EyePos ps_const[20] +#define g_FogParams ps_const[21] + +#define g_FlashlightAttenuationFactors ps_const[22] +#define g_FlashlightPos ps_const[23].xyz +#define g_FlashlightWorldToTexture mat4(ps_const[24], ps_const[25], ps_const[26], ps_const[27]) // through c27 + +layout(binding = 0) uniform sampler2D BaseTextureSampler; +layout(binding = 1) uniform samplerCube EnvmapSampler; +layout(binding = 2) uniform sampler2D DetailSampler; +layout(binding = 3) uniform sampler2D BumpmapSampler; +layout(binding = 4) uniform sampler2D EnvmapMaskSampler; +layout(binding = 5) uniform sampler2D NormalizeSampler; +layout(binding = 6) uniform sampler2D RandRotSampler; // RandomRotation sampler +layout(binding = 7) uniform sampler2D FlashlightSampler; +layout(binding = 8) uniform sampler2DShadow ShadowDepthSampler; // Flashlight shadow depth map sampler +layout(binding = 8) uniform sampler2D ShadowDepthSamplerRaw; +layout(binding = 9) uniform sampler2D DiffuseWarpSampler; // Lighting warp sampler (1D texture for diffuse lighting modification) + +// Calculate both types of Fog and lerp to get result +float CalcPixelFogFactorConst(float fPixelFogType, vec4 fogParams, float flEyePosZ, float flWorldPosZ, float flProjPosZ) +{ + float fRangeFog = CalcRangeFog(flProjPosZ, fogParams.x, fogParams.z, fogParams.w); + float fHeightFog = CalcWaterFogAlpha(fogParams.y, flEyePosZ, flWorldPosZ, flProjPosZ, fogParams.w); + return mix(fRangeFog, fHeightFog, fPixelFogType); +} + +// Blend both types of Fog and lerp to get result +vec3 BlendPixelFogConst(vec3 vShaderColor, float pixelFogFactor, vec3 vFogColor, float fPixelFogType) +{ + pixelFogFactor = clamp(pixelFogFactor, 0.0, 1.0); + vec3 fRangeResult = mix(vShaderColor.rgb, vFogColor.rgb, pixelFogFactor * pixelFogFactor); //squaring the factor will get the middle range mixing closer to hardware fog + vec3 fHeightResult = mix(vShaderColor.rgb, vFogColor.rgb, clamp(pixelFogFactor, 0.0, 1.0)); + return mix(fRangeResult, fHeightResult, fPixelFogType); +} + +vec4 FinalOutputConst(vec4 vShaderColor, float pixelFogFactor, float fPixelFogType, int iTONEMAP_SCALE_TYPE, float fWriteDepthToDestAlpha, float flProjZ) +{ + vec4 result = vShaderColor; + if (iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_LINEAR) + { + result.rgb *= LINEAR_LIGHT_SCALE; + } + else if (iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_GAMMA) + { + result.rgb *= GAMMA_LIGHT_SCALE; + } + + result.a = mix(result.a, DepthToDestAlpha(flProjZ), fWriteDepthToDestAlpha); + + // TODO! fog + // result.rgb = BlendPixelFogConst(result.rgb, pixelFogFactor, g_LinearFogColor.rgb, fPixelFogType); + result.rgb = SRGBOutput(result.rgb); //SRGB in pixel shader conversion + + return result; +} + +void main() +{ + bool bCubemap = CUBEMAP != 0; + bool bDiffuseLighting = DIFFUSELIGHTING != 0; + bool bDoDiffuseWarp = LIGHTWARPTEXTURE != 0; + bool bSelfIllum = SELFILLUM != 0; + bool bSelfIllumFresnel = SELFILLUMFRESNEL != 0; + bool bNormalMapAlphaEnvmapMask = NORMALMAPALPHAENVMAPMASK != 0; + bool bHalfLambert = HALFLAMBERT != 0; + bool bFlashlight = FLASHLIGHT != 0; + bool bAmbientLight = AMBIENT_LIGHT != 0; + bool bDetailTexture = DETAILTEXTURE != 0; + bool bBlendTintByBaseAlpha = BLENDTINTBYBASEALPHA != 0; + int nNumLights = NUM_LIGHTS; + + vec3 cAmbientCube[6] = vec3[6](ps_const[5].xyz, ps_const[6].xyz, ps_const[7].xyz, + ps_const[8].xyz, ps_const[9].xyz, ps_const[10].xyz); + + // 2 registers each - 6 registers total + PixelShaderLightInfo cLightInfo[3] = PixelShaderLightInfo[3]( + PixelShaderLightInfo(ps_const[13], ps_const[14]), + PixelShaderLightInfo(ps_const[15], ps_const[16]), + PixelShaderLightInfo(ps_const[17], ps_const[18])); // through c18 + + vec3 vWorldBinormal = cross(vs_WorldNormal.xyz, vs_WorldTangent.xyz) * vs_WorldTangent.w; + + // Unpack four light attenuations + vec4 vLightAtten = vec4(vs_LightAtten, vs_DetailTexCoord_Atten3.z); + + vec4 baseColor = vec4(1.0, 1.0, 1.0, 1.0); + baseColor = texture(BaseTextureSampler, vs_BaseTexCoord2_TangentSpaceVertToEyeVectorXY.xy); + +#if DETAILTEXTURE + vec4 detailColor = texture(DetailSampler, vs_DetailTexCoord_Atten3.xy); + baseColor = TextureCombine(baseColor, detailColor, DETAIL_BLEND_MODE, g_DetailBlendFactor); +#endif + + float specularFactor = 1.0; + vec4 normalTexel = texture(BumpmapSampler, vs_BaseTexCoord2_TangentSpaceVertToEyeVectorXY.xy); + vec3 tangentSpaceNormal = normalTexel.xyz * 2.0 - 1.0; + + if (bNormalMapAlphaEnvmapMask) + specularFactor = normalTexel.a; + + vec3 diffuseLighting = vec3(1.0, 1.0, 1.0); + + vec3 worldSpaceNormal = vec3(0.0, 0.0, 1.0); + if (bDiffuseLighting || bFlashlight || bCubemap || bSelfIllumFresnel) + { + worldSpaceNormal = Vec3TangentToWorld(tangentSpaceNormal, vs_WorldNormal, vs_WorldTangent.xyz, vWorldBinormal); + worldSpaceNormal = normalize(worldSpaceNormal); + } + + if (bDiffuseLighting) + { + diffuseLighting = PixelShaderDoLighting(vs_WorldPos_ProjPosZ.xyz, worldSpaceNormal, + vec3(0.0, 0.0, 0.0), false, bAmbientLight, vLightAtten, + cAmbientCube, nNumLights, cLightInfo, bHalfLambert, + false, 1.0, bDoDiffuseWarp, DiffuseWarpSampler); + } + + vec3 albedo = baseColor.rgb; + if (bBlendTintByBaseAlpha) + { + vec3 tintedColor = albedo * g_DiffuseModulation.rgb; + tintedColor = mix(tintedColor, g_DiffuseModulation.rgb, g_EnvmapTint_TintReplaceFactor.w); + albedo = mix(albedo, tintedColor, baseColor.a); + } + else + { + albedo = albedo * g_DiffuseModulation.rgb; + } + + float alpha = g_DiffuseModulation.a; + if (!bSelfIllum && !bBlendTintByBaseAlpha) + { + alpha *= baseColor.a; + } + +#if FLASHLIGHT + if (bFlashlight) + { + int nShadowSampleLevel = 0; + bool bDoShadows = false; + vec2 vProjPos = vec2(0, 0); +// On ps_2_b, we can do shadow mapping +#if FLASHLIGHTSHADOWS + nShadowSampleLevel = FLASHLIGHTDEPTHFILTERMODE; + bDoShadows = FLASHLIGHTSHADOWS != 0; + vProjPos = vs_ProjPos.xy / vs_ProjPos.w; // Screen-space position for shadow map noise +#endif + + vec4 flashlightSpacePosition = g_FlashlightWorldToTexture * vec4(vs_WorldPos_ProjPosZ.xyz, 1.0); + + vec3 flashlightColor = DoFlashlight(g_FlashlightPos, vs_WorldPos_ProjPosZ.xyz, flashlightSpacePosition, + worldSpaceNormal, g_FlashlightAttenuationFactors.xyz, + g_FlashlightAttenuationFactors.w, FlashlightSampler, ShadowDepthSampler, ShadowDepthSamplerRaw, + RandRotSampler, nShadowSampleLevel, bDoShadows, false, vProjPos, false, g_EnvmapContrast_ShadowTweaks, true); + + diffuseLighting = flashlightColor; + } +#endif + + vec3 diffuseComponent = albedo * diffuseLighting; + +#if !FLASHLIGHT + if (bSelfIllum) + { +#if (SELFILLUMFRESNEL == 1) // To free up the constant register...see top of file + // This will apply a fresnel term based on the vertex normal (not the per-pixel normal!) to help fake and internal glow look + { + vec3 vVertexNormal = normalize(vs_WorldNormal.xyz); + float flSelfIllumFresnel = (pow(clamp(dot(vVertexNormal.xyz, normalize(vs_WorldVertToEyeVectorXYZ_TangentSpaceVertToEyeVectorZ.xyz)), 0.0, 1.0), g_SelfIllumScaleBiasExpBrightness.z) * g_SelfIllumScaleBiasExpBrightness.x) + g_SelfIllumScaleBiasExpBrightness.y; + + vec3 selfIllumComponent = g_SelfIllumTint * albedo * g_SelfIllumScaleBiasExpBrightness.w; + diffuseComponent = mix(diffuseComponent, selfIllumComponent, baseColor.a * clamp(flSelfIllumFresnel, 0.0, 1.0)); + } +#else + { + vec3 selfIllumComponent = g_SelfIllumTint * albedo; + diffuseComponent = mix(diffuseComponent, selfIllumComponent, baseColor.a); + } +#endif + } +#endif + + vec3 specularLighting = vec3(0.0, 0.0, 0.0); +#if !FLASHLIGHT + if (bCubemap) + { + vec3 reflectVect = CalcReflectionVectorUnnormalized(worldSpaceNormal, vs_WorldVertToEyeVectorXYZ_TangentSpaceVertToEyeVectorZ.xyz); + + specularLighting = ENV_MAP_SCALE * texture(EnvmapSampler, reflectVect).xyz; + specularLighting *= specularFactor; + specularLighting *= g_EnvmapTint_TintReplaceFactor.rgb; + vec3 specularLightingSquared = specularLighting * specularLighting; + specularLighting = mix(specularLighting, specularLightingSquared, g_EnvmapContrast_ShadowTweaks.xyz); + vec3 greyScale = vec3(dot(specularLighting, vec3(0.299, 0.587, 0.114))); + specularLighting = mix(greyScale, specularLighting, g_EnvmapSaturation); + } +#endif + + vec3 result = diffuseComponent + specularLighting; + + float fogFactor = CalcPixelFogFactorConst(g_fPixelFogType, g_FogParams, g_EyePos.z, vs_WorldPos_ProjPosZ.z, vs_WorldPos_ProjPosZ.w); + + alpha = mix(alpha, fogFactor, g_fPixelFogType * g_fWriteWaterFogToDestAlpha); // Use the fog factor if it's height fog + + fragColor = FinalOutputConst(vec4(result.rgb, alpha), fogFactor, g_fPixelFogType, TONEMAP_SCALE_LINEAR, g_fWriteDepthToAlpha, vs_WorldPos_ProjPosZ.w); +} diff --git a/Game.Assets/hl2/shaders/vertexlitgeneric_bump_gl460.vs b/Game.Assets/hl2/shaders/vertexlitgeneric_bump_gl460.vs new file mode 100644 index 00000000..25d6c4e7 --- /dev/null +++ b/Game.Assets/hl2/shaders/vertexlitgeneric_bump_gl460.vs @@ -0,0 +1,153 @@ +#version 460 +// STATIC: "HALFLAMBERT" "0..1" +// STATIC: "USE_WITH_2B" "0..1" +// STATIC: "USE_STATIC_CONTROL_FLOW" "0..1" + +// DYNAMIC: "COMPRESSED_VERTS" "0..1" +// DYNAMIC: "DOWATERFOG" "0..1" +// DYNAMIC: "SKINNING" "0..1" +// DYNAMIC: "NUM_LIGHTS" "0..2" + +layout(location = 0) in vec3 v_Position; +layout(location = 1) in vec3 v_Normal; +layout(location = 2) in vec4 v_Color; +layout(location = 3) in vec4 v_Specular; +layout(location = 7) in ivec4 v_BoneIndex; +layout(location = 8) in vec2 v_BoneWeights; +layout(location = 9) in vec4 v_UserData; +layout(location = 10) in vec4 v_TexCoord0; +layout(location = 11) in vec4 v_TexCoord1; + +layout(std140, binding = 0) uniform source_matrices { + mat4 viewMatrix; + mat4 projectionMatrix; + mat4 modelMatrix; +}; + +layout(std140, binding = 2) uniform source_vertex_sharedUBO { + int numBones; + int lightCount; + int vertexSharedPad0; + int vertexSharedPad1; + vec4 lightEnabled; +}; + +layout(std140, binding = 4) uniform source_bone_matrices { + mat4 bones[256]; +}; + +layout(std140, binding = 5) uniform source_vs_constants { + vec4 vs_const[256]; +}; + +const int VERTEX_SHADER_CAMERA_POS = 2; +const int VERTEX_SHADER_AMBIENT_LIGHT = 21; +const int VERTEX_SHADER_LIGHT_INFO = 27; +const int SHADER_SPECIFIC_CONST_0 = 48; +const int SHADER_SPECIFIC_CONST_4 = 52; +const int SHADER_SPECIFIC_CONST_6 = 54; + +#include "common_gl460.vs" + +#define cBaseTexCoordTransform0 vs_const[SHADER_SPECIFIC_CONST_0 + 0] // 0 & 1 +#define cBaseTexCoordTransform1 vs_const[SHADER_SPECIFIC_CONST_0 + 1] +#define cDetailTexCoordTransform0 vs_const[SHADER_SPECIFIC_CONST_4 + 0] // 4 & 5 +#define cDetailTexCoordTransform1 vs_const[SHADER_SPECIFIC_CONST_4 + 1] + +const bool g_bSkinning = SKINNING != 0; +const int g_FogType = DOWATERFOG; + +//----------------------------------------------------------------------------- +// Output vertex format +//----------------------------------------------------------------------------- +out vec4 vs_BaseTexCoord2_TangentSpaceVertToEyeVectorXY; +out vec3 vs_LightAtten; +out vec4 vs_WorldVertToEyeVectorXYZ_TangentSpaceVertToEyeVectorZ; +out vec3 vs_WorldNormal; // World-space normal +out vec4 vs_WorldTangent; +#if USE_WITH_2B +out vec4 vs_ProjPos; +#else +out vec3 vs_WorldBinormal; +#endif +out vec4 vs_WorldPos_ProjPosZ; +out vec3 vs_DetailTexCoord_Atten3; +out vec4 vs_FogFactorW; + +//----------------------------------------------------------------------------- +// Main shader entry point +//----------------------------------------------------------------------------- +void main() +{ + vec4 vPosition = vec4(v_Position, 1.0); + vec3 vNormal = v_Normal; + vec4 vTangent = v_UserData; + + // Perform skinning + vec3 worldNormal, worldPos, worldTangentS, worldTangentT; + SkinPositionNormalAndTangentSpace(g_bSkinning, vPosition, vNormal, vTangent, + v_BoneIndex, v_BoneWeights, worldPos, + worldNormal, worldTangentS, worldTangentT); + + // Always normalize since flex path is controlled by runtime + // constant not a shader combo and will always generate the normalization + worldNormal = normalize(worldNormal); + worldTangentS = normalize(worldTangentS); + worldTangentT = normalize(worldTangentT); + + vs_WorldNormal.xyz = worldNormal.xyz; + vs_WorldTangent = vec4(worldTangentS.xyz, vTangent.w); // Propagate binormal sign in world tangent.w + + // Transform into projection space + vec4 vProjPos = projectionMatrix * viewMatrix * vec4(worldPos, 1.0); + gl_Position = vProjPos; + +#if USE_WITH_2B + vs_ProjPos = vProjPos; +#else + vs_WorldBinormal.xyz = worldTangentT.xyz; +#endif + + vs_FogFactorW = vec4(CalcFog(worldPos, vProjPos.xyz, g_FogType)); + + // Needed for water fog alpha and diffuse lighting + // FIXME: we shouldn't have to compute this all the time. + vs_WorldPos_ProjPosZ = vec4(worldPos, vProjPos.z); + + // Needed for cubemapping + parallax mapping + // FIXME: We shouldn't have to compute this all the time. + vs_WorldVertToEyeVectorXYZ_TangentSpaceVertToEyeVectorZ.xyz = normalize(cEyePos.xyz - worldPos.xyz); + + InitLightInfo(); + +#if !USE_STATIC_CONTROL_FLOW + vs_LightAtten.xyz = vec3(0, 0, 0); + vs_DetailTexCoord_Atten3.z = 0.0; +#if (NUM_LIGHTS > 0) + vs_LightAtten.x = GetVertexAttenForLight(worldPos, 0, false); +#endif +#if (NUM_LIGHTS > 1) + vs_LightAtten.y = GetVertexAttenForLight(worldPos, 1, false); +#endif +#if (NUM_LIGHTS > 2) + vs_LightAtten.z = GetVertexAttenForLight(worldPos, 2, false); +#endif +#if (NUM_LIGHTS > 3) + vs_DetailTexCoord_Atten3.z = GetVertexAttenForLight(worldPos, 3, false); +#endif +#else + // Scalar light attenuation + vs_LightAtten.x = GetVertexAttenForLight(worldPos, 0, true); + vs_LightAtten.y = GetVertexAttenForLight(worldPos, 1, true); + vs_LightAtten.z = GetVertexAttenForLight(worldPos, 2, true); + vs_DetailTexCoord_Atten3.z = GetVertexAttenForLight(worldPos, 3, true); +#endif + + // Base texture coordinate transform + vs_BaseTexCoord2_TangentSpaceVertToEyeVectorXY.x = dot(v_TexCoord0, cBaseTexCoordTransform0); + vs_BaseTexCoord2_TangentSpaceVertToEyeVectorXY.y = dot(v_TexCoord0, cBaseTexCoordTransform1); + + // Detail texture coordinate transform + vs_DetailTexCoord_Atten3.x = dot(v_TexCoord0, cDetailTexCoordTransform0); + vs_DetailTexCoord_Atten3.y = dot(v_TexCoord0, cDetailTexCoordTransform1); +} diff --git a/Game.Assets/hl2/shaders/vertexlitgeneric_gl460.fs b/Game.Assets/hl2/shaders/vertexlitgeneric_gl460.fs index 64d35a69..3cd61891 100644 --- a/Game.Assets/hl2/shaders/vertexlitgeneric_gl460.fs +++ b/Game.Assets/hl2/shaders/vertexlitgeneric_gl460.fs @@ -1,109 +1,411 @@ #version 460 -// STATIC: "CUBEMAP" "0..1" -// STATIC: "ENVMAPMASK" "0..1" -// STATIC: "BASEALPHAENVMAPMASK" "0..1" -// STATIC: "NORMALMAPALPHAENVMAPMASK" "0..1" -// STATIC: "SELFILLUM" "0..1" -// STATIC: "VERTEXCOLOR" "0..1" - -in vec2 vs_TexCoord; -in vec4 vs_Color; -#if VERTEXCOLOR -in vec4 vs_VertexColor; +// STATIC: "DETAILTEXTURE" "0..1" +// STATIC: "CUBEMAP" "0..1" +// STATIC: "DIFFUSELIGHTING" "0..1" +// STATIC: "ENVMAPMASK" "0..1" +// STATIC: "BASEALPHAENVMAPMASK" "0..1" +// STATIC: "SELFILLUM" "0..1" +// STATIC: "VERTEXCOLOR" "0..1" +// STATIC: "FLASHLIGHT" "0..1" +// STATIC: "SELFILLUM_ENVMAPMASK_ALPHA" "0..1" +// STATIC: "DETAIL_BLEND_MODE" "0..9" +// STATIC: "SEAMLESS_BASE" "0..1" +// STATIC: "SEAMLESS_DETAIL" "0..1" +// STATIC: "DISTANCEALPHA" "0..1" +// STATIC: "DISTANCEALPHAFROMDETAIL" "0..1" +// STATIC: "SOFT_MASK" "0..1" +// STATIC: "OUTLINE" "0..1" +// STATIC: "OUTER_GLOW" "0..1" +// STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" +// STATIC: "DEPTHBLEND" "0..1" +// STATIC: "BLENDTINTBYBASEALPHA" "0..1" +// STATIC: "SRGB_INPUT_ADAPTER" "0..1" +// STATIC: "CUBEMAP_SPHERE_LEGACY" "0..1" + +// DYNAMIC: "LIGHTING_PREVIEW" "0..2" +// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" + +#if SEAMLESS_BASE +in vec3 vs_SeamlessTexCoord; +#define i_baseTexCoord vs_SeamlessTexCoord +#else +in vec2 vs_BaseTexCoord; +#define i_baseTexCoord vs_BaseTexCoord #endif +#if SEAMLESS_DETAIL +in vec3 vs_SeamlessDetailTexCoord; +#define i_detailTexCoord vs_SeamlessDetailTexCoord +#else +in vec2 vs_DetailTexCoord; +#define i_detailTexCoord vs_DetailTexCoord +#endif +in vec4 vs_Color; #if CUBEMAP -in vec3 vs_WorldNormal; -in vec3 vs_WorldVertToEye; +in vec3 vs_WorldVertToEyeVector; +#endif +in vec3 vs_WorldSpaceNormal; +in vec4 vs_ProjPos; +in vec4 vs_WorldPos_ProjPosZ; +in vec4 vs_FogFactorW; +#if SEAMLESS_DETAIL || SEAMLESS_BASE +in vec3 vs_SeamlessWeights; #endif +layout(std140, binding = 6) uniform source_ps_constants { + vec4 ps_const[256]; +}; + layout(std140, binding = 3) uniform source_pixel_sharedUBO { bool isAlphaTesting; int alphaTestFunc; float alphaTestRef; }; -layout(std140, binding = 6) uniform source_ps_constants { - vec4 ps_const[256]; -}; +out vec4 fragColor; -const int VertexColor = 16; -const int VertexAlpha = 32; -const int PIXEL_SHADER_SELFILLUM_TINT = 1; -const int PIXEL_SHADER_ENVMAP_TINT = 2; -const int PIXEL_SHADER_MODULATION = 3; -const int PIXEL_SHADER_ENVMAP_CONTRAST = 4; -const int PIXEL_SHADER_ENVMAP_SATURATION = 5; +#include "common_flashlight_gl460.fs" +#include "common_vertexlitgeneric_gl460.fs" -uniform int flags; -layout(binding = 0) uniform sampler2D basetexture; -#if CUBEMAP -layout(binding = 1) uniform samplerCube envmap; -#if ENVMAPMASK -layout(binding = 2) uniform sampler2D envmapmask; -#endif -#if NORMALMAPALPHAENVMAPMASK -layout(binding = 4) uniform sampler2D bumpmap; -#endif +#define g_EnvmapTint_TintReplaceFactor ps_const[0] +#define g_DiffuseModulation ps_const[1] +#define g_EnvmapContrast_ShadowTweaks ps_const[2] +#define g_EnvmapSaturation_SelfIllumMask ps_const[3] +#define g_SelfIllumTint_and_BlendFactor ps_const[4] + +#define g_ShaderControls ps_const[12] +#define g_DepthFeatheringConstants ps_const[13] + +#define g_EyePos ps_const[20] +#define g_FogParams ps_const[21] + +#define g_SelfIllumTint g_SelfIllumTint_and_BlendFactor.xyz +#define g_DetailBlendFactor g_SelfIllumTint_and_BlendFactor.w +#define g_EnvmapSaturation g_EnvmapSaturation_SelfIllumMask.xyz +#define g_SelfIllumMaskControl g_EnvmapSaturation_SelfIllumMask.w + +#define g_FlashlightAttenuationFactors ps_const[22] +#define g_FlashlightPos ps_const[23].xyz +#define g_FlashlightWorldToTexture mat4(ps_const[24], ps_const[25], ps_const[26], ps_const[27]) // through c27 + +#define g_GlowParameters ps_const[5] +#define g_GlowColor ps_const[6] +#define GLOW_UV_OFFSET g_GlowParameters.xy +#define OUTER_GLOW_MIN_DVALUE g_GlowParameters.z +#define OUTER_GLOW_MAX_DVALUE g_GlowParameters.w +#define OUTER_GLOW_COLOR g_GlowColor + +#define g_fPixelFogType g_ShaderControls.x +#define g_fWriteDepthToAlpha g_ShaderControls.y +#define g_fWriteWaterFogToDestAlpha g_ShaderControls.z +#define g_fVertexAlpha g_ShaderControls.w + +#define g_DistanceAlphaParams ps_const[7] +#define SOFT_MASK_MAX g_DistanceAlphaParams.x +#define SOFT_MASK_MIN g_DistanceAlphaParams.y + +#define g_OutlineColor ps_const[8] +#define OUTLINE_COLOR g_OutlineColor + +// these are ordered this way for optimal ps20 swizzling +#define g_OutlineParams ps_const[9] +#define OUTLINE_MIN_VALUE0 g_OutlineParams.x +#define OUTLINE_MAX_VALUE1 g_OutlineParams.y +#define OUTLINE_MAX_VALUE0 g_OutlineParams.z +#define OUTLINE_MIN_VALUE1 g_OutlineParams.w + +#if DETAILTEXTURE +#define g_DetailTint ps_const[10].rgb #endif -out vec4 fragColor; +layout(binding = 0) uniform sampler2D BaseTextureSampler; +layout(binding = 1) uniform samplerCube EnvmapSampler; +layout(binding = 2) uniform sampler2D DetailSampler; +layout(binding = 4) uniform sampler2D EnvmapMaskSampler; +layout(binding = 6) uniform sampler2D RandRotSampler; // RandomRotation sampler +layout(binding = 7) uniform sampler2D FlashlightSampler; +layout(binding = 8) uniform sampler2DShadow ShadowDepthSampler; // Flashlight shadow depth map sampler +layout(binding = 8) uniform sampler2D ShadowDepthSamplerRaw; +layout(binding = 10) uniform sampler2D DepthSampler; //depth buffer sampler for depth blending +layout(binding = 11) uniform sampler2D SelfIllumMaskSampler; // selfillummask + +// Calculate unified fog +float CalcPixelFogFactorConst(float fPixelFogType, vec4 fogParams, float flEyePosZ, float flWorldPosZ, float flProjPosZ) +{ + float flDepthBelowWater = fPixelFogType * fogParams.y - flWorldPosZ; // above water = negative, below water = positive + float flDepthBelowEye = fPixelFogType * flEyePosZ - flWorldPosZ; // above eye = negative, below eye = positive + // if fPixelFogType == 0, then flDepthBelowWater == flDepthBelowEye and frac will be 1 + float frac = (flDepthBelowEye == 0.0) ? 1.0 : clamp(flDepthBelowWater / flDepthBelowEye, 0.0, 1.0); + return clamp(min(fogParams.z, flProjPosZ * fogParams.w * frac - fogParams.x), 0.0, 1.0); +} + +// Blend both types of Fog and lerp to get result +vec3 BlendPixelFogConst(vec3 vShaderColor, float pixelFogFactor, vec3 vFogColor, float fPixelFogType) +{ + pixelFogFactor = mix(pixelFogFactor * pixelFogFactor, pixelFogFactor, fPixelFogType); + return mix(vShaderColor.rgb, vFogColor.rgb, pixelFogFactor); +} + +vec4 FinalOutputConst(vec4 vShaderColor, float pixelFogFactor, float fPixelFogType, int iTONEMAP_SCALE_TYPE, float fWriteDepthToDestAlpha, float flProjZ) +{ + vec4 result = vShaderColor; + if (iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_LINEAR) + { + result.rgb *= LINEAR_LIGHT_SCALE; + } + else if (iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_GAMMA) + { + result.rgb *= GAMMA_LIGHT_SCALE; + } -#include "common_gl460.fs" + result.a = mix(result.a, DepthToDestAlpha(flProjZ), fWriteDepthToDestAlpha); + + // todo FOG + // result.rgb = BlendPixelFogConst(result.rgb, pixelFogFactor, g_LinearFogColor.rgb, fPixelFogType); + result.rgb = SRGBOutput(result.rgb); //SRGB in pixel shader conversion + + return result; +} void main() { - vec4 texelColor = texture(basetexture, vs_TexCoord); + bool bDetailTexture = DETAILTEXTURE != 0; + bool bCubemap = CUBEMAP != 0; + bool bDiffuseLighting = DIFFUSELIGHTING != 0; + bool bHasNormal = bCubemap || bDiffuseLighting; + bool bEnvmapMask = ENVMAPMASK != 0; + bool bBaseAlphaEnvmapMask = BASEALPHAENVMAPMASK != 0; + bool bSelfIllum = SELFILLUM != 0; + bool bVertexColor = VERTEXCOLOR != 0; + bool bFlashlight = FLASHLIGHT != 0; + bool bBlendTintByBaseAlpha = BLENDTINTBYBASEALPHA != 0; + + vec4 baseColor = vec4(1.0, 1.0, 1.0, 1.0); +#if SEAMLESS_BASE + baseColor = + vs_SeamlessWeights.x * texture(BaseTextureSampler, i_baseTexCoord.yz) + + vs_SeamlessWeights.y * texture(BaseTextureSampler, i_baseTexCoord.zx) + + vs_SeamlessWeights.z * texture(BaseTextureSampler, i_baseTexCoord.xy); +#else + baseColor = texture(BaseTextureSampler, i_baseTexCoord.xy); + +#if SRGB_INPUT_ADAPTER + baseColor.rgb = GammaToLinear(baseColor.rgb); +#endif + +#endif // !SEAMLESS_BASE + if(isAlphaTesting){ switch(alphaTestFunc){ - case 1: if(texelColor.a >= alphaTestRef){ discard; } break; - case 2: if(texelColor.a != alphaTestRef){ discard; } break; - case 3: if(texelColor.a > alphaTestRef){ discard; } break; - case 4: if(texelColor.a <= alphaTestRef){ discard; } break; - case 5: if(texelColor.a == alphaTestRef){ discard; } break; - case 6: if(texelColor.a < alphaTestRef){ discard; } break; - case 7: discard; break; + case 0: discard; break; + case 1: if(baseColor.a >= alphaTestRef){ discard; } break; + case 2: if(baseColor.a != alphaTestRef){ discard; } break; + case 3: if(baseColor.a > alphaTestRef){ discard; } break; + case 4: if(baseColor.a <= alphaTestRef){ discard; } break; + case 5: if(baseColor.a == alphaTestRef){ discard; } break; + case 6: if(baseColor.a < alphaTestRef){ discard; } break; } } - vec3 albedo = GammaToLinear(texelColor.rgb) * ps_const[PIXEL_SHADER_MODULATION].rgb; +#if DISTANCEALPHA + float distAlphaMask = baseColor.a; +#endif -#if VERTEXCOLOR - albedo *= GammaToLinear(vs_VertexColor.rgb); +#if DETAILTEXTURE +#if SEAMLESS_DETAIL + vec4 detailColor = + vs_SeamlessWeights.x * texture(DetailSampler, i_detailTexCoord.yz) + + vs_SeamlessWeights.y * texture(DetailSampler, i_detailTexCoord.zx) + + vs_SeamlessWeights.z * texture(DetailSampler, i_detailTexCoord.xy); +#else + vec4 detailColor = texture(DetailSampler, i_detailTexCoord.xy); #endif + detailColor.rgb *= g_DetailTint; - vec3 linearColor = albedo * vs_Color.rgb; +#if DISTANCEALPHA && (DISTANCEALPHAFROMDETAIL == 1) + distAlphaMask = detailColor.a; + detailColor.a = 1.0; // make tcombine treat as 1.0 +#endif + baseColor = + TextureCombine(baseColor, detailColor, DETAIL_BLEND_MODE, g_DetailBlendFactor); +#endif -#if SELFILLUM - vec3 selfIllumComponent = albedo * ps_const[PIXEL_SHADER_SELFILLUM_TINT].rgb; - linearColor = mix(linearColor, selfIllumComponent, texelColor.a); +#if DISTANCEALPHA + // now, do all distance alpha effects +#if OUTLINE + { + vec4 oFactors = smoothstep(g_OutlineParams.xyzw, g_OutlineParams.wzyx, vec4(distAlphaMask)); + baseColor = mix(baseColor, g_OutlineColor, oFactors.x * oFactors.y); + } #endif -#if CUBEMAP + float mskUsed; +#if SOFT_MASK + { + mskUsed = smoothstep(SOFT_MASK_MIN, SOFT_MASK_MAX, distAlphaMask); + baseColor.a *= mskUsed; + } +#else + { + mskUsed = distAlphaMask >= 0.5 ? 1.0 : 0.0; +#if DETAILTEXTURE + baseColor.a *= mskUsed; +#else + baseColor.a = mskUsed; +#endif + } +#endif + +#if OUTER_GLOW + { +#if DISTANCEALPHAFROMDETAIL + vec4 glowTexel = texture(DetailSampler, i_detailTexCoord.xy + GLOW_UV_OFFSET); +#else + vec4 glowTexel = texture(BaseTextureSampler, i_baseTexCoord.xy + GLOW_UV_OFFSET); +#endif + vec4 glowc = OUTER_GLOW_COLOR * smoothstep(OUTER_GLOW_MIN_DVALUE, OUTER_GLOW_MAX_DVALUE, glowTexel.a); + baseColor = mix(glowc, baseColor, mskUsed); + } +#endif + +#endif // DISTANCEALPHA + vec3 specularFactor = vec3(1.0); -#if ENVMAPMASK - specularFactor *= texture(envmapmask, vs_TexCoord).rgb; + vec4 envmapMaskTexel = vec4(0.0); + if (bEnvmapMask) + { + envmapMaskTexel = texture(EnvmapMaskSampler, i_baseTexCoord.xy); + specularFactor *= envmapMaskTexel.xyz; + } + + if (bBaseAlphaEnvmapMask) + { + specularFactor *= 1.0 - baseColor.a; // this blows! + } + + vec3 diffuseLighting = vec3(1.0, 1.0, 1.0); + if (bDiffuseLighting || bVertexColor && !(bVertexColor && bDiffuseLighting)) + { + diffuseLighting = vs_Color.rgb; + } + + vec3 albedo = baseColor.rgb; + + if (bBlendTintByBaseAlpha) + { + vec3 tintedColor = albedo * g_DiffuseModulation.rgb; + tintedColor = mix(tintedColor, g_DiffuseModulation.rgb, g_EnvmapTint_TintReplaceFactor.w); + albedo = mix(albedo, tintedColor, baseColor.a); + } + else + { + albedo = albedo * g_DiffuseModulation.rgb; + } + + float alpha = g_DiffuseModulation.a; + if (!bBaseAlphaEnvmapMask && !bSelfIllum && !bBlendTintByBaseAlpha) + { + alpha *= baseColor.a; + } + + if (bFlashlight) + { + int nShadowSampleLevel = 0; + bool bDoShadows = false; +// On ps_2_b, we can do shadow mapping +#if FLASHLIGHTSHADOWS + nShadowSampleLevel = FLASHLIGHTDEPTHFILTERMODE; + bDoShadows = true; #endif -#if BASEALPHAENVMAPMASK - specularFactor *= 1.0 - texelColor.a; + + vec4 flashlightSpacePosition = g_FlashlightWorldToTexture * vec4(vs_WorldPos_ProjPosZ.xyz, 1.0); + + // We want the N.L to happen on the flashlight pass, but can't afford it on ps20 + bool bUseWorldNormal = true; + vec3 flashlightColor = DoFlashlight(g_FlashlightPos, vs_WorldPos_ProjPosZ.xyz, flashlightSpacePosition, + vs_WorldSpaceNormal, g_FlashlightAttenuationFactors.xyz, + g_FlashlightAttenuationFactors.w, FlashlightSampler, ShadowDepthSampler, ShadowDepthSamplerRaw, + RandRotSampler, nShadowSampleLevel, bDoShadows, false, vs_ProjPos.xy / vs_ProjPos.w, false, g_EnvmapContrast_ShadowTweaks, bUseWorldNormal); + + diffuseLighting = flashlightColor; + } + + if (bVertexColor && bDiffuseLighting) + { + albedo *= vs_Color.rgb; + } + + alpha = mix(alpha, alpha * vs_Color.a, g_fVertexAlpha); + + vec3 diffuseComponent = albedo * diffuseLighting; + +#if DETAILTEXTURE + diffuseComponent = + TextureCombinePostLighting(diffuseComponent, detailColor, DETAIL_BLEND_MODE, g_DetailBlendFactor); #endif -#if NORMALMAPALPHAENVMAPMASK - specularFactor *= texture(bumpmap, vs_TexCoord).a; + + vec3 specularLighting = vec3(0.0, 0.0, 0.0); + +#if !FLASHLIGHT +#if SELFILLUM_ENVMAPMASK_ALPHA + // range of alpha: + // 0 - 0.125 = lerp(diffuse,selfillum,alpha*8) + // 0.125-1.0 = selfillum*(1+alpha-0.125)*8 (over bright glows) + { + vec3 selfIllumComponent = g_SelfIllumTint * albedo; + float Adj_Alpha = 8.0 * envmapMaskTexel.a; + diffuseComponent = (max(0.0, 1.0 - Adj_Alpha) * diffuseComponent) + Adj_Alpha * selfIllumComponent; + } +#else + if (bSelfIllum) + { + vec3 vSelfIllumMask = texture(SelfIllumMaskSampler, i_baseTexCoord.xy).xyz; + vSelfIllumMask = mix(baseColor.aaa, vSelfIllumMask, g_SelfIllumMaskControl); + diffuseComponent = mix(diffuseComponent, g_SelfIllumTint * albedo, vSelfIllumMask); + } #endif - vec3 reflectVect = 2.0 * vs_WorldNormal * dot(vs_WorldNormal, vs_WorldVertToEye) - vs_WorldVertToEye * dot(vs_WorldNormal, vs_WorldNormal); - vec3 specularLighting = GammaToLinear(texture(envmap, reflectVect).rgb); - specularLighting *= specularFactor; - specularLighting *= ps_const[PIXEL_SHADER_ENVMAP_TINT].rgb; - vec3 specularLightingSquared = specularLighting * specularLighting; - specularLighting = mix(specularLighting, specularLightingSquared, ps_const[PIXEL_SHADER_ENVMAP_CONTRAST].rgb); - vec3 greyScale = vec3(dot(specularLighting, vec3(0.299, 0.587, 0.114))); - specularLighting = mix(greyScale, specularLighting, ps_const[PIXEL_SHADER_ENVMAP_SATURATION].rgb); - linearColor += specularLighting; +#if CUBEMAP + if (bCubemap) + { +#if CUBEMAP_SPHERE_LEGACY + vec3 reflectVect = normalize(CalcReflectionVectorUnnormalized(vs_WorldSpaceNormal, vs_WorldVertToEyeVector.xyz)); + + specularLighting = 0.5 * texture(EnvmapSampler, reflectVect).xyz * g_DiffuseModulation.rgb * diffuseLighting; +#else + vec3 reflectVect = CalcReflectionVectorUnnormalized(vs_WorldSpaceNormal, vs_WorldVertToEyeVector.xyz); + + specularLighting = ENV_MAP_SCALE * texture(EnvmapSampler, reflectVect).xyz; + specularLighting *= specularFactor; + specularLighting *= g_EnvmapTint_TintReplaceFactor.rgb; + vec3 specularLightingSquared = specularLighting * specularLighting; + specularLighting = mix(specularLighting, specularLightingSquared, g_EnvmapContrast_ShadowTweaks.xyz); + vec3 greyScale = vec3(dot(specularLighting, vec3(0.299, 0.587, 0.114))); + specularLighting = mix(greyScale, specularLighting, g_EnvmapSaturation); +#endif + } +#endif #endif - fragColor.rgb = LinearToGamma(linearColor); - fragColor.a = texelColor.a * ps_const[PIXEL_SHADER_MODULATION].a; + vec3 result = diffuseComponent + specularLighting; + +#if LIGHTING_PREVIEW == 1 + float dotprod = 0.7 + 0.25 * dot(vs_WorldSpaceNormal, normalize(vec3(1, 2, -0.5))); + fragColor = FinalOutput(vec4(dotprod * albedo.xyz, alpha), 0.0, PIXEL_FOG_TYPE_NONE, TONEMAP_SCALE_LINEAR); +#else - // Gradient for testing - //fragColor = vec4(vs_TexCoord.x, vs_TexCoord.y, 1.0, 1.0); -} \ No newline at end of file +#if (DEPTHBLEND == 1) + { + vec2 vScreenPos; + vScreenPos.x = vs_ProjPos.x; + vScreenPos.y = -vs_ProjPos.y; + vScreenPos = (vScreenPos + vs_ProjPos.w) * 0.5; + alpha *= DepthFeathering(DepthSampler, vScreenPos / vs_ProjPos.w, vs_ProjPos.w - vs_ProjPos.z, vs_ProjPos.w, g_DepthFeatheringConstants); + } +#endif + + float fogFactor = CalcPixelFogFactorConst(g_fPixelFogType, g_FogParams, g_EyePos.z, vs_WorldPos_ProjPosZ.z, vs_ProjPos.z); + alpha = mix(alpha, fogFactor, g_fWriteWaterFogToDestAlpha); // Use the fog factor if it's height fog + fragColor = FinalOutputConst(vec4(result.rgb, alpha), fogFactor, g_fPixelFogType, TONEMAP_SCALE_LINEAR, g_fWriteDepthToAlpha, vs_ProjPos.z); + +#endif +} diff --git a/Game.Assets/hl2/shaders/vertexlitgeneric_gl460.vs b/Game.Assets/hl2/shaders/vertexlitgeneric_gl460.vs index da2f6493..99871ba9 100644 --- a/Game.Assets/hl2/shaders/vertexlitgeneric_gl460.vs +++ b/Game.Assets/hl2/shaders/vertexlitgeneric_gl460.vs @@ -6,7 +6,6 @@ // STATIC: "SEAMLESS_BASE" "0..1" // STATIC: "SEAMLESS_DETAIL" "0..1" // STATIC: "SEPARATE_DETAIL_UVS" "0..1" -// STATIC: "DECAL" "0..1" // STATIC: "USE_STATIC_CONTROL_FLOW" "0..1" // STATIC: "DONT_GAMMA_CONVERT_VERTEX_COLOR" "0..1" // DYNAMIC: "COMPRESSED_VERTS" "0..1" @@ -15,17 +14,17 @@ // DYNAMIC: "DOWATERFOG" "0..1" // DYNAMIC: "SKINNING" "0..1" // DYNAMIC: "LIGHTING_PREVIEW" "0..1" -// DYNAMIC: "MORPHING" "0..1" -// DYNAMIC: "NUM_LIGHTS" "0..4" +// DYNAMIC: "NUM_LIGHTS" "0..2" layout(location = 0) in vec3 v_Position; layout(location = 1) in vec3 v_Normal; layout(location = 2) in vec4 v_Color; layout(location = 3) in vec4 v_Specular; -layout(location = 7) in ivec2 v_BoneIndex; +layout(location = 7) in ivec4 v_BoneIndex; layout(location = 8) in vec2 v_BoneWeights; layout(location = 9) in vec4 v_UserData; -layout(location = 10) in vec2 v_TexCoord; +layout(location = 10) in vec4 v_TexCoord0; +layout(location = 11) in vec4 v_TexCoord1; layout(std140, binding = 0) uniform source_matrices { mat4 viewMatrix; @@ -33,8 +32,12 @@ layout(std140, binding = 0) uniform source_matrices { mat4 modelMatrix; }; -layout(std140, binding = 2) uniform source_base_vertex { +layout(std140, binding = 2) uniform source_vertex_sharedUBO { int numBones; + int lightCount; + int vertexSharedPad0; + int vertexSharedPad1; + vec4 lightEnabled; }; layout(std140, binding = 4) uniform source_bone_matrices { @@ -48,66 +51,144 @@ layout(std140, binding = 5) uniform source_vs_constants { const int VERTEX_SHADER_CAMERA_POS = 2; const int VERTEX_SHADER_AMBIENT_LIGHT = 21; const int VERTEX_SHADER_LIGHT_INFO = 27; -const int VERTEX_SHADER_BASE_TEXCOORD_TRANSFORM = 48; // SHADER_SPECIFIC_CONST_0 -const float cOverbright = 2.0; +const int SHADER_SPECIFIC_CONST_0 = 48; +const int SHADER_SPECIFIC_CONST_2 = 50; +const int SHADER_SPECIFIC_CONST_4 = 52; -out vec2 vs_TexCoord; -out vec4 vs_Color; -#if VERTEXCOLOR -out vec4 vs_VertexColor; +#include "common_gl460.vs" + +#define cBaseTexCoordTransform0 vs_const[SHADER_SPECIFIC_CONST_0 + 0] +#define cBaseTexCoordTransform1 vs_const[SHADER_SPECIFIC_CONST_0 + 1] +#define cSeamlessScale vs_const[SHADER_SPECIFIC_CONST_2] +#define SEAMLESS_SCALE cSeamlessScale.x +#define cDetailTexCoordTransform0 vs_const[SHADER_SPECIFIC_CONST_4 + 0] +#define cDetailTexCoordTransform1 vs_const[SHADER_SPECIFIC_CONST_4 + 1] + +const bool g_bSkinning = SKINNING != 0; +const int g_FogType = DOWATERFOG; +const bool g_bVertexColor = VERTEXCOLOR != 0; +const bool g_bCubemap = CUBEMAP != 0; +const bool g_bFlashlight = FLASHLIGHT != 0; +const bool g_bHalfLambert = HALFLAMBERT != 0; + +#if SEAMLESS_BASE +out vec3 vs_SeamlessTexCoord; // Base texture x/y/z (indexed by swizzle) +#else +out vec2 vs_BaseTexCoord; // Base texture coordinate #endif +#if SEAMLESS_DETAIL +out vec3 vs_SeamlessDetailTexCoord; // Detail texture coordinate +#else +out vec2 vs_DetailTexCoord; // Detail texture coordinate +#endif +out vec4 vs_Color; // Vertex color (from lighting or unlit) + #if CUBEMAP -out vec3 vs_WorldNormal; -out vec3 vs_WorldVertToEye; +out vec3 vs_WorldVertToEyeVector; // Necessary for cubemaps #endif -#include "common_gl460.vs" +out vec3 vs_WorldSpaceNormal; // Necessary for cubemaps and flashlight + +out vec4 vs_ProjPos; +out vec4 vs_WorldPos_ProjPosZ; +out vec4 vs_FogFactorW; +#if SEAMLESS_DETAIL || SEAMLESS_BASE +out vec3 vs_SeamlessWeights; // x y z projection weights +#endif void main() { - vec4 localPos = vec4(0.0); - vec3 worldNormal = vec3(0.0); - vec3 worldPos = vec3(0.0); - mat4 mvp; - - if (numBones == 0) { - mvp = projectionMatrix * viewMatrix * modelMatrix; - gl_Position = mvp * vec4(v_Position, 1.0); - worldNormal = mat3(modelMatrix) * v_Normal; - worldPos = (modelMatrix * vec4(v_Position, 1.0)).xyz; - } - else{ - if (numBones >= 1) { - localPos += (bones[v_BoneIndex.x] * vec4(v_Position, 1.0)) * v_BoneWeights.x; - worldNormal += (mat3(bones[v_BoneIndex.x]) * v_Normal) * v_BoneWeights.x; - } - - if (numBones >= 2) { - localPos += (bones[v_BoneIndex.y] * vec4(v_Position, 1.0)) * v_BoneWeights.y; - worldNormal += (mat3(bones[v_BoneIndex.y]) * v_Normal) * v_BoneWeights.y; - } - mvp = projectionMatrix * viewMatrix; - gl_Position = mvp * localPos; - worldPos = localPos.xyz; - } - - vec4 texCoordInput = vec4(v_TexCoord, 0.0, 1.0); - vs_TexCoord.x = dot(texCoordInput, vs_const[VERTEX_SHADER_BASE_TEXCOORD_TRANSFORM + 0]); - vs_TexCoord.y = dot(texCoordInput, vs_const[VERTEX_SHADER_BASE_TEXCOORD_TRANSFORM + 1]); - -#if VERTEXCOLOR - vs_VertexColor = v_Color; + bool bDynamicLight = DYNAMIC_LIGHT != 0; + bool bStaticLight = STATIC_LIGHT != 0; + bool bDoLighting = !g_bVertexColor && (bDynamicLight || bStaticLight); + + vec4 vPosition = vec4(v_Position, 1.0); + vec3 vNormal = v_Normal; + +#if SEAMLESS_BASE || SEAMLESS_DETAIL + // compute blend weights in rgb + vec3 NNormal = normalize(vNormal); + vs_SeamlessWeights.xyz = NNormal * NNormal; // sums to 1. #endif + // Perform skinning + vec3 worldNormal, worldPos; + SkinPositionAndNormal( + g_bSkinning, + vPosition, vNormal, + v_BoneIndex, v_BoneWeights, + worldPos, worldNormal); + + if (!g_bVertexColor) + { + worldNormal = normalize(worldNormal); + } + + vs_WorldSpaceNormal = worldNormal; + + // Transform into projection space + vec4 vProjPos = projectionMatrix * viewMatrix * vec4(worldPos, 1.0); + gl_Position = vProjPos; + + vs_ProjPos = vProjPos; + vs_FogFactorW.w = CalcFog(worldPos, vProjPos.xyz, g_FogType); + vs_WorldPos_ProjPosZ.xyz = worldPos.xyz; + vs_WorldPos_ProjPosZ.w = vProjPos.z; + + // Needed for cubemaps #if CUBEMAP - vs_WorldNormal = worldNormal; - vs_WorldVertToEye = vs_const[VERTEX_SHADER_CAMERA_POS].xyz - worldPos; + vs_WorldVertToEyeVector.xyz = cEyePos - worldPos; #endif - bool bDynamicLight = DYNAMIC_LIGHT != 0; - bool bStaticLight = STATIC_LIGHT != 0; +#if FLASHLIGHT + vs_Color = vec4(0.0, 0.0, 0.0, 0.0); +#else + if (g_bVertexColor) + { + // Assume that this is unlitgeneric if you are using vertex color. + vs_Color.rgb = (DONT_GAMMA_CONVERT_VERTEX_COLOR != 0) ? v_Color.rgb : GammaToLinear(v_Color.rgb); + vs_Color.a = v_Color.a; + } + else + { + InitLightInfo(); +#if USE_STATIC_CONTROL_FLOW + { + vs_Color.xyz = DoLighting(worldPos, worldNormal, v_Specular.rgb, bStaticLight, bDynamicLight, g_bHalfLambert); + } +#else + { + vs_Color.xyz = DoLightingUnrolled(worldPos, worldNormal, v_Specular.rgb, bStaticLight, bDynamicLight, g_bHalfLambert, NUM_LIGHTS); + } +#endif + } +#endif - InitLightInfo(); - vs_Color.xyz = DoLightingUnrolled(worldPos, normalize(worldNormal), v_Specular.rgb, bStaticLight, bDynamicLight, HALFLAMBERT != 0, NUM_LIGHTS); - vs_Color.w = 1.0; +#if SEAMLESS_BASE + vs_SeamlessTexCoord.xyz = SEAMLESS_SCALE * v_Position.xyz; +#else + // Base texture coordinates + vs_BaseTexCoord.x = dot(v_TexCoord0, cBaseTexCoordTransform0); + vs_BaseTexCoord.y = dot(v_TexCoord0, cBaseTexCoordTransform1); +#endif + +#if SEAMLESS_DETAIL + // FIXME: detail texcoord as a 2d xform doesn't make much sense here, so I just do enough so + // that scale works. More smartness could allow 3d xform. + vs_SeamlessDetailTexCoord.xyz = (SEAMLESS_SCALE * cDetailTexCoordTransform0.x) * v_Position.xyz; +#else + // Detail texture coordinates + // FIXME: This shouldn't have to be computed all the time. + vs_DetailTexCoord.x = dot(v_TexCoord0, cDetailTexCoordTransform0); + vs_DetailTexCoord.y = dot(v_TexCoord0, cDetailTexCoordTransform1); +#endif + +#if SEPARATE_DETAIL_UVS + vs_DetailTexCoord.xy = v_TexCoord1.xy; +#endif + +#if LIGHTING_PREVIEW + float d = (0.5 + 0.5 * worldNormal * vec3(0.7071, 0.7071, 0)).x; + vs_Color.xyz = vec3(d, d, d); +#endif } diff --git a/Game.Assets/hl2/shaders/worldvertextransition_gl460.fs b/Game.Assets/hl2/shaders/worldvertextransition_gl460.fs index 89ae55ef..9b6e88bf 100644 --- a/Game.Assets/hl2/shaders/worldvertextransition_gl460.fs +++ b/Game.Assets/hl2/shaders/worldvertextransition_gl460.fs @@ -30,13 +30,13 @@ void main() if(isAlphaTesting){ switch(alphaTestFunc){ + case 0: discard; break; case 1: if(texelColor.a >= alphaTestRef){ discard; } break; case 2: if(texelColor.a != alphaTestRef){ discard; } break; case 3: if(texelColor.a > alphaTestRef){ discard; } break; case 4: if(texelColor.a <= alphaTestRef){ discard; } break; case 5: if(texelColor.a == alphaTestRef){ discard; } break; case 6: if(texelColor.a < alphaTestRef){ discard; } break; - case 7: discard; break; } } diff --git a/Game.Client/C_BaseEntity.cs b/Game.Client/C_BaseEntity.cs index b95868fc..11fcffbd 100644 --- a/Game.Client/C_BaseEntity.cs +++ b/Game.Client/C_BaseEntity.cs @@ -407,6 +407,10 @@ internal static void AddVisibleEntities() { ent.UpdateVisibility(); } + +#if DEBUG + DrawEntityDebugOverlays(); +#endif } public bool IsNoInterpolationFrame() => OldInterpolationFrame != InterpolationFrame; @@ -642,6 +646,7 @@ public C_BaseEntity() { DataChangeEventRef.Struct = unchecked((ulong)-1); EntClientFlags = 0; + ColorRender = new(255, 255, 255, 255); RenderFXBlend = 255; Predictable = false; @@ -872,6 +877,33 @@ public virtual void DoAnimationEvents() { static readonly ConVar r_drawrenderboxes = new("r_drawrenderboxes", "0", FCvar.Cheat); +#if DEBUG + internal static readonly ConVar sdn_entdebug = new("sdn_entdebug", "0"); + + internal static void DrawEntityDebugOverlays() { + if (!sdn_entdebug.GetBool()) + return; + + Span text = stackalloc char[256]; + int highest = cl_entitylist.GetHighestEntityIndex(); + for (int i = 0; i <= highest; i++) { + C_BaseEntity? ent = cl_entitylist.GetBaseEntity(i); + if (ent == null) + continue; + + text.Clear(); + + ref readonly Vector3 origin = ref ent.GetAbsOrigin(); + sprintf(text, "[%d] %s midx=%d model=%s (%d %d %d)%s") + .D(i).S(ent.GetClassname()).D(ent.ModelIndex).S(ent.Model == null ? "NULL" : "ok") + .D((int)origin.X).D((int)origin.Y).D((int)origin.Z) + .S(ent.ShouldDraw() ? "" : ent.IsEffectActive(EntityEffects.NoDraw) ? " EF_NODRAW" : " NODRAW"); + + debugoverlay.AddTextOverlay(in origin, i & 14, 0, text.SliceNullTerminatedString()); + } + } +#endif + public void DrawBBoxVisualizations() { if (r_drawrenderboxes.GetInt() != 0) { GetRenderBounds(out Vector3 vecRenderMins, out Vector3 vecRenderMaxs); @@ -2445,7 +2477,7 @@ void UpdateBaseVelocity() { } public bool IsVisible() => renderHandle != INVALID_CLIENT_RENDER_HANDLE; - public bool IsFollowingEntity() => IsEffectActive(EntityEffects.BoneMerge) && (GetMoveType() != Source.MoveType.None && GetMoveParent() != null); + public bool IsFollowingEntity() => IsEffectActive(EntityEffects.BoneMerge) && (GetMoveType() == Source.MoveType.None) && GetMoveParent() != null; public virtual C_BaseEntity? GetFollowedEntity() { if (!IsFollowingEntity()) diff --git a/Game.Client/HLClient.cs b/Game.Client/HLClient.cs index 45e86f5a..627431f6 100644 --- a/Game.Client/HLClient.cs +++ b/Game.Client/HLClient.cs @@ -283,6 +283,8 @@ private void OnRenderStart() { } C_BaseEntity.CalcAimEntPositions(); + + C_BaseEntity.AddVisibleEntities(); } class DataChangedEvent : IPoolableObject diff --git a/Game.Client/ViewRender.cs b/Game.Client/ViewRender.cs index 36b7c4fe..ded54993 100644 --- a/Game.Client/ViewRender.cs +++ b/Game.Client/ViewRender.cs @@ -168,6 +168,8 @@ public Rendering3dView(ViewRender mainView) : base(mainView) { } protected void BuildWorldRenderLists(bool drawEntities, int forceViewLeaf = -1, bool useCacheIfEnabled = true, bool shadowDepth = false, Span reflectionWaterHeight = default) { + Assert(WorldRenderList == null); + mainView.IncWorldListsNumber(); WorldRenderList = render.CreateWorldList(); @@ -217,6 +219,8 @@ public virtual void Setup(in ViewSetup setup) { RenderablesList = ClientRenderablesList.Shared.Alloc(); } public virtual void ReleaseLists() { + WorldRenderList?.Release(); + WorldRenderList = null; ClientRenderablesList.Shared.Free(RenderablesList); } public override DrawFlags GetDrawFlags() { @@ -245,7 +249,7 @@ private void DrawOpaqueRenderables_DrawStaticProps(RenderGroup group, RenderDept render.SetBlend(1.0f); const int MAX_STATICS_PER_BATCH = 512; - IClientRenderable[] statics = new IClientRenderable[MAX_STATICS_PER_BATCH]; + InlineArray512 statics = new(); int numScheduled = 0, numAvailable = MAX_STATICS_PER_BATCH; @@ -258,13 +262,13 @@ private void DrawOpaqueRenderables_DrawStaticProps(RenderGroup group, RenderDept if (--numAvailable > 0) continue; - StaticPropMgrGlobals.g_StaticPropMgr.DrawStaticProps(statics, numScheduled, depthMode != RenderDepthMode.Normal, false /*vcollide_wireframe*/); + StaticPropMgrGlobals.g_StaticPropMgr.DrawStaticProps(ref statics, numScheduled, depthMode != RenderDepthMode.Normal, false /*vcollide_wireframe*/); numScheduled = 0; numAvailable = MAX_STATICS_PER_BATCH; } if (numScheduled != 0) - StaticPropMgrGlobals.g_StaticPropMgr.DrawStaticProps(statics, numScheduled, depthMode != RenderDepthMode.Normal, false /*vcollide_wireframe*/); + StaticPropMgrGlobals.g_StaticPropMgr.DrawStaticProps(ref statics, numScheduled, depthMode != RenderDepthMode.Normal, false /*vcollide_wireframe*/); } private void DrawOpaqueRenderables_Range(RenderGroup group, RenderDepthMode depthMode) { diff --git a/Source.Bitmap/ImageLoader.cs b/Source.Bitmap/ImageLoader.cs index cfa36fa8..d85a766c 100644 --- a/Source.Bitmap/ImageLoader.cs +++ b/Source.Bitmap/ImageLoader.cs @@ -241,6 +241,11 @@ public static int GetNumMipMapLevels(int width, int height, int depth) { const int GL_DEPTH_COMPONENT16 = 0x81A5; const int GL_DEPTH_COMPONENT24 = 0x81A6; const int GL_DEPTH_COMPONENT32 = 0x81A7; + const int GL_SRGB8 = 0x8C41; + const int GL_SRGB8_ALPHA8 = 0x8C43; + const int GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT = 0x8C4D; + const int GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT = 0x8C4E; + const int GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT = 0x8C4F; // An uncomfortable amount of this is guessing... // The various forms of oddly rearranged formatting needs transmutations - we'll figure that out later as well. @@ -260,6 +265,7 @@ public static int GetNumMipMapLevels(int width, int height, int depth) { const int GL_RGBA = 0x1908; const int GL_BGR = 0x80E0; const int GL_BGRA = 0x80E1; + const int GL_RG = 0x8227; public static int GetGLImageUploadFormat(ImageFormat format) => format switch { ImageFormat.RGBA8888 => GL_RGBA, ImageFormat.RGB888 => GL_RGB, @@ -275,7 +281,30 @@ public static int GetNumMipMapLevels(int width, int height, int depth) { ImageFormat.RGB323232F => GL_RGB, ImageFormat.RGBA32323232F => GL_RGBA, ImageFormat.I8 => GL_RED, + ImageFormat.IA88 => GL_RG, }; + public static int GetGLImageInternalFormat(ImageFormat format, bool srgb) => srgb ? GetGLImageInternalFormatSRGB(format) : GetGLImageInternalFormat(format); + + private static int GetGLImageInternalFormatSRGB(ImageFormat format) => format switch { + ImageFormat.RGBA8888 => GL_SRGB8_ALPHA8, + ImageFormat.RGB888 => GL_SRGB8, + ImageFormat.BGR888 => GL_SRGB8, + ImageFormat.ARGB8888 => GL_SRGB8_ALPHA8, + ImageFormat.BGRA8888 => GL_SRGB8_ALPHA8, + ImageFormat.BGRX8888 => GL_SRGB8_ALPHA8, + ImageFormat.RGB888_Bluescreen => GL_SRGB8, + ImageFormat.BGR888_Bluescreen => GL_SRGB8, + + ImageFormat.DXT1 => GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, + ImageFormat.DXT3 => GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, + ImageFormat.DXT5 => GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, + ImageFormat.DXT1_OneBitAlpha => GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, + ImageFormat.DXT1_Runtime => GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, + ImageFormat.DXT5_Runtime => GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, + + _ => GetGLImageInternalFormat(format), + }; + public static int GetGLImageInternalFormat(ImageFormat format) => format switch { // Uncompressed color formats ImageFormat.RGBA8888 => GL_RGBA8, diff --git a/Source.Common/CommandBuffer.cs b/Source.Common/CommandBuffer.cs new file mode 100644 index 00000000..ac93f040 --- /dev/null +++ b/Source.Common/CommandBuffer.cs @@ -0,0 +1,102 @@ +namespace Source.Common; + +public enum CommandBufferCommand +{ + /// End of stream. + End = 0, + /// int cmd, int reference. Jump to another stream. Can be used to implement non-sequentially allocated storage. + Jump = 1, + /// int cmd, int reference. Subroutine call to another stream. + Jsr = 2, + /// int cmd, int first_reg, int nregs, float values[nregs*4] + SetPixelShaderFloatConst = 256, + /// int cmd, int first_reg, int nregs, float values[nregs*4] + SetVertexShaderFloatConst = 257, + /// int cmd, int first_reg, int nregs, &float values[nregs*4] + SetVertexShaderFloatConstRef = 258, + /// int cmd, int regdest + SetPixelShaderFogParams = 259, + /// int cmd, int regdest + StoreEyePosInPsConst = 260, + /// int cmd, int regdest + CommitPixelShaderLighting = 261, + /// int cmd, int regdest + SetPixelShaderStateAmbientLightCube = 262, + /// int cmd + SetAmbientCubeDynamicStateVertexShader = 263, + /// int cmd, int constant register, float blend scale + SetDepthFeatheringConst = 264, + /// cmd, sampler, texture id + BindStandardTexture = 512, + /// cmd, sampler, texture handle + BindShaderApiTextureHandle = 513, + /// cmd, idx + SetPsHIndex = 1024, + /// cmd, idx + SetVsHIndex = 1025, + /// cmd, int first_reg (for worldToTexture matrix) + SetVertexShaderFlashlightState = 2000, + /// cmd, int color reg, int atten reg, int origin reg, sampler (for flashlight texture) + SetPixelShaderFlashlightState = 2001, + /// cmd + SetPixelShaderUberlightState = 2002, + /// cmd + SetVertexShaderNearZFarZState = 2003 +} + +public enum CommandBufferInstanceCommand +{ + /// End of stream. + End = 0, + /// int cmd, void* adr. Jump to another stream. Can be used to implement non-sequentially allocated storage. + Jump, + /// int cmd, void* adr. Subroutine call to another stream. + Jsr, + /// int cmd + SetSkinningMatrices, + /// int cmd + SetVertexShaderLocalLighting, + /// int cmd, int regdest + SetPixelShaderLocalLighting, + /// int cmd + SetVertexShaderAmbientLightCube, + /// int cmd, int regdest + SetPixelShaderAmbientLightCube, + /// int cmd, int regdest + SetPixelShaderAmbientLightCubeLuminance, + /// int cmd, int regdest + SetPixelShaderGlintDamping, + /// cmd, sampler + BindEnvCubemapTexture, + SetModulationPixelShaderDynamicState, + /// int cmd, int constant register, Vector color2 + SetModulationPixelShaderDynamicStateLinearColorSpaceLinearScale, + /// int cmd, int constant register, Vector color2 + SetModulationPixelShaderDynamicStateLinearColorSpace, + /// int cmd, int constant register, Vector color2, float scale + SetModulationPixelShaderDynamicStateLinearScale, + /// int cmd, int constant register, Vector color2 + SetModulationVertexShaderDynamicState, + /// int cmd, int constant register + SetModulationPixelShaderDynamicStateIdentity, + /// int cmd, int constant register, Vector color2, float scale + SetModulationVertexShaderDynamicStateLinearScale, + /// This must be last. + Count +} + +public interface ICommandStorageBuffer +{ + void EnsureCapacity(int size); + void Put(in T value) where T : unmanaged; + void PutInt(int value); + void PutIntPtr(nint value); + void PutFloat(float value); + void PutPtr(nint ptr); + void PutMemory(ReadOnlySpan memory); + int AddReference(ICommandStorageBuffer buffer); + ICommandStorageBuffer Reference(int index); + Span Base(); + void Reset(); + int Size(); +} \ No newline at end of file diff --git a/Source.Common/Engine/IStaticPropMgr.cs b/Source.Common/Engine/IStaticPropMgr.cs index a42bd4a5..b0a79ad2 100644 --- a/Source.Common/Engine/IStaticPropMgr.cs +++ b/Source.Common/Engine/IStaticPropMgr.cs @@ -26,7 +26,7 @@ public interface IStaticPropMgrClient : IStaticPropMgr void GetAllStaticProps(List output); void GetAllStaticPropsInAABB(Vector3 mins, Vector3 maxs, List output); void GetAllStaticPropsInOBB(Vector3 origin, Vector3 extent1, Vector3 extent2, Vector3 extent3, List output); - void DrawStaticProps(IClientRenderable[] props, int count, bool shadowDepth, bool drawVCollideWireframe); + void DrawStaticProps(ref InlineArray512 props, int count, bool shadowDepth, bool drawVCollideWireframe); } public interface IStaticPropMgrServer : IStaticPropMgr diff --git a/Source.Common/Formats/Keyvalues/KeyValues.cs b/Source.Common/Formats/Keyvalues/KeyValues.cs index 8cb06ef1..39fcc191 100644 --- a/Source.Common/Formats/Keyvalues/KeyValues.cs +++ b/Source.Common/Formats/Keyvalues/KeyValues.cs @@ -431,6 +431,8 @@ public static bool SkipComments(StreamReader reader) { return didAnything; } + // TODO FIXME: These should... return early if parsed successfully + // but rn doing that breaks some things :[ private void DetermineValueType(string input) { // Try Int32 if (int.TryParse(input, NumberStyles.Integer, CultureInfo.InvariantCulture, out int i32)) { diff --git a/Source.Common/IRenderView.cs b/Source.Common/IRenderView.cs index 0f20c45d..98d93b0b 100644 --- a/Source.Common/IRenderView.cs +++ b/Source.Common/IRenderView.cs @@ -7,7 +7,7 @@ namespace Source.Common; -public interface IWorldRenderList +public interface IWorldRenderList : IRefCounted { } diff --git a/Source.Common/MaterialSystem/IMaterial.cs b/Source.Common/MaterialSystem/IMaterial.cs index a463f5d2..9714ebf0 100644 --- a/Source.Common/MaterialSystem/IMaterial.cs +++ b/Source.Common/MaterialSystem/IMaterial.cs @@ -482,6 +482,8 @@ public interface IMaterialInternal : IMaterial bool IsUsingVertexID(); void Precache(); bool PrecacheVars(KeyValues? inVmtKeyValues = null, KeyValues? inPatchKeyValues = null, List? includes = null, MaterialFindContext findContext = 0); + void ReportVarChanged(IMaterialVar? var); + uint GetChangeID(); void SetEnumerationID(int id); void SetMaxLightmapPageID(int value); void SetMinLightmapPageID(int value); diff --git a/Source.Common/MaterialSystem/IMaterialSystem.cs b/Source.Common/MaterialSystem/IMaterialSystem.cs index a41363bf..934bcb59 100644 --- a/Source.Common/MaterialSystem/IMaterialSystem.cs +++ b/Source.Common/MaterialSystem/IMaterialSystem.cs @@ -350,6 +350,8 @@ public interface IMatRenderContext float ComputePixelDiameterOfSphere(Vector3 origin, float radius); float ComputePixelWidthOfSphere(Vector3 origin, float radius); void SetNumBoneWeights(int v); + void TurnOnToneMapping(); + void SetToneMappingScaleLinear(in Vector3 scale); void SetAmbientLightCube(ReadOnlySpan cube); void LoadBoneMatrix(int hardwareID, in Matrix3x4 matrix4x4); void GetWorldSpaceCameraPosition(out Vector3 vecCameraPos); @@ -446,9 +448,8 @@ public void PushRenderTargetAndViewport(ITexture? renderTarget, int x, int y, in public int GetMaxVerticesToRender(IMaterial material) => ctx.GetMaxVerticesToRender(material); public int GetMaxIndicesToRender() => ctx.GetMaxIndicesToRender(); - public void TurnOnToneMapping() { - // todo - } + public void TurnOnToneMapping() => ctx.TurnOnToneMapping(); + public void SetToneMappingScaleLinear(in Vector3 scale) => ctx.SetToneMappingScaleLinear(in scale); public void PushRenderTargetAndViewport(ITexture? rtColor, ITexture? rtDepth, int x, int y, int width, int height) => ctx.PushRenderTargetAndViewport(rtColor, rtDepth, x, y, width, height); diff --git a/Source.Common/MaterialSystem/IMaterialVar.cs b/Source.Common/MaterialSystem/IMaterialVar.cs index 1bb9610a..4a576d10 100644 --- a/Source.Common/MaterialSystem/IMaterialVar.cs +++ b/Source.Common/MaterialSystem/IMaterialVar.cs @@ -66,6 +66,7 @@ public override string ToString() { [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetVecValue(in Vector4 xyzw) => SetVecValue(xyzw.X, xyzw.Y, xyzw.Z, xyzw.W); public abstract void GetVecValue(Span color); + public abstract Span GetVecValue(); public void GetLinearVecValue(Span pVal, int numComps) { Assert(numComps <= 4); @@ -101,18 +102,17 @@ public void GetVecValue(out Vector2 vec) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void GetVecValue(out Vector3 vec) { - Span retv = stackalloc float[2]; + Span retv = stackalloc float[3]; GetVecValue(retv); vec = new(retv[0], retv[1], retv[2]); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void GetVecValue(out Vector4 vec) { - Span retv = stackalloc float[2]; + Span retv = stackalloc float[4]; GetVecValue(retv); vec = new(retv[0], retv[1], retv[2], retv[3]); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector2 GetVec2Value() { Span retv = stackalloc float[2]; @@ -121,13 +121,13 @@ public Vector2 GetVec2Value() { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector3 GetVec3Value() { - Span retv = stackalloc float[2]; + Span retv = stackalloc float[3]; GetVecValue(retv); return new(retv[0], retv[1], retv[2]); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 GetVec4Value() { - Span retv = stackalloc float[2]; + Span retv = stackalloc float[4]; GetVecValue(retv); return new(retv[0], retv[1], retv[2], retv[3]); } diff --git a/Source.Common/MaterialSystem/IMesh.cs b/Source.Common/MaterialSystem/IMesh.cs index 27f36268..aac54b2e 100644 --- a/Source.Common/MaterialSystem/IMesh.cs +++ b/Source.Common/MaterialSystem/IMesh.cs @@ -246,6 +246,13 @@ public void Normal3f(float x, float y, float z) { *pDst = z; } + public void UserData(ReadOnlySpan pData) { + int userDataSize = 4; + float* pUserData = OffsetFloatPointer(Desc.UserData, CurrentVertex, Desc.UserDataSize); + fixed (float* src = pData) + memcpy(pUserData, src, sizeof(float) * userDataSize); + } + public void Color3f(float r, float g, float b) { byte* pDst = CurrColor; *pDst++ = (byte)Math.Clamp(r * 255, 0, 255); @@ -912,7 +919,11 @@ public unsafe void Color4ubv(in Color rgba) { public void BoneMatrix(int idx, int matrixIndex) => VertexBuilder.BoneMatrix(idx, (byte)matrixIndex); // Generic per-vertex data - public void UserData(ReadOnlySpan pData) => throw new NotImplementedException(); + public void UserData(ReadOnlySpan pData) => VertexBuilder.UserData(pData); + public void UserData(in Vector4 vec) { + fixed (Vector4* ptr = &vec) + VertexBuilder.UserData(new((float*)ptr, 4)); + } // Used to define the indices (only used if you aren't using primitives) public void Index(ushort index) => IndexBuilder.Index(index); diff --git a/Source.Common/MaterialSystem/IShader.cs b/Source.Common/MaterialSystem/IShader.cs index 938e8d06..e40d74eb 100644 --- a/Source.Common/MaterialSystem/IShader.cs +++ b/Source.Common/MaterialSystem/IShader.cs @@ -28,7 +28,7 @@ public interface IShader string? GetFallbackShader(IMaterialVar[] vars); void InitShaderParams(IMaterialVar[] vars, IShaderAPI shaderAPI, ReadOnlySpan materialName); void InitShaderInstance(IMaterialVar[] shaderParams, IShaderAPI shaderAPI, IShaderInit shaderManager, ReadOnlySpan materialName, ReadOnlySpan textureGroupName); - void DrawElements(IMaterialVar[] shaderParams, IShaderShadow? shadow, IShaderDynamicAPI? shaderAPI, VertexCompressionType none); + void DrawElements(IMaterialVar[] shaderParams, IShaderShadow? shadow, IShaderDynamicAPI? shaderAPI, VertexCompressionType none, ref BasePerMaterialContextData? contextData); bool IsTranslucent(IMaterialVar[]? shaderParams); bool NeedsPowerOfTwoFrameBufferTexture(IMaterialVar[]? shaderParams, bool checkSpecificToThisFrame); bool NeedsFullFrameBufferTexture(IMaterialVar[]? shaderParams, bool checkSpecificToThisFrame); @@ -40,6 +40,7 @@ public interface IShaderInit public void LoadCubeMap(IMaterialVar[] parms, IMaterialVar textureVar, int additionalCreationFlags = 0); VertexShaderHandle LoadVertexShader(ReadOnlySpan name, ReadOnlySpan defines = default); PixelShaderHandle LoadPixelShader(ReadOnlySpan name, ReadOnlySpan defines = default); + void LoadBumpMap(IMaterialVar nameVar, string? textureGroupName); } @@ -133,6 +134,18 @@ public interface IShaderDynamicAPI void GetMatrix(MaterialMatrixMode matrixMode, out Matrix4x4 dst); void CommitVertexShaderLighting(); void GetLightState(out LightState state); + void GetBackBufferDimensions(out int width, out int height); + FlashlightState GetFlashlightState(out Matrix4x4 worldToTexture); + bool IsHWMorphingEnabled(); + void GetWorldSpaceCameraPosition(ref Span eyePos); + int GetPixelFogCombo(); + FlashlightState GetFlashlightStateEx(out Matrix4x4 worldToTexture, out ITexture? flashlightDepthTexture); + bool ShouldWriteDepthToDestAlpha(); + void MarkUnusedVertexFields(int v, Span unusedTexCoords); + int GetIntRenderingParameter(RenderParamInt parm); + void ExecuteCommandBuffer(ICommandStorageBuffer storage); + void CommitPixelShaderLighting(int lightInfoArray); + void SetPixelShaderStateAmbientLightCube(int ambientCube, bool v); } public struct LightState @@ -143,3 +156,9 @@ public struct LightState public bool StaticLightTexel; public readonly int HasDynamicLight() => (AmbientLight || (NumLights > 0)) ? 1 : 0; } + +public class BasePerMaterialContextData() +{ + public UInt32 VarChangeID = 0xffffffff; + public bool MaterialVarsChanged = true; +} \ No newline at end of file diff --git a/Source.Common/MaterialSystem/IShaderSystem.cs b/Source.Common/MaterialSystem/IShaderSystem.cs index 177cd054..ac4cc6ad 100644 --- a/Source.Common/MaterialSystem/IShaderSystem.cs +++ b/Source.Common/MaterialSystem/IShaderSystem.cs @@ -25,6 +25,7 @@ public static bool IsTranslucent(IShaderShadow renderState) { void DrawElements(IShader shader, IMaterialVar[] parms, IShaderShadow renderState, VertexCompressionType vertexCompression, uint materialVarTimeStamp); IEnumerable GetShaders(); ReadOnlySpan ShaderStateString(int i); + int GetShaderAPITextureBindHandle(ITexture? texture, int v, int textureChannel); } public interface IShaderDLL { diff --git a/Source.Common/MaterialSystem/ITextureInternal.cs b/Source.Common/MaterialSystem/ITextureInternal.cs index c5d56ed9..f3fefd14 100644 --- a/Source.Common/MaterialSystem/ITextureInternal.cs +++ b/Source.Common/MaterialSystem/ITextureInternal.cs @@ -13,8 +13,9 @@ public static string NormalizeTextureName(ReadOnlySpan name) { return Path.ChangeExtension(new(name.SliceNullTerminatedString()), null); // todo. } + void Bind(Sampler sampler); void Bind(Sampler sampler, int frame); - int GetTextureHandle(int v); + int GetTextureHandle(int frame, int textureChannel = 0); void OnRestore(); void Precache(); bool SetRenderTarget(int rt, ITexture? depthTexture = null); @@ -60,10 +61,12 @@ public static bool IsTextureInternalEnvCubemap(ITexture? texture) { public void ForceLODOverride(int numLodOverrideUpOrDown) => throw new NotSupportedException(); public bool SaveToFile(ReadOnlySpan fileName) => throw new NotSupportedException(); public void Dispose() { } + public void Bind(Sampler sampler) => throw new NotImplementedException(); public void Bind(Sampler sampler, int frame) => throw new NotSupportedException(); public int GetTextureHandle(int v) => throw new NotSupportedException(); public void OnRestore() => throw new NotSupportedException(); public void Precache() => throw new NotSupportedException(); public bool SetRenderTarget(int rt, ITexture? depthTexture = null) => throw new NotSupportedException(); public void GetReflectivity(out Vector3 reflectivity) => throw new NotSupportedException(); + public int GetTextureHandle(int frame, int textureChannel = 0) => throw new NotImplementedException(); } diff --git a/Source.Common/MaterialSystem/ITextureManager.cs b/Source.Common/MaterialSystem/ITextureManager.cs index c4ca28e4..92b7b878 100644 --- a/Source.Common/MaterialSystem/ITextureManager.cs +++ b/Source.Common/MaterialSystem/ITextureManager.cs @@ -2,8 +2,10 @@ namespace Source.Common.MaterialSystem; -public interface ITextureManager { +public interface ITextureManager +{ void Init(); ITextureInternal CreateFileTexture(ReadOnlySpan fileName, ReadOnlySpan textureGroupName); ITextureInternal ErrorTexture(); + ITextureInternal SignedNormalizationCubemap(); } \ No newline at end of file diff --git a/Source.Common/MaterialSystem/MaterialSystem_Config.cs b/Source.Common/MaterialSystem/MaterialSystem_Config.cs index 7716382c..09067752 100644 --- a/Source.Common/MaterialSystem/MaterialSystem_Config.cs +++ b/Source.Common/MaterialSystem/MaterialSystem_Config.cs @@ -106,6 +106,7 @@ public void SetFlag(MaterialSystem_Config_Flags flag, bool val) { public bool UseSpecular() => (Flags & (int)MaterialSystem_Config_Flags.DisableSpecular) == 0; public bool DisableSpecular() => (Flags & (int)MaterialSystem_Config_Flags.DisableSpecular) != 0; public bool DisableBumpmap() => (Flags & (int)MaterialSystem_Config_Flags.DisableBumpmap) != 0; + public bool UseBumpmapping() => (Flags & (int)MaterialSystem_Config_Flags.DisableBumpmap) == 0; public bool EnableParallaxMapping() => (Flags & (int)MaterialSystem_Config_Flags.EnableParallaxMapping) != 0; public bool UseZPrefill() => (Flags & (int)MaterialSystem_Config_Flags.UseZPrefill) != 0; public bool ReduceFillrate() => (Flags & (int)MaterialSystem_Config_Flags.ReduceFillrate) != 0; @@ -113,6 +114,7 @@ public void SetFlag(MaterialSystem_Config_Flags flag, bool val) { public bool ScaleToOutputResolution() => (Flags & (int)MaterialSystem_Config_Flags.ScaleToOutputResolution) != 0; public bool UsingMultipleWindows() => (Flags & (int)MaterialSystem_Config_Flags.UsingMultipleWindows) != 0; public bool DisablePhong() => (Flags & (int)MaterialSystem_Config_Flags.DisablePhong) != 0; + public bool UsePhong() => (Flags & (int)MaterialSystem_Config_Flags.DisablePhong) == 0; public bool VRMode() => (Flags & (int)MaterialSystem_Config_Flags.VRMode) != 0; public MaterialSystem_Config() { diff --git a/Source.Common/RefCount.cs b/Source.Common/RefCount.cs new file mode 100644 index 00000000..81c0892a --- /dev/null +++ b/Source.Common/RefCount.cs @@ -0,0 +1,7 @@ +namespace Source.Common; + +public interface IRefCounted +{ + int AddRef(); + int Release(); +} diff --git a/Source.Common/RenderParam.cs b/Source.Common/RenderParam.cs new file mode 100644 index 00000000..6af4d6d7 --- /dev/null +++ b/Source.Common/RenderParam.cs @@ -0,0 +1,55 @@ +namespace Source.Common; + +public enum RenderParamVector +{ + HmdWarpLeftCentre = 0, + HmdWarpLeftCoeff012, + HmdWarpLeftCoeff34RedOffset, + HmdWarpRightCentre, + HmdWarpRightCoeff012, + HmdWarpRightCoeff34BlueOffset, + HmdWarpGrowOutIn, + HmdWarpGrowAboveBelow, + HmdWarpAspect, + DistortionType, + WindDirection, + + Max = 20 +} + +public enum RenderParamInt +{ + EnableFixedLighting = 0, + MorphAccumulatorXOffset, + MorphAccumulatorYOffset, + MorphAccumulatorSubrectWidth, + MorphAccumulatorSubrectHeight, + MorphAccumulator4TupleCount, + MorphWeightXOffset, + MorphWeightYOffset, + MorphWeightSubrectWidth, + MorphWeightSubrectHeight, + WriteDepthToDestAlpha, + BackBufferIndex, + Max = 20 +} + +public enum RenderParamTexture +{ + AmbientOcclusion = 0, + Max = 2 +} + +public enum BackBufferIndex +{ + Default = 0, + Hdr = 1 +} + +public enum EnableFixedLighting +{ + None = 0, + BasicLight = 1, + OutputMrtsForDeferredLighting = 2, + OutputNormalAndDepth = 3 +} diff --git a/Source.Common/ShaderAPI/IShaderAPI.cs b/Source.Common/ShaderAPI/IShaderAPI.cs index dc8ac37a..fbd68cc3 100644 --- a/Source.Common/ShaderAPI/IShaderAPI.cs +++ b/Source.Common/ShaderAPI/IShaderAPI.cs @@ -4,6 +4,7 @@ using Source.Common.Mathematics; using System.Numerics; +using System.Runtime.CompilerServices; namespace Source.Common.ShaderAPI; @@ -21,11 +22,24 @@ public enum CreateTextureFlags SRGB = 0x4000, } +public struct SamplerShadowState +{ + public bool SRGBReadEnable; +} + +[InlineArray((int)Sampler.MaxSamplers)] +public struct SamplerShadowStates +{ + private SamplerShadowState element; +} + /// /// A basic representation of the graphics state machine /// public struct GraphicsBoardState { + public SamplerShadowStates SamplerState; + public bool Blending; public ShaderBlendFactor SourceBlend; public ShaderBlendFactor DestinationBlend; @@ -42,6 +56,7 @@ public struct GraphicsBoardState public bool DepthWrite; public bool CullEnable; public bool AlphaToCoverage; + public bool SRGBWriteEnable; public ShaderDepthFunc DepthFunc; public ShaderPolyMode FillMode; @@ -58,6 +73,9 @@ public interface IShaderAPI : IShaderDynamicAPI void SetViewports(ReadOnlySpan viewports); void GetViewports(Span viewports); + void SetToneMappingScaleLinear(in Vector3 scale); + ref readonly Vector3 GetToneMappingScaleLinear(); + void PreInit(IShaderUtil shaderUtil, IServiceProvider services); void DrawMesh(IMesh mesh); void Bind(IMaterial? material); diff --git a/Source.Common/ShaderAPI/IShaderShadow.cs b/Source.Common/ShaderAPI/IShaderShadow.cs index 1e398e91..270200c0 100644 --- a/Source.Common/ShaderAPI/IShaderShadow.cs +++ b/Source.Common/ShaderAPI/IShaderShadow.cs @@ -324,4 +324,6 @@ public interface IShaderShadow ShaderFlags GetFlags(); void SetFlags(ShaderFlags flags); + void EnableSRGBRead(Sampler sampler, bool state); + void EnableSRGBWrite(bool state); } diff --git a/Source.Common/ShaderLib/BaseShader.cs b/Source.Common/ShaderLib/BaseShader.cs index 155e8932..24339839 100644 --- a/Source.Common/ShaderLib/BaseShader.cs +++ b/Source.Common/ShaderLib/BaseShader.cs @@ -68,4 +68,48 @@ public static class VertexShaderConst public const int ShaderSpecificConst12 = 224; public const int FlexWeights = 1024; public const int MaxFlexWeightCount = 512; +} + +public enum PixelShaderConst +{ + SelfIllumTint = 0, + DiffuseModulation = 1, + EnvMapTintShadowTweaks = 2, + SelfIllumScaleBiasExp = 3, + AmbientCube = 4, + Constant05 = 5, + Constant06 = 6, + Constant07 = 7, + Constant08 = 8, + Constant09 = 9, + EnvMapFresnelSelfIllumMask = 10, + EyePosSpecExponent = 11, + FogParams = 12, + FlashlightAttenuation = 13, + FlashlightPositionRimBoost = 14, + FlashlightToWorldTexture = 15, + Constant16 = 16, + Constant17 = 17, + Constant18 = 18, + FresnelSpecParams = 19, + LightInfoArray = 20, + Constant21 = 21, + Constant22 = 22, + Constant23 = 23, + Constant24 = 24, + Constant25 = 25, + SpecRimParams = 26, + Constant27 = 27, + FlashlightColor = 28, + LinearFogColor = 29, + LightScale = 30, + FlashlightScreenScale = 31 +} + +public enum BlendType +{ + None = 0, + Blend, + Add, + BlendAdd } \ No newline at end of file diff --git a/Source.Common/Studio.cs b/Source.Common/Studio.cs index 618292fe..78866939 100644 --- a/Source.Common/Studio.cs +++ b/Source.Common/Studio.cs @@ -425,9 +425,13 @@ public int GetGlobalVertexIndex(int i) { return i + (model.VertexIndex / Unsafe.SizeOf()); } + public int GetGlobalTangentIndex(int i) { + return i + (model.TangentsIndex / Unsafe.SizeOf()); + } + public ref Vector3 Position(int i) => ref Vertex(i).Position; public ref Vector3 Normal(int i) => ref Vertex(i).Normal; - public ref Vector4 TangentS(int i) => ref ((Memory)GetTangentData()!).Span[i]; + public ref Vector4 TangentS(int i) => ref ((Memory)GetTangentData()!).Span[GetGlobalTangentIndex(i)]; public ref Vector2 TexCoord(int i) => ref Vertex(i).TexCoord; public ref MStudioBoneWeight BoneWeights(int i) => ref Vertex(i).BoneWeights; public ref MStudioVertex Vertex(int i) => ref GetVertexData().Span.Cast()[GetGlobalVertexIndex(i)]; diff --git a/Source.Common/Usings/ShaderDefines.cs b/Source.Common/Usings/ShaderDefines.cs index b9fd4d08..4fe5aa64 100644 --- a/Source.Common/Usings/ShaderDefines.cs +++ b/Source.Common/Usings/ShaderDefines.cs @@ -20,6 +20,8 @@ public static class ShaderDefines public static bool IsFlag2Set(Span shaderParams, int flag) => (shaderParams[(int)ShaderMaterialVars.Flags2].GetIntValue() & flag) != 0; public static bool IsFlag2Set(Span shaderParams, MaterialVarFlags2 flag) => IsFlag2Set(shaderParams, (int)flag); + public static bool IsParamDefined(Span shaderParams, int parm) => parm >= 0 && shaderParams[parm].IsDefined(); + public static void SetFlags(Span shaderParams, int flag) => shaderParams[(int)ShaderMaterialVars.Flags].SetIntValue(shaderParams[(int)ShaderMaterialVars.Flags].GetIntValue() | flag); public static void SetFlags(Span shaderParams, MaterialVarFlags flag) => SetFlags(shaderParams, (int)flag); diff --git a/Source.Engine/DebugOverlay.cs b/Source.Engine/DebugOverlay.cs index 86287726..c394aa05 100644 --- a/Source.Engine/DebugOverlay.cs +++ b/Source.Engine/DebugOverlay.cs @@ -235,7 +235,25 @@ public void AddTextOverlay(in Vector3 origin, float duration, ReadOnlySpan } public void AddTextOverlay(in Vector3 origin, int line_offset, float duration, ReadOnlySpan text) { - throw new NotImplementedException(); + if (cl.IsPaused()) + return; + + lock (s_OverlayMutex) { + OverlayText new_overlay = new(); + + MathLib.VectorCopy(origin, out new_overlay.Origin); + strcpy(new_overlay.Text, text); + new_overlay.UseOrigin = true; + new_overlay.LineOffset = line_offset; + new_overlay.SetEndTime(duration); + new_overlay.R = 255; + new_overlay.G = 255; + new_overlay.B = 255; + new_overlay.A = 255; + + new_overlay.NextOverlayText = s_pOverlayText; + s_pOverlayText = new_overlay; + } } public void AddTextOverlay(in Vector3 origin, int line_offset, float duration, int r, int g, int b, int a, ReadOnlySpan text) { diff --git a/Source.Engine/GLRSurf.cs b/Source.Engine/GLRSurf.cs index 3e5dc249..f9dc3673 100644 --- a/Source.Engine/GLRSurf.cs +++ b/Source.Engine/GLRSurf.cs @@ -154,6 +154,7 @@ public class WorldRenderList : IWorldRenderList public VarBitVec VisitedSurfs = new(); public bool SkyVisible; + int Refs = 1; static readonly Stack g_Pool = new(); @@ -225,7 +226,16 @@ public void Reset() { VisitedSurfs.ClearAll(); } - public void AddRef() { } + public int AddRef() => ++Refs; + + public int Release() { + int result = --Refs; + if (result != 0) + return result; + + OnFinalRelease(); + return 0; + } } public static class GLRSurf @@ -441,7 +451,7 @@ public static void Shader_DrawSurfaceDynamic(IMatRenderContext renderContext, Su public static void Shader_DrawChainsDynamic(in MSurfaceSortList sortList, int sortGroup, bool shadowDepth) => throw new NotImplementedException(); public static void Shader_DrawChainsStatic(in MSurfaceSortList sortList, int sortGroup, bool shadowDepth) { List meshList = []; - int[] meshMap = new int[MAX_VERTEX_FORMAT_CHANGES]; + InlineArray256 meshMap = new(); List batchList = []; List dynamicGroups = []; bool bWarn = true; @@ -511,7 +521,7 @@ public static void Shader_DrawChainsStatic(in MSurfaceSortList sortList, int sor Assert(indexCount + numIndex < nMaxIndices); indexCount += numIndex; - CollectionsMarshal.AsSpan(meshList)[meshIndex].NumBatches++; + meshList.AsSpan()[meshIndex].NumBatches++; for (short blockIndex = group.ListHead; blockIndex != -1; blockIndex = sortList.GetSurfaceBlock(blockIndex).NextBlock) { ref MaterialList matList = ref sortList.GetSurfaceBlock(blockIndex); diff --git a/Source.Engine/Lightcache.cs b/Source.Engine/Lightcache.cs index e68d3975..4f0a9b29 100644 --- a/Source.Engine/Lightcache.cs +++ b/Source.Engine/Lightcache.cs @@ -889,9 +889,8 @@ private void AdjustLightCacheOrigin(LightCache cache, in Vector3 origin, int ori return null; } - private byte[]? ComputeStaticLightingForCacheEntry(BaseLightCache cache, in Vector3 origin, int leaf, bool staticProp = false) { - // todo - byte[]? vis = null; + private ReadOnlySpan ComputeStaticLightingForCacheEntry(BaseLightCache cache, scoped in Vector3 origin, int leaf, bool staticProp = false) { + ReadOnlySpan vis = CM.ClusterPVS(CM.LeafCluster(leaf)); R_StudioGetAmbientLightForPoint(leaf, origin, cache.StaticLightingState.BoxColor, staticProp, out bool addedLeafAmbientCube); @@ -901,7 +900,7 @@ private void AdjustLightCacheOrigin(LightCache cache, in Vector3 origin, int ori return vis; } - private byte[]? PrecalcLightingState(LightCache cache, byte[]? vis) { + private ReadOnlySpan PrecalcLightingState(LightCache cache, ReadOnlySpan vis) { LightingState lightingState = default; lightingState.ZeroLightingState(); @@ -933,7 +932,7 @@ private static void CopyPrecalcedLightingState(LightCache cache, ref LightingSta lightingState.LocalLight[i] = cache.StaticPrecalcLocalLight[i]; } - private byte[]? AddLightingState(ref LightingState dst, in LightingState src, LightingStateInfo info, in Vector3 bucketOrigin, byte[]? vis, bool dynamic, bool ignoreVis) { + private ReadOnlySpan AddLightingState(scoped ref LightingState dst, scoped in LightingState src, LightingStateInfo info, scoped in Vector3 bucketOrigin, ReadOnlySpan vis, bool dynamic, bool ignoreVis) { int i; for (i = 0; i < src.NumLights; i++) vis = AddWorldLightToLightingState(src.LocalLight[i], null, ref dst, info, bucketOrigin, vis, dynamic, ignoreVis); @@ -972,12 +971,12 @@ private static void AddWorldLightToLightCube(in BSPDWorldLight worldLight, Span< } } - private byte[]? FastRejectLightSource(bool ignoreVis, byte[]? vis, in Vector3 bucketOrigin, EmitType lightType, int lightCluster, out bool reject) { + private ReadOnlySpan FastRejectLightSource(bool ignoreVis, ReadOnlySpan vis, scoped in Vector3 bucketOrigin, EmitType lightType, int lightCluster, out bool reject) { reject = false; if (!ignoreVis) { - if (vis == null) { + if (vis.IsEmpty) { int bucketOriginLeaf = CM.PointLeafnum(bucketOrigin); - vis = CM.ClusterPVS(CM.LeafCluster(bucketOriginLeaf)).ToArray(); + vis = CM.ClusterPVS(CM.LeafCluster(bucketOriginLeaf)); } if (lightType == EmitType.SkyLight) { int bucketOriginLeaf = CM.PointLeafnum(bucketOrigin); @@ -993,7 +992,7 @@ private static void AddWorldLightToLightCube(in BSPDWorldLight worldLight, Span< return vis; } - private byte[]? AddWorldLightToLightingState(in BSPDWorldLightPtr light, LightZBuffer[]? zBuf, ref LightingState lightingState, LightingStateInfo info, in Vector3 bucketOrigin, byte[]? vis, bool dynamic = false, bool ignoreVis = false, bool ignoreVisTest = false) { + private ReadOnlySpan AddWorldLightToLightingState(scoped in BSPDWorldLightPtr light, LightZBuffer[]? zBuf, scoped ref LightingState lightingState, LightingStateInfo info, scoped in Vector3 bucketOrigin, ReadOnlySpan vis, bool dynamic = false, bool ignoreVis = false, bool ignoreVisTest = false) { Assert(lightingState.NumLights >= 0 && lightingState.NumLights <= MAXLOCALLIGHTS); BSPDWorldLightPtr worldLightPtr = light; @@ -1063,7 +1062,7 @@ private static void AddWorldLightToLightCube(in BSPDWorldLight worldLight, Span< return vis; } - private void AddStaticLighting(BaseLightCache cache, in Vector3 origin, byte[]? vis, bool staticProp, bool addedLeafAmbientCube) { + private void AddStaticLighting(BaseLightCache cache, in Vector3 origin, ReadOnlySpan vis, bool staticProp, bool addedLeafAmbientCube) { int i; cache.StaticLightingState.NumLights = 0; cache.LightingStateHasSkylight = false; @@ -1112,7 +1111,7 @@ private void AddStaticLighting(BaseLightCache cache, in Vector3 origin, byte[]? } } - private byte[]? ComputeLightStyles(LightCache cache, ref LightingState lightingState, in Vector3 origin, int leaf, byte[]? vis) { + private ReadOnlySpan ComputeLightStyles(LightCache cache, scoped ref LightingState lightingState, scoped in Vector3 origin, int leaf, ReadOnlySpan vis) { LightingStateInfo info = new(); lightingState.ZeroLightingState(); @@ -1127,8 +1126,8 @@ private void AddStaticLighting(BaseLightCache cache, in Vector3 origin, byte[]? if ((cache.Lightstyles[b] & (1 << bit)) == 0) continue; - if (vis == null) - vis = CM.ClusterPVS(CM.LeafCluster(leaf)).ToArray(); + if (vis.IsEmpty) + vis = CM.ClusterPVS(CM.LeafCluster(leaf)); AddWorldLightToLightingState(wl, null, ref lightingState, info, origin, vis); } @@ -1136,7 +1135,7 @@ private void AddStaticLighting(BaseLightCache cache, in Vector3 origin, byte[]? return vis; } - private byte[]? ComputeDynamicLighting(LightCache cache, ref LightingState lightingState, in Vector3 lightingOrigin, int leaf, byte[]? vis) { + private ReadOnlySpan ComputeDynamicLighting(LightCache cache, scoped ref LightingState lightingState, scoped in Vector3 lightingOrigin, int leaf, ReadOnlySpan vis) { cache.DynamicLightingState.ZeroLightingState(); // todo return vis; @@ -1224,7 +1223,7 @@ private void AddLightStylesForStaticProp(PropLightcache pcache, ref LightingStat BSPDWorldLightPtr wl = new(host_state.WorldBrush!.WorldLights!, pcache.LightStyleWorldLights[i]); Assert(wl.Dereference().Style != 0); - AddWorldLightToLightingState(wl, null, ref lightingState, pcache, pcache.LightingOrigin, null, false, true); + AddWorldLightToLightingState(wl, null, ref lightingState, pcache, pcache.LightingOrigin, default, false, true); } } @@ -1241,7 +1240,7 @@ private void AddDLightsForStaticProps(LightingStateInfo info, ref LightingState int bucket = LightcacheHashKey(x, y, z, originLeaf); - byte[]? vis = null; + ReadOnlySpan vis = default; bool computeLightStyles = (flags & LightCacheFlags.LightStyle) != 0; LightCache? cache = FindInCache(bucket, x, y, z, originLeaf); diff --git a/Source.Engine/StaticPropMgr.cs b/Source.Engine/StaticPropMgr.cs index 8f68ad30..0457204a 100644 --- a/Source.Engine/StaticPropMgr.cs +++ b/Source.Engine/StaticPropMgr.cs @@ -330,15 +330,15 @@ public void ComputePropOpacity(StaticProp prop) { #endif } - public void DrawStaticProps(IClientRenderable[] props, int count, bool shadowDepth, bool drawVCollideWireframe) { + public void DrawStaticProps(ref InlineArray512 props, int count, bool shadowDepth, bool drawVCollideWireframe) { if (!r_drawstaticprops.GetBool()) return; // todo: fast pipeline - DrawStaticProps_Slow(props, count, shadowDepth, drawVCollideWireframe); + DrawStaticProps_Slow(ref props, count, shadowDepth, drawVCollideWireframe); } - void DrawStaticProps_Slow(IClientRenderable[] props, int count, bool shadowDepth, bool drawVCollideWireframe) { + void DrawStaticProps_Slow(ref InlineArray512 props, int count, bool shadowDepth, bool drawVCollideWireframe) { StudioFlags flags = StudioFlags.Render; if (shadowDepth) flags |= StudioFlags.ShadowDepthTexture; @@ -351,8 +351,8 @@ void DrawStaticProps_Slow(IClientRenderable[] props, int count, bool shadowDepth } } - void DrawStaticProps_Fast(IClientRenderable[] props, int count, bool shadowDepth) => throw new NotImplementedException(); - void DrawStaticProps_FastPipeline(IClientRenderable[] props, int count, bool shadowDepth) => throw new NotImplementedException(); + void DrawStaticProps_Fast(ref InlineArray512 props, int count, bool shadowDepth) => throw new NotImplementedException(); + void DrawStaticProps_FastPipeline(ref InlineArray512 props, int count, bool shadowDepth) => throw new NotImplementedException(); void OutputLevelStats() => throw new NotImplementedException(); void PrecacheLighting() { diff --git a/Source.MaterialSystem/MatRenderContext.cs b/Source.MaterialSystem/MatRenderContext.cs index 390daba6..76df116f 100644 --- a/Source.MaterialSystem/MatRenderContext.cs +++ b/Source.MaterialSystem/MatRenderContext.cs @@ -5,8 +5,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; namespace Source.MaterialSystem; @@ -302,6 +300,12 @@ public bool InFlashlightMode() { return FlashlightEnable; } + float CurToneMapScale = 1.0f; + + public void TurnOnToneMapping() => SetToneMappingScaleLinear(new(CurToneMapScale, CurToneMapScale, CurToneMapScale)); + + public void SetToneMappingScaleLinear(in Vector3 scale) => shaderAPI.SetToneMappingScaleLinear(in scale); + public void BeginFrame() => shaderAPI.BeginFrame(); public void EndFrame() => shaderAPI.EndFrame(); @@ -619,6 +623,7 @@ public void BindLightmapPage(int lightmapPageID) { [MethodImpl(MethodImplOptions.AggressiveInlining)] public ShaderAPITextureHandle_t GetGreyAlphaZeroTextureHandle() => materials.GetGreyAlphaZeroTextureHandle(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public ShaderAPITextureHandle_t GetWhiteTextureHandle() => materials.GetWhiteTextureHandle(); + readonly ITextureManager TextureSystem = (Singleton() as TextureManager)!; public void BindStandardTexture(Sampler sampler, StandardTextureId id) { switch (id) { case StandardTextureId.Lightmap: BindLightmap(sampler); break; @@ -627,6 +632,7 @@ public void BindStandardTexture(Sampler sampler, StandardTextureId id) { case StandardTextureId.Black: shaderAPI.BindTexture(sampler, GetBlackTextureHandle()); break; case StandardTextureId.Grey: shaderAPI.BindTexture(sampler, GetGreyTextureHandle()); break; case StandardTextureId.GreyAlphaZero: shaderAPI.BindTexture(sampler, GetGreyAlphaZeroTextureHandle()); break; + case StandardTextureId.NormalizationCubemapSigned: TextureSystem.SignedNormalizationCubemap().Bind(sampler); break; default: Assert(false); break; } } diff --git a/Source.MaterialSystem/MatStub.cs b/Source.MaterialSystem/MatStub.cs index eb8675ef..4a2779a5 100644 --- a/Source.MaterialSystem/MatStub.cs +++ b/Source.MaterialSystem/MatStub.cs @@ -265,6 +265,8 @@ protected override void GetVecValueInternal(Span val) { val[i] = 1; } protected override int VectorSizeInternal() => 3; + + public override Span GetVecValue() => vecvalX4; } public class DummyMaterial : IMaterial @@ -473,4 +475,6 @@ public void SetStencilWriteMask(uint msk) { } public void SetScissorRect(int left, int top, int right, int bottom, bool enableScissor) { } public ImageFormat GetShadowDepthTextureFormat() => ImageFormat.Unknown; public ImageFormat GetNullTextureFormat() => ImageFormat.Unknown; + public void TurnOnToneMapping() { } + public void SetToneMappingScaleLinear(in Vector3 scale) { } } diff --git a/Source.MaterialSystem/Material.cs b/Source.MaterialSystem/Material.cs index bbddcce6..e5e02e88 100644 --- a/Source.MaterialSystem/Material.cs +++ b/Source.MaterialSystem/Material.cs @@ -9,6 +9,7 @@ using Source.Common.Utilities; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Numerics; using System.Runtime.CompilerServices; @@ -701,8 +702,17 @@ private int ParseMaterialVars(IShader shader, KeyValues keyValues, KeyValues? fa IMaterialVar? matrixVar = CreateMatrixVarFromKeyValue(material, keyValue); if (matrixVar != null) return matrixVar; - if (!IsVector(str)) + if (!IsVector(str)) { + // FIXME KeyValues is meant to handle this + { + if (int.TryParse(str, NumberStyles.Integer, CultureInfo.InvariantCulture, out int i32)) + return new MaterialVar(material, name, i32); + + if (float.TryParse(str, NumberStyles.Float, CultureInfo.InvariantCulture, out float f32)) + return new MaterialVar(material, name, f32); + } return new MaterialVar(material, name, str); + } return CreateVectorMaterialVarFromKeyValue(material, keyValue); } @@ -917,6 +927,10 @@ private string MissingShaderName() { IShaderShadow ShaderRenderState; static uint DebugVarsSignature = 0; + public void ReportVarChanged(IMaterialVar? var) => ChangeID++; + + public uint GetChangeID() => ChangeID; + public void DrawMesh(VertexCompressionType vertexCompression) { if (Shader != null) { if ((GetMaterialVarFlags() & MaterialVarFlags.Debug) == 0) { diff --git a/Source.MaterialSystem/MaterialVar.cs b/Source.MaterialSystem/MaterialVar.cs index 4d9030a0..52bd93d9 100644 --- a/Source.MaterialSystem/MaterialVar.cs +++ b/Source.MaterialSystem/MaterialVar.cs @@ -8,6 +8,8 @@ public sealed class MaterialVar : IMaterialVar { IMaterialInternal owningMaterial; + void VarChanged() => owningMaterial?.ReportVarChanged(this); + void Init() { } @@ -119,6 +121,7 @@ public override void SetFloatValue(float val) { VecVal[0] = VecVal[1] = VecVal[2] = VecVal[3] = val; IntVal = (int)val; Type = MaterialVarType.Float; + VarChanged(); } public override void SetFourCCValue(ulong type, object? data) { @@ -129,6 +132,7 @@ public override void SetIntValue(int val) { IntVal = val; VecVal[0] = VecVal[1] = VecVal[2] = VecVal[3] = val; Type = MaterialVarType.Int; + VarChanged(); } public override void SetMaterialValue(IMaterial? material) { @@ -136,21 +140,24 @@ public override void SetMaterialValue(IMaterial? material) { } public override void SetMatrixValue(in Matrix4x4 matrix) { - + VarChanged(); } public override void SetStringValue(ReadOnlySpan val) { StringVal = new(val.SliceNullTerminatedString()); Type = MaterialVarType.String; + VarChanged(); } public override void SetTextureValue(ITexture? texture) { Type = MaterialVarType.Texture; TextureValue = texture; + VarChanged(); } public override void SetUndefined() { Type = MaterialVarType.Undefined; + VarChanged(); } public override void SetValueAutodetectType(ReadOnlySpan val) { @@ -167,6 +174,7 @@ public override unsafe void SetVecValue(ReadOnlySpan val) { Type = MaterialVarType.Vector; NumVectorComps = (byte)Math.Min(val.Length, 4); IntVal = (int)VecVal[0]; + VarChanged(); } public override void SetVecValue(float x, float y) { @@ -175,6 +183,7 @@ public override void SetVecValue(float x, float y) { Type = MaterialVarType.Vector; NumVectorComps = 2; IntVal = (int)VecVal[0]; + VarChanged(); } public override void SetVecValue(float x, float y, float z) { @@ -184,10 +193,18 @@ public override void SetVecValue(float x, float y, float z) { Type = MaterialVarType.Vector; NumVectorComps = 3; IntVal = (int)VecVal[0]; + VarChanged(); } public override void SetVecValue(float x, float y, float z, float w) { - throw new NotImplementedException(); + VecVal[0] = x; + VecVal[1] = y; + VecVal[2] = z; + VecVal[3] = w; + Type = MaterialVarType.Vector; + NumVectorComps = 4; + IntVal = (int)VecVal[0]; + VarChanged(); } protected override float GetFloatValueInternal() { @@ -209,4 +226,6 @@ protected override void GetVecValueInternal(Span val) { protected override int VectorSizeInternal() { return NumVectorComps; } + + public override Span GetVecValue() => new[] { VecVal.X, VecVal.Y, VecVal.Z, VecVal.W }; } diff --git a/Source.MaterialSystem/Texture.cs b/Source.MaterialSystem/Texture.cs index 3ad671fc..56dab2c1 100644 --- a/Source.MaterialSystem/Texture.cs +++ b/Source.MaterialSystem/Texture.cs @@ -1286,8 +1286,23 @@ public bool SetRenderTarget(int renderTargetID, ITexture? depthTexture = null) { return true; } - public int GetTextureHandle(int v) { - return TextureHandles![v]; + public int GetTextureHandle(int frame, int textureChannel = 0) { + if (frame < 0) { + frame = 0; + Warning("CTexture::GetTextureHandle(): nFrame is < 0!\n"); + } + + if (frame >= FrameCount) { + Assert(frame < FrameCount); + return INVALID_SHADERAPI_TEXTURE_HANDLE; + } + + Assert(TextureHandles); + Assert(HasBeenAllocated()); + if (TextureHandles == null || !HasBeenAllocated()) + return INVALID_SHADERAPI_TEXTURE_HANDLE; + + return TextureHandles![frame]; } Vector3 Reflectivity; diff --git a/Source.MaterialSystem/TextureManager.cs b/Source.MaterialSystem/TextureManager.cs index 354f8915..6b0a7d6b 100644 --- a/Source.MaterialSystem/TextureManager.cs +++ b/Source.MaterialSystem/TextureManager.cs @@ -27,6 +27,7 @@ public ITextureInternal ErrorTexture() { private ITextureInternal errorTexture; private ITextureInternal whiteTexture; + private ITextureInternal signedNormalizationCubemap; const int ERROR_TEXTURE_SIZE = 32; const int WHITE_TEXTURE_SIZE = 1; @@ -54,7 +55,7 @@ public void Init() { private void CreateCheckerboardTexture(ITexture errorTexture, int checkerSize, Color color1, Color color2) => errorTexture.SetTextureRegenerator(new CheckerboardTexture(checkerSize, color1, color2)); - private void CreateSolidTexture(ITexture tex, Color color) + private void CreateSolidTexture(ITexture tex, Color color) => tex.SetTextureRegenerator(new SolidTexture(color)); public ITextureInternal? CreateProceduralTexture(ReadOnlySpan name, ReadOnlySpan textureGroup, int w, int h, int d, ImageFormat imageFormat, TextureFlags flags, ITextureRegenerator? generator = null) { @@ -81,7 +82,7 @@ public void RemoveTexture(ITextureInternal texture) { ITextureInternal? texture = FindTexture(textureName); if (texture == null) { texture = LoadTexture(textureName, textureGroupName, additionalCreationFlags); - if (texture != null) + if (texture != null) TextureList.Add(texture.GetName().Hash(), texture); } @@ -90,9 +91,9 @@ public void RemoveTexture(ITextureInternal texture) { public ITextureInternal? LoadTexture(ReadOnlySpan textureName, ReadOnlySpan textureGroupName, int additionalCreationFlags, bool download = true) { ITextureInternal? newTexture = CreateFileTexture(textureName, textureGroupName); - if(newTexture != null) { + if (newTexture != null) { if (download) - newTexture.Download(); + newTexture.Download(default, additionalCreationFlags); } return newTexture; @@ -121,7 +122,7 @@ internal void RestoreRenderTargets() { } internal void RestoreNonRenderTargetTextures() { - foreach(var tex in TextureList) + foreach (var tex in TextureList) if (!tex.Value.IsRenderTarget()) RestoreTexture(tex.Value); } @@ -143,9 +144,9 @@ internal void GenerateErrorTexture(Texture texture, IVTFTexture vtfTexture) { internal ITextureInternal? CreateRenderTargetTexture(ReadOnlySpan rtName, int w, int h, RenderTargetSizeMode sizeMode, ImageFormat format, RenderTargetType type, TextureFlags textureFlags, CreateRenderTargetFlags renderTargetFlags) { ITextureInternal? texture; - if(!rtName.IsEmpty) { + if (!rtName.IsEmpty) { texture = FindTexture(rtName); - if(texture != null) { + if (texture != null) { ((Texture)texture)!.InitRenderTarget(texture.GetName(), w, h, sizeMode, format, type, textureFlags, renderTargetFlags); texture.Download(); return texture; @@ -160,4 +161,8 @@ internal void GenerateErrorTexture(Texture texture, IVTFTexture vtfTexture) { texture.Download(); return texture; } + + public ITextureInternal SignedNormalizationCubemap() { + return errorTexture; // TODO! + } } diff --git a/Source.ShaderAPI.Gl46/HardwareConfig.cs b/Source.ShaderAPI.Gl46/HardwareConfig.cs index bcfda75b..2114488f 100644 --- a/Source.ShaderAPI.Gl46/HardwareConfig.cs +++ b/Source.ShaderAPI.Gl46/HardwareConfig.cs @@ -54,17 +54,28 @@ public int GetMaxVertexTextureDimension() { public unsafe int GetSamplerCount() { int count; glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &count); - return count; + return Math.Min(count, (int)Sampler.MaxSamplers); } public ReadOnlySpan GetShaderDLLName() { throw new NotImplementedException(); } - public int GetShadowFilterMode() { - throw new NotImplementedException(); + public enum ShadowFilterMode + { + None = 0, + NvidiaPcfPoisson = 0, + AtiNoPcf = 1, + AtiNoPcfFetch4 = 2 } + public int GetShadowFilterMode() { + return ShadowDepthTextureFormat switch { + ImageFormat.NV_DST16 or ImageFormat.NV_DST24 => (int)ShadowFilterMode.NvidiaPcfPoisson, + ImageFormat.ATI_DST16 or ImageFormat.ATI_DST24 => (int)ShadowFilterMode.AtiNoPcfFetch4, + _ => (int)ShadowFilterMode.None, + }; + } public int GetTextureStageCount() { return GetSamplerCount(); } @@ -78,7 +89,7 @@ public bool HasDestAlphaBuffer() { } public bool HasFastVertexTextures() { - throw new NotImplementedException(); + return false; } public bool HasProjectedBumpEnv() { @@ -204,7 +215,8 @@ public int StencilBufferBits() { } public bool SupportsBorderColor() { - throw new NotImplementedException(); + // throw new NotImplementedException(); + return true; } public bool SupportsColorOnSecondStream() { @@ -274,11 +286,12 @@ public bool SupportsSpheremapping() { } public bool SupportsSRGB() { - throw new NotImplementedException(); + return true; } public bool SupportsStaticControlFlow() { - throw new NotImplementedException(); + // throw new NotImplementedException(); + return true; } public bool SupportsStaticPlusDynamicLighting() { diff --git a/Source.ShaderAPI.Gl46/ShaderAPIGl46.cs b/Source.ShaderAPI.Gl46/ShaderAPIGl46.cs index 3109b27b..134ea802 100644 --- a/Source.ShaderAPI.Gl46/ShaderAPIGl46.cs +++ b/Source.ShaderAPI.Gl46/ShaderAPIGl46.cs @@ -194,6 +194,10 @@ public void CallCommitFuncs(CommitFuncType func, bool usingFixedFunction, bool f uint uboVertexConstants; uint uboPixelConstants; + SourceVertexSharedShadowState vertexShared; + + internal SourceVertexSharedShadowState GetVertexSharedState() => vertexShared; + private unsafe void CreateMatrixStacks() { uboMatrices = glCreateBuffer(); glObjectLabel(GL_BUFFER, uboMatrices, "ShaderAPI Shared Matrix UBO"); @@ -232,6 +236,8 @@ public void InitRenderState() { if (!IsDeactivated()) ResetRenderState(); + + SetToneMappingScaleLinear(new Vector3(1.0f, 1.0f, 1.0f)); } public void SetPresentParameters(in ShaderDeviceInfo info) { @@ -297,7 +303,7 @@ public void SetLightingOrigin(Vector3 lightingOrigin) { } public void SetAmbientLight(float r, float g, float b) { - // todo + // todo todo } public const int MAX_NUM_LIGHTS = 4; @@ -662,7 +668,14 @@ public void CommitVertexShaderLighting() { SetVertexShaderConstant(VertexShaderConst.Lights + i * 5, MemoryMarshal.Cast(lightState)); } - // todo + vertexShared.LightCount = NumLights; + + Span lightEnable = stackalloc float[MAX_NUM_LIGHTS]; + lightEnable.Clear(); + for (i = 0; i < NumLights; ++i) + lightEnable[i] = 1.0f; + + vertexShared.LightEnabled = new(lightEnable[0], lightEnable[1], lightEnable[2], lightEnable[3]); } public void GetLightState(out LightState state) { @@ -927,6 +940,55 @@ public bool InEditorMode() { return false; // todo...? } + Vector4 ToneMappingScale = new(1.0f, 1.0f, 1.0f, 1.0f); + + private float GetLightMapScaleFactor() { + switch (HardwareConfig.GetHDRType()) { + case HDRType.Float: + return 1.0f; + + case HDRType.Integer: + return 16.0f; + + case HDRType.None: + default: + return MathLib.GammaToLinearFullRange(2.0f); + } + } + + public void SetToneMappingScaleLinear(in Vector3 scale) { + if (HardwareConfig.SupportsPixelShaders_2_0()) { + FlushBufferedPrimitives(); + + Vector3 scaleToUse = scale; + ToneMappingScale = new(scaleToUse, ToneMappingScale.W); + + switch (HardwareConfig.GetHDRType()) { + case HDRType.None: + ToneMappingScale.X = 1.0f; + ToneMappingScale.Z = 1.0f; + break; + + case HDRType.Float: + ToneMappingScale.X = scaleToUse.X; + ToneMappingScale.Z = 1.0f; + break; + + case HDRType.Integer: + ToneMappingScale.X = scaleToUse.X; + ToneMappingScale.Z = 16.0f; + break; + } + + ToneMappingScale.Y = GetLightMapScaleFactor(); + + ToneMappingScale.W = MathLib.LinearToGammaFullRange(ToneMappingScale.X); + SetPixelShaderConstant((int)PixelShaderConst.LightScale, MemoryMarshal.CreateSpan(ref ToneMappingScale.X, 4)); + } + } + + public ref readonly Vector3 GetToneMappingScaleLinear() => ref Unsafe.As(ref ToneMappingScale); + public void SetVertexShaderConstant(int var, Span vec) { SetVertexShaderConstantInternal(var, vec); } @@ -1402,6 +1464,30 @@ public void SetShaderUniform(int uniform, ReadOnlySpan flConsts) { glProgramUniform1fv(GetCurrentProgramInternal(), uniform, flConsts); } + const int GL_TEXTURE_SRGB_DECODE_EXT = 0x8A48; + const int GL_DECODE_EXT = 0x8A49; + const int GL_SKIP_DECODE_EXT = 0x8A4A; + + bool? supportsSRGBDecode; + private unsafe bool SupportsSRGBDecode() { + if (supportsSRGBDecode == null) { + supportsSRGBDecode = false; + int count; + glGetIntegerv(GL_NUM_EXTENSIONS, &count); + for (uint i = 0; i < count; i++) { + if (MemoryMarshal.CreateReadOnlySpanFromNullTerminated(glGetStringi(GL_EXTENSIONS, i)).SequenceEqual("GL_EXT_texture_sRGB_decode"u8)) { + supportsSRGBDecode = true; + break; + } + } + + if (supportsSRGBDecode == false) + Warning("GL_EXT_texture_sRGB_decode unsupported\n"); + } + + return supportsSRGBDecode.Value; + } + readonly int[] LastBoundTextures = new int[(int)Sampler.MaxSamplers]; int lastActiveTexture = -1; public void BindTexture(Sampler sampler, ShaderAPITextureHandle_t textureHandle) { @@ -1418,6 +1504,13 @@ public void BindTexture(Sampler sampler, ShaderAPITextureHandle_t textureHandle) glBindTexture(GetTexture(textureHandle).DetermineGLObjectType(), GetGL46Texture(textureHandle)); LastBoundTextures[(int)sampler] = textureHandle; } + + // D3D9 takes sRGB read from sampler state, but GL bakes it into the internal format. The same + // texture is shared by shaders that disagree, so skip the decode when the shadow state wants linear. + if (SupportsSRGBDecode()) { + bool srgbRead = currentShadow != null && currentShadow.State.SamplerState[(int)sampler].SRGBReadEnable; + glTextureParameteri(GetGL46Texture(textureHandle), GL_TEXTURE_SRGB_DECODE_EXT, srgbRead ? GL_DECODE_EXT : GL_SKIP_DECODE_EXT); + } } public bool CanDownloadTextures() { @@ -1446,6 +1539,7 @@ public ref struct TextureLoadInfo public ImageFormat SrcFormat; public Span SrcData; public bool TextureIsLockable; + public bool SRGB; } public void TexImageFromVTF(IVTFTexture? vtf, int vtfFrame) { @@ -1470,6 +1564,7 @@ public void TexImageFromVTF(IVTFTexture? vtf, int vtfFrame) { info.SrcFormat = vtf.Format(); info.SrcData = null; info.TextureIsLockable = (tex.Flags & InternalTextureFlags.IsLockable) != 0; + info.SRGB = (tex.CreationFlags & CreateTextureFlags.SRGB) != 0; if (vtf.Depth() > 1) { throw new NotImplementedException("Multidepth textures not supported yet"); } @@ -1515,7 +1610,7 @@ private unsafe void LoadCubeTextureFromVTF(in TextureLoadInfo info, IVTFTexture Span data = vtf.ImageData(vtfFrame, face, mip); if (info.SrcFormat.IsCompressed()) { fixed (byte* bytes = data) - glCompressedTextureSubImage3D((uint)info.Texture, mip, 0, 0, face, w, h, 1, ImageLoader.GetGLImageInternalFormat(info.SrcFormat), data.Length, bytes); + glCompressedTextureSubImage3D((uint)info.Texture, mip, 0, 0, face, w, h, 1, ImageLoader.GetGLImageInternalFormat(info.SrcFormat, info.SRGB), data.Length, bytes); } else { ConvertDataToAcceptableGLFormat(info.SrcFormat, data, out ImageFormat uploadFormat, out Span convertedData); @@ -1533,7 +1628,7 @@ private unsafe void LoadTextureFromVTF(in TextureLoadInfo info, IVTFTexture vtf, if (info.SrcFormat.IsCompressed()) { Span data = vtf.ImageData(vtfFrame, 0, info.Level); fixed (byte* bytes = data) - glCompressedTextureSubImage2D((uint)info.Texture, info.Level, 0, 0, w, h, ImageLoader.GetGLImageInternalFormat(info.SrcFormat), data.Length, bytes); + glCompressedTextureSubImage2D((uint)info.Texture, info.Level, 0, 0, w, h, ImageLoader.GetGLImageInternalFormat(info.SrcFormat, info.SRGB), data.Length, bytes); // Msg("err: " + glGetErrorName() + "\n"); } else { @@ -1605,7 +1700,8 @@ public unsafe void CreateTextures( if (copies <= 1) { texture.NumCopies = 1; uint glTex = glCreateTexture(texture.DetermineGLObjectType()); - glTextureStorage2D(glTex, mipCount, ImageLoader.GetGLImageInternalFormat(dstFormat), width, height); + glTextureStorage2D(glTex, mipCount, ImageLoader.GetGLImageInternalFormat(dstFormat, isSRGB), width, height); + SetLuminanceSwizzle(glTex, dstFormat); glObjectLabel(GL_TEXTURE, glTex, $"ShaderAPI Texture '{debugName.SliceNullTerminatedString()}' [frame {i}]"); texture.SetTexture(glTex); } @@ -1614,7 +1710,8 @@ public unsafe void CreateTextures( texture.TextureCopies = new uint[copies]; for (int k = 0; k < copies; k++) { uint glTex = glCreateTexture(texture.DetermineGLObjectType()); - glTextureStorage2D(glTex, mipCount, ImageLoader.GetGLImageInternalFormat(dstFormat), width, height); + glTextureStorage2D(glTex, mipCount, ImageLoader.GetGLImageInternalFormat(dstFormat, isSRGB), width, height); + SetLuminanceSwizzle(glTex, dstFormat); glObjectLabel(GL_TEXTURE, glTex, $"ShaderAPI Texture '{debugName.SliceNullTerminatedString()}' [frame {i} copy {k}]"); texture.SetTexture(k, glTex); } @@ -1626,6 +1723,23 @@ public unsafe void CreateTextures( } } + private static void SetLuminanceSwizzle(uint glTex, ImageFormat format) { + switch (format) { + case ImageFormat.I8: + glTextureParameteri(glTex, GL_TEXTURE_SWIZZLE_R, GL_RED); + glTextureParameteri(glTex, GL_TEXTURE_SWIZZLE_G, GL_RED); + glTextureParameteri(glTex, GL_TEXTURE_SWIZZLE_B, GL_RED); + glTextureParameteri(glTex, GL_TEXTURE_SWIZZLE_A, GL_ONE); + break; + case ImageFormat.IA88: + glTextureParameteri(glTex, GL_TEXTURE_SWIZZLE_R, GL_RED); + glTextureParameteri(glTex, GL_TEXTURE_SWIZZLE_G, GL_RED); + glTextureParameteri(glTex, GL_TEXTURE_SWIZZLE_B, GL_RED); + glTextureParameteri(glTex, GL_TEXTURE_SWIZZLE_A, GL_GREEN); + break; + } + } + InternalTextureInfo GetTexture(ShaderAPITextureHandle_t handle) => Textures[handle]; private void ComputeStatsInfo(ShaderAPITextureHandle_t hTexture, bool isCubeMap, bool isVolumeTexture) { @@ -1863,6 +1977,7 @@ public unsafe void TexImage2D(int mip, int face, ImageFormat dstFormat, int zOff info.SrcFormat = srcFormat; info.SrcData = imageData; info.TextureIsLockable = (tex.Flags & InternalTextureFlags.IsLockable) != 0; + info.SRGB = (tex.CreationFlags & CreateTextureFlags.SRGB) != 0; LoadTexture(ref info); SetModifyTexture(info.Texture); } @@ -2030,6 +2145,7 @@ public bool SetBoardState(in GraphicsBoardState state) { glPolygonMode(GL_FRONT_AND_BACK, state.FillMode.GLEnum()); glToggle(GL_CULL_FACE, state.CullEnable); glToggle(GL_SAMPLE_ALPHA_TO_COVERAGE, state.AlphaToCoverage); + glToggle(GL_FRAMEBUFFER_SRGB, state.SRGBWriteEnable); bool polyOffsetEnabled = state.ZBias != PolygonOffsetMode.Disable; glToggle(GL_POLYGON_OFFSET_FILL, polyOffsetEnabled && state.FillMode == ShaderPolyMode.Fill); @@ -2229,6 +2345,7 @@ public void SetNumBoneWeights(int numBones) { if (this.numBones != numBones) { FlushBufferedPrimitives(); this.numBones = numBones; + vertexShared.NumBones = numBones; } } @@ -2314,4 +2431,204 @@ public unsafe void TexUnlock() { public void BindStandardTexture(Sampler sampler, StandardTextureId id) { ShaderUtil.BindStandardTexture(sampler, id); } + + public FlashlightState GetFlashlightState(out Matrix4x4 worldToTexture) { + worldToTexture = FlashlightWorldToTexture; + return FlashlightState; + } + + public FlashlightState GetFlashlightStateEx(out Matrix4x4 worldToTexture, out ITexture? flashlightDepthTexture) { + worldToTexture = FlashlightWorldToTexture; + flashlightDepthTexture = FlashlightDepthTexture; + return FlashlightState; + } + + public bool IsHWMorphingEnabled() { + throw new NotImplementedException(); + } + + public void GetWorldSpaceCameraPosition(ref Span eyePos) { + eyePos[0] = WorldSpaceCameraPosition.X; + eyePos[1] = WorldSpaceCameraPosition.Y; + eyePos[2] = WorldSpaceCameraPosition.Z; + eyePos[3] = WorldSpaceCameraPosition.W; + } + + public int GetPixelFogCombo() { + // throw new NotImplementedException(); + return (int)MaterialFogMode.None; // TODO! + } + + public bool ShouldWriteDepthToDestAlpha() => + HardwareConfig.SupportsPixelShaders_2_b() && + (SceneFogMode != MaterialFogMode.LinearBelowFogZ) && + (GetIntRenderingParameter(RenderParamInt.WriteDepthToDestAlpha) != 0); + + public void MarkUnusedVertexFields(int v, Span unusedTexCoords) { + throw new NotImplementedException(); + } + + public void ExecuteCommandBuffer(ICommandStorageBuffer storage) { + ExecuteCommandBuffer(storage, storage.Base()); + } + + private void ExecuteCommandBuffer(ICommandStorageBuffer storage, Span cmdBuf) { + while (true) { + int offset = 0; + + while (true) { + CommandBufferCommand cmd = (CommandBufferCommand)MemoryMarshal.Read(cmdBuf[offset..]); + switch (cmd) { + case CommandBufferCommand.End: + return; + + case CommandBufferCommand.Jump: { + int reference = MemoryMarshal.Read(cmdBuf[(offset + 4)..]); + storage = storage.Reference(reference); + cmdBuf = storage.Base(); + offset = 0; + continue; + } + + case CommandBufferCommand.Jsr: { + int reference = MemoryMarshal.Read(cmdBuf[(offset + 4)..]); + ExecuteCommandBuffer(storage.Reference(reference)); + offset += 8; + break; + } + + case CommandBufferCommand.SetPixelShaderFloatConst: { + int firstReg = MemoryMarshal.Read(cmdBuf[(offset + 4)..]); + int numRegs = MemoryMarshal.Read(cmdBuf[(offset + 8)..]); + +#if DEBUG + Assert(numRegs > 0); + Assert(offset + 12 + numRegs * 16 <= cmdBuf.Length); +#endif + + Span values = MemoryMarshal.Cast(cmdBuf.Slice(offset + 12, numRegs * 16)); + + SetPixelShaderConstantInternal(firstReg, values); + + offset += 12 + numRegs * 16; + break; + } + + case CommandBufferCommand.SetVertexShaderFloatConst: { + int firstReg = MemoryMarshal.Read(cmdBuf[(offset + 4)..]); + int numRegs = MemoryMarshal.Read(cmdBuf[(offset + 8)..]); + +#if DEBUG + Assert(numRegs > 0); + Assert(offset + 12 + numRegs * 16 <= cmdBuf.Length); +#endif + + Span values = MemoryMarshal.Cast(cmdBuf.Slice(offset + 12, numRegs * 16)); + + SetVertexShaderConstantInternal(firstReg, values); + + offset += 12 + numRegs * 16; + break; + } + + case CommandBufferCommand.SetPixelShaderFogParams: { + int reg = MemoryMarshal.Read(cmdBuf[(offset + 4)..]); + // SetPixelShaderFogParams(reg); + offset += 8; + break; + } + + case CommandBufferCommand.StoreEyePosInPsConst: { + int reg = MemoryMarshal.Read(cmdBuf[(offset + 4)..]); + SetPixelShaderConstantInternal(reg, MemoryMarshal.CreateSpan(ref WorldSpaceCameraPosition.X, 4)); + offset += 8; + break; + } + + case CommandBufferCommand.CommitPixelShaderLighting: { + int reg = MemoryMarshal.Read(cmdBuf[(offset + 4)..]); + CommitPixelShaderLighting(reg); + offset += 8; + break; + } + + case CommandBufferCommand.SetPixelShaderStateAmbientLightCube: { + int reg = MemoryMarshal.Read(cmdBuf[(offset + 4)..]); + SetPixelShaderConstantInternal(reg, MemoryMarshal.CreateSpan(ref AmbientLightCube[0].X, 24)); + offset += 8; + break; + } + + case CommandBufferCommand.SetAmbientCubeDynamicStateVertexShader: { + SetVertexShaderStateAmbientLightCube(); + offset += 4; + break; + } + + case CommandBufferCommand.SetDepthFeatheringConst: { + int reg = MemoryMarshal.Read(cmdBuf[(offset + 4)..]); + float scale = MemoryMarshal.Read(cmdBuf[(offset + 8)..]); + SetDepthFeatheringPixelShaderConstant(reg, scale); + offset += 12; + break; + } + + case CommandBufferCommand.BindStandardTexture: { + Sampler sampler = (Sampler)MemoryMarshal.Read(cmdBuf[(offset + 4)..]); + StandardTextureId texture = (StandardTextureId)MemoryMarshal.Read(cmdBuf[(offset + 8)..]); + BindStandardTexture(sampler, texture); + offset += 12; + break; + } + + case CommandBufferCommand.BindShaderApiTextureHandle: { + Sampler sampler = (Sampler)MemoryMarshal.Read(cmdBuf[(offset + 4)..]); + ShaderAPITextureHandle_t texture = (ShaderAPITextureHandle_t)MemoryMarshal.Read(cmdBuf[(offset + 8)..]); + BindTexture(sampler, texture); + offset += 8 + IntPtr.Size; + break; + } + + case CommandBufferCommand.SetPsHIndex: { + SetPixelShaderIndex(MemoryMarshal.Read(cmdBuf[(offset + 4)..])); + offset += 8; + break; + } + + case CommandBufferCommand.SetVsHIndex: { + SetVertexShaderIndex(MemoryMarshal.Read(cmdBuf[(offset + 4)..])); + offset += 8; + break; + } + + default: + throw new InvalidOperationException($"Unknown command {(int)cmd}"); + } + } + } + } + + private void SetDepthFeatheringPixelShaderConstant(int reg, float scale) { + // TODO! + // Span consts = [0, 0, 0, 0]; + + // consts[0] = DestAlphaDepthRange / scale; + // consts[1] = consts[2] = consts[3] = 0.0f; + + // SetPixelShaderConstant(reg, consts); + } + + public int GetIntRenderingParameter(RenderParamInt parm) { + // throw new NotImplementedException(); + return 0;// todo + } + + public void SetPixelShaderStateAmbientLightCube(int reg, bool forceToBlack) { + if (forceToBlack) { + Span tempCube = stackalloc Vector4[6]; + SetPixelShaderConstant(reg, MemoryMarshal.Cast(tempCube)); + } + else + SetPixelShaderConstant(reg, MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref AmbientLightCube[0], 6))); + } } diff --git a/Source.ShaderAPI.Gl46/ShaderShadow.cs b/Source.ShaderAPI.Gl46/ShaderShadow.cs index a745e9f4..11e1f134 100644 --- a/Source.ShaderAPI.Gl46/ShaderShadow.cs +++ b/Source.ShaderAPI.Gl46/ShaderShadow.cs @@ -1,6 +1,8 @@ using Source.Common.MaterialSystem; using Source.Common.ShaderAPI; +using System.Numerics; +using System.Runtime.CompilerServices; using System.Text; namespace Source.ShaderAPI.Gl46; @@ -19,12 +21,16 @@ public struct SourceSharedShadowState public struct SourceVertexSharedShadowState { public int NumBones; + public int LightCount; + public int Pad0; + public int Pad1; + public Vector4 LightEnabled; } /// /// Uniforms for the pixel shader the ShadowState represents. /// -public unsafe struct SourcePixelSharedShadowState +public struct SourcePixelSharedShadowState { public int IsAlphaTesting; public int AlphaTestFunc; @@ -39,6 +45,7 @@ public class ShadowStateGl46 : IShaderShadow { internal readonly IShaderSystemInternal Shaders; internal readonly IShaderAPI ShaderAPI; + readonly IMaterialSystemHardwareConfig HardwareConfig = Singleton(); public uint BASE_UBO; public uint VERTEX_UBO; @@ -52,6 +59,8 @@ public class ShadowStateGl46 : IShaderShadow public VertexShaderHandle VertexShader; public PixelShaderHandle PixelShader; + public BasePerMaterialContextData? ContextData; + List shaderUniforms = []; public void SetShaderUniform(IMaterialVar textureVar) { @@ -121,6 +130,8 @@ public unsafe void Activate() { CreateShaderObjects(); // Recreate UBO's, if we were lazy-loaded ReuploadBuffers(); // Reupload UBO's, if needed + ComputeAggregateShadowState(); + // Set GL states. We compare our last upload state to the current desired state and adjust if it differs. ShaderAPI.SetBoardState(in State); @@ -141,15 +152,16 @@ public unsafe void Activate() { } private unsafe void ReuploadBuffers() { - int curBones = ShaderAPI.GetCurrentNumBones(); - if (curBones != Vertex.NumBones) + SourceVertexSharedShadowState curVertex = ((ShaderAPIGl46)ShaderAPI).GetVertexSharedState(); + curVertex.NumBones = ShaderAPI.GetCurrentNumBones(); + if (curVertex.NumBones != Vertex.NumBones || curVertex.LightCount != Vertex.LightCount || curVertex.LightEnabled != Vertex.LightEnabled) needsBufferUpload = true; if (!needsBufferUpload) return; // Reupload UBO states. - Vertex.NumBones = curBones; + Vertex = curVertex; fixed (SourceSharedShadowState* pBase = &Base) fixed (SourceVertexSharedShadowState* pVertex = &Vertex) @@ -233,7 +245,8 @@ public void EnableConstantColor(bool enable) { } public void VertexShaderVertexFormat(VertexFormat format, int texCoordCount, Span texCoordDimensions, int userDataSize) { - VertexFormat = format; + // TODO: MeshMgr.ComputeVertexFormat + VertexFormat = format | VertexFormat.BoneIndex | VertexFormat.BoneWeights2 | VertexFormat.UserData4 | VertexFormat.TexCoord2D_0; } public GraphicsDriver GetDriver() => ShaderAPI.GetDriver(); @@ -323,6 +336,15 @@ public void BlendFuncSeparateAlpha(ShaderBlendFactor srcFactor, ShaderBlendFacto State.AlphaDestinationBlend = dstFactor; } + public void ComputeAggregateShadowState() { + // Alpha to coverage + if (State.AlphaToCoverage) { + // Only allow this to be enabled if blending is disabled and testing is enabled + if ((State.Blending == true) || (Pixel.IsAlphaTesting == 0)) + State.AlphaToCoverage = false; + } + } + public void FogMode(ShaderFogMode fogMode) { throw new NotImplementedException(); } @@ -340,7 +362,8 @@ public void EnableAlphaToCoverage(bool enable) { } public void SetShadowDepthFiltering(Sampler stage) { - throw new NotImplementedException(); + // throw new NotImplementedException(); + // TODO! } public void BlendOp(ShaderBlendOp blendOp) { @@ -354,9 +377,10 @@ public void BlendOpSeparateAlpha(ShaderBlendOp blendOp) { public void SetDefaultState() { DepthFunc(ShaderDepthFunc.NearerOrEqual); EnableColorWrites(true); - EnableAlphaWrites(true); + EnableAlphaWrites(false); EnableDepthWrites(true); EnableDepthTest(true); + EnableAlphaTest(false); EnableBlending(false); EnableCulling(true); PolyMode(ShaderPolyModeFace.FrontAndBack, ShaderPolyMode.Fill); @@ -365,7 +389,35 @@ public void SetDefaultState() { EnableBlendingSeparateAlpha(false); BlendFuncSeparateAlpha(ShaderBlendFactor.One, ShaderBlendFactor.Zero); BlendOpSeparateAlpha(ShaderBlendOp.Add); + AlphaFunc(ShaderAlphaFunc.GreaterEqual, 0.7f); + EnableAlphaToCoverage(false); + EnableSRGBWrite(false); EnablePolyOffset(PolygonOffsetMode.Disable); + + int samplerCount = HardwareConfig.GetSamplerCount(); + for (int i = 0; i < samplerCount; i++) { + EnableTexture((Sampler)i, false); + EnableSRGBRead((Sampler)i, false); + } + } + + public void EnableSRGBRead(Sampler sampler, bool enable) { + if (!HardwareConfig.SupportsSRGB()) { + State.SamplerState[(int)sampler].SRGBReadEnable = false; + return; + } + + if ((int)sampler < HardwareConfig.GetSamplerCount()) + State.SamplerState[(int)sampler].SRGBReadEnable = enable; + else + Warning($"Attempting set SRGBRead state on an invalid sampler ({(int)sampler})!\n"); + } + + public void EnableSRGBWrite(bool enable) { + if (HardwareConfig.SupportsSRGB()) + State.SRGBWriteEnable = enable; + else + State.SRGBWriteEnable = false; } } @@ -379,18 +431,22 @@ internal sealed class ShaderComboState(IShaderSystemInternal shaders, ShaderType int numDynamicCombos = 1; int staticComboIndex; readonly Dictionary variants = []; + readonly StringBuilder defines = new(256); public nint SetShader(string fileName, int staticIndex) { file = fileName; staticComboIndex = staticIndex; var (statics, dynamics) = ((ShaderSystem)shaders).GetShaderCombos(fileName); + staticCombos = [.. statics]; dynamicCombos = [.. dynamics]; staticComboScales = ComputeComboScales(staticCombos, out _); dynamicComboScales = ComputeComboScales(dynamicCombos, out numDynamicCombos); + variants.Clear(); + return GetVariant(0); } @@ -425,11 +481,14 @@ public int GetDynamicComboScale(ReadOnlySpan name) { private static void AppendComboDefines(StringBuilder defines, ShaderCombo[] combos, int[] scales, int index) { for (int i = 0; i < combos.Length; i++) { - if (defines.Length > 0) + if (defines.Length != 0) defines.Append(';'); - defines.Append(combos[i].Name); + + ref readonly var combo = ref combos[i]; + + defines.Append(combo.Name); defines.Append(' '); - defines.Append(combos[i].Min + (index / scales[i]) % combos[i].Range); + defines.Append(combo.Min + (index / scales[i]) % combo.Range); } } @@ -437,7 +496,7 @@ private nint Compile(int variant) { int staticIndex = variant / numDynamicCombos; int dynamicIndex = variant % numDynamicCombos; - StringBuilder defines = new(); + defines.Clear(); AppendComboDefines(defines, staticCombos, staticComboScales, staticIndex); AppendComboDefines(defines, dynamicCombos, dynamicComboScales, dynamicIndex); @@ -449,7 +508,7 @@ public nint GetVariant(int dynamicIndex) { int variant = staticComboIndex * numDynamicCombos + dynamicIndex; if (!variants.TryGetValue(variant, out nint handle)) { handle = Compile(variant); - variants[variant] = handle; + variants.Add(variant, handle); } return handle; } diff --git a/Source.ShaderAPI.Gl46/ShaderSystem.cs b/Source.ShaderAPI.Gl46/ShaderSystem.cs index c207bf64..8e44a7de 100644 --- a/Source.ShaderAPI.Gl46/ShaderSystem.cs +++ b/Source.ShaderAPI.Gl46/ShaderSystem.cs @@ -7,6 +7,7 @@ using Source.MaterialSystem; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Numerics; using System.Text; @@ -27,6 +28,7 @@ public class ShaderSystem : IShaderSystemInternal private IMaterialSystem MaterialSystem => _MaterialSystem ??= Singleton(); private IShaderAPI ShaderAPI => _ShaderAPI ??= Singleton(); private MaterialSystem_Config Config => _Config ??= Singleton(); + readonly ITextureManager TextureSystem = Singleton()!; public void BindTexture(Sampler sampler, ITexture texture, int frame) { if (texture == null) return; @@ -55,7 +57,14 @@ public void DrawElements(IShader shader, IMaterialVar[] parms, IShaderShadow ren PrepForShaderDraw(shader, parms, renderState); - shader.DrawElements(parms, null, ShaderAPI, vertexCompression); + ref BasePerMaterialContextData? contextData = ref ((ShadowStateGl46)renderState).ContextData; + + if (contextData != null && contextData.VarChangeID != materialVarTimeStamp) { + contextData.MaterialVarsChanged = true; + contextData.VarChangeID = materialVarTimeStamp; + } + + shader.DrawElements(parms, null, ShaderAPI, vertexCompression, ref contextData); DoneWithShaderDraw(); } } @@ -287,7 +296,7 @@ private void ComputeRenderStateFlagsFromSnapshot(IShaderShadow renderState) { private void InitState(IShader shader, IMaterialVar[] shaderParams, ref IShaderShadow renderState) { PrepForShaderDraw(shader, shaderParams, renderState); - shader.DrawElements(shaderParams, renderState, null, VertexCompressionType.None); + shader.DrawElements(shaderParams, renderState, null, VertexCompressionType.None, ref ((ShadowStateGl46)renderState).ContextData); DoneWithShaderDraw(); } @@ -574,7 +583,7 @@ public VertexShaderHandle LoadVertexShader(ReadOnlySpan name, ReadOnlySpan return vsh; } - public unsafe PixelShaderHandle LoadPixelShader(ReadOnlySpan name, ReadOnlySpan defines = default) { + public PixelShaderHandle LoadPixelShader(ReadOnlySpan name, ReadOnlySpan defines = default) { ulong symbol = ComboSymbol(name, defines); if (pshs.TryGetValue(symbol, out PixelShaderHandle value)) return value; @@ -588,4 +597,30 @@ public unsafe PixelShaderHandle LoadPixelShader(ReadOnlySpan name, ReadOnl return psh; } + public int GetShaderAPITextureBindHandle(ITexture? texture, int frame, int textureChannel) { + Assert(!ITextureInternal.IsTextureInternalEnvCubemap((ITextureInternal?)texture)); + if (texture != null) { + ITextureInternal tex = (ITextureInternal)texture; + // RequestAllMipmaps todo + + return tex.GetTextureHandle(frame, textureChannel); + } + else + return INVALID_SHADERAPI_TEXTURE_HANDLE; + } + + public void LoadBumpMap(IMaterialVar textureVar, string? textureGroupName) { + Assert(textureVar); + + if (textureVar.GetVarType() != MaterialVarType.String) { + if (textureVar.GetVarType() != MaterialVarType.Texture) + textureVar.SetTextureValue(TextureSystem.ErrorTexture()); + return; + } + + ITexture? text = MaterialSystem.FindTexture(textureVar.GetStringValue(), textureGroupName, false, 0); + text ??= TextureSystem.ErrorTexture(); + + textureVar.SetTextureValue(text); + } } diff --git a/Source.StdShader.Gl46/BaseShader.cs b/Source.StdShader.Gl46/BaseShader.cs index 6dd76365..e7207aac 100644 --- a/Source.StdShader.Gl46/BaseShader.cs +++ b/Source.StdShader.Gl46/BaseShader.cs @@ -1,4 +1,5 @@ using Source.Common.MaterialSystem; +using Source.Common.Mathematics; using Source.Common.ShaderAPI; using Source.Common.ShaderLib; @@ -90,21 +91,14 @@ public void InitShaderParams(IMaterialVar[] vars, IShaderAPI shaderAPI, ReadOnly Params = null; } - protected virtual void OnInitShaderParams(IMaterialVar[] vars, ReadOnlySpan materialName) { - - } - - protected virtual void OnInitShaderInstance(IMaterialVar[] vars, ReadOnlySpan materialName) { - - } - - protected virtual void OnDrawElements(IMaterialVar[] vars, IShaderDynamicAPI shaderAPI, VertexCompressionType vertexCompression) { - - } + protected virtual void OnInitShaderParams(IMaterialVar[] vars, ReadOnlySpan materialName) { } + protected virtual void OnInitShaderInstance(IMaterialVar[] vars, ReadOnlySpan materialName) { } + protected virtual void OnDrawElements(IMaterialVar[] vars, IShaderDynamicAPI shaderAPI, VertexCompressionType vertexCompression, ref BasePerMaterialContextData? contextData) => OnDrawElements(vars, shaderAPI, vertexCompression); + protected virtual void OnDrawElements(IMaterialVar[] vars, IShaderDynamicAPI shaderAPI, VertexCompressionType vertexCompression) { } public virtual bool IsTranslucent(IMaterialVar[]? parms) => IsFlagSet(parms, (int)MaterialVarFlags.Translucent); - public void DrawElements(IMaterialVar[] vars, IShaderShadow? shadow, IShaderDynamicAPI? shaderAPI, VertexCompressionType vertexCompression) { + public void DrawElements(IMaterialVar[] vars, IShaderShadow? shadow, IShaderDynamicAPI? shaderAPI, VertexCompressionType vertexCompression, ref BasePerMaterialContextData? contextData) { Assert(Params == null); Params = vars; ShaderShadow = shadow; @@ -113,7 +107,7 @@ public void DrawElements(IMaterialVar[] vars, IShaderShadow? shadow, IShaderDyna if (IsSnapshotting()) SetInitialShadowState(); - OnDrawElements(vars, shaderAPI, vertexCompression); + OnDrawElements(vars, shaderAPI, vertexCompression, ref contextData); Params = null; ShaderShadow = null; @@ -121,7 +115,7 @@ public void DrawElements(IMaterialVar[] vars, IShaderShadow? shadow, IShaderDyna // MeshBuilder = null } - private void SetInitialShadowState() { + internal void SetInitialShadowState() { ShaderShadow!.SetDefaultState(); MaterialVarFlags flags = (MaterialVarFlags)Params![(int)ShaderMaterialVars.Flags].GetIntValue(); @@ -150,7 +144,7 @@ private void SetInitialShadowState() { } [MemberNotNullWhen(true, nameof(ShaderShadow))] - protected bool IsSnapshotting() => ShaderShadow != null; + internal bool IsSnapshotting() => ShaderShadow != null; public bool TextureIsTranslucent(int textureVar = -1, bool isBaseTexture = true) { if (textureVar < 0) @@ -300,9 +294,61 @@ protected void ComputeModulationColor(Span color) { } public bool NeedsPowerOfTwoFrameBufferTexture(IMaterialVar[]? shaderParams, bool checkSpecificToThisFrame) - => IsFlag2Set(shaderParams, MaterialVarFlags2.NeedsPowerOfTwoFrameBufferTexture); + => IsFlag2Set(shaderParams, MaterialVarFlags2.NeedsPowerOfTwoFrameBufferTexture); public bool NeedsFullFrameBufferTexture(IMaterialVar[]? shaderParams, bool checkSpecificToThisFrame) - => IsFlag2Set(shaderParams, MaterialVarFlags2.NeedsFullFrameBufferTexture); + => IsFlag2Set(shaderParams, MaterialVarFlags2.NeedsFullFrameBufferTexture); + + internal bool UsingFlashlight(IMaterialVar[] shaderParams) { + if (IsSnapshotting()) + return IsFlag2Set(shaderParams, MaterialVarFlags2.UseFlashlight); + else + return ShaderAPI!.InFlashlightMode(); + } + protected void EnableAlphaBlending(ShaderBlendFactor srcFactor, ShaderBlendFactor dstFactor) { + ShaderShadow!.EnableBlending(true); + ShaderShadow!.BlendFunc(srcFactor, dstFactor); + ShaderShadow!.EnableDepthWrites(false); + } + + protected void DisableAlphaBlending() { + ShaderShadow!.EnableBlending(false); + } + + internal void SetBlendingShadowState(BlendType blendType) { + switch (blendType) { + case BlendType.None: + DisableAlphaBlending(); + break; + case BlendType.Blend: + EnableAlphaBlending(ShaderBlendFactor.SrcAlpha, ShaderBlendFactor.OneMinusSrcAlpha); + break; + case BlendType.Add: + EnableAlphaBlending(ShaderBlendFactor.One, ShaderBlendFactor.One); + break; + case BlendType.BlendAdd: + EnableAlphaBlending(ShaderBlendFactor.SrcAlpha, ShaderBlendFactor.One); + break; + } + } + + internal void LoadBumpMap(int textureVar) { + if (Params == null || textureVar == -1) + return; + + IMaterialVar? nameVar = Params[textureVar]; + if (nameVar != null && nameVar.IsDefined()) + ShaderInit!.LoadBumpMap(nameVar, TextureGroupName); + } + + internal void HashShadow2DJitter(float shadowJitterSeed, out float v1, out float v2) { + const int texRes = 32; + const int texResx2 = texRes * texRes; + int seed = (int)(MathLib.Fmod(shadowJitterSeed, 1.0f) * texResx2); + int row = seed / texRes; + int col = seed % texRes; + v1 = row / (float)texRes; + v2 = col / (float)texRes; + } } diff --git a/Source.StdShader.Gl46/BaseVSShader.cs b/Source.StdShader.Gl46/BaseVSShader.cs index 60b7fd1a..92880907 100644 --- a/Source.StdShader.Gl46/BaseVSShader.cs +++ b/Source.StdShader.Gl46/BaseVSShader.cs @@ -8,7 +8,7 @@ namespace Source.StdShader.Gl46; -public abstract class BaseVSShader : BaseShader +public partial class BaseVSShader : BaseShader { public static bool IsTextureSet(int index, Span parms) { return index != -1 && parms[index].GetTextureValue() != null; @@ -223,6 +223,20 @@ protected void SetDefaultBlendingShadowState(int baseTextureVar = -1, bool isBas SetNormalBlendingShadowState(baseTextureVar, isBaseTexture); } + internal BlendType EvaluateBlendRequirements(int textureVar, bool isBaseTexture, int detailTextureVar = -1) { + bool isTranslucent = IsAlphaModulating(); + isTranslucent = isTranslucent || ((CurrentMaterialVarFlags() & (int)MaterialVarFlags.VertexAlpha) != 0); + isTranslucent = isTranslucent || (TextureIsTranslucent(textureVar, isBaseTexture) && !((CurrentMaterialVarFlags() & (int)MaterialVarFlags.AlphaTest) != 0)); + + if ((detailTextureVar != -1) && (!isTranslucent)) + isTranslucent = TextureIsTranslucent(detailTextureVar, isBaseTexture); + + if ((CurrentMaterialVarFlags() & (int)MaterialVarFlags.Additive) != 0) + return isTranslucent ? BlendType.BlendAdd : BlendType.Add; + else + return isTranslucent ? BlendType.Blend : BlendType.None; + } + private void SetAdditiveBlendingShadowState(int baseTextureVar, bool isBaseTexture) { Assert(IsSnapshotting()); bool isTranslucent = false; @@ -252,17 +266,7 @@ private void SetNormalBlendingShadowState(int textureVar, bool isBaseTexture) { } } - protected void EnableAlphaBlending(ShaderBlendFactor srcFactor, ShaderBlendFactor dstFactor) { - ShaderShadow!.EnableBlending(true); - ShaderShadow!.BlendFunc(srcFactor, dstFactor); - ShaderShadow!.EnableDepthWrites(false); - } - - protected void DisableAlphaBlending() { - ShaderShadow!.EnableBlending(false); - } - - protected void BindTexture(Sampler sampler, int textureVarIdx, int frameVarIdx) { + protected void BindTexture(Sampler sampler, int textureVarIdx, int frameVarIdx = -1) { IMaterialVar textureVar = Params![textureVarIdx]; IMaterialVar? frameVar = frameVarIdx != -1 ? Params[frameVarIdx] : null; var tex = textureVar.GetTextureValue()!; @@ -270,6 +274,14 @@ protected void BindTexture(Sampler sampler, int textureVarIdx, int frameVarIdx) ShaderAPI!.SetShaderUniform(ShaderAPI!.LocateShaderUniform(textureVar.GetName()), (int)sampler); } + private void BindTexture(Sampler sampler, ITexture? texture, int frameVarIdx = -1) { + if (texture != null) { + IMaterialVar? frameVar = frameVarIdx != -1 ? Params[frameVarIdx] : null; + ShaderSystem.BindTexture(sampler, texture, frameVar?.GetIntValue() ?? 0); + ShaderAPI!.SetShaderUniform(ShaderAPI!.LocateShaderUniform(texture.GetName()), (int)sampler); + } + } + protected void Draw(bool makeActualDrawCall = true) { if (IsSnapshotting()) return; @@ -380,11 +392,10 @@ public void SetColorPixelShaderConstant(int nPixelReg, int colorVar, int alphaVa public void SetEnvMapTintPixelShaderDynamicState(int pixelReg, int tintVar, int alphaVar, bool convertFromGammaToLinear = false) { IMaterialVar[] shaderParams = Params!; - MaterialSystem_Config config = Materials.GetCurrentConfigForVideoCard(); Span color = stackalloc float[4]; color[0] = color[1] = color[2] = color[3] = 1.0f; - if (config.ShowSpecular && config.Fullbright != 2) { + if (Config.ShowSpecular && Config.Fullbright != 2) { IMaterialVar? pAlphaVar = alphaVar >= 0 ? shaderParams[alphaVar] : null; if (pAlphaVar != null) color[3] = pAlphaVar.GetFloatValue(); @@ -492,4 +503,80 @@ public void InitUnlitGeneric(int baseTextureVar, int detailVar, int envmapVar, i LoadTexture(envmapMaskVar); } } + + internal ShaderAPITextureHandle_t GetShaderApiTextureBindHandle(int textureVar, int frameVar, int textureChannel = 0) { + Assert(textureVar != -1); + Assert(Params); + + IMaterialVar? pFrameVar = (frameVar != -1) ? Params![frameVar] : null; + return ShaderSystem.GetShaderAPITextureBindHandle(Params![textureVar].GetTextureValue(), pFrameVar != null ? pFrameVar.GetIntValue() : 0, textureChannel); + } + + internal float ShadowAttenFromState(FlashlightState flashlightState) { + if (HardwareConfig.UsesSRGBCorrectBlending()) + return flashlightState.ShadowAtten * .1f; + return flashlightState.ShadowAtten; + } + + internal float ShadowFilterFromState(FlashlightState flashlightState) { + throw new NotImplementedException(); + } + + internal void SetFlashLightColorFromState(FlashlightState state, IShaderDynamicAPI shaderAPI, int psRegister, bool flashlightNoLambert = false) { + float flashlightScale = 0.25f; + + if (!HardwareConfig.GetHDREnabled()) + flashlightScale = 2.0f; + + if (HardwareConfig.UsesSRGBCorrectBlending()) + flashlightScale *= 2.5f; + + InlineArray4 flashlightColor = state.Color; + Span psConst = [flashlightScale * flashlightColor[0], flashlightScale * flashlightColor[1], flashlightScale * flashlightColor[2], flashlightColor[3]]; + psConst[3] = flashlightNoLambert ? 2.0f : 0.0f; + + ShaderAPI!.SetPixelShaderConstant(psRegister, psConst); + } + + internal void SetModulationPixelShaderDynamicState_LinearColorSpace(int modulationVar) { + Span color = [1.0f, 1.0f, 1.0f, 1.0f]; + ComputeModulationColor(color); + color[0] = color[0] > 1.0f ? color[0] : MathLib.GammaToLinear(color[0]); + color[1] = color[1] > 1.0f ? color[1] : MathLib.GammaToLinear(color[1]); + color[2] = color[2] > 1.0f ? color[2] : MathLib.GammaToLinear(color[2]); + + ShaderAPI!.SetPixelShaderConstant(modulationVar, color); + } + + internal void SetModulationPixelShaderDynamicState_LinearColorSpace_LinearScale(int modulationVar, float scale) { + Span color = [1.0f, 1.0f, 1.0f, 1.0f]; + ComputeModulationColor(color); + color[0] = (color[0] > 1.0f ? color[0] : MathLib.GammaToLinear(color[0])) * scale; + color[1] = (color[1] > 1.0f ? color[1] : MathLib.GammaToLinear(color[1])) * scale; + color[2] = (color[2] > 1.0f ? color[2] : MathLib.GammaToLinear(color[2])) * scale; + + ShaderAPI!.SetPixelShaderConstant(modulationVar, color); + } + + internal bool IsHDREnabled() { + // throw new NotImplementedException(); + return false; + } + + internal void SetPixelShaderConstant_W(int pixelReg, int constantVar, float wValue) { + Assert(!IsSnapshotting()); + if ((Params == null) || (constantVar == -1)) + return; + + IMaterialVar? pixelVar = Params[constantVar]; + Assert(pixelVar); + + Span val = stackalloc float[4]; + if (pixelVar.GetVarType() == MaterialVarType.Vector) + pixelVar.GetVecValue(val); + else + val[0] = val[1] = val[2] = val[3] = pixelVar.GetFloatValue(); + val[3] = wValue; + ShaderAPI!.SetPixelShaderConstant(pixelReg, val); + } } diff --git a/Source.StdShader.Gl46/CommandBuilder.cs b/Source.StdShader.Gl46/CommandBuilder.cs new file mode 100644 index 00000000..be2e0191 --- /dev/null +++ b/Source.StdShader.Gl46/CommandBuilder.cs @@ -0,0 +1,379 @@ +using Source.Common; +using Source.Common.MaterialSystem; +using Source.Common.Mathematics; +using Source.Common.ShaderAPI; + +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Source.StdShader.Gl46; + +public class FixedCommandStorageBuffer : ICommandStorageBuffer +{ + private readonly byte[] Data; + private readonly List References = []; + private int Position; + +#if DEBUG + private int Remaining; +#endif + + public FixedCommandStorageBuffer(int capacity) { + Data = GC.AllocateUninitializedArray(capacity); + Reset(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void EnsureCapacity(int size) { +#if DEBUG + Debug.Assert(Remaining >= size); +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Put(in T value) where T : unmanaged { + EnsureCapacity(Unsafe.SizeOf()); + MemoryMarshal.Write(Data.AsSpan(Position), in value); + Position += Unsafe.SizeOf(); +#if DEBUG + Remaining -= Unsafe.SizeOf(); +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PutInt(int value) => Put(value); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PutIntPtr(nint value) => Put(value); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PutFloat(float value) => Put(value); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PutPtr(nint ptr) => Put(ptr); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PutMemory(ReadOnlySpan memory) { + EnsureCapacity(memory.Length); + memory.CopyTo(Data.AsSpan(Position)); + Position += memory.Length; +#if DEBUG + Remaining -= memory.Length; +#endif + } + + public int AddReference(ICommandStorageBuffer buffer) { + int index = References.IndexOf(buffer); + if (index < 0) { + index = References.Count; + References.Add(buffer); + } + return index; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ICommandStorageBuffer Reference(int index) => References[index]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span Base() => Data.AsSpan(0, Position); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Reset() { + Position = 0; + References.Clear(); +#if DEBUG + Remaining = Data.Length; +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int Size() => Position; +} + +public class CommandBufferBuilder where TStorage : ICommandStorageBuffer +{ + static readonly Lazy s_materials = new(Singleton); + protected static IMaterialSystem Materials => s_materials.Value; + public static MaterialSystem_Config Config => Materials.GetCurrentConfigForVideoCard(); + + public TStorage Storage = default!; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void End() => Storage.PutInt((int)CommandBufferCommand.End); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public IMaterialVar Param(int var) => BaseShader.Params![var]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetPixelShaderConstants(int firstConstant, int constants) { + Storage.PutInt((int)CommandBufferCommand.SetPixelShaderFloatConst); + Storage.PutInt(firstConstant); + Storage.PutInt(constants); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void OutputConstantData(ReadOnlySpan srcData) { + Storage.PutFloat(srcData[0]); + Storage.PutFloat(srcData[1]); + Storage.PutFloat(srcData[2]); + Storage.PutFloat(srcData[3]); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void OutputConstantData4(float val0, float val1, float val2, float val3) { + Storage.PutFloat(val0); + Storage.PutFloat(val1); + Storage.PutFloat(val2); + Storage.PutFloat(val3); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetPixelShaderConstant(int firstConstant, ReadOnlySpan srcData, int numConstantsToSet) { + SetPixelShaderConstants(firstConstant, numConstantsToSet); + Storage.PutMemory(MemoryMarshal.AsBytes(srcData[..(4 * numConstantsToSet)])); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetPixelShaderConstant(int firstConstant, int var) { + Span vec = stackalloc float[4]; + Param(var).GetVecValue(vec); + SetPixelShaderConstant(firstConstant, vec); + } + + public void SetPixelShaderConstantGammaToLinear(int pixelReg, int constantVar) { + Span val = stackalloc float[4]; + Param(constantVar).GetVecValue(val[..3]); + val[0] = val[0] > 1.0f ? val[0] : MathLib.GammaToLinear(val[0]); + val[1] = val[1] > 1.0f ? val[1] : MathLib.GammaToLinear(val[1]); + val[2] = val[2] > 1.0f ? val[2] : MathLib.GammaToLinear(val[2]); + val[3] = 1.0f; + SetPixelShaderConstant(pixelReg, val); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetPixelShaderConstant(int firstConstant, ReadOnlySpan srcData) { + SetPixelShaderConstants(firstConstant, 1); + OutputConstantData(srcData); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetPixelShaderConstant4(int firstConstant, float val0, float val1, float val2, float val3) { + SetPixelShaderConstants(firstConstant, 1); + OutputConstantData4(val0, val1, val2, val3); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetPixelShaderConstant_W(int pixelReg, int constantVar, float wValue) { + if (constantVar != -1) { + Span val = stackalloc float[3]; + Param(constantVar).GetVecValue(val); + SetPixelShaderConstant4(pixelReg, val[0], val[1], val[2], wValue); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetVertexShaderConstant(int firstConstant, ReadOnlySpan srcData) { + Storage.PutInt((int)CommandBufferCommand.SetVertexShaderFloatConst); + Storage.PutInt(firstConstant); + Storage.PutInt(1); + OutputConstantData(srcData); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetVertexShaderConstant(int firstConstant, ReadOnlySpan srcData, int consts) { + Storage.PutInt((int)CommandBufferCommand.SetVertexShaderFloatConst); + Storage.PutInt(firstConstant); + Storage.PutInt(consts); + Storage.PutMemory(MemoryMarshal.AsBytes(srcData[..(4 * consts)])); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetVertexShaderConstant4(int firstConstant, float val0, float val1, float val2, float val3) { + Storage.PutInt((int)CommandBufferCommand.SetVertexShaderFloatConst); + Storage.PutInt(firstConstant); + Storage.PutInt(1); + Storage.PutFloat(val0); + Storage.PutFloat(val1); + Storage.PutFloat(val2); + Storage.PutFloat(val3); + } + + public void SetVertexShaderTextureTransform(int vertexReg, int transformVar) { + Span transformation = stackalloc Vector4[2]; + IMaterialVar? transformationVar = Param(transformVar); + + if (transformationVar is not null && transformationVar.GetVarType() == MaterialVarType.Matrix) { + Matrix4x4 mat = transformationVar.GetMatrixValue(); + transformation[0] = new Vector4(mat.M11, mat.M12, mat.M13, mat.M14); + transformation[1] = new Vector4(mat.M21, mat.M22, mat.M23, mat.M24); + } + else { + transformation[0] = new Vector4(1.0f, 0.0f, 0.0f, 0.0f); + transformation[1] = new Vector4(0.0f, 1.0f, 0.0f, 0.0f); + } + + SetVertexShaderConstant(vertexReg, MemoryMarshal.Cast(transformation), 2); + } + + public void SetVertexShaderTextureScaledTransform(int vertexReg, int transformVar, int scaleVar) { + Span transformation = stackalloc Vector4[2]; + IMaterialVar? transformationVar = Param(transformVar); + + if (transformationVar is not null && transformationVar.GetVarType() == MaterialVarType.Matrix) { + Matrix4x4 mat = transformationVar.GetMatrixValue(); + transformation[0] = new Vector4(mat.M11, mat.M12, mat.M13, mat.M14); + transformation[1] = new Vector4(mat.M21, mat.M22, mat.M23, mat.M24); + } + else { + transformation[0] = new Vector4(1.0f, 0.0f, 0.0f, 0.0f); + transformation[1] = new Vector4(0.0f, 1.0f, 0.0f, 0.0f); + } + + Vector2 scale = new(1.0f, 1.0f); + IMaterialVar? scaleVarParam = Param(scaleVar); + if (scaleVarParam is not null) { + if (scaleVarParam.GetVarType() == MaterialVarType.Vector) { + Span scaleValues = stackalloc float[2]; + scaleVarParam.GetVecValue(scaleValues); + scale = new Vector2(scaleValues[0], scaleValues[1]); + } + else if (scaleVarParam.IsDefined()) { + float s = scaleVarParam.GetFloatValue(); + scale = new Vector2(s, s); + } + } + + transformation[0].X *= scale.X; + transformation[0].Y *= scale.Y; + transformation[1].X *= scale.X; + transformation[1].Y *= scale.Y; + transformation[0].W *= scale.X; + transformation[1].W *= scale.Y; + + SetVertexShaderConstant(vertexReg, MemoryMarshal.Cast(transformation), 2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetEnvMapTintPixelShaderDynamicState(int pixelReg, int tintVar) { + if (Config.ShowSpecular/* && mat_fullbright.GetInt() != 2*/) { + Span vec = default; + Param(tintVar).GetVecValue(vec); + SetPixelShaderConstant(pixelReg, vec); + } + else + SetPixelShaderConstant4(pixelReg, 0.0f, 0.0f, 0.0f, 0.0f); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetEnvMapTintPixelShaderDynamicStateGammaToLinear(int pixelReg, int tintVar, float alphaValue = 1.0f) { + if (tintVar != -1 && Config.ShowSpecular/* && mat_fullbright.GetInt() != 2*/) { + Span color = stackalloc float[4]; + color[3] = alphaValue; + Param(tintVar).GetLinearVecValue(color, 3); + SetPixelShaderConstant(pixelReg, color); + } + else { + SetPixelShaderConstant4(pixelReg, 0.0f, 0.0f, 0.0f, alphaValue); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void StoreEyePosInPixelShaderConstant(int constant) { + Storage.PutInt((int)CommandBufferCommand.StoreEyePosInPsConst); + Storage.PutInt(constant); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CommitPixelShaderLighting(int constant) { + Storage.PutInt((int)CommandBufferCommand.CommitPixelShaderLighting); + Storage.PutInt(constant); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetPixelShaderStateAmbientLightCube(int constant) { + Storage.PutInt((int)CommandBufferCommand.SetPixelShaderStateAmbientLightCube); + Storage.PutInt(constant); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetAmbientCubeDynamicStateVertexShader() => Storage.PutInt((int)CommandBufferCommand.SetAmbientCubeDynamicStateVertexShader); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetPixelShaderFogParams(int reg) { + Storage.PutInt((int)CommandBufferCommand.SetPixelShaderFogParams); + Storage.PutInt(reg); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void BindStandardTexture(Sampler sampler, StandardTextureId textureId) { + Storage.PutInt((int)CommandBufferCommand.BindStandardTexture); + Storage.PutInt((int)sampler); + Storage.PutInt((int)textureId); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void BindTexture(Sampler sampler, ShaderAPITextureHandle_t texture) { + Debug.Assert(texture != INVALID_SHADERAPI_TEXTURE_HANDLE); + if (texture != INVALID_SHADERAPI_TEXTURE_HANDLE) { + Storage.PutInt((int)CommandBufferCommand.BindShaderApiTextureHandle); + Storage.PutInt((int)sampler); + Storage.PutIntPtr(texture); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void BindTexture(BaseVSShader shader, Sampler sampler, int textureVar, int frameVar) { + int texture = shader.GetShaderApiTextureBindHandle(textureVar, frameVar); + BindTexture(sampler, texture); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void BindMultiTexture(BaseVSShader shader, Sampler sampler1, Sampler sampler2, int textureVar, int frameVar) { + int texture = shader.GetShaderApiTextureBindHandle(textureVar, frameVar, 0); + BindTexture(sampler1, texture); + texture = shader.GetShaderApiTextureBindHandle(textureVar, frameVar, 1); + BindTexture(sampler2, texture); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetPixelShaderIndex(int index) { + Storage.PutInt((int)CommandBufferCommand.SetPsHIndex); + Storage.PutInt(index); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetVertexShaderIndex(int index) { + Storage.PutInt((int)CommandBufferCommand.SetVsHIndex); + Storage.PutInt(index); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetDepthFeatheringPixelShaderConstant(int constant, float depthBlendScale) { + Storage.PutInt((int)CommandBufferCommand.SetDepthFeatheringConst); + Storage.PutInt(constant); + Storage.PutFloat(depthBlendScale); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Goto(ICommandStorageBuffer cmdBuf) { + Storage.PutInt((int)CommandBufferCommand.Jump); + Storage.PutInt(Storage.AddReference(cmdBuf)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Call(ICommandStorageBuffer cmdBuf) { + Storage.PutInt((int)CommandBufferCommand.Jsr); + Storage.PutInt(Storage.AddReference(cmdBuf)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Reset() => Storage.Reset(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int Size() => Storage.Size(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span Base() => Storage.Base(); +} \ No newline at end of file diff --git a/Source.StdShader.Gl46/LightmappedGeneric.cs b/Source.StdShader.Gl46/LightmappedGeneric.cs index 86a3c0c4..41c9d455 100644 --- a/Source.StdShader.Gl46/LightmappedGeneric.cs +++ b/Source.StdShader.Gl46/LightmappedGeneric.cs @@ -125,8 +125,8 @@ protected override void OnInitShaderInstance(IMaterialVar[] vars, ReadOnlySpan materialName, ref VertexLitGeneric_Vars info) { + Assert(info.FlashlightTexture >= 0); + + if (HardwareConfig.SupportsBorderColor()) + parms[(int)ShaderMaterialVars.FlashLightTexture].SetStringValue("effects/flashlight_border"); + else + parms[(int)ShaderMaterialVars.FlashLightTexture].SetStringValue("effects/flashlight001"); + + if (info.Albedo != -1 && Config.UseBumpmapping() && info.Bumpmap != -1 && parms[info.Bumpmap].IsDefined() && parms[info.Albedo].IsDefined() && + parms[info.BaseTexture].IsDefined()) { + parms[info.BaseTexture].SetStringValue(parms[info.Albedo].GetStringValue()); + } + + SetFlags2(parms, MaterialVarFlags2.SupportsHardwareSkinning); + SetFlags2(parms, MaterialVarFlags2.LightingVertexLit); + + if (!parms[info.BaseTexture].IsDefined()) + ClearFlags(parms, MaterialVarFlags.BaseAlphaEnvMapMask); + + if (IsFlagSet(parms, MaterialVarFlags.Decal)) + SetFlags(parms, MaterialVarFlags.NoDebugOverride); + + bool bump = (info.Bumpmap != -1) && Config.UseBumpmapping() && parms[info.Bumpmap].IsDefined(); + bool envMap = (info.Envmap != -1) && parms[info.Envmap].IsDefined(); + bool diffuseWarp = (info.DiffuseWarpTexture != -1) && parms[info.DiffuseWarpTexture].IsDefined(); + bool phong = (info.Phong != -1) && parms[info.Phong].IsDefined(); + if (bump || envMap || diffuseWarp || phong) + SetFlags2(parms, MaterialVarFlags2.NeedsTangentSpaces); + else + ClearFlags(parms, MaterialVarFlags.NormalMapAlphaEnvMapMask); + + if ((info.SelfIllumFresnel != -1) && (!parms[info.SelfIllumFresnel].IsDefined())) + parms[info.SelfIllumFresnel].SetIntValue(0); + + if ((info.SelfIllumFresnelMinMaxExp != -1) && (!parms[info.SelfIllumFresnelMinMaxExp].IsDefined())) + parms[info.SelfIllumFresnelMinMaxExp].SetVecValue(0.0f, 1.0f, 1.0f); + + if ((info.BaseMapAlphaPhongMask != -1) && (!parms[info.BaseMapAlphaPhongMask].IsDefined())) + parms[info.BaseMapAlphaPhongMask].SetIntValue(0); + + if ((info.EnvmapFresnel != -1) && (!parms[info.EnvmapFresnel].IsDefined())) + parms[info.EnvmapFresnel].SetFloatValue(0); + } + + public static readonly ConVar r_flashlight_version2 = new("r_flashlight_version2", "0", FCvar.Cheat | FCvar.DevelopmentOnly); + internal void DrawSkin(BaseVSShader shader, IMaterialVar[] parms, IShaderDynamicAPI? shaderAPI, IShaderShadow? shaderShadow, ref VertexLitGeneric_Vars info, VertexCompressionType vertexCompression, ref BasePerMaterialContextData? contextData) { + bool hasFlashlight = shader.UsingFlashlight(parms); + + if (hasFlashlight || r_flashlight_version2.GetBool()) { + DrawSkin_Internal(shader, parms, shaderAPI, shaderShadow, false, ref info, vertexCompression, ref contextData); + if (shaderShadow != null) + SetInitialShadowState(); + } + + DrawSkin_Internal(shader, parms, shaderAPI, shaderShadow, hasFlashlight, ref info, vertexCompression, ref contextData); + } + + internal void InitSkin(BaseVSShader shader, IMaterialVar[] parms, ref VertexLitGeneric_Vars info) { + Assert(info.FlashlightTexture >= 0); + shader.LoadTexture(info.FlashlightTexture, (int)TextureFlags.SRGB); + + bool isBaseTextureTranslucent = false; + if (parms[info.BaseTexture].IsDefined()) { + shader.LoadTexture(info.BaseTexture, (int)TextureFlags.SRGB); + + if (parms[info.BaseTexture].GetTextureValue()!.IsTranslucent()) + isBaseTextureTranslucent = true; + + if ((info.Wrinkle != -1) && (info.Stretch != -1) && + parms[info.Wrinkle].IsDefined() && parms[info.Stretch].IsDefined()) { + shader.LoadTexture(info.Wrinkle, (int)TextureFlags.SRGB); + shader.LoadTexture(info.Stretch, (int)TextureFlags.SRGB); + } + } + + bool hasSelfIllumMask = IsFlagSet(parms, MaterialVarFlags.SelfIllum) && (info.SelfIllumMask != -1) && parms[info.SelfIllumMask].IsDefined(); + + if (!isBaseTextureTranslucent) { + bool hasSelfIllumFresnel = IsFlagSet(parms, MaterialVarFlags.SelfIllum) && (info.SelfIllumFresnel != -1) && (parms[info.SelfIllumFresnel].GetIntValue() != 0); + + if (!hasSelfIllumFresnel && !hasSelfIllumMask) + ClearFlags(parms, MaterialVarFlags.SelfIllum); + + ClearFlags(parms, MaterialVarFlags.BaseAlphaEnvMapMask); + } + + if ((info.PhongExponentTexture != -1) && parms[info.PhongExponentTexture].IsDefined() && (info.Phong != -1) && parms[info.Phong].IsDefined()) + shader.LoadTexture(info.PhongExponentTexture); + + if ((info.DiffuseWarpTexture != -1) && parms[info.DiffuseWarpTexture].IsDefined() && (info.Phong != -1) && parms[info.Phong].IsDefined()) + shader.LoadTexture(info.DiffuseWarpTexture); + + if ((info.PhongWarpTexture != -1) && parms[info.PhongWarpTexture].IsDefined() && (info.Phong != -1) && parms[info.Phong].IsDefined()) + shader.LoadTexture(info.PhongWarpTexture); + + if (info.Detail != -1 && parms[info.Detail].IsDefined()) { + int detailBlendMode = (info.DetailTextureCombineMode == -1) ? 0 : parms[info.DetailTextureCombineMode].GetIntValue(); + if (detailBlendMode == 0) + shader.LoadTexture(info.Detail); + else + shader.LoadTexture(info.Detail, (int)TextureFlags.SRGB); + } + + if (Config.UseBumpmapping()) { + if ((info.Bumpmap != -1) && parms[info.Bumpmap].IsDefined()) { + shader.LoadBumpMap(info.Bumpmap); + SetFlags2(parms, MaterialVarFlags2.DiffuseBumpmappedModel); + + if ((info.NormalWrinkle != -1) && (info.NormalStretch != -1) && + parms[info.NormalWrinkle].IsDefined() && parms[info.NormalStretch].IsDefined()) { + shader.LoadTexture(info.NormalWrinkle); + shader.LoadTexture(info.NormalStretch); + } + } + } + + if (parms[info.Envmap].IsDefined()) + shader.LoadCubeMap(info.Envmap, HardwareConfig.GetHDRType() == HDRType.None ? (int)TextureFlags.SRGB : 0); + + if (hasSelfIllumMask) + shader.LoadTexture(info.SelfIllumMask); + } + + private void DrawSkin_Internal(BaseVSShader shader, IMaterialVar[] parms, IShaderDynamicAPI? shaderAPI, IShaderShadow? shaderShadow, bool hasFlashlight, ref VertexLitGeneric_Vars info, VertexCompressionType vertexCompression, ref BasePerMaterialContextData? context) { + bool hasBaseTexture = (info.BaseTexture != -1) && parms[info.BaseTexture].IsTexture(); + bool hasBump = (info.Bumpmap != -1) && parms[info.Bumpmap].IsTexture(); + bool hasBaseWrinkleTexture = hasBaseTexture && (info.Wrinkle != -1) && parms[info.Wrinkle].IsTexture() && (info.Stretch != -1) && parms[info.Stretch].IsTexture(); + bool hasBumpWrinkle = hasBump && (info.NormalWrinkle != -1) && parms[info.NormalWrinkle].IsTexture() && (info.NormalStretch != -1) && parms[info.NormalStretch].IsTexture(); + bool hasVertexColor = IsFlagSet(parms, MaterialVarFlags.VertexColor); + bool hasVertexAlpha = IsFlagSet(parms, MaterialVarFlags.VertexAlpha); + bool isAlphaTested = IsFlagSet(parms, MaterialVarFlags.AlphaTest); + bool hasSelfIllum = IsFlagSet(parms, MaterialVarFlags.SelfIllum); + bool hasSelfIllumFresnel = hasSelfIllum && (info.SelfIllumFresnel != -1) && (parms[info.SelfIllumFresnel].GetIntValue() != 0); + bool hasSelfIllumMask = hasSelfIllum && (info.SelfIllumMask != -1) && parms[info.SelfIllumMask].IsTexture(); + bool hasPhong = (info.Phong != -1) && (parms[info.Phong].GetIntValue() != 0); + bool hasSpecularExponentTexture = (info.PhongExponentTexture != -1) && parms[info.PhongExponentTexture].IsTexture(); + bool hasPhongTintMap = hasSpecularExponentTexture && (info.PhongAlbedoTint != -1) && (parms[info.PhongAlbedoTint].GetIntValue() != 0); + bool hasDiffuseWarp = (info.DiffuseWarpTexture != -1) && parms[info.DiffuseWarpTexture].IsTexture(); + bool hasPhongWarp = (info.PhongWarpTexture != -1) && parms[info.PhongWarpTexture].IsTexture(); + bool hasNormalMapAlphaEnvmapMask = IsFlagSet(parms, MaterialVarFlags.NormalMapAlphaEnvMapMask); + bool isDecal = IsFlagSet(parms, MaterialVarFlags.Decal); + bool hasRimLight = r_rimlight.GetBool() && hasPhong && (info.RimLight != -1) && (parms[info.RimLight].GetIntValue() != 0); + bool hasRimMapMask = hasSpecularExponentTexture && hasRimLight && (info.RimMask != -1) && (parms[info.RimMask].GetIntValue() != 0); + float blendFactor = (info.DetailTextureBlendFactor == -1) ? 1 : parms[info.DetailTextureBlendFactor].GetFloatValue(); + bool hasDetailTexture = (info.Detail != -1) && parms[info.Detail].IsTexture(); + int detailBlendMode = (hasDetailTexture && info.DetailTextureCombineMode != -1) ? parms[info.DetailTextureCombineMode].GetIntValue() : 0; + bool blendTintByBaseAlpha = IsBoolSet(info.BlendTintByBaseAlpha, parms) && !hasSelfIllum; + float tintReplacementAmount = GetFloatParam(info.TintReplacesBaseColor, parms); + + BlendType blendType = shader.EvaluateBlendRequirements(blendTintByBaseAlpha ? -1 : info.BaseTexture, true); + + bool fullyOpaque = (blendType != BlendType.BlendAdd) && (blendType != BlendType.Blend) && !isAlphaTested && !hasFlashlight; + + if (context is not Skin_Context contextData) { + contextData = new(); + context = contextData; + } + + if (shader.IsSnapshotting()) { + bool hasEnvmap = !hasFlashlight && parms[info.Envmap].IsTexture(); + bool hasNormal = parms[info.Bumpmap].IsTexture(); + bool canUseBaseAlphaPhongMaskFastPath = (info.BaseMapAlphaPhongMask != -1) && (parms[info.BaseMapAlphaPhongMask].GetIntValue() != 0); + + if (!parms[info.BaseTexture].GetTextureValue()!.IsTranslucent()) + canUseBaseAlphaPhongMaskFastPath = true; + + contextData.FastPath = + (!hasBump) && + (!hasSpecularExponentTexture) && + (!hasPhongTintMap) && + (!hasPhongWarp) && + (!hasRimLight) && + (!hasDetailTexture) && + canUseBaseAlphaPhongMaskFastPath && + (!hasSelfIllum) && + (!blendTintByBaseAlpha); + + shaderShadow!.EnableAlphaTest(isAlphaTested); + + if (info.AlphaTestReference != -1 && parms[info.AlphaTestReference].GetFloatValue() > 0.0f) + shaderShadow.AlphaFunc(ShaderAlphaFunc.GreaterEqual, parms[info.AlphaTestReference].GetFloatValue()); + + int shadowFilterMode = 0; + if (hasFlashlight) { + if (parms[info.BaseTexture].IsTexture()) + shader.SetAdditiveBlendingShadowState(info.BaseTexture, true); + + if (isAlphaTested) { + shaderShadow.EnableAlphaTest(false); + shaderShadow.DepthFunc(ShaderDepthFunc.Equal); + } + shaderShadow.EnableBlending(true); + shaderShadow.EnableDepthWrites(false); + + shaderShadow.EnableAlphaWrites(false); + + shadowFilterMode = HardwareConfig.GetShadowFilterMode(); + } + else { + if (parms[info.BaseTexture].IsTexture()) + shader.SetDefaultBlendingShadowState(info.BaseTexture, true); + + if (hasEnvmap) { + shaderShadow.EnableTexture(Sampler.Sampler8, true); + if (HardwareConfig.GetHDRType() == HDRType.None) + shaderShadow.EnableSRGBRead(Sampler.Sampler8, true); + } + } + + VertexFormat flags = VertexFormat.Position; + if (hasNormal) + flags |= VertexFormat.Normal; + + int userDataSize = 0; + + shaderShadow.EnableTexture(Sampler.Sampler0, true); + shaderShadow.EnableSRGBRead(Sampler.Sampler0, true); + + if (hasBaseWrinkleTexture || hasBumpWrinkle) { + shaderShadow.EnableTexture(Sampler.Sampler9, true); + shaderShadow.EnableSRGBRead(Sampler.Sampler9, true); + shaderShadow.EnableTexture(Sampler.Sampler10, true); + shaderShadow.EnableSRGBRead(Sampler.Sampler10, true); + } + + if (hasDiffuseWarp) + shaderShadow.EnableTexture(Sampler.Sampler2, true); + + if (hasPhongWarp) + shaderShadow.EnableTexture(Sampler.Sampler1, true); + + shaderShadow.EnableTexture(Sampler.Sampler7, true); + + if (hasFlashlight) { + shaderShadow.EnableTexture(Sampler.Sampler4, true); + shaderShadow.SetShadowDepthFiltering(Sampler.Sampler4); + shaderShadow.EnableSRGBRead(Sampler.Sampler4, false); + shaderShadow.EnableTexture(Sampler.Sampler5, true); + shaderShadow.EnableTexture(Sampler.Sampler6, true); + shaderShadow.EnableSRGBRead(Sampler.Sampler6, true); + userDataSize = 4; + } + + shaderShadow.EnableTexture(Sampler.Sampler3, true); + userDataSize = 4; + shaderShadow.EnableTexture(Sampler.Sampler5, true); + + if (hasBaseWrinkleTexture || hasBumpWrinkle) { + shaderShadow.EnableTexture(Sampler.Sampler11, true); + shaderShadow.EnableTexture(Sampler.Sampler12, true); + } + + if (hasDetailTexture) { + shaderShadow.EnableTexture(Sampler.Sampler13, true); + if (detailBlendMode != 0) + shaderShadow.EnableSRGBRead(Sampler.Sampler13, true); + } + + if (hasSelfIllum) + shaderShadow.EnableTexture(Sampler.Sampler14, true); + + if (hasVertexColor || hasVertexAlpha) + flags |= VertexFormat.Color; + + shaderShadow.EnableSRGBWrite(true); + + Span texCoordDim = [2, 0, 3]; + int texCoordCount = 1; + + if (isDecal && HardwareConfig.HasFastVertexTextures()) + texCoordCount = 3; + + // flags |= VertexFormat.Compressed; + + shaderShadow.VertexShaderVertexFormat(flags, texCoordCount, texCoordDim, userDataSize); + + + if (!HardwareConfig.HasFastVertexTextures()) { + bool useStaticControlFlow = HardwareConfig.SupportsStaticControlFlow(); + + StaticShaderIndex vshIndex = new(shaderShadow, ShaderType.Vertex, "skin"); + vshIndex.Set("USE_STATIC_CONTROL_FLOW", useStaticControlFlow); + shaderShadow.SetVertexShader("skin", vshIndex.GetIndex()); + + StaticShaderIndex pshIndex = new(shaderShadow, ShaderType.Pixel, "skin"); + pshIndex.Set("FLASHLIGHT", hasFlashlight); + pshIndex.Set("SELFILLUM", hasSelfIllum && !hasFlashlight); + pshIndex.Set("SELFILLUMFRESNEL", hasSelfIllumFresnel && !hasFlashlight); + pshIndex.Set("LIGHTWARPTEXTURE", hasDiffuseWarp && hasPhong); + pshIndex.Set("PHONGWARPTEXTURE", hasPhongWarp && hasPhong); + pshIndex.Set("WRINKLEMAP", hasBaseWrinkleTexture || hasBumpWrinkle); + pshIndex.Set("DETAILTEXTURE", hasDetailTexture); + pshIndex.Set("DETAIL_BLEND_MODE", detailBlendMode); + pshIndex.Set("RIMLIGHT", hasRimLight); + pshIndex.Set("CUBEMAP", hasEnvmap); + pshIndex.Set("FLASHLIGHTDEPTHFILTERMODE", shadowFilterMode); + pshIndex.Set("CONVERT_TO_SRGB", 0); + pshIndex.Set("FASTPATH_NOBUMP", contextData.FastPath); + pshIndex.Set("BLENDTINTBYBASEALPHA", blendTintByBaseAlpha); + shaderShadow.SetPixelShader("skin", pshIndex.GetIndex()); + } + else { + SetFlags2(parms, MaterialVarFlags2.UsesVertexID); + + StaticShaderIndex vshIndex = new(shaderShadow, ShaderType.Vertex, "skin"); + vshIndex.Set("DECAL", isDecal); + shaderShadow.SetVertexShader("skin", vshIndex.GetIndex()); + + StaticShaderIndex pshIndex = new(shaderShadow, ShaderType.Pixel, "skin"); + pshIndex.Set("FLASHLIGHT", hasFlashlight); + pshIndex.Set("SELFILLUM", hasSelfIllum && !hasFlashlight); + pshIndex.Set("SELFILLUMFRESNEL", hasSelfIllumFresnel && !hasFlashlight); + pshIndex.Set("LIGHTWARPTEXTURE", hasDiffuseWarp && hasPhong); + pshIndex.Set("PHONGWARPTEXTURE", hasPhongWarp && hasPhong); + pshIndex.Set("WRINKLEMAP", hasBaseWrinkleTexture || hasBumpWrinkle); + pshIndex.Set("DETAILTEXTURE", hasDetailTexture); + pshIndex.Set("DETAIL_BLEND_MODE", detailBlendMode); + pshIndex.Set("RIMLIGHT", hasRimLight); + pshIndex.Set("CUBEMAP", hasEnvmap); + pshIndex.Set("FLASHLIGHTDEPTHFILTERMODE", shadowFilterMode); + pshIndex.Set("CONVERT_TO_SRGB", 0); + pshIndex.Set("FASTPATH_NOBUMP", contextData.FastPath); + pshIndex.Set("BLENDTINTBYBASEALPHA", blendTintByBaseAlpha); + shaderShadow.SetPixelShader("skin", pshIndex.GetIndex()); + } + + // if (hasFlashlight) + // shader.FogToBlack(); + // else + // shader.DefaultFog(); + + shaderShadow.EnableAlphaWrites(fullyOpaque); + } + else if (shaderAPI != null) { + bool lightingOnly = mat_fullbright.GetInt() == 2 && !IsFlagSet(parms, MaterialVarFlags.NoDebugOverride); + bool hasEnvmap = !hasFlashlight && parms[info.Envmap].IsTexture(); + + if (hasBaseTexture) + shader.BindTexture(Sampler.Sampler0, info.BaseTexture, info.BaseTextureFrame); + else + shaderAPI.BindStandardTexture(Sampler.Sampler0, StandardTextureId.White); + + if (hasBaseWrinkleTexture) { + shader.BindTexture(Sampler.Sampler9, info.Wrinkle, info.BaseTextureFrame); + shader.BindTexture(Sampler.Sampler10, info.Stretch, info.BaseTextureFrame); + } + else if (hasBumpWrinkle) { + shader.BindTexture(Sampler.Sampler9, info.BaseTexture, info.BaseTextureFrame); + shader.BindTexture(Sampler.Sampler10, info.BaseTexture, info.BaseTextureFrame); + } + + if (hasDiffuseWarp && hasPhong) { + if (r_lightwarpidentity.GetBool()) + shaderAPI.BindStandardTexture(Sampler.Sampler2, StandardTextureId.IdentityLightwarp); + else + shader.BindTexture(Sampler.Sampler2, info.DiffuseWarpTexture); + } + + if (hasPhongWarp) + shader.BindTexture(Sampler.Sampler1, info.PhongWarpTexture); + + if (hasSpecularExponentTexture && hasPhong) + shader.BindTexture(Sampler.Sampler7, info.PhongExponentTexture); + else + shaderAPI.BindStandardTexture(Sampler.Sampler7, StandardTextureId.White); + + if (!Config.FastNoBump) { + if (hasBump) + shader.BindTexture(Sampler.Sampler3, info.Bumpmap, info.BumpFrame); + else + shaderAPI.BindStandardTexture(Sampler.Sampler3, StandardTextureId.NormalMapFlat); + + if (hasBumpWrinkle) { + shader.BindTexture(Sampler.Sampler11, info.NormalWrinkle, info.BumpFrame); + shader.BindTexture(Sampler.Sampler12, info.NormalStretch, info.BumpFrame); + } + else if (hasBaseWrinkleTexture) { + shader.BindTexture(Sampler.Sampler11, info.Bumpmap, info.BumpFrame); + shader.BindTexture(Sampler.Sampler12, info.Bumpmap, info.BumpFrame); + } + } + else { + if (hasBump) + shaderAPI.BindStandardTexture(Sampler.Sampler3, StandardTextureId.NormalMapFlat); + if (hasBaseWrinkleTexture || hasBumpWrinkle) { + shaderAPI.BindStandardTexture(Sampler.Sampler11, StandardTextureId.NormalMapFlat); + shaderAPI.BindStandardTexture(Sampler.Sampler12, StandardTextureId.NormalMapFlat); + } + } + + if (hasDetailTexture) + shader.BindTexture(Sampler.Sampler13, info.Detail, info.DetailFrame); + + if (hasSelfIllum) { + if (hasSelfIllumMask) + shader.BindTexture(Sampler.Sampler14, info.SelfIllumMask); + else + shaderAPI.BindStandardTexture(Sampler.Sampler14, StandardTextureId.Black); + } + + LightState lightState = default; + bool flashlightShadows = false; + if (hasFlashlight) { + Assert(info.FlashlightTexture >= 0 && info.FlashlightTextureFrame >= 0); + shader.BindTexture(Sampler.Sampler6, info.FlashlightTexture, info.FlashlightTextureFrame); + FlashlightState state = ShaderAPI!.GetFlashlightStateEx(out _, out ITexture? flashlightDepthTexture); + flashlightShadows = state.EnableShadows && (flashlightDepthTexture != null); + + SetFlashLightColorFromState(state, ShaderAPI, (int)PixelShaderConst.FlashlightColor); + + if (flashlightDepthTexture != null && Config.ShadowDepthTexture && state.EnableShadows) { + shader.BindTexture(Sampler.Sampler4, flashlightDepthTexture, 0); + ShaderAPI.BindStandardTexture(Sampler.Sampler5, StandardTextureId.ShadowNoise2D); + } + } + else { + if (hasEnvmap) + shader.BindTexture(Sampler.Sampler8, info.Envmap, info.EnvmapFrame); + + shaderAPI.GetLightState(out lightState); + } + + MaterialFogMode fogType = shaderAPI.GetSceneFogMode(); + int fogIndex = (fogType == MaterialFogMode.LinearBelowFogZ) ? 1 : 0; + int numBones = shaderAPI.GetCurrentNumBones(); + + bool writeDepthToAlpha = false; + bool writeWaterFogToAlpha = false; + if (fullyOpaque) { + writeDepthToAlpha = shaderAPI.ShouldWriteDepthToDestAlpha(); + writeWaterFogToAlpha = fogType == MaterialFogMode.LinearBelowFogZ; + AssertMsg(!(writeDepthToAlpha && writeWaterFogToAlpha), "Can't write two values to alpha at the same time."); + } + + if (!HardwareConfig.HasFastVertexTextures()) { + bool useStaticControlFlow = HardwareConfig.SupportsStaticControlFlow(); + + DynamicShaderIndex vshIndex = new(shaderAPI!, ShaderType.Vertex); + vshIndex.Set("DOWATERFOG", fogIndex); + vshIndex.Set("SKINNING", numBones > 0); + vshIndex.Set("LIGHTING_PREVIEW", shaderAPI.GetIntRenderingParameter(RenderParamInt.EnableFixedLighting) != 0); + vshIndex.Set("COMPRESSED_VERTS", (int)vertexCompression); + vshIndex.Set("NUM_LIGHTS", useStaticControlFlow ? 0 : lightState.NumLights); + shaderAPI.SetVertexShaderIndex(vshIndex.GetIndex()); + + DynamicShaderIndex pshIndex = new(shaderAPI!, ShaderType.Pixel); + pshIndex.Set("NUM_LIGHTS", lightState.NumLights); + pshIndex.Set("WRITEWATERFOGTODESTALPHA", writeWaterFogToAlpha); + pshIndex.Set("WRITE_DEPTH_TO_DESTALPHA", writeDepthToAlpha); + pshIndex.Set("PIXELFOGTYPE", shaderAPI.GetPixelFogCombo()); + pshIndex.Set("FLASHLIGHTSHADOWS", flashlightShadows); + shaderAPI.SetPixelShaderIndex(pshIndex.GetIndex()); + } + else { + // shader.SetHWMorphVertexShaderState(VertexShaderConst.ShaderSpecificConst6, VertexShaderConst.ShaderSpecificConst7, SHADER_VERTEXTEXTURE_SAMPLER0); + + DynamicShaderIndex vshIndex = new(shaderAPI!, ShaderType.Vertex); + vshIndex.Set("DOWATERFOG", fogIndex); + vshIndex.Set("SKINNING", numBones > 0); + vshIndex.Set("LIGHTING_PREVIEW", shaderAPI.GetIntRenderingParameter(RenderParamInt.EnableFixedLighting) != 0); + vshIndex.Set("MORPHING", shaderAPI.IsHWMorphingEnabled()); + vshIndex.Set("COMPRESSED_VERTS", (int)vertexCompression); + shaderAPI.SetVertexShaderIndex(vshIndex.GetIndex()); + + DynamicShaderIndex pshIndex = new(shaderAPI!, ShaderType.Pixel); + pshIndex.Set("NUM_LIGHTS", lightState.NumLights); + pshIndex.Set("WRITEWATERFOGTODESTALPHA", writeWaterFogToAlpha); + pshIndex.Set("WRITE_DEPTH_TO_DESTALPHA", writeDepthToAlpha); + pshIndex.Set("PIXELFOGTYPE", shaderAPI.GetPixelFogCombo()); + pshIndex.Set("FLASHLIGHTSHADOWS", flashlightShadows); + shaderAPI.SetPixelShaderIndex(pshIndex.GetIndex()); + + Span unusedTexCoords = [false, false, !shaderAPI.IsHWMorphingEnabled() || !isDecal]; + shaderAPI.MarkUnusedVertexFields(0, unusedTexCoords); + } + + shader.SetVertexShaderTextureTransform(VertexShaderConst.ShaderSpecificConst0, info.BaseTextureTransform); + + if (hasBump) + shader.SetVertexShaderTextureTransform(VertexShaderConst.ShaderSpecificConst2, info.BumpTransform); + + if (hasDetailTexture) { + if (IsParamDefined(parms, info.DetailTextureTransform)) + shader.SetVertexShaderTextureScaledTransform(VertexShaderConst.ShaderSpecificConst4, info.DetailTextureTransform, info.DetailScale); + else + shader.SetVertexShaderTextureScaledTransform(VertexShaderConst.ShaderSpecificConst4, info.BaseTextureTransform, info.DetailScale); + } + + shader.SetModulationPixelShaderDynamicState_LinearColorSpace(1); + shader.SetPixelShaderConstant_W((int)PixelShaderConst.SelfIllumTint, info.SelfIllumTint, blendFactor); + bool invertPhongMask = (info.InvertPhongMask != -1) && (parms[info.InvertPhongMask].GetIntValue() != 0); + float fInvertPhongMask = invertPhongMask ? 1 : 0; + + bool hasBaseAlphaPhongMask = (info.BaseMapAlphaPhongMask != -1) && (parms[info.BaseMapAlphaPhongMask].GetIntValue() != 0); + float fHasBaseAlphaPhongMask = hasBaseAlphaPhongMask ? 1 : 0; + Span shaderControls = [fHasBaseAlphaPhongMask, 0.0f, tintReplacementAmount, fInvertPhongMask]; + shaderAPI.SetPixelShaderConstant((int)PixelShaderConst.Constant27, shaderControls); + + if (hasSelfIllumFresnel && !hasFlashlight) { + Span constScaleBiasExp = [1.0f, 0.0f, 1.0f, 0.0f]; + float min = IsParamDefined(parms, info.SelfIllumFresnelMinMaxExp) ? parms[info.SelfIllumFresnelMinMaxExp].GetVecValue()[0] : 0.0f; + float max = IsParamDefined(parms, info.SelfIllumFresnelMinMaxExp) ? parms[info.SelfIllumFresnelMinMaxExp].GetVecValue()[1] : 1.0f; + float exp = IsParamDefined(parms, info.SelfIllumFresnelMinMaxExp) ? parms[info.SelfIllumFresnelMinMaxExp].GetVecValue()[2] : 1.0f; + + constScaleBiasExp[1] = (max != 0.0f) ? (min / max) : 0.0f; + constScaleBiasExp[0] = 1.0f - constScaleBiasExp[1]; + constScaleBiasExp[2] = exp; + constScaleBiasExp[3] = max; + + shaderAPI.SetPixelShaderConstant((int)PixelShaderConst.SelfIllumScaleBiasExp, constScaleBiasExp); + } + + shader.SetAmbientCubeDynamicStateVertexShader(); + + if (!hasFlashlight) { + shaderAPI.BindStandardTexture(Sampler.Sampler5, StandardTextureId.NormalizationCubemapSigned); + + Span envMapFresnel_SelfIllumMask = [0.0f, 0.0f, 0.0f, 0.0f]; + envMapFresnel_SelfIllumMask[3] = hasSelfIllumMask ? 1.0f : 0.0f; + + if (hasEnvmap) { + Span envMapTint_MaskControl = [1.0f, 1.0f, 1.0f, 0.0f]; + + if ((info.EnvmapTint != -1) && parms[info.EnvmapTint].IsDefined()) + parms[info.EnvmapTint].GetVecValue(envMapTint_MaskControl); + + envMapTint_MaskControl[3] = hasNormalMapAlphaEnvmapMask ? 1.0f : 0.0f; + + if ((info.EnvmapFresnel != -1) && parms[info.EnvmapFresnel].IsDefined()) + envMapFresnel_SelfIllumMask[0] = parms[info.EnvmapFresnel].GetFloatValue(); + + if (lightingOnly) + envMapTint_MaskControl[0] = envMapTint_MaskControl[1] = envMapTint_MaskControl[2] = 0.0f; + + shaderAPI.SetPixelShaderConstant((int)PixelShaderConst.EnvMapTintShadowTweaks, envMapTint_MaskControl); + } + + shaderAPI.SetPixelShaderConstant((int)PixelShaderConst.EnvMapFresnelSelfIllumMask, envMapFresnel_SelfIllumMask); + } + + shaderAPI.SetPixelShaderStateAmbientLightCube((int)PixelShaderConst.AmbientCube, !lightState.AmbientLight); + shaderAPI.CommitPixelShaderLighting((int)PixelShaderConst.LightInfoArray); + + Span eyePos_SpecExponent = [0, 0, 0, 0], fresnelRanges_SpecBoost = [1, 0.5f, 1, 1], vRimBoost = [1, 1, 1, 1]; + Span specularTint = [1, 1, 1, 4]; + shaderAPI.GetWorldSpaceCameraPosition(ref eyePos_SpecExponent); + + eyePos_SpecExponent[3] = -1.0f; + if ((info.PhongExponent != -1) && parms[info.PhongExponent].IsDefined()) { + float value = parms[info.PhongExponent].GetFloatValue(); + if (value > 0.0f) + eyePos_SpecExponent[3] = value; + } + + if ((info.PhongTint != -1) && parms[info.PhongTint].IsDefined()) + parms[info.PhongTint].GetVecValue(specularTint[..3]); + + if (hasRimLight && (info.RimLightPower != -1) && parms[info.RimLightPower].IsDefined()) { + specularTint[3] = parms[info.RimLightPower].GetFloatValue(); + specularTint[3] = Math.Max(specularTint[3], 1.0f); + } + + if (hasRimLight && (info.RimLightBoost != -1) && parms[info.RimLightBoost].IsDefined()) + vRimBoost[3] = parms[info.RimLightBoost].GetFloatValue(); + + if (!hasFlashlight) { + Span rimMaskControl = [0, 0, 0, 0]; + rimMaskControl[0] = hasRimMapMask ? parms[info.RimMask].GetFloatValue() : 0.0f; + shaderAPI.SetPixelShaderConstant((int)PixelShaderConst.FlashlightAttenuation, rimMaskControl); + } + + if ((specularTint[0] == 0.0f) && (specularTint[1] == 0.0f) && (specularTint[2] == 0.0f)) { + if (hasPhongTintMap) + specularTint[0] = -1; + else { + specularTint[0] = 1.0f; + specularTint[1] = 1.0f; + specularTint[2] = 1.0f; + } + } + + if (lightingOnly) { + if (hasSelfIllum && !hasFlashlight) + shaderAPI.BindStandardTexture(Sampler.Sampler0, StandardTextureId.GreyAlphaZero); + else + shaderAPI.BindStandardTexture(Sampler.Sampler0, StandardTextureId.Grey); + + if (hasDetailTexture) + shaderAPI.BindStandardTexture(Sampler.Sampler13, StandardTextureId.Grey); + + specularTint[0] = specularTint[1] = specularTint[2] = 0.0f; + } + + if ((info.PhongFresnelRanges != -1) && parms[info.PhongFresnelRanges].IsDefined()) { + parms[info.PhongFresnelRanges].GetVecValue(fresnelRanges_SpecBoost[..3]); + fresnelRanges_SpecBoost[0] = (fresnelRanges_SpecBoost[1] - fresnelRanges_SpecBoost[0]) * 2; + fresnelRanges_SpecBoost[2] = (fresnelRanges_SpecBoost[2] - fresnelRanges_SpecBoost[1]) * 2; + } + + if ((info.PhongBoost != -1) && parms[info.PhongBoost].IsDefined()) + fresnelRanges_SpecBoost[3] = parms[info.PhongBoost].GetFloatValue(); + else + fresnelRanges_SpecBoost[3] = 1.0f; + + shaderAPI.SetPixelShaderConstant((int)PixelShaderConst.EyePosSpecExponent, eyePos_SpecExponent); + shaderAPI.SetPixelShaderConstant((int)PixelShaderConst.FresnelSpecParams, fresnelRanges_SpecBoost); + shaderAPI.SetPixelShaderConstant((int)PixelShaderConst.FlashlightPositionRimBoost, vRimBoost); + shaderAPI.SetPixelShaderConstant((int)PixelShaderConst.SpecRimParams, specularTint); + // ShaderAPI.SetPixelShaderFogParams(PSREG_FOG_PARAMS); + + if (hasFlashlight) { + Span atten = [0, 0, 0, 0], pos = [0, 0, 0, 0], tweaks = [0, 0, 0, 0]; + + FlashlightState flashlightState = shaderAPI.GetFlashlightState(out Matrix4x4 worldToTexture); + SetFlashLightColorFromState(flashlightState, shaderAPI, (int)PixelShaderConst.FlashlightColor); + + shader.BindTexture(Sampler.Sampler6, flashlightState.SpotlightTexture, flashlightState.SpotlightTextureFrame); + + atten[0] = flashlightState.ConstantAtten; + atten[1] = flashlightState.LinearAtten; + atten[2] = flashlightState.QuadraticAtten; + atten[3] = flashlightState.FarZ; + shaderAPI.SetPixelShaderConstant((int)PixelShaderConst.FlashlightAttenuation, atten); + + pos[0] = flashlightState.LightOrigin[0]; + pos[1] = flashlightState.LightOrigin[1]; + pos[2] = flashlightState.LightOrigin[2]; + shaderAPI.SetPixelShaderConstant((int)PixelShaderConst.FlashlightPositionRimBoost, pos); + + Span values = [ + worldToTexture.M11, worldToTexture.M12, worldToTexture.M13, worldToTexture.M14, + worldToTexture.M21, worldToTexture.M22, worldToTexture.M23, worldToTexture.M24, + worldToTexture.M31, worldToTexture.M32, worldToTexture.M33, worldToTexture.M34, + worldToTexture.M41, worldToTexture.M42, worldToTexture.M43, worldToTexture.M44 + ]; + shaderAPI.SetPixelShaderConstant((int)PixelShaderConst.FlashlightToWorldTexture, values); + + tweaks[0] = ShadowFilterFromState(flashlightState); + tweaks[1] = ShadowAttenFromState(flashlightState); + shader.HashShadow2DJitter(flashlightState.ShadowJitterSeed, out tweaks[2], out tweaks[3]); + shaderAPI.SetPixelShaderConstant((int)PixelShaderConst.EnvMapTintShadowTweaks, tweaks); + + Span screenScale = [1280.0f / 32.0f, 720.0f / 32.0f, 0, 0]; + shaderAPI.GetBackBufferDimensions(out int width, out int height); + screenScale[0] = width / 32.0f; + screenScale[1] = height / 32.0f; + shaderAPI.SetPixelShaderConstant((int)PixelShaderConst.FlashlightScreenScale, screenScale); + } + } + shader.Draw(); + } +} + +class Skin_Context : BasePerMaterialContextData +{ + public readonly CommandBufferBuilder SemiStaticCmdsOut = new() { Storage = new FixedCommandStorageBuffer(800) }; + public bool FastPath; +}; \ No newline at end of file diff --git a/Source.StdShader.Gl46/VertexLitGeneric.cs b/Source.StdShader.Gl46/VertexLitGeneric.cs index b928df8b..a4479e3e 100644 --- a/Source.StdShader.Gl46/VertexLitGeneric.cs +++ b/Source.StdShader.Gl46/VertexLitGeneric.cs @@ -2,6 +2,8 @@ using Source.Common.ShaderAPI; using Source.Common.ShaderLib; +using System.Runtime.InteropServices; + namespace Source.StdShader.Gl46; public class VertexLitGeneric : BaseVSShader @@ -22,14 +24,7 @@ public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan default public ReadOnlySpan GetHelp() => Info.Help; } - public static readonly ShaderParam SELFILLUMTINT = new($"${nameof(SELFILLUMTINT)}", ShaderParamType.Color, "[1 1 1]", "Slef-illumunation tint"); + public static readonly ShaderParam ALBEDO = new($"${nameof(ALBEDO)}", ShaderParamType.Texture, "shadertest/BaseTexture", "albedo (Base texture with no baked lighting)"); + public static readonly ShaderParam COMPRESS = new($"${nameof(COMPRESS)}", ShaderParamType.Texture, "shadertest/BaseTexture", "compression wrinklemap"); + public static readonly ShaderParam STRETCH = new($"${nameof(STRETCH)}", ShaderParamType.Texture, "shadertest/BaseTexture", "expansion wrinklemap"); + public static readonly ShaderParam SELFILLUMTINT = new($"${nameof(SELFILLUMTINT)}", ShaderParamType.Color, "[1 1 1]", "Self-illumination tint"); public static readonly ShaderParam DETAIL = new($"${nameof(DETAIL)}", ShaderParamType.Texture, "shadertest/detail", "detail texture"); - public static readonly ShaderParam DETAILSCALE = new($"${nameof(DETAILSCALE)}", ShaderParamType.Float, "4", "scale of the detail texture"); public static readonly ShaderParam DETAILFRAME = new($"${nameof(DETAILFRAME)}", ShaderParamType.Integer, "0", "frame number for $detail"); + public static readonly ShaderParam DETAILSCALE = new($"${nameof(DETAILSCALE)}", ShaderParamType.Float, "4", "scale of the detail texture"); public static readonly ShaderParam ENVMAP = new($"${nameof(ENVMAP)}", ShaderParamType.Texture, "shadertest/shadertest_env", "envmap"); - public static readonly ShaderParam ENVMAPFRAME = new($"${nameof(ENVMAPFRAME)}", ShaderParamType.Integer, "0", ""); + public static readonly ShaderParam ENVMAPFRAME = new($"${nameof(ENVMAPFRAME)}", ShaderParamType.Integer, "0", "envmap frame number"); public static readonly ShaderParam ENVMAPMASK = new($"${nameof(ENVMAPMASK)}", ShaderParamType.Texture, "shadertest/shadertest_envmask", "envmap mask"); public static readonly ShaderParam ENVMAPMASKFRAME = new($"${nameof(ENVMAPMASKFRAME)}", ShaderParamType.Integer, "0", ""); - public static readonly ShaderParam ENVMAPMASKSCALE = new($"${nameof(ENVMAPMASKSCALE)}", ShaderParamType.Float, "1", "envmap mask scale"); + public static readonly ShaderParam ENVMAPMASKTRANSFORM = new($"${nameof(ENVMAPMASKTRANSFORM)}", ShaderParamType.Matrix, "center .5 .5 scale 1 1 rotate 0 translate 0 0", "$envmapmask texcoord transform"); public static readonly ShaderParam ENVMAPTINT = new($"${nameof(ENVMAPTINT)}", ShaderParamType.Color, "[1 1 1]", "envmap tint"); - public static readonly ShaderParam ENVMAPCONTRAST = new($"${nameof(ENVMAPCONTRAST)}", ShaderParamType.Float, "0.0", "controls the contrast of the envmap. 0.0 == normal, 1.0 == color*color"); - public static readonly ShaderParam ENVMAPSATURATION = new($"${nameof(ENVMAPSATURATION)}", ShaderParamType.Float, "1.0", "saturation 0 == greyscale 1 == normal"); - public static readonly ShaderParam ENVMAPOPTIONAL = new($"${nameof(ENVMAPOPTIONAL)}", ShaderParamType.Bool, "0", "Make the envmap only apply to dx9 and higher hardware"); public static readonly ShaderParam BUMPMAP = new($"${nameof(BUMPMAP)}", ShaderParamType.Texture, "models/shadertest/shader1_normal", "bump map"); + public static readonly ShaderParam BUMPCOMPRESS = new($"${nameof(BUMPCOMPRESS)}", ShaderParamType.Texture, "models/shadertest/shader3_normal", "compression bump map"); + public static readonly ShaderParam BUMPSTRETCH = new($"${nameof(BUMPSTRETCH)}", ShaderParamType.Texture, "models/shadertest/shader1_normal", "expansion bump map"); public static readonly ShaderParam BUMPFRAME = new($"${nameof(BUMPFRAME)}", ShaderParamType.Integer, "0", "frame number for $bumpmap"); + public static readonly ShaderParam BUMPTRANSFORM = new($"${nameof(BUMPTRANSFORM)}", ShaderParamType.Matrix, "center .5 .5 scale 1 1 rotate 0 translate 0 0", "$bumpmap texcoord transform"); + public static readonly ShaderParam ENVMAPCONTRAST = new($"${nameof(ENVMAPCONTRAST)}", ShaderParamType.Float, "0.0", "contrast 0 == normal 1 == color*color"); + public static readonly ShaderParam ENVMAPSATURATION = new($"${nameof(ENVMAPSATURATION)}", ShaderParamType.Float, "1.0", "saturation 0 == greyscale 1 == normal"); + public static readonly ShaderParam SELFILLUM_ENVMAPMASK_ALPHA = new($"${nameof(SELFILLUM_ENVMAPMASK_ALPHA)}", ShaderParamType.Float, "0.0", "defines that self illum value comes from env map mask alpha"); + public static readonly ShaderParam SELFILLUMFRESNEL = new($"${nameof(SELFILLUMFRESNEL)}", ShaderParamType.Bool, "0", "Self illum fresnel"); + public static readonly ShaderParam SELFILLUMFRESNELMINMAXEXP = new($"${nameof(SELFILLUMFRESNELMINMAXEXP)}", ShaderParamType.Vec4, "0", "Self illum fresnel min, max, exp"); + public static readonly ShaderParam ALPHATESTREFERENCE = new($"${nameof(ALPHATESTREFERENCE)}", ShaderParamType.Float, "0.0", ""); + public static readonly ShaderParam FLASHLIGHTNOLAMBERT = new($"${nameof(FLASHLIGHTNOLAMBERT)}", ShaderParamType.Bool, "0", "Flashlight pass sets N.L=1.0"); + public static readonly ShaderParam AMBIENTONLY = new($"${nameof(AMBIENTONLY)}", ShaderParamType.Integer, "0", "Control drawing of non-ambient light ()"); + public static readonly ShaderParam PHONGEXPONENT = new($"${nameof(PHONGEXPONENT)}", ShaderParamType.Float, "5.0", "Phong exponent for local specular lights"); + public static readonly ShaderParam PHONGTINT = new($"${nameof(PHONGTINT)}", ShaderParamType.Vec3, "5.0", "Phong tint for local specular lights"); + public static readonly ShaderParam PHONGALBEDOTINT = new($"${nameof(PHONGALBEDOTINT)}", ShaderParamType.Bool, "1.0", "Apply tint by albedo (controlled by spec exponent texture"); + public static readonly ShaderParam LIGHTWARPTEXTURE = new($"${nameof(LIGHTWARPTEXTURE)}", ShaderParamType.Texture, "shadertest/BaseTexture", "1D ramp texture for tinting scalar diffuse term"); + public static readonly ShaderParam PHONGWARPTEXTURE = new($"${nameof(PHONGWARPTEXTURE)}", ShaderParamType.Texture, "shadertest/BaseTexture", "warp the specular term"); + public static readonly ShaderParam PHONGFRESNELRANGES = new($"${nameof(PHONGFRESNELRANGES)}", ShaderParamType.Vec3, "[0 0.5 1]", "Parameters for remapping fresnel output"); + public static readonly ShaderParam PHONGBOOST = new($"${nameof(PHONGBOOST)}", ShaderParamType.Float, "1.0", "Phong overbrightening factor (specular mask channel should be authored to account for this)"); + public static readonly ShaderParam PHONGEXPONENTTEXTURE = new($"${nameof(PHONGEXPONENTTEXTURE)}", ShaderParamType.Texture, "shadertest/BaseTexture", "Phong Exponent map"); + public static readonly ShaderParam PHONG = new($"${nameof(PHONG)}", ShaderParamType.Bool, "0", "enables phong lighting"); + public static readonly ShaderParam BASEMAPALPHAPHONGMASK = new($"${nameof(BASEMAPALPHAPHONGMASK)}", ShaderParamType.Integer, "0", "indicates that there is no normal map and that the phong mask is in base alpha"); + public static readonly ShaderParam INVERTPHONGMASK = new($"${nameof(INVERTPHONGMASK)}", ShaderParamType.Integer, "0", "invert the phong mask (0=full phong, 1=no phong)"); + public static readonly ShaderParam ENVMAPFRESNEL = new($"${nameof(ENVMAPFRESNEL)}", ShaderParamType.Float, "0", "Degree to which Fresnel should be applied to env map"); + public static readonly ShaderParam SELFILLUMMASK = new($"${nameof(SELFILLUMMASK)}", ShaderParamType.Texture, "shadertest/BaseTexture", "If we bind a texture here, it overrides base alpha (if any) for self illum"); public static readonly ShaderParam DETAILBLENDMODE = new($"${nameof(DETAILBLENDMODE)}", ShaderParamType.Integer, "0", "mode for combining detail texture with base. 0=normal, 1= additive, 2=alpha blend detail over base, 3=crossfade"); - public static readonly ShaderParam ALPHATESTREFERENCE = new($"${nameof(ALPHATESTREFERENCE)}", ShaderParamType.Float, "0.7", ""); - public static readonly ShaderParam OUTLINE = new($"${nameof(OUTLINE)}", ShaderParamType.Bool, "0", "Enable outline for distance coded textures."); - public static readonly ShaderParam OUTLINECOLOR = new($"${nameof(OUTLINECOLOR)}", ShaderParamType.Color, "[1 1 1]", "color of outline for distance coded images."); - public static readonly ShaderParam OUTLINESTART0 = new($"${nameof(OUTLINESTART0)}", ShaderParamType.Float, "0.0", "outer start value for outline"); - public static readonly ShaderParam OUTLINESTART1 = new($"${nameof(OUTLINESTART1)}", ShaderParamType.Float, "0.0", "inner start value for outline"); - public static readonly ShaderParam OUTLINEEND0 = new($"${nameof(OUTLINEEND0)}", ShaderParamType.Float, "0.0", "inner end value for outline"); - public static readonly ShaderParam OUTLINEEND1 = new($"${nameof(OUTLINEEND1)}", ShaderParamType.Float, "0.0", "outer end value for outline"); - public static readonly ShaderParam SEPARATEDETAILUVS = new($"${nameof(SEPARATEDETAILUVS)}", ShaderParamType.Integer, "0", ""); - - - protected override void OnInitShaderParams(IMaterialVar[] vars, ReadOnlySpan materialName) { - InitParamsUnlitGeneric((int)ShaderMaterialVars.BaseTexture, DETAILSCALE, ENVMAPOPTIONAL, ENVMAP, ENVMAPTINT, ENVMAPMASKSCALE, DETAILBLENDMODE); + public static readonly ShaderParam DETAILBLENDFACTOR = new($"${nameof(DETAILBLENDFACTOR)}", ShaderParamType.Float, "1", "blend amount for detail texture."); + public static readonly ShaderParam DETAILTINT = new($"${nameof(DETAILTINT)}", ShaderParamType.Color, "[1 1 1]", "detail texture tint"); + public static readonly ShaderParam DETAILTEXTURETRANSFORM = new($"${nameof(DETAILTEXTURETRANSFORM)}", ShaderParamType.Matrix, "center .5 .5 scale 1 1 rotate 0 translate 0 0", "$detail texcoord transform"); + public static readonly ShaderParam RIMLIGHT = new($"${nameof(RIMLIGHT)}", ShaderParamType.Bool, "0", "enables rim lighting"); + public static readonly ShaderParam RIMLIGHTEXPONENT = new($"${nameof(RIMLIGHTEXPONENT)}", ShaderParamType.Float, "4.0", "Exponent for rim lights"); + public static readonly ShaderParam RIMLIGHTBOOST = new($"${nameof(RIMLIGHTBOOST)}", ShaderParamType.Float, "1.0", "Boost for rim lights"); + public static readonly ShaderParam RIMMASK = new($"${nameof(RIMMASK)}", ShaderParamType.Bool, "0", "Indicates whether or not to use alpha channel of exponent texture to mask the rim term"); + public static readonly ShaderParam SEAMLESS_BASE = new($"${nameof(SEAMLESS_BASE)}", ShaderParamType.Bool, "0", "whether to apply seamless mapping to the base texture. requires a smooth model."); + public static readonly ShaderParam SEAMLESS_DETAIL = new($"${nameof(SEAMLESS_DETAIL)}", ShaderParamType.Bool, "0", "where to apply seamless mapping to the detail texture."); + public static readonly ShaderParam SEAMLESS_SCALE = new($"${nameof(SEAMLESS_SCALE)}", ShaderParamType.Float, "1.0", "the scale for the seamless mapping. # of repetions of texture per inch."); + public static readonly ShaderParam EMISSIVEBLENDENABLED = new($"${nameof(EMISSIVEBLENDENABLED)}", ShaderParamType.Bool, "0", "Enable emissive blend pass"); + public static readonly ShaderParam EMISSIVEBLENDBASETEXTURE = new($"${nameof(EMISSIVEBLENDBASETEXTURE)}", ShaderParamType.Texture, "", "self-illumination map"); + public static readonly ShaderParam EMISSIVEBLENDSCROLLVECTOR = new($"${nameof(EMISSIVEBLENDSCROLLVECTOR)}", ShaderParamType.Vec2, "[0.11 0.124]", "Emissive scroll vec"); + public static readonly ShaderParam EMISSIVEBLENDSTRENGTH = new($"${nameof(EMISSIVEBLENDSTRENGTH)}", ShaderParamType.Float, "1.0", "Emissive blend strength"); + public static readonly ShaderParam EMISSIVEBLENDTEXTURE = new($"${nameof(EMISSIVEBLENDTEXTURE)}", ShaderParamType.Texture, "", "self-illumination map"); + public static readonly ShaderParam EMISSIVEBLENDTINT = new($"${nameof(EMISSIVEBLENDTINT)}", ShaderParamType.Color, "[1 1 1]", "Self-illumination tint"); + public static readonly ShaderParam EMISSIVEBLENDFLOWTEXTURE = new($"${nameof(EMISSIVEBLENDFLOWTEXTURE)}", ShaderParamType.Texture, "", "flow map"); + public static readonly ShaderParam TIME = new($"${nameof(TIME)}", ShaderParamType.Float, "0.0", "Needs CurrentTime Proxy"); + public static readonly ShaderParam CLOAKPASSENABLED = new($"${nameof(CLOAKPASSENABLED)}", ShaderParamType.Bool, "0", "Enables cloak render in a second pass"); + public static readonly ShaderParam CLOAKFACTOR = new($"${nameof(CLOAKFACTOR)}", ShaderParamType.Float, "0.0", ""); + public static readonly ShaderParam CLOAKCOLORTINT = new($"${nameof(CLOAKCOLORTINT)}", ShaderParamType.Color, "[1 1 1]", "Cloak color tint"); + public static readonly ShaderParam REFRACTAMOUNT = new($"${nameof(REFRACTAMOUNT)}", ShaderParamType.Float, "2", ""); + public static readonly ShaderParam SHEENPASSENABLED = new($"${nameof(SHEENPASSENABLED)}", ShaderParamType.Bool, "0", "Enables weapon sheen render in a second pass"); + public static readonly ShaderParam SHEENMAP = new($"${nameof(SHEENMAP)}", ShaderParamType.Texture, "shadertest/shadertest_env", "sheenmap"); + public static readonly ShaderParam SHEENMAPMASK = new($"${nameof(SHEENMAPMASK)}", ShaderParamType.Texture, "shadertest/shadertest_envmask", "sheenmap mask"); + public static readonly ShaderParam SHEENMAPMASKFRAME = new($"${nameof(SHEENMAPMASKFRAME)}", ShaderParamType.Integer, "0", ""); + public static readonly ShaderParam SHEENMAPTINT = new($"${nameof(SHEENMAPTINT)}", ShaderParamType.Color, "[1 1 1]", "sheenmap tint"); + public static readonly ShaderParam SHEENMAPMASKSCALEX = new($"${nameof(SHEENMAPMASKSCALEX)}", ShaderParamType.Float, "1", "X Scale the size of the map mask to the size of the target"); + public static readonly ShaderParam SHEENMAPMASKSCALEY = new($"${nameof(SHEENMAPMASKSCALEY)}", ShaderParamType.Float, "1", "Y Scale the size of the map mask to the size of the target"); + public static readonly ShaderParam SHEENMAPMASKOFFSETX = new($"${nameof(SHEENMAPMASKOFFSETX)}", ShaderParamType.Float, "0", "X Offset of the mask relative to model space coords of target"); + public static readonly ShaderParam SHEENMAPMASKOFFSETY = new($"${nameof(SHEENMAPMASKOFFSETY)}", ShaderParamType.Float, "0", "Y Offset of the mask relative to model space coords of target"); + public static readonly ShaderParam SHEENMAPMASKDIRECTION = new($"${nameof(SHEENMAPMASKDIRECTION)}", ShaderParamType.Integer, "0", "The direction the sheen should move (length direction of weapon) XYZ, 0,1,2"); + public static readonly ShaderParam SHEENINDEX = new($"${nameof(SHEENINDEX)}", ShaderParamType.Integer, "0", "Index of the Effect Type (Color Additive, Override etc...)"); + public static readonly ShaderParam FLESHINTERIORENABLED = new($"${nameof(FLESHINTERIORENABLED)}", ShaderParamType.Bool, "0", "Enable Flesh interior blend pass"); + public static readonly ShaderParam FLESHINTERIORTEXTURE = new($"${nameof(FLESHINTERIORTEXTURE)}", ShaderParamType.Texture, "", "Flesh color texture"); + public static readonly ShaderParam FLESHINTERIORNOISETEXTURE = new($"${nameof(FLESHINTERIORNOISETEXTURE)}", ShaderParamType.Texture, "", "Flesh noise texture"); + public static readonly ShaderParam FLESHBORDERTEXTURE1D = new($"${nameof(FLESHBORDERTEXTURE1D)}", ShaderParamType.Texture, "", "Flesh border 1D texture"); + public static readonly ShaderParam FLESHNORMALTEXTURE = new($"${nameof(FLESHNORMALTEXTURE)}", ShaderParamType.Texture, "", "Flesh normal texture"); + public static readonly ShaderParam FLESHSUBSURFACETEXTURE = new($"${nameof(FLESHSUBSURFACETEXTURE)}", ShaderParamType.Texture, "", "Flesh subsurface texture"); + public static readonly ShaderParam FLESHCUBETEXTURE = new($"${nameof(FLESHCUBETEXTURE)}", ShaderParamType.Texture, "", "Flesh cubemap texture"); + public static readonly ShaderParam FLESHBORDERNOISESCALE = new($"${nameof(FLESHBORDERNOISESCALE)}", ShaderParamType.Float, "1.5", "Flesh Noise UV scalar for border"); + public static readonly ShaderParam FLESHDEBUGFORCEFLESHON = new($"${nameof(FLESHDEBUGFORCEFLESHON)}", ShaderParamType.Bool, "0", "Flesh Debug full flesh"); + public static readonly ShaderParam FLESHEFFECTCENTERRADIUS1 = new($"${nameof(FLESHEFFECTCENTERRADIUS1)}", ShaderParamType.Vec4, "[0 0 0 0.001]", "Flesh effect center and radius"); + public static readonly ShaderParam FLESHEFFECTCENTERRADIUS2 = new($"${nameof(FLESHEFFECTCENTERRADIUS2)}", ShaderParamType.Vec4, "[0 0 0 0.001]", "Flesh effect center and radius"); + public static readonly ShaderParam FLESHEFFECTCENTERRADIUS3 = new($"${nameof(FLESHEFFECTCENTERRADIUS3)}", ShaderParamType.Vec4, "[0 0 0 0.001]", "Flesh effect center and radius"); + public static readonly ShaderParam FLESHEFFECTCENTERRADIUS4 = new($"${nameof(FLESHEFFECTCENTERRADIUS4)}", ShaderParamType.Vec4, "[0 0 0 0.001]", "Flesh effect center and radius"); + public static readonly ShaderParam FLESHSUBSURFACETINT = new($"${nameof(FLESHSUBSURFACETINT)}", ShaderParamType.Color, "[1 1 1]", "Subsurface Color"); + public static readonly ShaderParam FLESHBORDERWIDTH = new($"${nameof(FLESHBORDERWIDTH)}", ShaderParamType.Float, "0.3", "Flesh border"); + public static readonly ShaderParam FLESHBORDERSOFTNESS = new($"${nameof(FLESHBORDERSOFTNESS)}", ShaderParamType.Float, "0.42", "Flesh border softness (> 0.0 && <= 0.5)"); + public static readonly ShaderParam FLESHBORDERTINT = new($"${nameof(FLESHBORDERTINT)}", ShaderParamType.Color, "[1 1 1]", "Flesh border Color"); + public static readonly ShaderParam FLESHGLOBALOPACITY = new($"${nameof(FLESHGLOBALOPACITY)}", ShaderParamType.Float, "1.0", "Flesh global opacity"); + public static readonly ShaderParam FLESHGLOSSBRIGHTNESS = new($"${nameof(FLESHGLOSSBRIGHTNESS)}", ShaderParamType.Float, "0.66", "Flesh gloss brightness"); + public static readonly ShaderParam FLESHSCROLLSPEED = new($"${nameof(FLESHSCROLLSPEED)}", ShaderParamType.Float, "1.0", "Flesh scroll speed"); + public static readonly ShaderParam SEPARATEDETAILUVS = new($"${nameof(SEPARATEDETAILUVS)}", ShaderParamType.Bool, "0", "Use texcoord1 for detail texture"); + public static readonly ShaderParam LINEARWRITE = new($"${nameof(LINEARWRITE)}", ShaderParamType.Integer, "0", "Disables SRGB conversion of shader results."); + public static readonly ShaderParam DEPTHBLEND = new($"${nameof(DEPTHBLEND)}", ShaderParamType.Integer, "0", "fade at intersection boundaries. Only supported without bumpmaps"); + public static readonly ShaderParam DEPTHBLENDSCALE = new($"${nameof(DEPTHBLENDSCALE)}", ShaderParamType.Float, "50.0", "Amplify or reduce DEPTHBLEND fading. Lower values make harder edges."); + public static readonly ShaderParam BLENDTINTBYBASEALPHA = new($"${nameof(BLENDTINTBYBASEALPHA)}", ShaderParamType.Bool, "0", "Use the base alpha to blend in the $color modulation"); + public static readonly ShaderParam BLENDTINTCOLOROVERBASE = new($"${nameof(BLENDTINTCOLOROVERBASE)}", ShaderParamType.Float, "0", "blend between tint acting as a multiplication versus a replace"); + + + protected override void OnInitShaderParams(IMaterialVar[] parms, ReadOnlySpan materialName) { + VertexLitGeneric_Vars shaderVars = new(); + SetupVars(ref shaderVars); + InitParamsVertexLitGeneric(this, parms, materialName, true, ref shaderVars); + + if (!parms[CLOAKPASSENABLED].IsDefined()) + parms[CLOAKPASSENABLED].SetIntValue(0); + else if (parms[CLOAKPASSENABLED].GetIntValue() != 0) { + // CloakBlendedPassVars_t info; + // SetupVarsCloakBlendedPass(info); + // InitParamsCloakBlendedPass(this, parms, pMaterialName, info); + } - if (!vars[ENVMAPCONTRAST].IsDefined()) - vars[ENVMAPCONTRAST].SetFloatValue(0.0f); + if (!parms[SHEENPASSENABLED].IsDefined()) + parms[SHEENPASSENABLED].SetIntValue(0); + else if (parms[SHEENPASSENABLED].GetIntValue() != 0) { + // WeaponSheenPassVars_t info; + // SetupVarsWeaponSheenPass(info); + // InitParamsWeaponSheenPass(this, parms, pMaterialName, info); + } - if (!vars[ENVMAPSATURATION].IsDefined()) - vars[ENVMAPSATURATION].SetFloatValue(1.0f); + if (!parms[EMISSIVEBLENDENABLED].IsDefined()) + parms[EMISSIVEBLENDENABLED].SetIntValue(0); + else if (parms[EMISSIVEBLENDENABLED].GetIntValue() != 0) { + // EmissiveScrollBlendedPassVars_t info; + // SetupVarsEmissiveScrollBlendedPass(info); + // InitParamsEmissiveScrollBlendedPass(this, parms, pMaterialName, info); + } - SetFlags2(vars, MaterialVarFlags2.SupportsHardwareSkinning); - SetFlags2(vars, MaterialVarFlags2.LightingVertexLit); + if (!parms[FLESHINTERIORENABLED].IsDefined()) + parms[FLESHINTERIORENABLED].SetIntValue(0); + else if (parms[FLESHINTERIORENABLED].GetIntValue() != 0) { + // FleshInteriorBlendedPassVars_t info; + // SetupVarsFleshInteriorBlendedPass(info); + // InitParamsFleshInteriorBlendedPass(this, parms, pMaterialName, info); + } } - public override string? GetFallbackShader(IMaterialVar[] vars) { - return null; - } + public override string? GetFallbackShader(IMaterialVar[] vars) => null; public override int GetFlags() => Flags; public override int GetNumParams() => base.GetNumParams() + ShaderParams.Count; public override ReadOnlySpan GetParamName(int paramIndex) { @@ -122,128 +215,221 @@ public override ReadOnlySpan GetParamDefault(int paramIndex) { else return ShaderParams[paramIndex - baseClassParamCount].GetDefaultValue(); } - protected override void OnInitShaderInstance(IMaterialVar[] vars, ReadOnlySpan materialName) { - InitUnlitGeneric((int)ShaderMaterialVars.BaseTexture, DETAIL, ENVMAP, ENVMAPMASK); - - if (vars[BUMPMAP].IsDefined()) - LoadTexture(BUMPMAP); - } - protected override void OnDrawElements(IMaterialVar[] vars, IShaderDynamicAPI shaderAPI, VertexCompressionType vertexCompression) { - DrawUnbumpedUsingVertexShader(vars, shaderAPI, ShaderShadow, false); - } - - private void DrawUnbumpedUsingVertexShader(IMaterialVar[] vars, IShaderDynamicAPI shaderAPI, IShaderShadow? shaderShadow, bool skipEnvmap) { - if (shaderShadow != null) { - shaderShadow.EnableTexture(Sampler.Sampler0, true); - shaderShadow.EnableAlphaTest(IsFlagSet(vars, MaterialVarFlags.AlphaTest)); - - if (vars[ALPHATESTREFERENCE].GetFloatValue() > 0.0f) - shaderShadow.AlphaFunc(ShaderAlphaFunc.GreaterEqual, vars[ALPHATESTREFERENCE].GetFloatValue()); - - VertexFormat fmt = VertexFormat.Position | VertexFormat.Normal | VertexFormat.Color | VertexFormat.BoneIndex | VertexFormat.BoneWeights2 | VertexFormat.UserData4 | VertexFormat.TexCoord2D_0; - - if (IsFlagSet(vars, MaterialVarFlags.VertexColor) || IsFlagSet(vars, MaterialVarFlags.VertexAlpha)) - fmt |= VertexFormat.Color; - - if (vars[ENVMAP].IsTexture() && !skipEnvmap) { - shaderShadow.EnableTexture(Sampler.Sampler1, true); - - if (vars[ENVMAPMASK].IsTexture() || IsFlagSet(vars, MaterialVarFlags.BaseAlphaEnvMapMask)) - shaderShadow.EnableTexture(Sampler.Sampler2, true); - - if (IsFlagSet(vars, MaterialVarFlags.NormalMapAlphaEnvMapMask) && vars[BUMPMAP].IsTexture()) - shaderShadow.EnableTexture(Sampler.Sampler4, true); - } - - if (vars[(int)ShaderMaterialVars.BaseTexture].IsTexture()) - SetDefaultBlendingShadowState((int)ShaderMaterialVars.BaseTexture, true); - else - SetDefaultBlendingShadowState(ENVMAPMASK, false); - - if (vars[DETAIL].IsTexture()) - shaderShadow.EnableTexture(Sampler.Sampler3, true); - - shaderShadow.VertexShaderVertexFormat(fmt, 1, null, 0); - - bool hasEnvmap = vars[ENVMAP].IsTexture() && !skipEnvmap; - - StaticShaderIndex vshIndex = new(shaderShadow, ShaderType.Vertex, "vertexlitgeneric"); - vshIndex.Set("CUBEMAP", hasEnvmap); - vshIndex.Set("VERTEXCOLOR", IsFlagSet(vars, MaterialVarFlags.VertexColor)); - vshIndex.Set("HALFLAMBERT", IsFlagSet(vars, MaterialVarFlags.HalfLambert)); - shaderShadow.SetVertexShader("vertexlitgeneric", vshIndex.GetIndex()); - - StaticShaderIndex pshIndex = new(shaderShadow, ShaderType.Pixel, "vertexlitgeneric"); - pshIndex.Set("CUBEMAP", hasEnvmap); - pshIndex.Set("ENVMAPMASK", hasEnvmap && vars[ENVMAPMASK].IsTexture()); - pshIndex.Set("BASEALPHAENVMAPMASK", hasEnvmap && IsFlagSet(vars, MaterialVarFlags.BaseAlphaEnvMapMask)); - pshIndex.Set("NORMALMAPALPHAENVMAPMASK", hasEnvmap && IsFlagSet(vars, MaterialVarFlags.NormalMapAlphaEnvMapMask) && vars[BUMPMAP].IsTexture()); - pshIndex.Set("SELFILLUM", IsFlagSet(vars, MaterialVarFlags.SelfIllum)); - pshIndex.Set("VERTEXCOLOR", IsFlagSet(vars, MaterialVarFlags.VertexColor)); - shaderShadow.SetPixelShader("vertexlitgeneric", pshIndex.GetIndex()); - - SetStandardShaderUniforms(); + protected override void OnInitShaderInstance(IMaterialVar[] parms, ReadOnlySpan materialName) { + VertexLitGeneric_Vars vars = new(); + SetupVars(ref vars); + InitVertexLitGeneric(this, parms, true, ref vars); + + if (parms[CLOAKPASSENABLED].GetIntValue() != 0) { + // CloakBlendedPassVars_t info; + // SetupVarsCloakBlendedPass(info); + // InitCloakBlendedPass(this, parms, info); + } - shaderShadow.EnableAlphaWrites(true); + if (parms[SHEENPASSENABLED].GetIntValue() != 0) { + // WeaponSheenPassVars_t info; + // SetupVarsWeaponSheenPass(info); + // InitWeaponSheenPass(this, parms, info); } - if (shaderAPI != null) { - if (vars[(int)ShaderMaterialVars.BaseTexture].IsTexture()) { - BindTexture(Sampler.Sampler0, (int)ShaderMaterialVars.BaseTexture, (int)ShaderMaterialVars.Frame); - SetVertexShaderTextureTransform(VertexShaderConst.ShaderSpecificConst0, (int)ShaderMaterialVars.BaseTextureTransform); - } + if (parms[EMISSIVEBLENDENABLED].GetIntValue() != 0) { + // EmissiveScrollBlendedPassVars_t info; + // SetupVarsEmissiveScrollBlendedPass(info); + // InitEmissiveScrollBlendedPass(this, parms, info); + } - if (vars[ENVMAP].IsTexture() && !skipEnvmap) { - ITexture? resolvedEnvmap = vars[ENVMAP].GetTextureValue(); + if (parms[FLESHINTERIORENABLED].GetIntValue() != 0) { + // FleshInteriorBlendedPassVars_t info; + // SetupVarsFleshInteriorBlendedPass(info); + // InitFleshInteriorBlendedPass(this, parms, info); + } + } - BindTexture(Sampler.Sampler1, ENVMAP, ENVMAPFRAME); + protected override void OnDrawElements(IMaterialVar[] vars, IShaderDynamicAPI shaderAPI, VertexCompressionType vertexCompression, ref BasePerMaterialContextData? contextData) { + bool drawStandardPass = true; + if (vars[CLOAKPASSENABLED].GetIntValue() != 0 && ShaderShadow == null) { - if (vars[ENVMAPMASK].IsTexture() || IsFlagSet(vars, MaterialVarFlags.BaseAlphaEnvMapMask)) { - if (vars[ENVMAPMASK].IsTexture()) - BindTexture(Sampler.Sampler2, ENVMAPMASK, ENVMAPMASKFRAME); - else - BindTexture(Sampler.Sampler2, (int)ShaderMaterialVars.BaseTexture, (int)ShaderMaterialVars.Frame); + } - SetVertexShaderTextureScaledTransform(VertexShaderConst.ShaderSpecificConst2, (int)ShaderMaterialVars.BaseTextureTransform, ENVMAPMASKSCALE); - } + if (drawStandardPass) { + VertexLitGeneric_Vars shaderVars = new(); + SetupVars(ref shaderVars); + DrawVertexLitGeneric(this, vars, ShaderAPI, ShaderShadow, true, ref shaderVars, vertexCompression, ref contextData); + } + else + Draw(false); - if (IsFlagSet(vars, MaterialVarFlags.NormalMapAlphaEnvMapMask) && vars[BUMPMAP].IsTexture()) - BindTexture(Sampler.Sampler4, BUMPMAP, BUMPFRAME); + if (vars[SHEENPASSENABLED].GetIntValue() != 0) { - if (IsFlagSet(vars, MaterialVarFlags.EnvMapSphere) || IsFlagSet(vars, MaterialVarFlags.EnvMapCameraSpace)) { - LoadViewMatrixIntoVertexShaderConstant(VertexShaderConst.ViewModel); - } + } - SetEnvMapTintPixelShaderDynamicState(2, ENVMAPTINT, -1); + if (vars[CLOAKPASSENABLED].GetIntValue() != 0) { - float envmapContrast = vars[ENVMAPCONTRAST].GetFloatValue(); - float envmapSaturation = vars[ENVMAPSATURATION].GetFloatValue(); - Span envmapContrastConst = [envmapContrast, envmapContrast, envmapContrast, 0.0f]; - Span envmapSaturationConst = [envmapSaturation, envmapSaturation, envmapSaturation, 0.0f]; - shaderAPI.SetPixelShaderConstant(4, envmapContrastConst); - shaderAPI.SetPixelShaderConstant(5, envmapSaturationConst); - } + } - if (vars[DETAIL].IsTexture()) { - BindTexture(Sampler.Sampler3, DETAIL, DETAILFRAME); - SetVertexShaderTextureScaledTransform(VertexShaderConst.ShaderSpecificConst4, (int)ShaderMaterialVars.BaseTextureTransform, DETAILSCALE); - } + if (vars[EMISSIVEBLENDENABLED].GetIntValue() != 0) { - SetAmbientCubeDynamicStateVertexShader(); - shaderAPI.GetLightState(out LightState lightState); + } - DynamicShaderIndex vshIndex = new(shaderAPI, ShaderType.Vertex); - vshIndex.Set("DYNAMIC_LIGHT", lightState.HasDynamicLight()); - vshIndex.Set("STATIC_LIGHT", lightState.StaticLightVertex); - vshIndex.Set("NUM_LIGHTS", lightState.NumLights); - shaderAPI.SetVertexShaderIndex(vshIndex.GetIndex()); - SetModulationPixelShaderDynamicState(3); - EnablePixelShaderOverbright(0, true, true); - SetPixelShaderConstant(1, SELFILLUMTINT); + if (vars[FLESHINTERIORENABLED].GetIntValue() != 0) { - // TODO: Set skinning, etc } + } - Draw(); + private void SetupVars(ref VertexLitGeneric_Vars info) { + info.BaseTexture = (int)ShaderMaterialVars.BaseTexture; + info.Wrinkle = COMPRESS; + info.Stretch = STRETCH; + info.BaseTextureFrame = (int)ShaderMaterialVars.Frame; + info.BaseTextureTransform = (int)ShaderMaterialVars.BaseTextureTransform; + info.Albedo = ALBEDO; + info.SelfIllumTint = SELFILLUMTINT; + info.Detail = DETAIL; + info.DetailFrame = DETAILFRAME; + info.DetailScale = DETAILSCALE; + info.Envmap = ENVMAP; + info.EnvmapFrame = ENVMAPFRAME; + info.EnvmapMask = ENVMAPMASK; + info.EnvmapMaskFrame = ENVMAPMASKFRAME; + info.EnvmapMaskTransform = ENVMAPMASKTRANSFORM; + info.EnvmapTint = ENVMAPTINT; + info.Bumpmap = BUMPMAP; + info.NormalWrinkle = BUMPCOMPRESS; + info.NormalStretch = BUMPSTRETCH; + info.BumpFrame = BUMPFRAME; + info.BumpTransform = BUMPTRANSFORM; + info.EnvmapContrast = ENVMAPCONTRAST; + info.EnvmapSaturation = ENVMAPSATURATION; + info.AlphaTestReference = ALPHATESTREFERENCE; + info.FlashlightNoLambert = FLASHLIGHTNOLAMBERT; + info.FlashlightTexture = (int)ShaderMaterialVars.FlashLightTexture; + info.FlashlightTextureFrame = (int)ShaderMaterialVars.FlashLightTextureFrame; + info.SelfIllumEnvMapMask_Alpha = SELFILLUM_ENVMAPMASK_ALPHA; + info.SelfIllumFresnel = SELFILLUMFRESNEL; + info.SelfIllumFresnelMinMaxExp = SELFILLUMFRESNELMINMAXEXP; + info.AmbientOnly = AMBIENTONLY; + info.PhongExponent = PHONGEXPONENT; + info.PhongExponentTexture = PHONGEXPONENTTEXTURE; + info.PhongTint = PHONGTINT; + info.PhongAlbedoTint = PHONGALBEDOTINT; + info.DiffuseWarpTexture = LIGHTWARPTEXTURE; + info.PhongWarpTexture = PHONGWARPTEXTURE; + info.PhongBoost = PHONGBOOST; + info.PhongFresnelRanges = PHONGFRESNELRANGES; + info.Phong = PHONG; + info.BaseMapAlphaPhongMask = BASEMAPALPHAPHONGMASK; + info.EnvmapFresnel = ENVMAPFRESNEL; + info.DetailTextureCombineMode = DETAILBLENDMODE; + info.DetailTextureBlendFactor = DETAILBLENDFACTOR; + info.DetailTextureTransform = DETAILTEXTURETRANSFORM; + info.RimLight = RIMLIGHT; + info.RimLightPower = RIMLIGHTEXPONENT; + info.RimLightBoost = RIMLIGHTBOOST; + info.RimMask = RIMMASK; + info.SeamlessScale = SEAMLESS_SCALE; + info.SeamlessDetail = SEAMLESS_DETAIL; + info.SeamlessBase = SEAMLESS_BASE; + info.SeparateDetailUVs = SEPARATEDETAILUVS; + info.LinearWrite = LINEARWRITE; + info.DetailTint = DETAILTINT; + info.InvertPhongMask = INVERTPHONGMASK; + info.DepthBlend = DEPTHBLEND; + info.DepthBlendScale = DEPTHBLENDSCALE; + info.SelfIllumMask = SELFILLUMMASK; + info.BlendTintByBaseAlpha = BLENDTINTBYBASEALPHA; + info.TintReplacesBaseColor = BLENDTINTCOLOROVERBASE; } } + + +struct VertexLitGeneric_Vars +{ + public VertexLitGeneric_Vars() => memset(MemoryMarshal.AsBytes(new Span(ref this)), (byte)0xFF); + + public int BaseTexture; + public int Wrinkle; + public int Stretch; + public int BaseTextureFrame; + public int BaseTextureTransform; + public int Albedo; + public int Detail; + public int DetailFrame; + public int DetailScale; + public int Envmap; + public int EnvmapFrame; + public int EnvmapMask; + public int EnvmapMaskFrame; + public int EnvmapMaskTransform; + public int EnvmapTint; + public int Bumpmap; + public int NormalWrinkle; + public int NormalStretch; + public int BumpFrame; + public int BumpTransform; + public int EnvmapContrast; + public int EnvmapSaturation; + public int AlphaTestReference; + public int VertexAlphaTest; + public int FlashlightNoLambert; + public int FlashlightTexture; + public int FlashlightTextureFrame; + public int SelfIllumTint; + public int SelfIllumFresnel; + public int SelfIllumFresnelMinMaxExp; + public int PhongExponent; + public int PhongTint; + public int PhongAlbedoTint; + public int PhongExponentTexture; + public int DiffuseWarpTexture; + public int PhongWarpTexture; + public int PhongBoost; + public int PhongFresnelRanges; + public int SelfIllumEnvMapMask_Alpha; + public int AmbientOnly; + public int HDRColorScale; + public int Phong; + public int BaseMapAlphaPhongMask; + public int EnvmapFresnel; + public int DetailTextureCombineMode; + public int DetailTextureBlendFactor; + public int RimLight; + public int RimLightPower; + public int RimLightBoost; + public int RimMask; + public int SeamlessScale; + public int SeamlessBase; + public int SeamlessDetail; + public int DistanceAlpha; + public int DistanceAlphaFromDetail; + public int SoftEdges; + public int EdgeSoftnessStart; + public int EdgeSoftnessEnd; + public int ScaleEdgeSoftnessBasedOnScreenRes; + public int Glow; + public int GlowColor; + public int GlowAlpha; + public int GlowStart; + public int GlowEnd; + public int GlowX; + public int GlowY; + public int Outline; + public int OutlineColor; + public int OutlineAlpha; + public int OutlineStart0; + public int OutlineStart1; + public int OutlineEnd0; + public int OutlineEnd1; + public int ScaleOutlineSoftnessBasedOnScreenRes; + public int SeparateDetailUVs; + public int DetailTextureTransform; + public int LinearWrite; + public int GammaColorRead; + public int DetailTint; + public int InvertPhongMask; + public int DepthBlend; + public int DepthBlendScale; + public int SelfIllumMask; + public int ReceiveFlashlight; + public int BlendTintByBaseAlpha; + public int TintReplacesBaseColor; +}; \ No newline at end of file diff --git a/Source.StdShader.Gl46/VertexLitGenericHelper.cs b/Source.StdShader.Gl46/VertexLitGenericHelper.cs new file mode 100644 index 00000000..2e82c397 --- /dev/null +++ b/Source.StdShader.Gl46/VertexLitGenericHelper.cs @@ -0,0 +1,1014 @@ +using Source.Common; +using Source.Common.Commands; +using Source.Common.MaterialSystem; +using Source.Common.ShaderAPI; +using Source.Common.ShaderLib; + +using System.Numerics; + +namespace Source.StdShader.Gl46; + +public partial class BaseVSShader +{ + internal readonly static ConVar mat_fullbright = new("mat_fullbright", "0", FCvar.Cheat); + internal readonly static ConVar r_lightwarpidentity = new("r_lightwarpidentity", "0", FCvar.Cheat); + + private static bool WantsSkinShader(IMaterialVar[] parms, ref VertexLitGeneric_Vars info) { + if (info.Phong == -1) + return false; + + if (parms[info.Phong].GetIntValue() == 0) + return false; + + if ((info.DiffuseWarpTexture != -1) && parms[info.DiffuseWarpTexture].IsTexture()) + return true; + + if ((info.BaseMapAlphaPhongMask != -1) && parms[info.BaseMapAlphaPhongMask].GetIntValue() != 1) { + if (info.Bumpmap == -1) + return false; + + if (!parms[info.Bumpmap].IsTexture()) + return false; + } + return true; + } + + internal void InitParamsVertexLitGeneric(BaseVSShader shader, IMaterialVar[] parms, ReadOnlySpan materialName, bool vertexLitGeneric, ref VertexLitGeneric_Vars info) { + InitIntParam(info.Phong, parms, 0); + + InitFloatParam(info.AlphaTestReference, parms, 0.0f); + InitIntParam(info.VertexAlphaTest, parms, 0); + + InitIntParam(info.FlashlightNoLambert, parms, 0); + + if (info.DetailTint != -1 && !parms[info.DetailTint].IsDefined()) + parms[info.DetailTint].SetVecValue(1.0f, 1.0f, 1.0f); + + if (info.EnvmapTint != -1 && !parms[info.EnvmapTint].IsDefined()) + parms[info.EnvmapTint].SetVecValue(1.0f, 1.0f, 1.0f); + + InitIntParam(info.EnvmapFrame, parms, 0); + InitIntParam(info.BumpFrame, parms, 0); + InitFloatParam(info.DetailTextureBlendFactor, parms, 1.0f); + InitIntParam(info.ReceiveFlashlight, parms, 0); + + InitFloatParam(info.DetailScale, parms, 4.0f); + + if ((info.BlendTintByBaseAlpha != -1) && (!parms[info.BlendTintByBaseAlpha].IsDefined())) + parms[info.BlendTintByBaseAlpha].SetIntValue(0); + + InitFloatParam(info.TintReplacesBaseColor, parms, 0); + + if ((info.SelfIllumTint != -1) && (!parms[info.SelfIllumTint].IsDefined())) + parms[info.SelfIllumTint].SetVecValue(1.0f, 1.0f, 1.0f); + + if (WantsSkinShader(parms, ref info)) { + if (!HardwareConfig.SupportsPixelShaders_2_b() || !Config.UsePhong()) + parms[info.Phong].SetIntValue(0); + else { + InitParamsSkin(shader, parms, materialName, ref info); + return; + } + } + + if (info.FlashlightTexture != -1) { + if (HardwareConfig.SupportsBorderColor()) + parms[(int)ShaderMaterialVars.FlashLightTexture].SetStringValue("effects/flashlight_border"); + else + parms[(int)ShaderMaterialVars.FlashLightTexture].SetStringValue("effects/flashlight001"); + } + + if (info.Albedo != -1 && Config.UseBumpmapping() && info.Bumpmap != -1 && parms[info.Bumpmap].IsDefined() && parms[info.Albedo].IsDefined() && + parms[info.BaseTexture].IsDefined()) { + parms[info.BaseTexture].SetStringValue(parms[info.Albedo].GetStringValue()); + } + + SetFlags2(parms, MaterialVarFlags2.SupportsHardwareSkinning); + + if (vertexLitGeneric) + SetFlags2(parms, MaterialVarFlags2.LightingVertexLit); + else + ClearFlags(parms, MaterialVarFlags.SelfIllum); + + InitIntParam(info.EnvmapMaskFrame, parms, 0); + InitFloatParam(info.EnvmapContrast, parms, 0.0f); + InitFloatParam(info.EnvmapSaturation, parms, 1.0f); + InitFloatParam(info.SeamlessScale, parms, 0.0f); + InitFloatParam(info.EdgeSoftnessStart, parms, 0.5f); + InitFloatParam(info.EdgeSoftnessEnd, parms, 0.5f); + InitFloatParam(info.GlowAlpha, parms, 1.0f); + InitFloatParam(info.OutlineAlpha, parms, 1.0f); + + if (info.BaseTexture != -1 && !parms[info.BaseTexture].IsDefined()) { + ClearFlags(parms, MaterialVarFlags.SelfIllum); + ClearFlags(parms, MaterialVarFlags.BaseAlphaEnvMapMask); + } + + if (IsFlagSet(parms, MaterialVarFlags.Decal)) + SetFlags(parms, MaterialVarFlags.NoDebugOverride); + + if ((info.Bumpmap != -1) && Config.UseBumpmapping() && parms[info.Bumpmap].IsDefined()) + SetFlags2(parms, MaterialVarFlags2.NeedsTangentSpaces); + else if ((info.DiffuseWarpTexture != -1) && parms[info.DiffuseWarpTexture].IsDefined()) + SetFlags2(parms, MaterialVarFlags2.NeedsTangentSpaces); + else + ClearFlags(parms, MaterialVarFlags.NormalMapAlphaEnvMapMask); + + bool hasNormalMapAlphaEnvmapMask = IsFlagSet(parms, MaterialVarFlags.NormalMapAlphaEnvMapMask); + if (hasNormalMapAlphaEnvmapMask) { + parms[info.EnvmapMask].SetUndefined(); + ClearFlags(parms, MaterialVarFlags.BaseAlphaEnvMapMask); + } + + if (IsFlagSet(parms, MaterialVarFlags.BaseAlphaEnvMapMask) && info.Bumpmap != -1 && + parms[info.Bumpmap].IsDefined() && !hasNormalMapAlphaEnvmapMask) { + Warning($"material {materialName} has a normal map and $basealphaenvmapmask. Must use $normalmapalphaenvmapmask to get specular.\n\n"); + parms[info.Envmap].SetUndefined(); + } + + if (info.EnvmapMask != -1 && parms[info.EnvmapMask].IsDefined() && info.Bumpmap != -1 && parms[info.Bumpmap].IsDefined()) { + parms[info.EnvmapMask].SetUndefined(); + if (!hasNormalMapAlphaEnvmapMask) { + Warning($"material {materialName} has a normal map and an envmapmask. Must use $normalmapalphaenvmapmask.\n\n"); + parms[info.Envmap].SetUndefined(); + } + } + + if (!Config.UseSpecular() && info.Envmap != -1 && parms[info.Envmap].IsDefined() && parms[info.BaseTexture].IsDefined()) + parms[info.Envmap].SetUndefined(); + + InitFloatParam(info.HDRColorScale, parms, 1.0f); + + InitIntParam(info.LinearWrite, parms, 0); + InitIntParam(info.GammaColorRead, parms, 0); + + InitIntParam(info.DepthBlend, parms, 0); + InitFloatParam(info.DepthBlendScale, parms, 50.0f); + } + + internal void DrawVertexLitGeneric(BaseVSShader shader, IMaterialVar[] parms, IShaderDynamicAPI? shaderAPI, IShaderShadow? shaderShadow, bool vertexLitGeneric, ref VertexLitGeneric_Vars info, VertexCompressionType vertexCompression, ref BasePerMaterialContextData? contextData) { + if (WantsSkinShader(parms, ref info) && HardwareConfig.SupportsPixelShaders_2_b() && Config.UseBumpmapping() && Config.UsePhong()) { + DrawSkin(shader, parms, shaderAPI, shaderShadow, ref info, vertexCompression, ref contextData); + return; + } + + bool receiveFlashlight = vertexLitGeneric; + bool hasFlashlight = receiveFlashlight && shader.UsingFlashlight(parms); + + DrawVertexLitGeneric_Internal(shader, parms, shaderAPI, shaderShadow, vertexLitGeneric, hasFlashlight, ref info, vertexCompression, ref contextData); + } + + private void DrawVertexLitGeneric_Internal(BaseVSShader shader, IMaterialVar[] parms, IShaderDynamicAPI? shaderAPI, IShaderShadow? shaderShadow, bool vertexLitGeneric, bool hasFlashlight, ref VertexLitGeneric_Vars info, VertexCompressionType vertexCompression, ref BasePerMaterialContextData? context) { + VertexLitGeneric_Context? contextData = context as VertexLitGeneric_Context; + + bool hasBump = IsTextureSet(info.Bumpmap, parms); + bool isDecal = IsFlagSet(parms, MaterialVarFlags.Decal); + bool hasDiffuseLighting = vertexLitGeneric; + + if (IsFlagSet(parms, MaterialVarFlags.EnvMapSphere)) + hasFlashlight = false; + + bool isAlphaTested = IsFlagSet(parms, MaterialVarFlags.AlphaTest) != false; + bool hasDiffuseWarp = !hasFlashlight && hasDiffuseLighting && (info.DiffuseWarpTexture != -1) && parms[info.DiffuseWarpTexture].IsTexture(); + + bool flashlightNoLambert = false; + if ((info.FlashlightNoLambert != -1) && parms[info.FlashlightNoLambert].GetIntValue() != 0) + flashlightNoLambert = true; + + bool ambientOnly = IsBoolSet(info.AmbientOnly, parms); + + float blendFactor = GetFloatParam(info.DetailTextureBlendFactor, parms, 1.0f); + bool hasDetailTexture = IsTextureSet(info.Detail, parms); + int detailBlendMode = hasDetailTexture ? GetIntParam(info.DetailTextureCombineMode, parms) : 0; + int detailTranslucencyTexture = -1; + + if (hasDetailTexture) { + if ((detailBlendMode == 6) && (!HardwareConfig.SupportsPixelShaders_2_b())) + detailBlendMode = 5; + + if ((detailBlendMode == 3) || (detailBlendMode == 8) || (detailBlendMode == 9)) + detailTranslucencyTexture = info.Detail; + } + + bool blendTintByBaseAlpha = IsBoolSet(info.BlendTintByBaseAlpha, parms); + float fTintReplaceFactor = GetFloatParam(info.TintReplacesBaseColor, parms, 0.0f); + + BlendType blendType; + bool hasBaseTexture = IsTextureSet(info.BaseTexture, parms); + if (hasBaseTexture) + blendType = shader.EvaluateBlendRequirements(blendTintByBaseAlpha ? -1 : info.BaseTexture, true, detailTranslucencyTexture); + else + blendType = shader.EvaluateBlendRequirements(info.EnvmapMask, false); + + bool fullyOpaque = (blendType != BlendType.Add) && (blendType != BlendType.Blend) && !isAlphaTested && !hasFlashlight; + bool hasEnvmap = !hasFlashlight && info.Envmap != -1 && parms[info.Envmap].IsTexture(); + + bool hasVertexColor = !vertexLitGeneric && IsFlagSet(parms, MaterialVarFlags.VertexColor); + bool hasVertexAlpha = !vertexLitGeneric && IsFlagSet(parms, MaterialVarFlags.VertexAlpha); + + if (shader.IsSnapshotting() || contextData == null || contextData.MaterialVarsChanged) { + bool seamlessBase = IsBoolSet(info.SeamlessBase, parms); + bool seamlessDetail = IsBoolSet(info.SeamlessDetail, parms); + bool distanceAlpha = IsBoolSet(info.DistanceAlpha, parms); + bool hasSelfIllum = (!hasFlashlight) && IsFlagSet(parms, MaterialVarFlags.SelfIllum); + bool hasEnvmapMask = (!hasFlashlight) && info.EnvmapMask != -1 && parms[info.EnvmapMask].IsTexture(); + bool hasSelfIllumFresnel = (!IsTextureSet(info.Detail, parms)) && (hasSelfIllum) && (info.SelfIllumFresnel != -1) && (parms[info.SelfIllumFresnel].GetIntValue() != 0); + + bool hasSelfIllumMask = hasSelfIllum && IsTextureSet(info.SelfIllumMask, parms); + bool hasSelfIllumInEnvMapMask = + (info.SelfIllumEnvMapMask_Alpha != -1) && + (parms[info.SelfIllumEnvMapMask_Alpha].GetFloatValue() != 0.0); + + if (shader.IsSnapshotting()) { + bool hasBaseAlphaEnvmapMask = IsFlagSet(parms, MaterialVarFlags.BaseAlphaEnvMapMask); + bool hasNormalMapAlphaEnvmapMask = IsFlagSet(parms, MaterialVarFlags.NormalMapAlphaEnvMapMask); + + + if (info.VertexAlphaTest != -1 && parms[info.VertexAlphaTest].GetIntValue() > 0) + hasVertexAlpha = true; + + if (hasSelfIllumFresnel) { + ClearFlags(parms, MaterialVarFlags.NormalMapAlphaEnvMapMask); + hasNormalMapAlphaEnvmapMask = false; + } + + // bool hasEnvmap = (!hasFlashlight) && (info.Envmap != -1) && parms[info.Envmap].IsTexture(); + bool hasLegacyEnvSphereMap = hasEnvmap && IsFlagSet(parms, MaterialVarFlags.EnvMapSphere); + bool hasNormal = vertexLitGeneric || hasEnvmap || hasFlashlight || seamlessBase || seamlessDetail; + if (IsPC()) + hasNormal = true; + + bool halfLambert = IsFlagSet(parms, MaterialVarFlags.HalfLambert); + shaderShadow!.EnableAlphaTest(isAlphaTested); + + if (info.AlphaTestReference != -1 && parms[info.AlphaTestReference].GetFloatValue() > 0.0f) + shaderShadow.AlphaFunc(ShaderAlphaFunc.GreaterEqual, parms[info.AlphaTestReference].GetFloatValue()); + + int shadowFilterMode = 0; + if (hasFlashlight) { + if (HardwareConfig.SupportsPixelShaders_2_b()) + shadowFilterMode = HardwareConfig.GetShadowFilterMode(); + + if (parms[info.BaseTexture].IsTexture()) + shader.SetAdditiveBlendingShadowState(info.BaseTexture, true); + else + shader.SetAdditiveBlendingShadowState(info.EnvmapMask, false); + + if (isAlphaTested) { + shaderShadow.EnableAlphaTest(false); + shaderShadow.DepthFunc(ShaderDepthFunc.Equal); + } + + shaderShadow.EnableAlphaWrites(false); + shaderShadow.EnableBlending(true); + shaderShadow.EnableDepthWrites(false); + } + else + shader.SetBlendingShadowState(blendType); + + VertexFormat flags = VertexFormat.Position; + if (hasNormal) + flags |= VertexFormat.Normal; + + int userDataSize = 0; + bool bSRGBInputAdapter = false; + + shaderShadow.EnableTexture(Sampler.Sampler0, true); + if (hasBaseTexture) { + if ((info.GammaColorRead != -1) && (parms[info.GammaColorRead].GetIntValue() == 1)) + shaderShadow.EnableSRGBRead(Sampler.Sampler0, false); + else + shaderShadow.EnableSRGBRead(Sampler.Sampler0, true); + + if (IsOSX() && !HardwareConfig.CanDoSRGBReadFromRTs()) { + ITexture? baseTexture = parms[info.BaseTexture].GetTextureValue(); + if (baseTexture != null && baseTexture.IsRenderTarget()) + bSRGBInputAdapter = true; + } + } + + if (hasEnvmap) { + shaderShadow.EnableTexture(Sampler.Sampler1, true); + if (HardwareConfig.GetHDRType() == HDRType.None) + shaderShadow.EnableSRGBRead(Sampler.Sampler1, true); + } + if (hasFlashlight) { + shaderShadow.EnableTexture(Sampler.Sampler8, true); + shaderShadow.SetShadowDepthFiltering(Sampler.Sampler8); + shaderShadow.EnableTexture(Sampler.Sampler6, true); + shaderShadow.EnableTexture(Sampler.Sampler7, true); + shaderShadow.EnableSRGBRead(Sampler.Sampler7, true); + userDataSize = 4; + } + + if (hasDetailTexture) { + shaderShadow.EnableTexture(Sampler.Sampler2, true); + if (detailBlendMode != 0) + shaderShadow.EnableSRGBRead(Sampler.Sampler2, true); + } + + if (hasBump || hasDiffuseWarp) { + shaderShadow.EnableTexture(Sampler.Sampler3, true); + userDataSize = 4; + shaderShadow.EnableTexture(Sampler.Sampler5, true); + } + if (hasEnvmapMask) + shaderShadow.EnableTexture(Sampler.Sampler4, true); + + if (hasVertexColor || hasVertexAlpha) + flags |= VertexFormat.Color; + + if (hasDiffuseWarp && (!hasFlashlight) && !hasSelfIllumFresnel) + shaderShadow.EnableTexture(Sampler.Sampler9, true); + + if ((info.DepthBlend != -1) && (parms[info.DepthBlend].GetIntValue() != 0)) { + if (hasBump) + Warning("DEPTHBLEND not supported by bump mapped variations of vertexlitgeneric to avoid shader bloat. Either remove the bump map or convince a graphics programmer that it's worth it.\n"); + + shaderShadow.EnableTexture(Sampler.Sampler10, true); + } + + if (hasSelfIllum) + shaderShadow.EnableTexture(Sampler.Sampler11, true); + + bool bSRGBWrite = true; + if ((info.LinearWrite != -1) && (parms[info.LinearWrite].GetIntValue() == 1)) + bSRGBWrite = false; + + shaderShadow.EnableSRGBWrite(bSRGBWrite); + + Span pTexCoordDim = [2, 2, 3]; + int nTexCoordCount = 1; + + if (IsBoolSet(info.SeparateDetailUVs, parms)) + ++nTexCoordCount; + else + pTexCoordDim[1] = 0; + + if (isDecal && HardwareConfig.HasFastVertexTextures()) + nTexCoordCount = 3; + + // flags |= VERTEX_FORMAT_COMPRESSED; todo? + + shaderShadow.VertexShaderVertexFormat(flags, nTexCoordCount, pTexCoordDim, userDataSize); + + if (hasBump || hasDiffuseWarp) { + if (!HardwareConfig.HasFastVertexTextures()) { + bool useStaticControlFlow = HardwareConfig.SupportsStaticControlFlow(); + + StaticShaderIndex vshIndex = new(shaderShadow, ShaderType.Vertex, "vertexlitgeneric_bump"); + vshIndex.Set("HALFLAMBERT", halfLambert); + vshIndex.Set("USE_WITH_2B", HardwareConfig.SupportsPixelShaders_2_b()); + vshIndex.Set("USE_STATIC_CONTROL_FLOW", useStaticControlFlow); + shaderShadow.SetVertexShader("vertexlitgeneric_bump", vshIndex.GetIndex()); + + if (HardwareConfig.SupportsPixelShaders_2_b() || HardwareConfig.ShouldAlwaysUseShaderModel2bShaders()) { + StaticShaderIndex pshIndex = new(shaderShadow, ShaderType.Pixel, "vertexlitgeneric_bump"); + pshIndex.Set("CUBEMAP", hasEnvmap); + pshIndex.Set("DIFFUSELIGHTING", hasDiffuseLighting); + pshIndex.Set("LIGHTWARPTEXTURE", hasDiffuseWarp && !hasSelfIllumFresnel); + pshIndex.Set("SELFILLUM", hasSelfIllum); + pshIndex.Set("SELFILLUMFRESNEL", hasSelfIllumFresnel); + pshIndex.Set("NORMALMAPALPHAENVMAPMASK", hasNormalMapAlphaEnvmapMask && hasEnvmap); + pshIndex.Set("HALFLAMBERT", halfLambert); + pshIndex.Set("FLASHLIGHT", hasFlashlight); + pshIndex.Set("DETAILTEXTURE", hasDetailTexture); + pshIndex.Set("DETAIL_BLEND_MODE", detailBlendMode); + pshIndex.Set("FLASHLIGHTDEPTHFILTERMODE", shadowFilterMode); + pshIndex.Set("BLENDTINTBYBASEALPHA", blendTintByBaseAlpha); + shaderShadow.SetPixelShader("vertexlitgeneric_bump", pshIndex.GetIndex()); + } + else { + StaticShaderIndex pshIndex = new(shaderShadow, ShaderType.Pixel, "vertexlitgeneric_bump"); + pshIndex.Set("CUBEMAP", hasEnvmap); + pshIndex.Set("DIFFUSELIGHTING", hasDiffuseLighting); + pshIndex.Set("LIGHTWARPTEXTURE", hasDiffuseWarp && !hasSelfIllumFresnel); + pshIndex.Set("SELFILLUM", hasSelfIllum); + pshIndex.Set("SELFILLUMFRESNEL", hasSelfIllumFresnel); + pshIndex.Set("NORMALMAPALPHAENVMAPMASK", hasNormalMapAlphaEnvmapMask && hasEnvmap); + pshIndex.Set("HALFLAMBERT", halfLambert); + pshIndex.Set("FLASHLIGHT", hasFlashlight); + pshIndex.Set("DETAILTEXTURE", hasDetailTexture); + pshIndex.Set("DETAIL_BLEND_MODE", detailBlendMode); + pshIndex.Set("BLENDTINTBYBASEALPHA", blendTintByBaseAlpha); + shaderShadow.SetPixelShader("vertexlitgeneric_bump", pshIndex.GetIndex()); + } + } + else { + SetFlags2(parms, MaterialVarFlags2.UsesVertexID); + + StaticShaderIndex vshIndex = new(shaderShadow, ShaderType.Vertex, "vertexlitgeneric_bump"); + vshIndex.Set("HALFLAMBERT", halfLambert); + vshIndex.Set("USE_WITH_2B", true); + vshIndex.Set("DECAL", isDecal); + shaderShadow.SetVertexShader("vertexlitgeneric_bump", vshIndex.GetIndex()); + + StaticShaderIndex pshIndex = new(shaderShadow, ShaderType.Pixel, "vertexlitgeneric_bump"); + pshIndex.Set("CUBEMAP", hasEnvmap); + pshIndex.Set("DIFFUSELIGHTING", hasDiffuseLighting); + pshIndex.Set("LIGHTWARPTEXTURE", hasDiffuseWarp && !hasSelfIllumFresnel); + pshIndex.Set("SELFILLUM", hasSelfIllum); + pshIndex.Set("SELFILLUMFRESNEL", hasSelfIllumFresnel); + pshIndex.Set("NORMALMAPALPHAENVMAPMASK", hasNormalMapAlphaEnvmapMask && hasEnvmap); + pshIndex.Set("HALFLAMBERT", halfLambert); + pshIndex.Set("FLASHLIGHT", hasFlashlight); + pshIndex.Set("DETAILTEXTURE", hasDetailTexture); + pshIndex.Set("DETAIL_BLEND_MODE", detailBlendMode); + pshIndex.Set("FLASHLIGHTDEPTHFILTERMODE", shadowFilterMode); + pshIndex.Set("BLENDTINTBYBASEALPHA", blendTintByBaseAlpha); + shaderShadow.SetPixelShader("vertexlitgeneric_bump", pshIndex.GetIndex()); + } + } + else { + bool distanceAlphaFromDetail = false; + bool softMask = false; + bool bGlow = false; + bool outline = false; + + bool doDepthBlend = IsBoolSet(info.DepthBlend, parms) && !mat_reduceparticles.GetBool(); + + if (distanceAlpha) { + distanceAlphaFromDetail = IsBoolSet(info.DistanceAlphaFromDetail, parms); + softMask = IsBoolSet(info.SoftEdges, parms); + bGlow = IsBoolSet(info.Glow, parms); + outline = IsBoolSet(info.Outline, parms); + } + + if (!HardwareConfig.HasFastVertexTextures()) { + bool useStaticControlFlow = HardwareConfig.SupportsStaticControlFlow(); + + StaticShaderIndex vshIndex = new(shaderShadow, ShaderType.Vertex, "vertexlitgeneric"); + vshIndex.Set("VERTEXCOLOR", hasVertexColor || hasVertexAlpha); + vshIndex.Set("CUBEMAP", hasEnvmap); + vshIndex.Set("HALFLAMBERT", halfLambert); + vshIndex.Set("FLASHLIGHT", hasFlashlight); + vshIndex.Set("SEAMLESS_BASE", seamlessBase); + vshIndex.Set("SEAMLESS_DETAIL", seamlessDetail); + vshIndex.Set("SEPARATE_DETAIL_UVS", IsBoolSet(info.SeparateDetailUVs, parms)); + vshIndex.Set("USE_STATIC_CONTROL_FLOW", useStaticControlFlow); + vshIndex.Set("DONT_GAMMA_CONVERT_VERTEX_COLOR", (!bSRGBWrite) && hasVertexColor); + shaderShadow.SetVertexShader("vertexlitgeneric", vshIndex.GetIndex()); + + if (HardwareConfig.SupportsPixelShaders_2_b() || HardwareConfig.ShouldAlwaysUseShaderModel2bShaders()) { + StaticShaderIndex pshIndex = new(shaderShadow, ShaderType.Pixel, "vertexlitgeneric"); + pshIndex.Set("SELFILLUM_ENVMAPMASK_ALPHA", hasSelfIllumInEnvMapMask && hasEnvmapMask); + pshIndex.Set("CUBEMAP", hasEnvmap); + pshIndex.Set("CUBEMAP_SPHERE_LEGACY", hasLegacyEnvSphereMap); + pshIndex.Set("DIFFUSELIGHTING", hasDiffuseLighting); + pshIndex.Set("ENVMAPMASK", hasEnvmapMask); + pshIndex.Set("BASEALPHAENVMAPMASK", hasBaseAlphaEnvmapMask); + pshIndex.Set("SELFILLUM", hasSelfIllum); + pshIndex.Set("VERTEXCOLOR", hasVertexColor); + pshIndex.Set("FLASHLIGHT", hasFlashlight); + pshIndex.Set("DETAILTEXTURE", hasDetailTexture); + pshIndex.Set("DETAIL_BLEND_MODE", detailBlendMode); + pshIndex.Set("SEAMLESS_BASE", seamlessBase); + pshIndex.Set("SEAMLESS_DETAIL", seamlessDetail); + pshIndex.Set("DISTANCEALPHA", distanceAlpha); + pshIndex.Set("DISTANCEALPHAFROMDETAIL", distanceAlphaFromDetail); + pshIndex.Set("SOFT_MASK", softMask); + pshIndex.Set("OUTLINE", outline); + pshIndex.Set("OUTER_GLOW", bGlow); + pshIndex.Set("FLASHLIGHTDEPTHFILTERMODE", shadowFilterMode); + pshIndex.Set("DEPTHBLEND", doDepthBlend); + pshIndex.Set("SRGB_INPUT_ADAPTER", bSRGBInputAdapter ? 1 : 0); + pshIndex.Set("BLENDTINTBYBASEALPHA", blendTintByBaseAlpha); + shaderShadow.SetPixelShader("vertexlitgeneric", pshIndex.GetIndex()); + } + else { + StaticShaderIndex pshIndex = new(shaderShadow, ShaderType.Pixel, "vertexlitgeneric"); + pshIndex.Set("SELFILLUM_ENVMAPMASK_ALPHA", hasSelfIllumInEnvMapMask && hasEnvmapMask); + pshIndex.Set("CUBEMAP", hasEnvmap); + pshIndex.Set("CUBEMAP_SPHERE_LEGACY", hasLegacyEnvSphereMap); + pshIndex.Set("DIFFUSELIGHTING", hasDiffuseLighting); + pshIndex.Set("ENVMAPMASK", hasEnvmapMask); + pshIndex.Set("BASEALPHAENVMAPMASK", hasBaseAlphaEnvmapMask); + pshIndex.Set("SELFILLUM", hasSelfIllum); + pshIndex.Set("VERTEXCOLOR", hasVertexColor); + pshIndex.Set("FLASHLIGHT", hasFlashlight); + pshIndex.Set("DETAILTEXTURE", hasDetailTexture); + pshIndex.Set("DETAIL_BLEND_MODE", detailBlendMode); + pshIndex.Set("SEAMLESS_BASE", seamlessBase); + pshIndex.Set("SEAMLESS_DETAIL", seamlessDetail); + pshIndex.Set("DISTANCEALPHA", distanceAlpha); + pshIndex.Set("DISTANCEALPHAFROMDETAIL", distanceAlphaFromDetail); + pshIndex.Set("SOFT_MASK", softMask); + pshIndex.Set("OUTLINE", outline); + pshIndex.Set("OUTER_GLOW", bGlow); + pshIndex.Set("BLENDTINTBYBASEALPHA", blendTintByBaseAlpha); + shaderShadow.SetPixelShader("vertexlitgeneric", pshIndex.GetIndex()); + } + } + else { + SetFlags2(parms, MaterialVarFlags2.UsesVertexID); + + StaticShaderIndex vshIndex = new(shaderShadow, ShaderType.Vertex, "vertexlitgeneric"); + vshIndex.Set("VERTEXCOLOR", hasVertexColor || hasVertexAlpha); + vshIndex.Set("CUBEMAP", hasEnvmap); + vshIndex.Set("HALFLAMBERT", halfLambert); + vshIndex.Set("FLASHLIGHT", hasFlashlight); + vshIndex.Set("SEAMLESS_BASE", seamlessBase); + vshIndex.Set("SEAMLESS_DETAIL", seamlessDetail); + vshIndex.Set("SEPARATE_DETAIL_UVS", IsBoolSet(info.SeparateDetailUVs, parms)); + vshIndex.Set("DECAL", isDecal); + vshIndex.Set("DONT_GAMMA_CONVERT_VERTEX_COLOR", bSRGBWrite ? 0 : 1); + shaderShadow.SetVertexShader("vertexlitgeneric", vshIndex.GetIndex()); + + StaticShaderIndex pshIndex = new(shaderShadow, ShaderType.Pixel, "vertexlitgeneric"); + pshIndex.Set("SELFILLUM_ENVMAPMASK_ALPHA", hasSelfIllumInEnvMapMask && hasEnvmapMask); + pshIndex.Set("CUBEMAP", hasEnvmap); + pshIndex.Set("CUBEMAP_SPHERE_LEGACY", hasLegacyEnvSphereMap); + pshIndex.Set("DIFFUSELIGHTING", hasDiffuseLighting); + pshIndex.Set("ENVMAPMASK", hasEnvmapMask); + pshIndex.Set("BASEALPHAENVMAPMASK", hasBaseAlphaEnvmapMask); + pshIndex.Set("SELFILLUM", hasSelfIllum); + pshIndex.Set("VERTEXCOLOR", hasVertexColor); + pshIndex.Set("FLASHLIGHT", hasFlashlight); + pshIndex.Set("DETAILTEXTURE", hasDetailTexture); + pshIndex.Set("DETAIL_BLEND_MODE", detailBlendMode); + pshIndex.Set("SEAMLESS_BASE", seamlessBase); + pshIndex.Set("SEAMLESS_DETAIL", seamlessDetail); + pshIndex.Set("DISTANCEALPHA", distanceAlpha); + pshIndex.Set("DISTANCEALPHAFROMDETAIL", distanceAlphaFromDetail); + pshIndex.Set("SOFT_MASK", softMask); + pshIndex.Set("OUTLINE", outline); + pshIndex.Set("OUTER_GLOW", bGlow); + pshIndex.Set("FLASHLIGHTDEPTHFILTERMODE", shadowFilterMode); + pshIndex.Set("DEPTHBLEND", doDepthBlend); + pshIndex.Set("BLENDTINTBYBASEALPHA", blendTintByBaseAlpha); + shaderShadow.SetPixelShader("vertexlitgeneric", pshIndex.GetIndex()); + } + } + + // todo + // if (hasFlashlight) + // shader.FogToBlack(); + // else + // shader.DefaultFog(); + + shaderShadow.EnableAlphaWrites(fullyOpaque); + } + + if (shaderAPI != null && ((contextData == null) || contextData.MaterialVarsChanged)) { + if (contextData == null) { + contextData = new(); + context = contextData; + } + contextData.SemiStaticCmdsOut.Reset(); + contextData.SemiStaticCmdsOut.SetPixelShaderFogParams(21); + if (hasBaseTexture) + contextData.SemiStaticCmdsOut.BindTexture(shader, Sampler.Sampler0, info.BaseTexture, info.BaseTextureFrame); + else { + if (hasEnvmap) + contextData.SemiStaticCmdsOut.BindStandardTexture(Sampler.Sampler0, StandardTextureId.Black); + else + contextData.SemiStaticCmdsOut.BindStandardTexture(Sampler.Sampler0, StandardTextureId.White); + } + if (hasDetailTexture) + contextData.SemiStaticCmdsOut.BindTexture(shader, Sampler.Sampler2, info.Detail, info.DetailFrame); + if (hasSelfIllum) { + if (hasSelfIllumMask) + contextData.SemiStaticCmdsOut.BindTexture(shader, Sampler.Sampler11, info.SelfIllumMask, -1); + else + contextData.SemiStaticCmdsOut.BindStandardTexture(Sampler.Sampler11, StandardTextureId.Black); + } + + if ((info.DepthBlend != -1) && (parms[info.DepthBlend].GetIntValue() != 0)) + contextData.SemiStaticCmdsOut.BindStandardTexture(Sampler.Sampler10, StandardTextureId.FrameBufferFullDepth); + if (seamlessDetail || seamlessBase) { + Span flSeamlessData = [parms[info.SeamlessScale].GetFloatValue(), 0, 0, 0]; + contextData.SemiStaticCmdsOut.SetVertexShaderConstant(VertexShaderConst.ShaderSpecificConst2, flSeamlessData); + } + + if (info.BaseTextureTransform != -1) + contextData.SemiStaticCmdsOut.SetVertexShaderTextureTransform(VertexShaderConst.ShaderSpecificConst0, info.BaseTextureTransform); + + + if (hasDetailTexture) { + if (IsParamDefined(parms, info.DetailTextureTransform)) + contextData.SemiStaticCmdsOut.SetVertexShaderTextureScaledTransform(VertexShaderConst.ShaderSpecificConst4, info.DetailTextureTransform, info.DetailScale); + else + contextData.SemiStaticCmdsOut.SetVertexShaderTextureScaledTransform(VertexShaderConst.ShaderSpecificConst4, info.BaseTextureTransform, info.DetailScale); + if (info.DetailTint != -1) + contextData.SemiStaticCmdsOut.SetPixelShaderConstantGammaToLinear(10, info.DetailTint); + else + contextData.SemiStaticCmdsOut.SetPixelShaderConstant4(10, 1, 1, 1, 1); + } + + if (distanceAlpha) { + float softStart = GetFloatParam(info.EdgeSoftnessStart, parms); + float softEnd = GetFloatParam(info.EdgeSoftnessEnd, parms); + bool scaleEdges = IsBoolSet(info.ScaleEdgeSoftnessBasedOnScreenRes, parms); + bool scaleOutline = IsBoolSet(info.ScaleOutlineSoftnessBasedOnScreenRes, parms); + float resScale; + float outlineStart0 = GetFloatParam(info.OutlineStart0, parms); + float outlineStart1 = GetFloatParam(info.OutlineStart1, parms); + float outlineEnd0 = GetFloatParam(info.OutlineEnd0, parms); + float outlineEnd1 = GetFloatParam(info.OutlineEnd1, parms); + + if (scaleEdges || scaleOutline) { + shaderAPI.GetBackBufferDimensions(out int width, out int height); + resScale = Math.Max(0.5f, Math.Max(1024.0f / width, 768.0f / height)); + + if (scaleEdges) { + float mid = 0.5f * (softStart + softEnd); + softStart = Math.Clamp(mid + resScale * (softStart - mid), 0.05f, 0.99f); + softEnd = Math.Clamp(mid + resScale * (softEnd - mid), 0.05f, 0.99f); + } + + + if (scaleOutline) { + float midS = 0.5f * (outlineStart1 + outlineStart0); + outlineStart1 = Math.Clamp(midS + resScale * (outlineStart1 - midS), 0.05f, 0.99f); + float midE = 0.5f * (outlineEnd1 + outlineEnd0); + outlineEnd1 = Math.Clamp(midE + resScale * (outlineEnd1 - midE), 0.05f, 0.99f); + } + } + + Span consts = [ + GetFloatParam(info.GlowX, parms), + GetFloatParam(info.GlowY, parms), + GetFloatParam(info.GlowStart, parms), + GetFloatParam(info.GlowEnd, parms), + 0,0,0, + GetFloatParam(info.GlowAlpha, parms), + softStart, + softEnd, + 0,0, + 0,0,0, + GetFloatParam(info.OutlineAlpha, parms), + outlineStart0, + outlineEnd1, + outlineEnd0, + outlineStart1, + ]; + + if (info.GlowColor != -1) + parms[info.GlowColor].GetVecValue(consts.Slice(4, 3)); + if (info.OutlineColor != -1) + parms[info.OutlineColor].GetVecValue(consts.Slice(12, 3)); + contextData.SemiStaticCmdsOut.SetPixelShaderConstant(5, consts, 5); + + } + if (!Config.FastNoBump) { + if (hasBump) + contextData.SemiStaticCmdsOut.BindTexture(shader, Sampler.Sampler3, info.Bumpmap, info.BumpFrame); + else if (hasDiffuseWarp) + contextData.SemiStaticCmdsOut.BindStandardTexture(Sampler.Sampler3, StandardTextureId.NormalMapFlat); + } + else { + if (hasBump) + contextData.SemiStaticCmdsOut.BindStandardTexture(Sampler.Sampler3, StandardTextureId.NormalMapFlat); + } + + Span envMapSaturation_SelfIllumMask = [1.0f, 1.0f, 1.0f, 0.0f]; + if (info.EnvmapSaturation != -1) + parms[info.EnvmapSaturation].GetVecValue(envMapSaturation_SelfIllumMask); + + envMapSaturation_SelfIllumMask[3] = hasSelfIllumMask ? 1.0f : 0.0f; + contextData.SemiStaticCmdsOut.SetPixelShaderConstant(3, envMapSaturation_SelfIllumMask, 1); + if (hasEnvmap) + contextData.SemiStaticCmdsOut.SetEnvMapTintPixelShaderDynamicStateGammaToLinear(0, info.EnvmapTint, fTintReplaceFactor); + else + contextData.SemiStaticCmdsOut.SetEnvMapTintPixelShaderDynamicStateGammaToLinear(0, -1, fTintReplaceFactor); + + if (hasEnvmapMask) + contextData.SemiStaticCmdsOut.BindTexture(shader, Sampler.Sampler4, info.EnvmapMask, info.EnvmapMaskFrame); + + if (hasSelfIllumFresnel && (!hasFlashlight)) { + Span vConstScaleBiasExp = [1.0f, 0.0f, 1.0f, 0.0f]; + float min = IsParamDefined(parms, info.SelfIllumFresnelMinMaxExp) ? parms[info.SelfIllumFresnelMinMaxExp].GetVecValue()[0] : 0.0f; + float max = IsParamDefined(parms, info.SelfIllumFresnelMinMaxExp) ? parms[info.SelfIllumFresnelMinMaxExp].GetVecValue()[1] : 1.0f; + float exp = IsParamDefined(parms, info.SelfIllumFresnelMinMaxExp) ? parms[info.SelfIllumFresnelMinMaxExp].GetVecValue()[2] : 1.0f; + + vConstScaleBiasExp[1] = (max != 0.0f) ? (min / max) : 0.0f; + vConstScaleBiasExp[0] = 1.0f - vConstScaleBiasExp[1]; + vConstScaleBiasExp[2] = exp; + vConstScaleBiasExp[3] = max; + + contextData.SemiStaticCmdsOut.SetPixelShaderConstant(11, vConstScaleBiasExp); + } + + if (hasDiffuseWarp && (!hasFlashlight) && !hasSelfIllumFresnel) { + if (r_lightwarpidentity.GetBool()) // TODO + contextData.SemiStaticCmdsOut.BindStandardTexture(Sampler.Sampler9, StandardTextureId.IdentityLightwarp); + else + contextData.SemiStaticCmdsOut.BindTexture(shader, Sampler.Sampler9, info.DiffuseWarpTexture, -1); + } + + if (hasFlashlight) { + FlashlightState flashlightState = shaderAPI.GetFlashlightState(out _); + Span tweaks = [0, 0, 0, 0]; + tweaks[0] = flashlightState.ShadowFilterSize / flashlightState.ShadowMapResolution; + tweaks[1] = ShadowAttenFromState(flashlightState); + shader.HashShadow2DJitter(flashlightState.ShadowJitterSeed, out tweaks[2], out tweaks[3]); + shaderAPI.SetPixelShaderConstant(2, tweaks); + + Span screenScale = [1280.0f / 32.0f, 720.0f / 32.0f, 0, 0]; + shaderAPI.GetBackBufferDimensions(out int width, out int height); + screenScale[0] = width / 32.0f; + screenScale[1] = height / 32.0f; + shaderAPI.SetPixelShaderConstant(31, screenScale); + } + + if ((!hasFlashlight) && (info.EnvmapContrast != -1)) + contextData.SemiStaticCmdsOut.SetPixelShaderConstant(2, info.EnvmapContrast); + + bool lightingOnly = vertexLitGeneric && mat_fullbright.GetInt() == 2 && false && !IsFlagSet(parms, MaterialVarFlags.NoDebugOverride); + if (lightingOnly) { + if (hasBaseTexture) { + if (hasSelfIllum && !hasSelfIllumInEnvMapMask) + contextData.SemiStaticCmdsOut.BindStandardTexture(Sampler.Sampler0, StandardTextureId.GreyAlphaZero); + else + contextData.SemiStaticCmdsOut.BindStandardTexture(Sampler.Sampler0, StandardTextureId.Grey); + } + if (hasDetailTexture) + contextData.SemiStaticCmdsOut.BindStandardTexture(Sampler.Sampler2, StandardTextureId.Grey); + } + + if (hasBump || hasDiffuseWarp) { + contextData.SemiStaticCmdsOut.BindStandardTexture(Sampler.Sampler5, StandardTextureId.NormalizationCubemapSigned); + contextData.SemiStaticCmdsOut.SetPixelShaderStateAmbientLightCube(5); + contextData.SemiStaticCmdsOut.CommitPixelShaderLighting(13); + } + contextData.SemiStaticCmdsOut.SetPixelShaderConstant_W(4, info.SelfIllumTint, blendFactor); + contextData.SemiStaticCmdsOut.SetAmbientCubeDynamicStateVertexShader(); + contextData.SemiStaticCmdsOut.End(); + } + } + + if (shaderAPI != null) { + dynamicCmdsOut.Reset(); + dynamicCmdsOut.Call(contextData!.SemiStaticCmdsOut.Storage); + if (hasEnvmap) + dynamicCmdsOut.BindTexture(shader, Sampler.Sampler1, info.Envmap, info.EnvmapFrame); + + bool bFlashlightShadows = false; + if (hasFlashlight) { + FlashlightState state = shaderAPI.GetFlashlightStateEx(out Matrix4x4 worldToTexture, out ITexture? pFlashlightDepthTexture); + bFlashlightShadows = state.EnableShadows && (pFlashlightDepthTexture != null); + + if (pFlashlightDepthTexture != null && Config.ShadowDepthTexture && state.EnableShadows) { + shader.BindTexture(Sampler.Sampler8, pFlashlightDepthTexture, 0); + dynamicCmdsOut.BindStandardTexture(Sampler.Sampler6, StandardTextureId.ShadowNoise2D); + } + + SetFlashLightColorFromState(state, shaderAPI, 28, flashlightNoLambert); + + Assert(info.FlashlightTexture >= 0 && info.FlashlightTextureFrame >= 0); + shader.BindTexture(Sampler.Sampler7, state.SpotlightTexture, state.SpotlightTextureFrame); + } + + LightState lightState = default; + if (vertexLitGeneric && (!hasFlashlight)) + shaderAPI.GetLightState(out lightState); + + MaterialFogMode fogType = shaderAPI.GetSceneFogMode(); + int fogIndex = (fogType == MaterialFogMode.LinearBelowFogZ) ? 1 : 0; + int numBones = shaderAPI.GetCurrentNumBones(); + + bool writeDepthToAlpha; + bool writeWaterFogToAlpha; + if (fullyOpaque) { + writeDepthToAlpha = shaderAPI.ShouldWriteDepthToDestAlpha(); + writeWaterFogToAlpha = fogType == MaterialFogMode.LinearBelowFogZ; + AssertMsg(!(writeDepthToAlpha && writeWaterFogToAlpha), "Can't write two values to alpha at the same time."); + } + else { + writeDepthToAlpha = false; + writeWaterFogToAlpha = false; + } + + if (hasBump || hasDiffuseWarp) { + if (!HardwareConfig.HasFastVertexTextures()) { + bool useStaticControlFlow = HardwareConfig.SupportsStaticControlFlow(); + + DynamicShaderIndex vshIndex = new(shaderAPI, ShaderType.Vertex); + vshIndex.Set("DOWATERFOG", fogIndex); + vshIndex.Set("SKINNING", numBones > 0); + vshIndex.Set("COMPRESSED_VERTS", (int)vertexCompression); + vshIndex.Set("NUM_LIGHTS", useStaticControlFlow ? 0 : lightState.NumLights); + dynamicCmdsOut.SetVertexShaderIndex(vshIndex.GetIndex()); + + if (HardwareConfig.SupportsPixelShaders_2_b() || HardwareConfig.ShouldAlwaysUseShaderModel2bShaders()) { + DynamicShaderIndex pshIndex = new(shaderAPI, ShaderType.Pixel); + pshIndex.Set("NUM_LIGHTS", useStaticControlFlow ? 0 : lightState.NumLights); + pshIndex.Set("AMBIENT_LIGHT", lightState.AmbientLight ? 1 : 0); + pshIndex.Set("FLASHLIGHTSHADOWS", bFlashlightShadows); + dynamicCmdsOut.SetPixelShaderIndex(pshIndex.GetIndex()); + } + else { + DynamicShaderIndex pshIndex = new(shaderAPI, ShaderType.Pixel); + pshIndex.Set("NUM_LIGHTS", useStaticControlFlow ? 0 : lightState.NumLights); + pshIndex.Set("AMBIENT_LIGHT", lightState.AmbientLight ? 1 : 0); + pshIndex.Set("WRITEWATERFOGTODESTALPHA", writeWaterFogToAlpha); + pshIndex.Set("PIXELFOGTYPE", shaderAPI.GetPixelFogCombo()); + dynamicCmdsOut.SetPixelShaderIndex(pshIndex.GetIndex()); + } + } + else { + // shader.SetHWMorphVertexShaderState(VERTEX_SHADER_SHADER_SPECIFIC_CONST_10, VERTEX_SHADER_SHADER_SPECIFIC_CONST_11, SHADER_VERTEXTEXTURE_SAMPLER0); + + DynamicShaderIndex vshIndex = new(shaderAPI, ShaderType.Vertex); + vshIndex.Set("DOWATERFOG", fogIndex); + vshIndex.Set("SKINNING", numBones > 0); + vshIndex.Set("MORPHING", shaderAPI.IsHWMorphingEnabled()); + vshIndex.Set("COMPRESSED_VERTS", (int)vertexCompression); + dynamicCmdsOut.SetVertexShaderIndex(vshIndex.GetIndex()); + + DynamicShaderIndex pshIndex = new(shaderAPI, ShaderType.Pixel); + pshIndex.Set("NUM_LIGHTS", lightState.NumLights); + pshIndex.Set("AMBIENT_LIGHT", lightState.AmbientLight ? 1 : 0); + pshIndex.Set("FLASHLIGHTSHADOWS", bFlashlightShadows); + dynamicCmdsOut.SetPixelShaderIndex(pshIndex.GetIndex()); + + Span unusedTexCoords = [false, false, !shaderAPI.IsHWMorphingEnabled() || !isDecal]; + shaderAPI.MarkUnusedVertexFields(0, unusedTexCoords); + } + } + else { + if (ambientOnly) { + lightState.AmbientLight = true; + lightState.StaticLightVertex = false; + lightState.NumLights = 0; + } + + if (!HardwareConfig.HasFastVertexTextures()) { + bool useStaticControlFlow = HardwareConfig.SupportsStaticControlFlow(); + + DynamicShaderIndex vshIndex = new(shaderAPI, ShaderType.Vertex); + vshIndex.Set("DYNAMIC_LIGHT", lightState.HasDynamicLight()); + vshIndex.Set("STATIC_LIGHT", lightState.StaticLightVertex ? 1 : 0); + vshIndex.Set("DOWATERFOG", fogIndex); + vshIndex.Set("SKINNING", numBones > 0); + vshIndex.Set("LIGHTING_PREVIEW", shaderAPI.GetIntRenderingParameter(RenderParamInt.EnableFixedLighting) != 0); + vshIndex.Set("COMPRESSED_VERTS", (int)vertexCompression); + vshIndex.Set("NUM_LIGHTS", useStaticControlFlow ? 0 : lightState.NumLights); + dynamicCmdsOut.SetVertexShaderIndex(vshIndex.GetIndex()); + + if (HardwareConfig.SupportsPixelShaders_2_b() || HardwareConfig.ShouldAlwaysUseShaderModel2bShaders()) { + DynamicShaderIndex pshIndex = new(shaderAPI, ShaderType.Pixel); + pshIndex.Set("FLASHLIGHTSHADOWS", bFlashlightShadows); + pshIndex.Set("LIGHTING_PREVIEW", shaderAPI.GetIntRenderingParameter(RenderParamInt.EnableFixedLighting)); + dynamicCmdsOut.SetPixelShaderIndex(pshIndex.GetIndex()); + } + else { + DynamicShaderIndex pshIndex = new(shaderAPI, ShaderType.Pixel); + pshIndex.Set("PIXELFOGTYPE", shaderAPI.GetPixelFogCombo()); + pshIndex.Set("LIGHTING_PREVIEW", shaderAPI.GetIntRenderingParameter(RenderParamInt.EnableFixedLighting)); + dynamicCmdsOut.SetPixelShaderIndex(pshIndex.GetIndex()); + } + } + else { + // shader.SetHWMorphVertexShaderState(VERTEX_SHADER_SHADER_SPECIFIC_CONST_10, VERTEX_SHADER_SHADER_SPECIFIC_CONST_11, SHADER_VERTEXTEXTURE_SAMPLER0); + + DynamicShaderIndex vshIndex = new(shaderAPI, ShaderType.Vertex); + vshIndex.Set("DYNAMIC_LIGHT", lightState.HasDynamicLight()); + vshIndex.Set("STATIC_LIGHT", lightState.StaticLightVertex ? 1 : 0); + vshIndex.Set("DOWATERFOG", fogIndex); + vshIndex.Set("SKINNING", numBones > 0); + vshIndex.Set("LIGHTING_PREVIEW", shaderAPI.GetIntRenderingParameter(RenderParamInt.EnableFixedLighting) != 0); + vshIndex.Set("MORPHING", shaderAPI.IsHWMorphingEnabled()); + vshIndex.Set("COMPRESSED_VERTS", (int)vertexCompression); + dynamicCmdsOut.SetVertexShaderIndex(vshIndex.GetIndex()); + + DynamicShaderIndex pshIndex = new(shaderAPI, ShaderType.Pixel); + pshIndex.Set("FLASHLIGHTSHADOWS", bFlashlightShadows); + pshIndex.Set("LIGHTING_PREVIEW", shaderAPI.GetIntRenderingParameter(RenderParamInt.EnableFixedLighting)); + dynamicCmdsOut.SetPixelShaderIndex(pshIndex.GetIndex()); + + Span unusedTexCoords = [false, false, !shaderAPI.IsHWMorphingEnabled() || !isDecal]; + shaderAPI.MarkUnusedVertexFields(0, unusedTexCoords); + } + } + + if ((info.HDRColorScale != -1) && shader.IsHDREnabled()) + shader.SetModulationPixelShaderDynamicState_LinearColorSpace_LinearScale(1, parms[info.HDRColorScale].GetFloatValue()); + else + shader.SetModulationPixelShaderDynamicState_LinearColorSpace(1); + + Span eyePos = [0, 0, 0, 0]; + shaderAPI.GetWorldSpaceCameraPosition(ref eyePos); + dynamicCmdsOut.SetPixelShaderConstant(20, eyePos); + + if (!hasBump && !hasDiffuseWarp) + dynamicCmdsOut.SetDepthFeatheringPixelShaderConstant(13, GetFloatParam(info.DepthBlendScale, parms, 50.0f)); + + float pixelFogType = shaderAPI.GetPixelFogCombo() == 1 ? 1.0f : 0.0f; + float fWriteDepthToAlpha = writeDepthToAlpha && IsPC() ? 1.0f : 0.0f; + float fWriteWaterFogToDestAlpha = (shaderAPI.GetPixelFogCombo() == 1 && writeWaterFogToAlpha) ? 1.0f : 0.0f; + float vertexAlpha = hasVertexAlpha ? 1.0f : 0.0f; + + Span shaderControls = [pixelFogType, fWriteDepthToAlpha, fWriteWaterFogToDestAlpha, vertexAlpha]; + dynamicCmdsOut.SetPixelShaderConstant(12, shaderControls, 1); + + if (hasFlashlight) { + FlashlightState flashlightState = shaderAPI.GetFlashlightState(out Matrix4x4 worldToTexture); + SetFlashLightColorFromState(flashlightState, shaderAPI, 28, flashlightNoLambert); + + Span values = [ + worldToTexture.M11, worldToTexture.M12, worldToTexture.M13, worldToTexture.M14, + worldToTexture.M21, worldToTexture.M22, worldToTexture.M23, worldToTexture.M24, + worldToTexture.M31, worldToTexture.M32, worldToTexture.M33, worldToTexture.M34, + worldToTexture.M41, worldToTexture.M42, worldToTexture.M43, worldToTexture.M44 + ]; + + shaderAPI.SetVertexShaderConstant(VertexShaderConst.ShaderSpecificConst6, values); + shader.BindTexture(Sampler.Sampler7, flashlightState.SpotlightTexture, flashlightState.SpotlightTextureFrame); + + Span atten_pos = [ + flashlightState.ConstantAtten, + flashlightState.LinearAtten, + flashlightState.QuadraticAtten, + flashlightState.FarZ, + flashlightState.LightOrigin[0], + flashlightState.LightOrigin[1], + flashlightState.LightOrigin[2], + 1.0f + ]; + dynamicCmdsOut.SetPixelShaderConstant(22, atten_pos, 2); + dynamicCmdsOut.SetPixelShaderConstant(24, values, 4); + } + + dynamicCmdsOut.End(); + shaderAPI.ExecuteCommandBuffer(dynamicCmdsOut.Storage); + } + + shader.Draw(); + } + + internal void InitVertexLitGeneric(VertexLitGeneric shader, IMaterialVar[] parms, bool vertexLitGeneric, ref VertexLitGeneric_Vars info) { + if (info.Phong != -1 && parms[info.Phong].GetIntValue() != 0 && HardwareConfig.SupportsPixelShaders_2_b()) { + InitSkin(shader, parms, ref info); + return; + } + + if (info.FlashlightTexture != -1) + shader.LoadTexture(info.FlashlightTexture, (int)TextureFlags.SRGB); + + bool isBaseTextureTranslucent = false; + if (info.BaseTexture != -1 && parms[info.BaseTexture].IsDefined()) { + shader.LoadTexture(info.BaseTexture, (info.GammaColorRead != -1) && (parms[info.GammaColorRead].GetIntValue() == 1) ? 0 : (int)TextureFlags.SRGB); + + if (parms[info.BaseTexture].GetTextureValue()!.IsTranslucent()) + isBaseTextureTranslucent = true; + } + + bool hasSelfIllumMask = IsFlagSet(parms, MaterialVarFlags.SelfIllum) && (info.SelfIllumMask != -1) && parms[info.SelfIllumMask].IsDefined(); + + if (!isBaseTextureTranslucent) { + bool hasSelfIllumFresnel = IsFlagSet(parms, MaterialVarFlags.SelfIllum) && (info.SelfIllumFresnel != -1) && (parms[info.SelfIllumFresnel].GetIntValue() != 0); + + if (!hasSelfIllumFresnel && !hasSelfIllumMask) + ClearFlags(parms, MaterialVarFlags.SelfIllum); + + ClearFlags(parms, MaterialVarFlags.BaseAlphaEnvMapMask); + } + + if (info.Detail != -1 && parms[info.Detail].IsDefined()) { + int detailBlendMode = (info.DetailTextureCombineMode == -1) ? 0 : parms[info.DetailTextureCombineMode].GetIntValue(); + if (detailBlendMode == 0) + shader.LoadTexture(info.Detail); + else + shader.LoadTexture(info.Detail, (int)TextureFlags.SRGB); + } + + if (Config.UseBumpmapping()) { + if ((info.Bumpmap != -1) && parms[info.Bumpmap].IsDefined()) { + shader.LoadBumpMap(info.Bumpmap); + SetFlags2(parms, MaterialVarFlags2.DiffuseBumpmappedModel); + } + else if ((info.DiffuseWarpTexture != -1) && parms[info.DiffuseWarpTexture].IsDefined()) + SetFlags2(parms, MaterialVarFlags2.DiffuseBumpmappedModel); + } + + if (IsFlagSet(parms, MaterialVarFlags.SelfIllum) || IsFlagSet(parms, MaterialVarFlags.BaseAlphaEnvMapMask)) + ClearFlags(parms, MaterialVarFlags.AlphaTest); + + if (info.Envmap != -1 && parms[info.Envmap].IsDefined()) { + if (!IsFlagSet(parms, MaterialVarFlags.EnvMapSphere)) + shader.LoadCubeMap(info.Envmap, HardwareConfig.GetHDRType() == HDRType.None ? (int)TextureFlags.SRGB : 0); + else + shader.LoadTexture(info.Envmap, HardwareConfig.GetHDRType() == HDRType.None ? (int)TextureFlags.SRGB : 0); + + if (!HardwareConfig.SupportsCubeMaps()) + SetFlags(parms, MaterialVarFlags.EnvMapSphere); + } + if (info.EnvmapMask != -1 && parms[info.EnvmapMask].IsDefined()) + shader.LoadTexture(info.EnvmapMask); + + if ((info.DiffuseWarpTexture != -1) && parms[info.DiffuseWarpTexture].IsDefined()) + shader.LoadTexture(info.DiffuseWarpTexture); + + if (hasSelfIllumMask) + shader.LoadTexture(info.SelfIllumMask); + } + + readonly static CommandBufferBuilder dynamicCmdsOut = new() { Storage = new FixedCommandStorageBuffer(1000) }; + static ConVarRef mat_reduceparticles = new("mat_reduceparticles"); +} + +public class VertexLitGeneric_Context : BasePerMaterialContextData +{ + public readonly CommandBufferBuilder SemiStaticCmdsOut = new() { Storage = new FixedCommandStorageBuffer(800) }; +} \ No newline at end of file diff --git a/Source.StudioRender/StudioRender.cs b/Source.StudioRender/StudioRender.cs index dff10f2f..ad3e80fa 100644 --- a/Source.StudioRender/StudioRender.cs +++ b/Source.StudioRender/StudioRender.cs @@ -481,9 +481,16 @@ private void R_PerformLighting(in Vector3 forward, float illum, in Vector3 pos, color = alphaMask; } - private static void R_TransformVert(in Vector3 srcPos, in Vector3 srcNorm, in Matrix3x4 skinMat, out Vector3 pos, out Vector3 norm) { + private static void R_TransformVert(in Vector3 srcPos, in Vector3 srcNorm, in Vector4 srcTangentS, in Matrix3x4 skinMat, out Vector3 pos, out Vector3 norm, out Vector4 tangentS, bool hasTangentSpace) { MathLib.VectorTransform(in srcPos, in skinMat, out pos); MathLib.VectorRotate(in srcNorm, in skinMat, out norm); + + if (hasTangentSpace) { + MathLib.VectorRotate(new Vector3(srcTangentS.X, srcTangentS.Y, srcTangentS.Z), in skinMat, out Vector3 rotated); + tangentS = new Vector4(rotated.X, rotated.Y, rotated.Z, srcTangentS.W); + } + else + tangentS = new Vector4(1.0f, 0.0f, 0.0f, 1.0f); } private void R_StudioSoftwareProcessMesh(MStudioMesh mesh, ref MeshBuilder meshBuilder, int numVertices, ushort[] groupToMesh, StudioModelLighting lighting, bool doFlex, float blend, bool needsTangentSpace, bool dx8Vertex, IMaterial material) { @@ -493,10 +500,10 @@ private void R_StudioSoftwareProcessMesh(MStudioMesh mesh, ref MeshBuilder meshB MStudioMeshVertexData? vertData = GetFatVertexData(mesh, StudioHdr!); if (vertData != null) - R_StudioSoftwareProcessMesh(vertData, PoseToWorld, ref meshBuilder, numVertices, groupToMesh, alphaMask, lighting, material); + R_StudioSoftwareProcessMesh(vertData, PoseToWorld, ref meshBuilder, numVertices, groupToMesh, alphaMask, lighting, material, needsTangentSpace, dx8Vertex); } - private void R_StudioSoftwareProcessMesh(MStudioMeshVertexData vertData, Span poseToWorld, ref MeshBuilder meshBuilder, int numVertices, ushort[] groupToMesh, uint alphaMask, StudioModelLighting lighting, IMaterial material) { + private void R_StudioSoftwareProcessMesh(MStudioMeshVertexData vertData, Span poseToWorld, ref MeshBuilder meshBuilder, int numVertices, ushort[] groupToMesh, uint alphaMask, StudioModelLighting lighting, IMaterial material, bool hasTangentSpace, bool dx8Vertex) { Assert(numVertices > 0); float illum = 1.0f; @@ -509,8 +516,10 @@ private void R_StudioSoftwareProcessMesh(MStudioMeshVertexData vertData, Span> 16), (byte)(color >> 8), (byte)color, (byte)(color >> 24)]); meshBuilder.TexCoord2fv(0, in vert.TexCoord); + if (dx8Vertex) + meshBuilder.UserData(in tangentS); meshBuilder.AdvanceVertex(); } } diff --git a/Source.StudioRender/StudioRenderContext.cs b/Source.StudioRender/StudioRenderContext.cs index 65e9c37f..083e0fef 100644 --- a/Source.StudioRender/StudioRenderContext.cs +++ b/Source.StudioRender/StudioRenderContext.cs @@ -314,7 +314,8 @@ private bool R_AddVertexToMesh(ReadOnlySpan pModelName, bool bNeedsTangent meshBuilder.TexCoord2fv(0, in vert.TexCoord); - // TODO: Tangents + if (vertData.HasTangentData()) + meshBuilder.UserData(in vertData.TangentS(idx)); meshBuilder.Color4ub(255, 255, 255, 255); @@ -414,7 +415,20 @@ private void R_StudioBuildMorph(StudioHeader studioHdr, StudioMeshGroup meshGrou } private bool MeshNeedsTangentSpace(StudioHeader studioHdr, StudioLODData studioLodData, MStudioMesh mesh) { - return false; // For now, todo + if (studioHdr == null || studioHdr.NumSkinFamilies == 0) + return false; + + Span skinref = studioHdr.SkinRef(0); + for (int i = 0; i < studioHdr.NumSkinFamilies; i++) { + IMaterial? material = studioLodData.Materials?[skinref[mesh.Material]]; + Assert(material != null); + if (material == null) + continue; + + if (material.NeedsTangentSpace()) + return true; + } + return false; } private VertexFormat CalculateVertexFormat(StudioHeader studioHdr, StudioLODData studioLodData, MStudioMesh mesh, OptimizedModel.StripGroupHeader group, bool isHwSkinned) { diff --git a/Source.VPK/VpkArchive.cs b/Source.VPK/VpkArchive.cs index 424ab527..99826b1e 100644 --- a/Source.VPK/VpkArchive.cs +++ b/Source.VPK/VpkArchive.cs @@ -20,7 +20,7 @@ public VpkArchive() { Directories = new List(); } - public void Dispose(){ + public void Dispose() { foreach (var part in Parts) part.Dispose(); FileHandle.Dispose(); @@ -125,7 +125,7 @@ private void LoadParts(string filePath) { var subFileName = Path.GetFileName(subFile); - if (!subFileName.Contains("_")) { + if (!subFileName.Contains('_')) { continue; }