победа над водопадами, шейдеры m2

This commit is contained in:
2026-07-07 10:45:43 +04:00
parent 8738c2495d
commit 44614a79e4
38 changed files with 4820 additions and 192 deletions
@@ -0,0 +1,336 @@
extends RefCounted
class_name WowWMOMaterial
const WMOBLEND_OPAQUE := 0
const WMOBLEND_ALPHA_KEY := 1
const WMOBLEND_ALPHA := 2
const SHADER_VERSION := 3
static var _shader_cache: Dictionary = {}
static func clear_cache() -> void:
_shader_cache.clear()
static func _ensure_wow_shader_globals() -> void:
pass
static func build(
texture: Texture2D,
texture2: Texture2D,
material_flags: int,
shader_id: int,
blend_mode: int,
texture_path: String = "",
texture2_path: String = "",
diffuse_color: Color = Color.WHITE,
emissive_color: Color = Color.BLACK,
secondary_color: Color = Color.WHITE,
texture3: Texture2D = null,
texture3_path: String = "") -> ShaderMaterial:
var mode := _blend_shader_mode(blend_mode)
var effective_diffuse := diffuse_color
if maxf(maxf(effective_diffuse.r, effective_diffuse.g), effective_diffuse.b) <= 0.001:
effective_diffuse = Color.WHITE
_ensure_wow_shader_globals()
var mat := ShaderMaterial.new()
mat.resource_name = texture_path.get_file().get_basename()
var pixel_shader := _wmo_pixel_shader_id(shader_id)
mat.shader = _get_shader(mode, texture2 != null, texture3 != null)
mat.render_priority = 0 if blend_mode <= WMOBLEND_ALPHA_KEY else 1
mat.set_shader_parameter("albedo_tex", texture)
mat.set_shader_parameter("detail_tex", texture2)
mat.set_shader_parameter("extra_tex", texture3)
mat.set_shader_parameter("use_texture", texture != null)
mat.set_shader_parameter("use_detail_texture", texture2 != null)
mat.set_shader_parameter("use_extra_texture", texture3 != null)
mat.set_shader_parameter("pixel_shader", pixel_shader)
mat.set_shader_parameter("alpha_ref", _alpha_ref(blend_mode, pixel_shader))
mat.set_shader_parameter("alpha_output_strength", _alpha_output_strength(blend_mode, pixel_shader))
mat.set_shader_parameter("vertex_color_strength", 0.72)
mat.set_shader_parameter("ambient_floor", 0.22)
mat.set_shader_parameter("diffuse_color", effective_diffuse)
mat.set_shader_parameter("emissive_color", emissive_color)
mat.set_shader_parameter("material_emissive_strength", _material_emissive_strength(material_flags, emissive_color, texture_path))
mat.set_shader_parameter("secondary_color", _effective_secondary_color(secondary_color))
mat.set_shader_parameter("detail_strength", 1.0 if texture2 != null else 0.0)
mat.set_meta("texture0_path", texture_path)
mat.set_meta("texture1_path", texture2_path)
mat.set_meta("texture2_path", texture3_path)
mat.set_meta("wow_flags", material_flags)
mat.set_meta("wow_shader", shader_id)
mat.set_meta("wow_wmo_pixel_shader", pixel_shader)
mat.set_meta("wow_blend_mode", blend_mode)
return mat
static func _wmo_pixel_shader_id(shader_id: int) -> int:
match shader_id:
0:
return 0 # Diffuse
1:
return 1 # Specular
2:
return 2 # Metal
3:
return 3 # Env
4:
return 4 # Opaque
5:
return 5 # EnvMetal
6:
return 6 # TwoLayerDiffuse
7:
return 7 # TwoLayerEnvMetal
8:
return 8 # TwoLayerTerrain
9:
return 9 # DiffuseEmissive
11:
return 10 # MaskedEnvMetal
12:
return 11 # EnvMetalEmissive
13:
return 12 # TwoLayerDiffuseOpaque
15:
return 13 # TwoLayerDiffuseEmissive
16:
return 0 # DiffuseTerrain
17:
return 14 # AdditiveMaskedEnvMetal
18:
return 15 # TwoLayerDiffuseMod2x
19:
return 16 # TwoLayerDiffuseMod2xNA
20:
return 17 # TwoLayerDiffuseAlpha
21:
return 18 # Lod
22:
return 19 # Parallax
23:
return 20 # DF shader
_:
return 0
static func _shader_alpha_is_opacity(pixel_shader: int) -> bool:
match pixel_shader:
0, 1, 2, 4, 18, 19:
return true
_:
return false
static func _alpha_ref(blend_mode: int, pixel_shader: int) -> float:
if blend_mode > WMOBLEND_OPAQUE:
return 0.501960814
return 0.0
static func _alpha_output_strength(blend_mode: int, pixel_shader: int) -> float:
if blend_mode == WMOBLEND_ALPHA and _shader_alpha_is_opacity(pixel_shader):
return 1.0
return 0.0
static func _effective_secondary_color(color: Color) -> Color:
if maxf(maxf(color.r, color.g), color.b) <= 0.001:
return Color.WHITE
return color
static func _material_emissive_strength(material_flags: int, color: Color, texture_path: String) -> float:
var color_strength := maxf(maxf(color.r, color.g), color.b) * color.a
if color_strength <= 0.001:
return 0.0
var strength := 0.12
if (material_flags & 0x10) != 0:
strength = 0.26
var lower_path := texture_path.to_lower()
if lower_path.contains("window") or lower_path.contains("_wnd_") or lower_path.contains("wnd_"):
strength = minf(strength, 0.18)
return strength
static func _blend_shader_mode(blend_mode: int) -> String:
match blend_mode:
WMOBLEND_ALPHA_KEY:
return "cutout"
WMOBLEND_ALPHA:
return "alpha"
_:
return "opaque"
static func _get_shader(mode: String, has_detail: bool, has_extra: bool) -> Shader:
var key := "v=%d|%s|detail=%s|extra=%s" % [SHADER_VERSION, mode, str(has_detail), str(has_extra)]
if _shader_cache.has(key):
return _shader_cache[key]
var shader := Shader.new()
shader.code = _shader_code(mode, has_detail, has_extra)
_shader_cache[key] = shader
return shader
static func _shader_code(mode: String, has_detail: bool, has_extra: bool) -> String:
var render_modes: Array[String] = ["blend_mix", "cull_disabled", "unshaded"]
if mode == "opaque" or mode == "cutout":
render_modes.append("depth_draw_opaque")
else:
render_modes.append("depth_prepass_alpha")
return """
shader_type spatial;
render_mode %s;
uniform sampler2D albedo_tex : source_color, filter_linear_mipmap_anisotropic, repeat_enable;
uniform sampler2D detail_tex : source_color, filter_linear_mipmap_anisotropic, repeat_enable;
uniform sampler2D extra_tex : source_color, filter_linear_mipmap_anisotropic, repeat_enable;
uniform bool use_texture = false;
uniform bool use_detail_texture = false;
uniform bool use_extra_texture = false;
uniform int pixel_shader = 0;
uniform float alpha_ref = 0.0;
uniform float alpha_output_strength = 0.0;
uniform float vertex_color_strength = 0.88;
uniform float ambient_floor = 0.20;
uniform vec4 diffuse_color : source_color = vec4(1.0);
uniform vec4 emissive_color : source_color = vec4(0.0);
uniform float material_emissive_strength = 0.0;
uniform vec4 secondary_color : source_color = vec4(1.0);
uniform float detail_strength = 0.0;
global uniform vec4 wow_ambient_color;
global uniform vec4 wow_light_color;
global uniform vec3 wow_light_dir;
global uniform vec4 wow_fog_color;
global uniform vec2 wow_fog_range;
global uniform float wow_fog_density;
global uniform float wow_sun_elevation;
varying vec3 world_normal;
varying vec3 world_pos;
varying vec3 view_pos;
void vertex() {
world_normal = normalize(MODEL_NORMAL_MATRIX * NORMAL);
world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
view_pos = (MODELVIEW_MATRIX * vec4(VERTEX, 1.0)).xyz;
}
vec3 apply_wow_fog(vec3 color) {
float dist = length(view_pos);
float range_fog = smoothstep(wow_fog_range.x, wow_fog_range.y, dist);
float fog_amount = range_fog * clamp(wow_fog_density, 0.0, 0.55);
return mix(color, wow_fog_color.rgb, fog_amount);
}
float color_luma(vec3 color) {
return dot(color, vec3(0.2126, 0.7152, 0.0722));
}
vec3 textured_material_tint(vec3 color) {
float luma = max(color_luma(color), 0.001);
vec3 hue = clamp(color / luma, vec3(0.58), vec3(1.20));
return mix(vec3(1.0), hue, 0.28);
}
vec3 untextured_material_color(vec3 color) {
float luma = color_luma(color);
vec3 muted = mix(vec3(luma), color, 0.42);
return clamp(muted * 0.82, vec3(0.08), vec3(0.72));
}
void resolve_wmo_pixel(vec4 tex1, vec4 tex2, vec4 tex3, bool has_detail, bool has_extra, out vec3 diffuse, out vec3 emissive) {
float layer_weight = clamp(secondary_color.a, 0.0, 1.0);
diffuse = tex1.rgb;
emissive = vec3(0.0);
if (!has_detail) {
return;
}
if (pixel_shader == 3) {
emissive = tex2.rgb * tex1.a;
} else if (pixel_shader == 5) {
emissive = (tex1.rgb * tex1.a) * tex2.rgb;
} else if (pixel_shader == 6) {
vec3 layer2 = mix(tex1.rgb, tex2.rgb, tex2.a);
diffuse = mix(layer2, tex1.rgb, layer_weight);
} else if (pixel_shader == 8 || pixel_shader == 12) {
diffuse = mix(tex2.rgb, tex1.rgb, layer_weight);
} else if (pixel_shader == 9) {
emissive = tex2.rgb * tex2.a * layer_weight;
} else if (pixel_shader == 10 && has_extra) {
float mix_factor = clamp(tex3.a * layer_weight, 0.0, 1.0);
diffuse = mix(mix(tex1.rgb * tex2.rgb * 2.0, tex3.rgb, mix_factor), tex1.rgb, tex1.a);
} else if (pixel_shader == 11 && has_extra) {
emissive = ((tex1.rgb * tex1.a) * tex2.rgb) + ((tex3.rgb * tex3.a) * layer_weight);
} else if (pixel_shader == 13) {
vec3 layer = tex2.rgb * (1.0 - tex2.a);
diffuse = mix(layer, tex1.rgb, layer_weight);
emissive = (tex2.rgb * tex2.a) * (1.0 - layer_weight);
} else if (pixel_shader == 14 && has_extra) {
diffuse = mix(
(tex1.rgb * tex2.rgb * 2.0) + (tex3.rgb * clamp(tex3.a * layer_weight, 0.0, 1.0)),
tex1.rgb,
tex1.a
);
} else if (pixel_shader == 15) {
vec3 layer2 = mix(tex1.rgb, tex2.rgb, tex2.a);
vec3 layer3 = mix(layer2, tex1.rgb, layer_weight);
diffuse = has_extra ? layer3 * tex3.rgb * 2.0 : layer3;
} else if (pixel_shader == 16) {
vec3 mod_layer = tex1.rgb * tex2.rgb * 2.0;
diffuse = mix(tex1.rgb, mod_layer, layer_weight);
} else if (pixel_shader == 17) {
vec3 layer2 = mix(tex1.rgb, tex2.rgb, tex2.a);
vec3 layer3 = mix(layer2, tex1.rgb, has_extra ? tex3.a : layer_weight);
diffuse = has_extra ? layer3 * tex3.rgb * 2.0 : layer3;
}
}
void fragment() {
vec4 tex1 = use_texture ? texture(albedo_tex, UV) : vec4(1.0);
vec4 tex2 = use_detail_texture ? texture(detail_tex, UV) : vec4(1.0);
vec4 tex3 = use_extra_texture ? texture(extra_tex, UV) : vec4(1.0);
if (alpha_ref > 0.0 && tex1.a < alpha_ref) {
discard;
}
vec3 mat_diffuse;
vec3 shader_emissive;
resolve_wmo_pixel(tex1, tex2, tex3, use_detail_texture, use_extra_texture, mat_diffuse, shader_emissive);
vec3 baked = max(COLOR.rgb, vec3(ambient_floor));
vec3 baked_lit = mix(vec3(1.0), baked, clamp(vertex_color_strength, 0.0, 1.0));
vec3 n = normalize(world_normal);
if (!FRONT_FACING) {
n = -n;
}
float ndl = max(dot(n, normalize(wow_light_dir)), 0.0);
vec3 zone_ambient = mix(vec3(0.78), max(wow_ambient_color.rgb, vec3(0.08)), 0.16);
vec3 zone_light = mix(vec3(1.0), wow_light_color.rgb, 0.10);
float day_strength = mix(0.48, 0.92, clamp(wow_sun_elevation, 0.0, 1.0));
vec3 lit = baked_lit * (zone_ambient * 0.86 + zone_light * ndl * 0.24 * day_strength);
lit = clamp(lit, vec3(0.46), vec3(1.05));
vec3 material_color = untextured_material_color(diffuse_color.rgb);
if (use_texture) {
material_color = mat_diffuse * textured_material_tint(diffuse_color.rgb);
}
float emissive_mask = smoothstep(0.35, 0.92, color_luma(mat_diffuse));
vec3 material_emissive = mat_diffuse * emissive_color.rgb * emissive_color.a * material_emissive_strength * emissive_mask;
vec3 emissive = shader_emissive * 0.35 + material_emissive;
vec3 color = clamp(material_color * lit + emissive, vec3(0.0), vec3(1.0));
ALBEDO = apply_wow_fog(color);
%s
}
""" % [
", ".join(render_modes),
"" if mode == "opaque" or mode == "cutout" else "\tALPHA = mix(diffuse_color.a, tex1.a * diffuse_color.a, clamp(alpha_output_strength, 0.0, 1.0));",
]