победа над водопадами, шейдеры m2
This commit is contained in:
@@ -21,6 +21,10 @@ static var _baked_texture_material_cache: Dictionary = {}
|
||||
static var _alpha_texture_cache: Dictionary = {}
|
||||
static var _layered_material_cache: Dictionary = {}
|
||||
|
||||
|
||||
static func _ensure_wow_shader_globals() -> void:
|
||||
pass
|
||||
|
||||
## Build a Node3D with 256 terrain chunk meshes.
|
||||
## `extracted_dir` must be an absolute path to the extracted/ folder so BLPs can be loaded.
|
||||
static func build(data: Dictionary, extracted_dir: String = "") -> Node3D:
|
||||
@@ -1253,6 +1257,7 @@ static func _get_control_splat_shader() -> Shader:
|
||||
if _control_splat_shader:
|
||||
return _control_splat_shader
|
||||
|
||||
_ensure_wow_shader_globals()
|
||||
_control_splat_shader = Shader.new()
|
||||
_control_splat_shader.code = """
|
||||
shader_type spatial;
|
||||
@@ -1269,10 +1274,34 @@ uniform float ambient = 0.62;
|
||||
uniform float diffuse = 0.38;
|
||||
uniform float hemi = 0.12;
|
||||
|
||||
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;
|
||||
|
||||
varying vec3 world_pos;
|
||||
varying vec3 world_normal;
|
||||
varying vec3 view_pos;
|
||||
|
||||
void vertex() {
|
||||
world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||
world_normal = normalize(MODEL_NORMAL_MATRIX * NORMAL);
|
||||
view_pos = (MODELVIEW_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||
}
|
||||
|
||||
vec3 sample_layer(float layer, vec2 continuous_chunk_uv) {
|
||||
return texture(terrain_tex, vec3(continuous_chunk_uv * uv_scale, layer), mip_bias).rgb;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec2 tile_uv = clamp(UV, vec2(0.0), vec2(0.999999));
|
||||
vec2 chunk_uv = tile_uv * 16.0;
|
||||
@@ -1300,15 +1329,18 @@ void fragment() {
|
||||
sample_layer(layers.z, chunk_uv) * w2 +
|
||||
sample_layer(layers.w, chunk_uv) * w3;
|
||||
|
||||
vec3 n = normalize(NORMAL);
|
||||
vec3 n = normalize(world_normal);
|
||||
if (!FRONT_FACING) {
|
||||
n = -n;
|
||||
}
|
||||
vec3 l = normalize(light_dir);
|
||||
vec3 l = normalize(wow_light_dir);
|
||||
float ndl = max(dot(n, l), 0.0);
|
||||
float hemi_term = clamp(n.y * 0.5 + 0.5, 0.0, 1.0);
|
||||
float light = ambient + diffuse * ndl + hemi * hemi_term;
|
||||
ALBEDO = albedo * light;
|
||||
vec3 ambient_tint = mix(vec3(0.74), max(wow_ambient_color.rgb, vec3(0.08)), 0.22);
|
||||
vec3 light_tint = mix(vec3(1.0), wow_light_color.rgb, 0.12);
|
||||
vec3 light = ambient_tint * ambient + light_tint * diffuse * ndl + vec3(hemi * hemi_term);
|
||||
light = clamp(light, vec3(0.48), vec3(1.08));
|
||||
ALBEDO = apply_wow_fog(albedo * light);
|
||||
}
|
||||
"""
|
||||
return _control_splat_shader
|
||||
@@ -1465,6 +1497,7 @@ static func _get_terrain_shader() -> Shader:
|
||||
if _terrain_shader:
|
||||
return _terrain_shader
|
||||
|
||||
_ensure_wow_shader_globals()
|
||||
_terrain_shader = Shader.new()
|
||||
_terrain_shader.code = """
|
||||
shader_type spatial;
|
||||
@@ -1487,6 +1520,30 @@ uniform float ambient = 0.62;
|
||||
uniform float diffuse = 0.38;
|
||||
uniform float hemi = 0.12;
|
||||
|
||||
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;
|
||||
|
||||
varying vec3 world_pos;
|
||||
varying vec3 world_normal;
|
||||
varying vec3 view_pos;
|
||||
|
||||
void vertex() {
|
||||
world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||
world_normal = normalize(MODEL_NORMAL_MATRIX * NORMAL);
|
||||
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);
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec2 tiled_uv = UV * uv_scale;
|
||||
|
||||
@@ -1516,15 +1573,18 @@ void fragment() {
|
||||
if (layer_count > 2) { albedo += c2 * w2; }
|
||||
if (layer_count > 3) { albedo += c3 * w3; }
|
||||
|
||||
vec3 n = normalize(NORMAL);
|
||||
vec3 n = normalize(world_normal);
|
||||
if (!FRONT_FACING) {
|
||||
n = -n;
|
||||
}
|
||||
vec3 l = normalize(light_dir);
|
||||
vec3 l = normalize(wow_light_dir);
|
||||
float ndl = max(dot(n, l), 0.0);
|
||||
float hemi_term = clamp(n.y * 0.5 + 0.5, 0.0, 1.0);
|
||||
float light = ambient + diffuse * ndl + hemi * hemi_term;
|
||||
ALBEDO = albedo.rgb * light;
|
||||
vec3 ambient_tint = mix(vec3(0.74), max(wow_ambient_color.rgb, vec3(0.08)), 0.22);
|
||||
vec3 light_tint = mix(vec3(1.0), wow_light_color.rgb, 0.12);
|
||||
vec3 light = ambient_tint * ambient + light_tint * diffuse * ndl + vec3(hemi * hemi_term);
|
||||
light = clamp(light, vec3(0.48), vec3(1.08));
|
||||
ALBEDO = apply_wow_fog(albedo.rgb * light);
|
||||
}
|
||||
"""
|
||||
return _terrain_shader
|
||||
@@ -1534,6 +1594,7 @@ static func _get_single_texture_shader() -> Shader:
|
||||
if _single_texture_shader:
|
||||
return _single_texture_shader
|
||||
|
||||
_ensure_wow_shader_globals()
|
||||
_single_texture_shader = Shader.new()
|
||||
_single_texture_shader.code = """
|
||||
shader_type spatial;
|
||||
@@ -1548,6 +1609,23 @@ uniform float ambient = 0.62;
|
||||
uniform float diffuse = 0.38;
|
||||
uniform float hemi = 0.12;
|
||||
|
||||
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;
|
||||
|
||||
varying vec3 world_pos;
|
||||
varying vec3 world_normal;
|
||||
varying vec3 view_pos;
|
||||
|
||||
void vertex() {
|
||||
world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||
world_normal = normalize(MODEL_NORMAL_MATRIX * NORMAL);
|
||||
view_pos = (MODELVIEW_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||
}
|
||||
|
||||
vec3 sample_sharp(sampler2D tex, vec2 uv) {
|
||||
vec3 center = texture(tex, uv, mip_bias).rgb;
|
||||
if (sharpen_strength <= 0.001) {
|
||||
@@ -1564,16 +1642,26 @@ vec3 sample_sharp(sampler2D tex, vec2 uv) {
|
||||
return clamp(center * (1.0 + 4.0 * sharpen_strength) - side * sharpen_strength, vec3(0.0), vec3(1.0));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec3 n = normalize(NORMAL);
|
||||
vec3 n = normalize(world_normal);
|
||||
if (!FRONT_FACING) {
|
||||
n = -n;
|
||||
}
|
||||
vec3 l = normalize(light_dir);
|
||||
vec3 l = normalize(wow_light_dir);
|
||||
float ndl = max(dot(n, l), 0.0);
|
||||
float hemi_term = clamp(n.y * 0.5 + 0.5, 0.0, 1.0);
|
||||
float light = ambient + diffuse * ndl + hemi * hemi_term;
|
||||
ALBEDO = sample_sharp(tex0, UV * uv_scale) * light;
|
||||
vec3 ambient_tint = mix(vec3(0.74), max(wow_ambient_color.rgb, vec3(0.08)), 0.22);
|
||||
vec3 light_tint = mix(vec3(1.0), wow_light_color.rgb, 0.12);
|
||||
vec3 light = ambient_tint * ambient + light_tint * diffuse * ndl + vec3(hemi * hemi_term);
|
||||
light = clamp(light, vec3(0.48), vec3(1.08));
|
||||
ALBEDO = apply_wow_fog(sample_sharp(tex0, UV * uv_scale) * light);
|
||||
}
|
||||
"""
|
||||
return _single_texture_shader
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
## add_child(node)
|
||||
class_name M2Builder
|
||||
|
||||
const WOW_M2_MATERIAL := preload("res://addons/mpq_extractor/loaders/wow_m2_material.gd")
|
||||
|
||||
static var _texture_cache: Dictionary = {}
|
||||
|
||||
static func build(data: Dictionary, extracted_dir: String = "") -> Node3D:
|
||||
@@ -14,6 +16,7 @@ static func build(data: Dictionary, extracted_dir: String = "") -> Node3D:
|
||||
var verts: PackedVector3Array = data.get("vertices", PackedVector3Array())
|
||||
var normals: PackedVector3Array = data.get("normals", PackedVector3Array())
|
||||
var uvs: PackedVector2Array = data.get("uvs", PackedVector2Array())
|
||||
var uvs2: PackedVector2Array = data.get("uvs2", PackedVector2Array())
|
||||
var indices: PackedInt32Array = data.get("indices", PackedInt32Array())
|
||||
var batches: Array = data.get("batches", [])
|
||||
|
||||
@@ -25,6 +28,14 @@ static func build(data: Dictionary, extracted_dir: String = "") -> Node3D:
|
||||
var texture_flags: PackedInt32Array = data.get("texture_flags", PackedInt32Array())
|
||||
var materials: Array = data.get("materials", [])
|
||||
var tex_combos: PackedInt32Array = data.get("texture_combos", PackedInt32Array())
|
||||
var tex_coord_combos: PackedInt32Array = data.get("texture_coord_combos", PackedInt32Array())
|
||||
var tex_weight_combos: PackedInt32Array = data.get("texture_weight_combos", PackedInt32Array())
|
||||
var tex_transform_combos: PackedInt32Array = data.get("texture_transform_combos", PackedInt32Array())
|
||||
var tex_combiner_combos: PackedInt32Array = data.get("texture_combiner_combos", PackedInt32Array())
|
||||
var m2_colors: Array = data.get("m2_colors", [])
|
||||
var tex_weights: PackedFloat32Array = data.get("texture_weights", PackedFloat32Array())
|
||||
var tex_transforms: Array = data.get("texture_transforms", [])
|
||||
var m2_flags: int = int(data.get("m2_flags", 0))
|
||||
var model_path: String = str(data.get("model_path", ""))
|
||||
|
||||
var mesh := ArrayMesh.new()
|
||||
@@ -52,6 +63,8 @@ static func build(data: Dictionary, extracted_dir: String = "") -> Node3D:
|
||||
arrays[Mesh.ARRAY_NORMAL] = normals
|
||||
if uvs.size() == verts.size():
|
||||
arrays[Mesh.ARRAY_TEX_UV] = uvs
|
||||
if uvs2.size() == verts.size():
|
||||
arrays[Mesh.ARRAY_TEX_UV2] = uvs2
|
||||
arrays[Mesh.ARRAY_INDEX] = batch_indices
|
||||
|
||||
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
|
||||
@@ -67,8 +80,17 @@ static func build(data: Dictionary, extracted_dir: String = "") -> Node3D:
|
||||
texture_types,
|
||||
texture_flags,
|
||||
tex_combos,
|
||||
tex_coord_combos,
|
||||
tex_weight_combos,
|
||||
tex_transform_combos,
|
||||
tex_combiner_combos,
|
||||
m2_colors,
|
||||
tex_weights,
|
||||
tex_transforms,
|
||||
m2_flags,
|
||||
extracted_dir,
|
||||
model_path))
|
||||
model_path,
|
||||
batch))
|
||||
|
||||
if mesh.get_surface_count() == 0:
|
||||
return root
|
||||
@@ -87,52 +109,342 @@ static func _build_material(
|
||||
texture_types: PackedInt32Array,
|
||||
texture_flags: PackedInt32Array,
|
||||
tex_combos: PackedInt32Array,
|
||||
tex_coord_combos: PackedInt32Array,
|
||||
tex_weight_combos: PackedInt32Array,
|
||||
tex_transform_combos: PackedInt32Array,
|
||||
tex_combiner_combos: PackedInt32Array,
|
||||
m2_colors: Array,
|
||||
tex_weights: PackedFloat32Array,
|
||||
tex_transforms: Array,
|
||||
m2_flags: int,
|
||||
extracted_dir: String,
|
||||
model_path: String) -> StandardMaterial3D:
|
||||
model_path: String,
|
||||
batch: Dictionary = {}) -> Material:
|
||||
|
||||
var mat := StandardMaterial3D.new()
|
||||
var flags: int = int(mat_def.get("flags", 0))
|
||||
mat.cull_mode = BaseMaterial3D.CULL_DISABLED if (flags & 0x04) != 0 else BaseMaterial3D.CULL_BACK
|
||||
mat.roughness = 0.85
|
||||
mat.metallic = 0.0
|
||||
mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED if (flags & 0x01) != 0 else BaseMaterial3D.SHADING_MODE_PER_PIXEL
|
||||
mat.specular_mode = BaseMaterial3D.SPECULAR_DISABLED
|
||||
mat.vertex_color_use_as_albedo = false
|
||||
mat.set_flag(BaseMaterial3D.FLAG_USE_TEXTURE_REPEAT, false)
|
||||
|
||||
var blend_mode: int = mat_def.get("blend_mode", 0)
|
||||
match blend_mode:
|
||||
0:
|
||||
mat.transparency = BaseMaterial3D.TRANSPARENCY_DISABLED
|
||||
1:
|
||||
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA_SCISSOR
|
||||
mat.alpha_scissor_threshold = 0.5
|
||||
3, 4, 6:
|
||||
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||
mat.blend_mode = BaseMaterial3D.BLEND_MODE_ADD
|
||||
mat.depth_draw_mode = BaseMaterial3D.DEPTH_DRAW_OPAQUE_ONLY
|
||||
_:
|
||||
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||
mat.depth_draw_mode = BaseMaterial3D.DEPTH_DRAW_OPAQUE_ONLY
|
||||
var tex_flags := 0
|
||||
var tex: Texture2D = null
|
||||
var tex2: Texture2D = null
|
||||
var tex3: Texture2D = null
|
||||
var tex4: Texture2D = null
|
||||
var tex2_flags := 0
|
||||
var tex3_flags := 0
|
||||
var tex4_flags := 0
|
||||
|
||||
if texture_combo_index >= 0 and texture_combo_index < tex_combos.size():
|
||||
var tex_idx: int = tex_combos[texture_combo_index]
|
||||
if tex_idx >= 0 and tex_idx < textures.size():
|
||||
var tex_path: String = str(textures[tex_idx]).replace("\\", "/")
|
||||
var tex_type := int(texture_types[tex_idx]) if tex_idx < texture_types.size() else 0
|
||||
var tex_flags := int(texture_flags[tex_idx]) if tex_idx < texture_flags.size() else 0
|
||||
if tex_path.is_empty():
|
||||
tex_path = _default_texture_path(model_path, tex_type)
|
||||
if not tex_path.is_empty():
|
||||
var tex := _load_texture(tex_path, extracted_dir)
|
||||
if tex:
|
||||
mat.albedo_texture = tex
|
||||
mat.texture_filter = BaseMaterial3D.TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC
|
||||
mat.set_flag(BaseMaterial3D.FLAG_USE_TEXTURE_REPEAT, (tex_flags & 0x03) != 0)
|
||||
var resolved0 := _resolve_combo_texture(texture_combo_index, textures, texture_types, texture_flags, tex_combos, extracted_dir, model_path)
|
||||
tex = resolved0.get("texture", null)
|
||||
tex_flags = int(resolved0.get("flags", 0))
|
||||
var texture_count := maxi(1, int(batch.get("texture_count", 1)))
|
||||
if texture_count > 1:
|
||||
var resolved1 := _resolve_combo_texture(texture_combo_index + 1, textures, texture_types, texture_flags, tex_combos, extracted_dir, model_path)
|
||||
tex2 = resolved1.get("texture", null)
|
||||
tex2_flags = int(resolved1.get("flags", 0))
|
||||
if texture_count > 2:
|
||||
var resolved2 := _resolve_combo_texture(texture_combo_index + 2, textures, texture_types, texture_flags, tex_combos, extracted_dir, model_path)
|
||||
tex3 = resolved2.get("texture", null)
|
||||
tex3_flags = int(resolved2.get("flags", 0))
|
||||
if texture_count > 3:
|
||||
var resolved3 := _resolve_combo_texture(texture_combo_index + 3, textures, texture_types, texture_flags, tex_combos, extracted_dir, model_path)
|
||||
tex4 = resolved3.get("texture", null)
|
||||
tex4_flags = int(resolved3.get("flags", 0))
|
||||
|
||||
var combiner := _resolve_combiner_state(blend_mode, texture_count, batch, tex_coord_combos, tex_combiner_combos, m2_flags)
|
||||
combiner["mesh_color"] = _resolve_mesh_color(batch, m2_colors, tex_weights, tex_weight_combos)
|
||||
combiner["tex_sample_alpha"] = _resolve_texture_sample_alpha(batch, tex_weights, tex_weight_combos)
|
||||
combiner["stage0_uv_transform"] = _resolve_texture_transform(batch, 0, tex_transform_combos, tex_transforms)
|
||||
combiner["stage1_uv_transform"] = _resolve_texture_transform(batch, 1, tex_transform_combos, tex_transforms)
|
||||
combiner["stage2_uv_transform"] = _resolve_texture_transform(batch, 2, tex_transform_combos, tex_transforms)
|
||||
combiner["stage3_uv_transform"] = _resolve_texture_transform(batch, 3, tex_transform_combos, tex_transforms)
|
||||
combiner["stage0_uv_scroll"] = _resolve_texture_scroll(batch, 0, tex_transform_combos, tex_transforms)
|
||||
combiner["stage1_uv_scroll"] = _resolve_texture_scroll(batch, 1, tex_transform_combos, tex_transforms)
|
||||
combiner["stage2_uv_scroll"] = _resolve_texture_scroll(batch, 2, tex_transform_combos, tex_transforms)
|
||||
combiner["stage3_uv_scroll"] = _resolve_texture_scroll(batch, 3, tex_transform_combos, tex_transforms)
|
||||
combiner["vertex_shader_id"] = _m2_vertex_shader_id(texture_count, int(batch.get("shader_id", 0)))
|
||||
combiner["pixel_shader_id"] = _m2_pixel_shader_id(texture_count, int(batch.get("shader_id", 0)))
|
||||
combiner["priority_plane"] = int(batch.get("priority_plane", 0))
|
||||
if _is_soft_waterfall_material(model_path, blend_mode, combiner):
|
||||
combiner["effect_alpha_scale"] = 1.12
|
||||
combiner["effect_alpha_power"] = 0.92
|
||||
combiner["uv_edge_fade_strength"] = 0.22
|
||||
|
||||
var mat := WOW_M2_MATERIAL.build(
|
||||
tex,
|
||||
tex2,
|
||||
tex3,
|
||||
tex4,
|
||||
flags,
|
||||
blend_mode,
|
||||
tex_flags,
|
||||
tex2_flags,
|
||||
tex3_flags,
|
||||
tex4_flags,
|
||||
model_path.get_file().get_basename(),
|
||||
combiner)
|
||||
mat.set_meta("wow_flags", flags)
|
||||
mat.set_meta("wow_blend_mode", blend_mode)
|
||||
mat.set_meta("wow_m2_shader_id", int(batch.get("shader_id", 0)))
|
||||
mat.set_meta("wow_m2_vertex_shader_id", int(combiner.get("vertex_shader_id", 0)))
|
||||
mat.set_meta("wow_m2_pixel_shader_id", int(combiner.get("pixel_shader_id", 0)))
|
||||
mat.set_meta("wow_priority_plane", int(batch.get("priority_plane", 0)))
|
||||
return mat
|
||||
|
||||
|
||||
static func _is_soft_waterfall_material(model_path: String, blend_mode: int, combiner: Dictionary) -> bool:
|
||||
if blend_mode != 2:
|
||||
return false
|
||||
var lower_path := model_path.to_lower()
|
||||
if not lower_path.contains("waterfall"):
|
||||
return false
|
||||
var scroll0: Vector2 = combiner.get("stage0_uv_scroll", Vector2.ZERO)
|
||||
var scroll1: Vector2 = combiner.get("stage1_uv_scroll", Vector2.ZERO)
|
||||
return scroll0.length_squared() > 0.000001 or scroll1.length_squared() > 0.000001
|
||||
|
||||
|
||||
static func _m2_vertex_shader_id(texture_count: int, shader_id: int) -> int:
|
||||
if (shader_id & 0x8000) != 0:
|
||||
return _m2_shader_array_vertex_id(shader_id & 0x7fff)
|
||||
if texture_count == 1:
|
||||
if (shader_id & 0x80) != 0:
|
||||
return 1 # Diffuse_Env
|
||||
if (shader_id & 0x4000) != 0:
|
||||
return 10 # Diffuse_T2
|
||||
return 0 # Diffuse_T1
|
||||
|
||||
if (shader_id & 0x80) != 0:
|
||||
if (shader_id & 0x8) != 0:
|
||||
return 5 # Diffuse_Env_Env
|
||||
return 4 # Diffuse_Env_T1
|
||||
if (shader_id & 0x8) != 0:
|
||||
return 3 # Diffuse_T1_Env
|
||||
if (shader_id & 0x4000) != 0:
|
||||
return 2 # Diffuse_T1_T2
|
||||
return 7 # Diffuse_T1_T1
|
||||
|
||||
|
||||
static func _m2_pixel_shader_id(texture_count: int, shader_id: int) -> int:
|
||||
if (shader_id & 0x8000) != 0:
|
||||
return _m2_shader_array_pixel_id(shader_id & 0x7fff)
|
||||
if texture_count == 1:
|
||||
return 1 if (shader_id & 0x70) != 0 else 0
|
||||
|
||||
if (shader_id & 0x70) != 0:
|
||||
match shader_id & 7:
|
||||
3:
|
||||
return 8 # Combiners_Mod_Add
|
||||
4:
|
||||
return 7 # Combiners_Mod_Mod2x
|
||||
6:
|
||||
return 9 # Combiners_Mod_Mod2xNA
|
||||
7:
|
||||
return 10 # Combiners_Mod_AddNA
|
||||
_:
|
||||
return 6 # Combiners_Mod_Mod
|
||||
|
||||
match shader_id & 7:
|
||||
0:
|
||||
return 5 # Combiners_Opaque_Opaque
|
||||
3, 7:
|
||||
return 13 # Combiners_Opaque_AddAlpha
|
||||
4:
|
||||
return 3 # Combiners_Opaque_Mod2x
|
||||
6:
|
||||
return 4 # Combiners_Opaque_Mod2xNA
|
||||
_:
|
||||
return 2 # Combiners_Opaque_Mod
|
||||
|
||||
|
||||
static func _m2_shader_array_vertex_id(shader_array_index: int) -> int:
|
||||
match shader_array_index:
|
||||
0:
|
||||
return 8
|
||||
1:
|
||||
return 0
|
||||
2:
|
||||
return 1
|
||||
3:
|
||||
return 1
|
||||
4:
|
||||
return 1
|
||||
5:
|
||||
return 1
|
||||
6:
|
||||
return 0
|
||||
7:
|
||||
return 12
|
||||
8:
|
||||
return 9
|
||||
9:
|
||||
return 12
|
||||
_:
|
||||
return 0
|
||||
|
||||
|
||||
static func _m2_shader_array_pixel_id(shader_array_index: int) -> int:
|
||||
match shader_array_index:
|
||||
0:
|
||||
return 35
|
||||
1:
|
||||
return 0
|
||||
2:
|
||||
return 7
|
||||
3:
|
||||
return 1
|
||||
4:
|
||||
return 6
|
||||
5:
|
||||
return 4
|
||||
6:
|
||||
return 0
|
||||
7:
|
||||
return 7
|
||||
8:
|
||||
return 1
|
||||
9:
|
||||
return 6
|
||||
_:
|
||||
return 0
|
||||
|
||||
|
||||
static func _resolve_combo_texture(
|
||||
combo_index: int,
|
||||
textures: PackedStringArray,
|
||||
texture_types: PackedInt32Array,
|
||||
texture_flags: PackedInt32Array,
|
||||
tex_combos: PackedInt32Array,
|
||||
extracted_dir: String,
|
||||
model_path: String) -> Dictionary:
|
||||
|
||||
if combo_index < 0 or combo_index >= tex_combos.size():
|
||||
return {}
|
||||
var tex_idx: int = tex_combos[combo_index]
|
||||
if tex_idx < 0 or tex_idx >= textures.size():
|
||||
return {}
|
||||
var tex_path: String = str(textures[tex_idx]).replace("\\", "/")
|
||||
var tex_type := int(texture_types[tex_idx]) if tex_idx < texture_types.size() else 0
|
||||
var tex_flags := int(texture_flags[tex_idx]) if tex_idx < texture_flags.size() else 0
|
||||
if tex_path.is_empty():
|
||||
tex_path = _default_texture_path(model_path, tex_type)
|
||||
var tex: Texture2D = null
|
||||
if not tex_path.is_empty():
|
||||
tex = _load_texture(tex_path, extracted_dir)
|
||||
return {
|
||||
"texture": tex,
|
||||
"flags": tex_flags,
|
||||
"path": tex_path,
|
||||
}
|
||||
|
||||
|
||||
static func _resolve_combiner_state(
|
||||
blend_mode: int,
|
||||
texture_count: int,
|
||||
batch: Dictionary,
|
||||
tex_coord_combos: PackedInt32Array,
|
||||
tex_combiner_combos: PackedInt32Array,
|
||||
m2_flags: int) -> Dictionary:
|
||||
|
||||
var state := {
|
||||
"op0": 0 if blend_mode == 0 else 1,
|
||||
"op1": 1,
|
||||
"env0": false,
|
||||
"env1": false,
|
||||
"use_uv2_0": false,
|
||||
"use_uv2_1": false,
|
||||
}
|
||||
var shader_id := int(batch.get("shader_id", 0))
|
||||
var coord_index := int(batch.get("texture_coord_combo_index", 0))
|
||||
if (m2_flags & 0x8) != 0:
|
||||
for texture_index in mini(texture_count, 2):
|
||||
var op := 0 if texture_index == 0 and blend_mode == 0 else 1
|
||||
var combiner_index := shader_id + texture_index
|
||||
if combiner_index >= 0 and combiner_index < tex_combiner_combos.size():
|
||||
op = int(tex_combiner_combos[combiner_index])
|
||||
state["op%d" % texture_index] = op
|
||||
_apply_texture_coord_state(state, texture_index, coord_index + texture_index, tex_coord_combos)
|
||||
else:
|
||||
_apply_texture_coord_state(state, 0, coord_index, tex_coord_combos)
|
||||
return state
|
||||
|
||||
|
||||
static func _apply_texture_coord_state(state: Dictionary, texture_index: int, combo_index: int, tex_coord_combos: PackedInt32Array) -> void:
|
||||
if combo_index < 0 or combo_index >= tex_coord_combos.size():
|
||||
return
|
||||
var coord := int(tex_coord_combos[combo_index])
|
||||
state["use_uv2_%d" % texture_index] = coord == 1
|
||||
state["env%d" % texture_index] = coord > 2
|
||||
|
||||
|
||||
static func _resolve_mesh_color(
|
||||
batch: Dictionary,
|
||||
m2_colors: Array,
|
||||
tex_weights: PackedFloat32Array,
|
||||
tex_weight_combos: PackedInt32Array) -> Color:
|
||||
|
||||
var color := Color.WHITE
|
||||
var color_index := int(batch.get("color_index", -1))
|
||||
if color_index >= 0 and color_index < m2_colors.size():
|
||||
var color_entry: Dictionary = m2_colors[color_index] if m2_colors[color_index] is Dictionary else {}
|
||||
color = color_entry.get("color", Color.WHITE)
|
||||
|
||||
var weight_combo_index := int(batch.get("texture_weight_combo_index", -1))
|
||||
if weight_combo_index >= 0 and weight_combo_index < tex_weight_combos.size():
|
||||
var weight_index := int(tex_weight_combos[weight_combo_index])
|
||||
if weight_index >= 0 and weight_index < tex_weights.size():
|
||||
color.a *= clampf(tex_weights[weight_index], 0.0, 1.0)
|
||||
return color
|
||||
|
||||
|
||||
static func _resolve_texture_sample_alpha(
|
||||
batch: Dictionary,
|
||||
tex_weights: PackedFloat32Array,
|
||||
tex_weight_combos: PackedInt32Array) -> Vector3:
|
||||
|
||||
var result := Vector3.ONE
|
||||
var weight_combo_index := int(batch.get("texture_weight_combo_index", -1))
|
||||
for stage in 3:
|
||||
var combo_index := weight_combo_index + stage
|
||||
if combo_index < 0 or combo_index >= tex_weight_combos.size():
|
||||
continue
|
||||
var weight_index := int(tex_weight_combos[combo_index])
|
||||
if weight_index >= 0 and weight_index < tex_weights.size():
|
||||
result[stage] = clampf(tex_weights[weight_index], 0.0, 1.0)
|
||||
return result
|
||||
|
||||
|
||||
static func _resolve_texture_transform(
|
||||
batch: Dictionary,
|
||||
stage: int,
|
||||
tex_transform_combos: PackedInt32Array,
|
||||
tex_transforms: Array) -> Vector4:
|
||||
|
||||
var combo_index := int(batch.get("texture_transform_combo_index", -1)) + stage
|
||||
if combo_index < 0 or combo_index >= tex_transform_combos.size():
|
||||
return Vector4(1.0, 1.0, 0.0, 0.0)
|
||||
var transform_index := int(tex_transform_combos[combo_index])
|
||||
if transform_index < 0 or transform_index >= tex_transforms.size():
|
||||
return Vector4(1.0, 1.0, 0.0, 0.0)
|
||||
var transform: Dictionary = tex_transforms[transform_index] if tex_transforms[transform_index] is Dictionary else {}
|
||||
var scale: Vector2 = transform.get("scale", Vector2.ONE)
|
||||
var translation: Vector2 = transform.get("translation", Vector2.ZERO)
|
||||
return Vector4(scale.x, scale.y, translation.x, translation.y)
|
||||
|
||||
|
||||
static func _resolve_texture_scroll(
|
||||
batch: Dictionary,
|
||||
stage: int,
|
||||
tex_transform_combos: PackedInt32Array,
|
||||
tex_transforms: Array) -> Vector2:
|
||||
|
||||
var combo_index := int(batch.get("texture_transform_combo_index", -1)) + stage
|
||||
if combo_index < 0 or combo_index >= tex_transform_combos.size():
|
||||
return Vector2.ZERO
|
||||
var transform_index := int(tex_transform_combos[combo_index])
|
||||
if transform_index < 0 or transform_index >= tex_transforms.size():
|
||||
return Vector2.ZERO
|
||||
var transform: Dictionary = tex_transforms[transform_index] if tex_transforms[transform_index] is Dictionary else {}
|
||||
return transform.get("translation_speed", Vector2.ZERO)
|
||||
|
||||
|
||||
static func _default_texture_path(model_path: String, texture_type: int) -> String:
|
||||
var normalized := model_path.replace("\\", "/")
|
||||
var rel_model := _relative_extracted_model_path(normalized)
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
## Builds a runtime-animated M2 node from native M2Loader data.
|
||||
## M2 skinning is evaluated explicitly because WoW's pivot/palette model does
|
||||
## not map cleanly to Godot Skeleton3D/Skin for composite doodads.
|
||||
class_name M2NativeAnimatedBuilder
|
||||
|
||||
const M2_BUILDER_SCRIPT := preload("res://addons/mpq_extractor/loaders/m2_builder.gd")
|
||||
const M2_NATIVE_ANIMATOR_SCRIPT := preload("res://src/scenes/streaming/m2_native_animator.gd")
|
||||
|
||||
|
||||
static func build(data: Dictionary, extracted_dir: String = "") -> Node3D:
|
||||
var root := Node3D.new()
|
||||
root.name = "M2NativeAnimated"
|
||||
|
||||
var bones: Array = data.get("bones", [])
|
||||
var surfaces: Array = data.get("animated_surfaces", [])
|
||||
if bones.is_empty() or surfaces.is_empty():
|
||||
return root
|
||||
|
||||
var mesh := _build_mesh(data, surfaces, extracted_dir)
|
||||
if mesh == null or mesh.get_surface_count() <= 0:
|
||||
return root
|
||||
|
||||
var mesh_instance := MeshInstance3D.new()
|
||||
mesh_instance.name = "Mesh"
|
||||
mesh_instance.mesh = mesh
|
||||
root.add_child(mesh_instance)
|
||||
|
||||
var animator = M2_NATIVE_ANIMATOR_SCRIPT.new()
|
||||
animator.name = "M2NativeAnimator"
|
||||
root.add_child(animator)
|
||||
animator.setup(mesh_instance, bones, surfaces, float(data.get("animation_length", 0.0)))
|
||||
return root
|
||||
|
||||
|
||||
static func _build_mesh(data: Dictionary, surfaces: Array, extracted_dir: String) -> ArrayMesh:
|
||||
var mesh := ArrayMesh.new()
|
||||
var textures: PackedStringArray = data.get("textures", PackedStringArray())
|
||||
var texture_types: PackedInt32Array = data.get("texture_types", PackedInt32Array())
|
||||
var texture_flags: PackedInt32Array = data.get("texture_flags", PackedInt32Array())
|
||||
var materials: Array = data.get("materials", [])
|
||||
var tex_combos: PackedInt32Array = data.get("texture_combos", PackedInt32Array())
|
||||
var tex_coord_combos: PackedInt32Array = data.get("texture_coord_combos", PackedInt32Array())
|
||||
var tex_weight_combos: PackedInt32Array = data.get("texture_weight_combos", PackedInt32Array())
|
||||
var tex_transform_combos: PackedInt32Array = data.get("texture_transform_combos", PackedInt32Array())
|
||||
var tex_combiner_combos: PackedInt32Array = data.get("texture_combiner_combos", PackedInt32Array())
|
||||
var m2_colors: Array = data.get("m2_colors", [])
|
||||
var tex_weights: PackedFloat32Array = data.get("texture_weights", PackedFloat32Array())
|
||||
var tex_transforms: Array = data.get("texture_transforms", [])
|
||||
var m2_flags: int = int(data.get("m2_flags", 0))
|
||||
var model_path := String(data.get("model_path", ""))
|
||||
|
||||
for surface_variant in surfaces:
|
||||
if not (surface_variant is Dictionary):
|
||||
continue
|
||||
var surface: Dictionary = surface_variant
|
||||
var verts: PackedVector3Array = surface.get("vertices", PackedVector3Array())
|
||||
var indices: PackedInt32Array = surface.get("indices", PackedInt32Array())
|
||||
if verts.is_empty() or indices.is_empty():
|
||||
continue
|
||||
|
||||
var arrays := []
|
||||
arrays.resize(Mesh.ARRAY_MAX)
|
||||
arrays[Mesh.ARRAY_VERTEX] = verts
|
||||
var normals: PackedVector3Array = surface.get("normals", PackedVector3Array())
|
||||
if normals.size() == verts.size():
|
||||
arrays[Mesh.ARRAY_NORMAL] = normals
|
||||
var uvs: PackedVector2Array = surface.get("uvs", PackedVector2Array())
|
||||
if uvs.size() == verts.size():
|
||||
arrays[Mesh.ARRAY_TEX_UV] = uvs
|
||||
var uvs2: PackedVector2Array = surface.get("uvs2", PackedVector2Array())
|
||||
if uvs2.size() == verts.size():
|
||||
arrays[Mesh.ARRAY_TEX_UV2] = uvs2
|
||||
arrays[Mesh.ARRAY_INDEX] = indices
|
||||
|
||||
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
|
||||
var surface_index := mesh.get_surface_count() - 1
|
||||
var material_id := int(surface.get("material_id", -1))
|
||||
var texture_combo_index := int(surface.get("texture_combo_index", -1))
|
||||
var mat_def: Dictionary = materials[material_id] if material_id >= 0 and material_id < materials.size() else {}
|
||||
mesh.surface_set_material(
|
||||
surface_index,
|
||||
M2_BUILDER_SCRIPT._build_material(
|
||||
mat_def,
|
||||
texture_combo_index,
|
||||
textures,
|
||||
texture_types,
|
||||
texture_flags,
|
||||
tex_combos,
|
||||
tex_coord_combos,
|
||||
tex_weight_combos,
|
||||
tex_transform_combos,
|
||||
tex_combiner_combos,
|
||||
m2_colors,
|
||||
tex_weights,
|
||||
tex_transforms,
|
||||
m2_flags,
|
||||
extracted_dir,
|
||||
model_path,
|
||||
surface))
|
||||
|
||||
return mesh
|
||||
@@ -0,0 +1 @@
|
||||
uid://dngobeqkb20pc
|
||||
@@ -7,7 +7,9 @@ class_name WMOBuilder
|
||||
|
||||
const M2_BUILDER_SCRIPT := preload("res://addons/mpq_extractor/loaders/m2_builder.gd")
|
||||
const WOW_LIQUID_MATERIAL := preload("res://addons/mpq_extractor/loaders/wow_liquid_material.gd")
|
||||
const M2_RIGHT_YAW_OFFSET := PI * 0.5
|
||||
const WOW_WMO_MATERIAL := preload("res://addons/mpq_extractor/loaders/wow_wmo_material.gd")
|
||||
const WMO_BUILDER_FORMAT_VERSION := 2
|
||||
const WMO_BUILDER_FORMAT_META := "openwc_wmo_builder_format_version"
|
||||
const BUILD_OCCLUDERS := false
|
||||
const OCCLUDER_MIN_TRIANGLES := 16
|
||||
|
||||
@@ -29,11 +31,20 @@ static func clear_caches() -> void:
|
||||
_m2_missing_cache.clear()
|
||||
_texture_cache.clear()
|
||||
WOW_LIQUID_MATERIAL.clear_cache()
|
||||
WOW_WMO_MATERIAL.clear_cache()
|
||||
|
||||
|
||||
static func is_scene_cache_current(root: Node) -> bool:
|
||||
if root == null:
|
||||
return false
|
||||
return int(root.get_meta(WMO_BUILDER_FORMAT_META, 0)) >= WMO_BUILDER_FORMAT_VERSION
|
||||
|
||||
|
||||
# Returns a Node3D containing one MeshInstance3D per WMO group.
|
||||
static func build(data: Dictionary, extracted_dir: String = "") -> Node3D:
|
||||
var root := Node3D.new()
|
||||
root.name = "WMO"
|
||||
root.set_meta(WMO_BUILDER_FORMAT_META, WMO_BUILDER_FORMAT_VERSION)
|
||||
|
||||
if not data.has("groups"):
|
||||
return root
|
||||
@@ -42,7 +53,7 @@ static func build(data: Dictionary, extracted_dir: String = "") -> Node3D:
|
||||
var materials: Array = data.get("materials", [])
|
||||
|
||||
# Build Godot materials from WMO material definitions
|
||||
var godot_materials: Array[StandardMaterial3D] = []
|
||||
var godot_materials: Array[Material] = []
|
||||
for mat_def in materials:
|
||||
godot_materials.append(_build_material(mat_def, textures, extracted_dir))
|
||||
|
||||
@@ -110,7 +121,10 @@ static func _build_default_doodads(root: Node3D, data: Dictionary,
|
||||
var pos: Vector3 = p.get("pos", Vector3.ZERO)
|
||||
var rot_q: Quaternion = p.get("rot", Quaternion.IDENTITY)
|
||||
var scl: float = float(p.get("scale", 1.0))
|
||||
var basis := (Basis(rot_q) * Basis(Vector3.UP, M2_RIGHT_YAW_OFFSET)).scaled(Vector3.ONE * maxf(scl, 0.001))
|
||||
# MODD is already a local WMO doodad transform. Do not apply the ADT M2
|
||||
# yaw correction here; it rotates anchored effects like waterfalls away
|
||||
# from their authored cliff attachment points.
|
||||
var basis := Basis(rot_q).scaled(Vector3.ONE * maxf(scl, 0.001))
|
||||
var xform := Transform3D(basis, pos)
|
||||
if not groups.has(rel_path):
|
||||
groups[rel_path] = []
|
||||
@@ -207,7 +221,7 @@ static func _build_raw_m2_prototype(rel_path: String, extracted_dir: String) ->
|
||||
|
||||
static func _build_group_mesh(
|
||||
g: Dictionary,
|
||||
godot_mats: Array[StandardMaterial3D]) -> MeshInstance3D:
|
||||
godot_mats: Array[Material]) -> MeshInstance3D:
|
||||
|
||||
var verts: PackedVector3Array = g.get("vertices", PackedVector3Array())
|
||||
var normals: PackedVector3Array = g.get("normals", PackedVector3Array())
|
||||
@@ -331,37 +345,54 @@ static func _material_can_occlude(mat_id: int, material_defs: Array) -> bool:
|
||||
static func _build_material(
|
||||
mat_def: Dictionary,
|
||||
textures: PackedStringArray,
|
||||
extracted_dir: String = "") -> StandardMaterial3D:
|
||||
|
||||
var mat := StandardMaterial3D.new()
|
||||
mat.cull_mode = BaseMaterial3D.CULL_DISABLED
|
||||
mat.roughness = 0.85
|
||||
mat.metallic = 0.0
|
||||
mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||
mat.specular_mode = BaseMaterial3D.SPECULAR_DISABLED
|
||||
mat.vertex_color_use_as_albedo = false
|
||||
mat.set_flag(BaseMaterial3D.FLAG_USE_TEXTURE_REPEAT, true)
|
||||
extracted_dir: String = "") -> Material:
|
||||
|
||||
var blend_mode: int = mat_def.get("blend_mode", 0)
|
||||
match blend_mode:
|
||||
0: mat.transparency = BaseMaterial3D.TRANSPARENCY_DISABLED
|
||||
1: mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA_SCISSOR
|
||||
_: mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||
var flags: int = int(mat_def.get("flags", 0))
|
||||
var shader_id: int = int(mat_def.get("shader", 0))
|
||||
var tex: Texture2D = null
|
||||
var tex2: Texture2D = null
|
||||
var tex3: Texture2D = null
|
||||
var tex_path := ""
|
||||
var tex2_path := ""
|
||||
var tex3_path := ""
|
||||
|
||||
# Texture is loaded later by the scene assembler (needs BLP→Image conversion)
|
||||
# Store the path as metadata for the assembler to pick up
|
||||
var tex0_id: int = mat_def.get("texture0", -1)
|
||||
if tex0_id >= 0 and tex0_id < textures.size():
|
||||
var tex_path: String = str(textures[tex0_id]).replace("\\", "/")
|
||||
mat.set_meta("texture0_path", tex_path)
|
||||
var tex := _load_texture(tex_path, extracted_dir)
|
||||
if tex:
|
||||
mat.albedo_texture = tex
|
||||
mat.texture_filter = BaseMaterial3D.TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC
|
||||
mat.set_flag(BaseMaterial3D.FLAG_USE_TEXTURE_REPEAT, true)
|
||||
tex_path = str(textures[tex0_id]).replace("\\", "/")
|
||||
tex = _load_texture(tex_path, extracted_dir)
|
||||
var tex1_id: int = mat_def.get("texture1", -1)
|
||||
if tex1_id >= 0 and tex1_id < textures.size():
|
||||
tex2_path = str(textures[tex1_id]).replace("\\", "/")
|
||||
tex2 = _load_texture(tex2_path, extracted_dir)
|
||||
var tex2_id: int = mat_def.get("texture2", -1)
|
||||
if tex2_id >= 0 and tex2_id < textures.size():
|
||||
tex3_path = str(textures[tex2_id]).replace("\\", "/")
|
||||
tex3 = _load_texture(tex3_path, extracted_dir)
|
||||
|
||||
mat.set_meta("wow_flags", mat_def.get("flags", 0))
|
||||
mat.set_meta("wow_shader", mat_def.get("shader", 0))
|
||||
var diffuse_color: Color = mat_def.get("diffuse_color", Color.WHITE)
|
||||
var emissive_color: Color = mat_def.get("emissive_color", Color.BLACK)
|
||||
var secondary_color: Color = mat_def.get("color2", Color.WHITE)
|
||||
var mat := WOW_WMO_MATERIAL.build(
|
||||
tex,
|
||||
tex2,
|
||||
flags,
|
||||
shader_id,
|
||||
blend_mode,
|
||||
tex_path,
|
||||
tex2_path,
|
||||
diffuse_color,
|
||||
emissive_color,
|
||||
secondary_color,
|
||||
tex3,
|
||||
tex3_path)
|
||||
mat.set_meta("texture0_path", tex_path)
|
||||
mat.set_meta("texture1_path", tex2_path)
|
||||
mat.set_meta("texture2_path", tex3_path)
|
||||
mat.set_meta("wow_flags", flags)
|
||||
mat.set_meta("wow_shader", shader_id)
|
||||
|
||||
return mat
|
||||
|
||||
|
||||
@@ -9,6 +9,10 @@ static func clear_cache() -> void:
|
||||
_cache.clear()
|
||||
|
||||
|
||||
static func _ensure_wow_shader_globals() -> void:
|
||||
pass
|
||||
|
||||
|
||||
static func build(liquid_id: int) -> ShaderMaterial:
|
||||
var key := "adt:%d" % liquid_id
|
||||
if _cache.has(key):
|
||||
@@ -108,6 +112,7 @@ static func _get_shader() -> Shader:
|
||||
if _shader:
|
||||
return _shader
|
||||
|
||||
_ensure_wow_shader_globals()
|
||||
_shader = Shader.new()
|
||||
_shader.code = """
|
||||
shader_type spatial;
|
||||
@@ -123,7 +128,12 @@ uniform float fresnel_power = 2.3;
|
||||
uniform float emission_strength = 0.025;
|
||||
uniform float magma_mode = 0.0;
|
||||
|
||||
global uniform vec4 wow_fog_color;
|
||||
global uniform vec2 wow_fog_range;
|
||||
global uniform float wow_fog_density;
|
||||
|
||||
varying vec3 world_pos;
|
||||
varying vec3 view_pos;
|
||||
|
||||
float hash21(vec2 p) {
|
||||
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
|
||||
@@ -140,8 +150,16 @@ float noise21(vec2 p) {
|
||||
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
|
||||
}
|
||||
|
||||
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, 1.0);
|
||||
return mix(color, wow_fog_color.rgb, fog_amount);
|
||||
}
|
||||
|
||||
void vertex() {
|
||||
world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||
view_pos = (MODELVIEW_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||
float t = TIME * wave_speed;
|
||||
float wave_a = sin(world_pos.x * wave_scale + t);
|
||||
float wave_b = cos(world_pos.z * wave_scale * 1.37 - t * 1.21);
|
||||
@@ -170,7 +188,7 @@ void fragment() {
|
||||
EMISSION = color * emission_strength;
|
||||
}
|
||||
|
||||
ALBEDO = color;
|
||||
ALBEDO = apply_wow_fog(color);
|
||||
ALPHA = clamp(alpha_base + fresnel * 0.18 + ripple * 0.06, 0.0, 1.0);
|
||||
}
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,517 @@
|
||||
extends RefCounted
|
||||
class_name WowM2Material
|
||||
|
||||
const M2BLEND_OPAQUE := 0
|
||||
const M2BLEND_ALPHA_KEY := 1
|
||||
const M2BLEND_ALPHA := 2
|
||||
const M2BLEND_NO_ALPHA_ADD := 3
|
||||
const M2BLEND_ADD := 4
|
||||
const M2BLEND_MOD := 5
|
||||
const M2BLEND_MOD_2X := 6
|
||||
|
||||
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,
|
||||
texture3: Texture2D,
|
||||
texture4: Texture2D,
|
||||
material_flags: int,
|
||||
blend_mode: int,
|
||||
texture_flags: int,
|
||||
texture2_flags: int,
|
||||
texture3_flags: int,
|
||||
texture4_flags: int,
|
||||
resource_name: String = "",
|
||||
combiner: Dictionary = {}) -> ShaderMaterial:
|
||||
|
||||
var two_sided := (material_flags & 0x04) != 0
|
||||
var unlit := (material_flags & 0x01) != 0 or blend_mode >= M2BLEND_MOD
|
||||
var depth_test := (material_flags & 0x08) == 0
|
||||
var depth_write := (material_flags & 0x10) == 0
|
||||
var repeat_enabled := (
|
||||
(texture_flags & 0x03) != 0
|
||||
or (texture2_flags & 0x03) != 0
|
||||
or (texture3_flags & 0x03) != 0
|
||||
or (texture4_flags & 0x03) != 0
|
||||
)
|
||||
var mode := _blend_shader_mode(blend_mode)
|
||||
|
||||
_ensure_wow_shader_globals()
|
||||
var mat := ShaderMaterial.new()
|
||||
mat.resource_name = resource_name
|
||||
mat.shader = _get_shader(mode, two_sided, depth_test, depth_write, repeat_enabled, texture2 != null, texture3 != null, texture4 != null)
|
||||
mat.render_priority = clampi(_render_priority(blend_mode) + int(combiner.get("priority_plane", 0)), -128, 127)
|
||||
mat.set_shader_parameter("albedo_tex", texture)
|
||||
mat.set_shader_parameter("stage2_tex", texture2)
|
||||
mat.set_shader_parameter("stage3_tex", texture3)
|
||||
mat.set_shader_parameter("stage4_tex", texture4)
|
||||
mat.set_shader_parameter("use_texture", texture != null)
|
||||
mat.set_shader_parameter("use_stage2", texture2 != null)
|
||||
mat.set_shader_parameter("use_stage3", texture3 != null)
|
||||
mat.set_shader_parameter("use_stage4", texture4 != null)
|
||||
mat.set_shader_parameter("alpha_ref", _alpha_ref(blend_mode))
|
||||
mat.set_shader_parameter("unlit_strength", 1.0 if unlit else 0.0)
|
||||
mat.set_shader_parameter("mesh_color", combiner.get("mesh_color", Color.WHITE))
|
||||
mat.set_shader_parameter("tex_sample_alpha", combiner.get("tex_sample_alpha", Vector3.ONE))
|
||||
mat.set_shader_parameter("ambient_color", Color(0.45, 0.45, 0.42, 1.0))
|
||||
mat.set_shader_parameter("light_color", Color(0.82, 0.78, 0.68, 1.0))
|
||||
mat.set_shader_parameter("light_dir", Vector3(-0.45, -0.75, -0.35).normalized())
|
||||
mat.set_shader_parameter("diffuse_strength", 0.72)
|
||||
mat.set_shader_parameter("modulate_2x", 1.0 if blend_mode == M2BLEND_MOD_2X else 0.0)
|
||||
mat.set_shader_parameter("vertex_shader_id", int(combiner.get("vertex_shader_id", 0)))
|
||||
mat.set_shader_parameter("pixel_shader_id", int(combiner.get("pixel_shader_id", 0)))
|
||||
mat.set_shader_parameter("stage0_op", int(combiner.get("op0", 0 if blend_mode == 0 else 1)))
|
||||
mat.set_shader_parameter("stage1_op", int(combiner.get("op1", 1)))
|
||||
mat.set_shader_parameter("stage0_env", bool(combiner.get("env0", false)))
|
||||
mat.set_shader_parameter("stage1_env", bool(combiner.get("env1", false)))
|
||||
mat.set_shader_parameter("stage0_uv2", bool(combiner.get("use_uv2_0", false)))
|
||||
mat.set_shader_parameter("stage1_uv2", bool(combiner.get("use_uv2_1", false)))
|
||||
mat.set_shader_parameter("stage0_uv_transform", combiner.get("stage0_uv_transform", Vector4(1.0, 1.0, 0.0, 0.0)))
|
||||
mat.set_shader_parameter("stage1_uv_transform", combiner.get("stage1_uv_transform", Vector4(1.0, 1.0, 0.0, 0.0)))
|
||||
mat.set_shader_parameter("stage2_uv_transform", combiner.get("stage2_uv_transform", Vector4(1.0, 1.0, 0.0, 0.0)))
|
||||
mat.set_shader_parameter("stage3_uv_transform", combiner.get("stage3_uv_transform", Vector4(1.0, 1.0, 0.0, 0.0)))
|
||||
mat.set_shader_parameter("stage0_uv_scroll", combiner.get("stage0_uv_scroll", Vector2.ZERO))
|
||||
mat.set_shader_parameter("stage1_uv_scroll", combiner.get("stage1_uv_scroll", Vector2.ZERO))
|
||||
mat.set_shader_parameter("stage2_uv_scroll", combiner.get("stage2_uv_scroll", Vector2.ZERO))
|
||||
mat.set_shader_parameter("stage3_uv_scroll", combiner.get("stage3_uv_scroll", Vector2.ZERO))
|
||||
mat.set_shader_parameter("effect_alpha_scale", combiner.get("effect_alpha_scale", 1.0))
|
||||
mat.set_shader_parameter("effect_alpha_power", combiner.get("effect_alpha_power", 1.0))
|
||||
mat.set_shader_parameter("uv_edge_fade_strength", combiner.get("uv_edge_fade_strength", 0.0))
|
||||
mat.set_meta("wow_flags", material_flags)
|
||||
mat.set_meta("wow_blend_mode", blend_mode)
|
||||
mat.set_meta("wow_priority_plane", int(combiner.get("priority_plane", 0)))
|
||||
mat.set_meta("wow_texture_flags", texture_flags)
|
||||
mat.set_meta("wow_texture2_flags", texture2_flags)
|
||||
mat.set_meta("wow_texture3_flags", texture3_flags)
|
||||
mat.set_meta("wow_texture4_flags", texture4_flags)
|
||||
return mat
|
||||
|
||||
|
||||
static func _blend_shader_mode(blend_mode: int) -> String:
|
||||
match blend_mode:
|
||||
M2BLEND_ALPHA_KEY:
|
||||
return "cutout"
|
||||
M2BLEND_ALPHA:
|
||||
return "alpha"
|
||||
M2BLEND_NO_ALPHA_ADD, M2BLEND_ADD:
|
||||
return "add"
|
||||
M2BLEND_MOD, M2BLEND_MOD_2X:
|
||||
return "mul"
|
||||
_:
|
||||
return "opaque"
|
||||
|
||||
|
||||
static func _render_priority(blend_mode: int) -> int:
|
||||
if blend_mode <= M2BLEND_ALPHA_KEY:
|
||||
return 0
|
||||
if blend_mode >= M2BLEND_MOD:
|
||||
return 2
|
||||
return 1
|
||||
|
||||
|
||||
static func _alpha_ref(blend_mode: int) -> float:
|
||||
if blend_mode == M2BLEND_ALPHA_KEY:
|
||||
return 0.87843138
|
||||
if blend_mode == M2BLEND_OPAQUE:
|
||||
return 0.0
|
||||
return 0.0039215689
|
||||
|
||||
|
||||
static func _get_shader(
|
||||
mode: String,
|
||||
two_sided: bool,
|
||||
depth_test: bool,
|
||||
depth_write: bool,
|
||||
repeat_enabled: bool,
|
||||
has_stage2: bool,
|
||||
has_stage3: bool,
|
||||
has_stage4: bool) -> Shader:
|
||||
|
||||
var key := "%s|sided=%s|dt=%s|dw=%s|repeat=%s" % [
|
||||
mode,
|
||||
str(two_sided),
|
||||
str(depth_test),
|
||||
str(depth_write),
|
||||
str(repeat_enabled),
|
||||
]
|
||||
key += "|stage2=%s" % str(has_stage2)
|
||||
key += "|stage3=%s|stage4=%s" % [str(has_stage3), str(has_stage4)]
|
||||
if _shader_cache.has(key):
|
||||
return _shader_cache[key]
|
||||
|
||||
var shader := Shader.new()
|
||||
shader.code = _shader_code(mode, two_sided, depth_test, depth_write, repeat_enabled, has_stage2, has_stage3, has_stage4)
|
||||
_shader_cache[key] = shader
|
||||
return shader
|
||||
|
||||
|
||||
static func _shader_code(
|
||||
mode: String,
|
||||
two_sided: bool,
|
||||
depth_test: bool,
|
||||
depth_write: bool,
|
||||
repeat_enabled: bool,
|
||||
has_stage2: bool,
|
||||
has_stage3: bool,
|
||||
has_stage4: bool) -> String:
|
||||
|
||||
var render_modes: Array[String] = []
|
||||
match mode:
|
||||
"alpha":
|
||||
render_modes.append("blend_mix")
|
||||
"add":
|
||||
render_modes.append("blend_add")
|
||||
"mul":
|
||||
render_modes.append("blend_mul")
|
||||
_:
|
||||
render_modes.append("blend_mix")
|
||||
|
||||
render_modes.append("cull_disabled" if two_sided else "cull_back")
|
||||
if not depth_test:
|
||||
render_modes.append("depth_test_disabled")
|
||||
if not depth_write:
|
||||
render_modes.append("depth_draw_never")
|
||||
elif mode == "opaque" or mode == "cutout":
|
||||
render_modes.append("depth_draw_opaque")
|
||||
else:
|
||||
render_modes.append("depth_draw_never")
|
||||
render_modes.append("unshaded")
|
||||
|
||||
var repeat_hint := "repeat_enable" if repeat_enabled else "repeat_disable"
|
||||
return """
|
||||
shader_type spatial;
|
||||
render_mode %s;
|
||||
|
||||
uniform sampler2D albedo_tex : source_color, filter_linear_mipmap_anisotropic, %s;
|
||||
uniform sampler2D stage2_tex : source_color, filter_linear_mipmap_anisotropic, %s;
|
||||
uniform sampler2D stage3_tex : source_color, filter_linear_mipmap_anisotropic, %s;
|
||||
uniform sampler2D stage4_tex : source_color, filter_linear_mipmap_anisotropic, %s;
|
||||
uniform bool use_texture = false;
|
||||
uniform bool use_stage2 = false;
|
||||
uniform bool use_stage3 = false;
|
||||
uniform bool use_stage4 = false;
|
||||
uniform float alpha_ref = 0.0;
|
||||
uniform float unlit_strength = 0.0;
|
||||
uniform vec4 mesh_color : source_color = vec4(1.0);
|
||||
uniform vec3 tex_sample_alpha = vec3(1.0);
|
||||
uniform vec4 ambient_color : source_color = vec4(0.45, 0.45, 0.42, 1.0);
|
||||
uniform vec4 light_color : source_color = vec4(0.82, 0.78, 0.68, 1.0);
|
||||
uniform vec3 light_dir = vec3(-0.45, -0.75, -0.35);
|
||||
uniform float diffuse_strength = 0.72;
|
||||
uniform float modulate_2x = 0.0;
|
||||
uniform int vertex_shader_id = 0;
|
||||
uniform int pixel_shader_id = 0;
|
||||
uniform int stage0_op = 0;
|
||||
uniform int stage1_op = 1;
|
||||
uniform bool stage0_env = false;
|
||||
uniform bool stage1_env = false;
|
||||
uniform bool stage0_uv2 = false;
|
||||
uniform bool stage1_uv2 = false;
|
||||
uniform vec4 stage0_uv_transform = vec4(1.0, 1.0, 0.0, 0.0);
|
||||
uniform vec4 stage1_uv_transform = vec4(1.0, 1.0, 0.0, 0.0);
|
||||
uniform vec4 stage2_uv_transform = vec4(1.0, 1.0, 0.0, 0.0);
|
||||
uniform vec4 stage3_uv_transform = vec4(1.0, 1.0, 0.0, 0.0);
|
||||
uniform vec2 stage0_uv_scroll = vec2(0.0);
|
||||
uniform vec2 stage1_uv_scroll = vec2(0.0);
|
||||
uniform vec2 stage2_uv_scroll = vec2(0.0);
|
||||
uniform vec2 stage3_uv_scroll = vec2(0.0);
|
||||
uniform float effect_alpha_scale = 1.0;
|
||||
uniform float effect_alpha_power = 1.0;
|
||||
uniform float uv_edge_fade_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;
|
||||
varying vec2 uv_interp;
|
||||
varying vec2 uv2_interp;
|
||||
varying float edge_fade;
|
||||
|
||||
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;
|
||||
uv_interp = UV;
|
||||
uv2_interp = UV2;
|
||||
vec3 view_normal = normalize((MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz);
|
||||
float edge_scan = 1.0 - abs(dot(normalize(-view_pos), view_normal));
|
||||
edge_fade = (vertex_shader_id == 9 || vertex_shader_id == 12 || vertex_shader_id == 13) ? clamp(edge_scan * 2.5, 0.0, 1.0) : 1.0;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
vec2 transform_uv(vec2 uv, vec4 uv_transform, vec2 uv_scroll) {
|
||||
return uv * uv_transform.xy + uv_transform.zw + uv_scroll * TIME;
|
||||
}
|
||||
|
||||
vec2 env_uv(vec3 normal) {
|
||||
vec3 n = normalize(normal);
|
||||
return n.xy * 0.5 + vec2(0.5);
|
||||
}
|
||||
|
||||
vec2 stage_uv(int stage, vec3 normal) {
|
||||
vec2 t1 = transform_uv(uv_interp, stage0_uv_transform, stage0_uv_scroll);
|
||||
vec2 t2 = transform_uv(uv2_interp, stage1_uv_transform, stage1_uv_scroll);
|
||||
vec2 t3 = transform_uv(uv2_interp, stage2_uv_transform, stage2_uv_scroll);
|
||||
vec2 t4 = transform_uv(uv2_interp, stage3_uv_transform, stage3_uv_scroll);
|
||||
vec2 env = env_uv(normal);
|
||||
|
||||
if (vertex_shader_id == 1) {
|
||||
return env;
|
||||
}
|
||||
if (vertex_shader_id == 2) {
|
||||
return stage == 0 ? t1 : t2;
|
||||
}
|
||||
if (vertex_shader_id == 3) {
|
||||
return stage == 0 ? t1 : env;
|
||||
}
|
||||
if (vertex_shader_id == 4) {
|
||||
return stage == 0 ? env : t1;
|
||||
}
|
||||
if (vertex_shader_id == 5) {
|
||||
return env;
|
||||
}
|
||||
if (vertex_shader_id == 6) {
|
||||
return stage == 1 ? env : t1;
|
||||
}
|
||||
if (vertex_shader_id == 7 || vertex_shader_id == 8) {
|
||||
return t1;
|
||||
}
|
||||
if (vertex_shader_id == 10) {
|
||||
return t2;
|
||||
}
|
||||
if (vertex_shader_id == 11) {
|
||||
return stage == 1 ? env : (stage == 2 ? t2 : t1);
|
||||
}
|
||||
if (vertex_shader_id == 12) {
|
||||
return stage == 0 ? t1 : t2;
|
||||
}
|
||||
if (vertex_shader_id == 13) {
|
||||
return env;
|
||||
}
|
||||
if (vertex_shader_id == 14) {
|
||||
return stage == 1 ? t2 : t1;
|
||||
}
|
||||
if (vertex_shader_id == 15) {
|
||||
return stage == 0 ? t1 : (stage == 1 ? t2 : uv2_interp);
|
||||
}
|
||||
if (vertex_shader_id == 16) {
|
||||
return stage == 0 ? t2 : uv2_interp;
|
||||
}
|
||||
if (stage == 2) {
|
||||
return t3;
|
||||
}
|
||||
if (stage == 3) {
|
||||
return t4;
|
||||
}
|
||||
if (stage == 0) {
|
||||
if (stage0_env) {
|
||||
return env;
|
||||
}
|
||||
return transform_uv(stage0_uv2 ? uv2_interp : uv_interp, stage0_uv_transform, stage0_uv_scroll);
|
||||
}
|
||||
if (stage1_env) {
|
||||
return env;
|
||||
}
|
||||
return transform_uv(stage1_uv2 ? uv2_interp : uv_interp, stage1_uv_transform, stage1_uv_scroll);
|
||||
}
|
||||
|
||||
void resolve_m2_pixel(vec4 tex1, vec4 tex2, vec4 tex3, vec4 tex4, out vec3 diffuse, out vec3 specular, out float discard_alpha, out float can_discard) {
|
||||
vec3 mesh_rgb = mesh_color.rgb;
|
||||
diffuse = mesh_rgb * tex1.rgb;
|
||||
specular = vec3(0.0);
|
||||
discard_alpha = tex1.a * mesh_color.a;
|
||||
can_discard = 0.0;
|
||||
|
||||
if (pixel_shader_id == 0) {
|
||||
diffuse = mesh_rgb * tex1.rgb;
|
||||
} else if (pixel_shader_id == 1) {
|
||||
diffuse = mesh_rgb * tex1.rgb;
|
||||
discard_alpha = tex1.a * mesh_color.a;
|
||||
can_discard = 1.0;
|
||||
} else if (pixel_shader_id == 2) {
|
||||
diffuse = mesh_rgb * tex1.rgb * tex2.rgb;
|
||||
discard_alpha = tex2.a * mesh_color.a;
|
||||
can_discard = 1.0;
|
||||
} else if (pixel_shader_id == 3) {
|
||||
diffuse = mesh_rgb * tex1.rgb * tex2.rgb * 2.0;
|
||||
discard_alpha = tex2.a * 2.0 * mesh_color.a;
|
||||
can_discard = 1.0;
|
||||
} else if (pixel_shader_id == 4) {
|
||||
diffuse = mesh_rgb * tex1.rgb * tex2.rgb * 2.0;
|
||||
} else if (pixel_shader_id == 5) {
|
||||
diffuse = mesh_rgb * tex1.rgb * tex2.rgb;
|
||||
} else if (pixel_shader_id == 6) {
|
||||
diffuse = mesh_rgb * tex1.rgb * tex2.rgb;
|
||||
discard_alpha = tex1.a * tex2.a * mesh_color.a;
|
||||
can_discard = 1.0;
|
||||
} else if (pixel_shader_id == 7) {
|
||||
diffuse = mesh_rgb * tex1.rgb * tex2.rgb * 2.0;
|
||||
discard_alpha = tex1.a * tex2.a * 2.0 * mesh_color.a;
|
||||
can_discard = 1.0;
|
||||
} else if (pixel_shader_id == 8) {
|
||||
diffuse = mesh_rgb * tex1.rgb;
|
||||
specular = tex2.rgb;
|
||||
discard_alpha = (tex1.a + tex2.a) * mesh_color.a;
|
||||
can_discard = 1.0;
|
||||
} else if (pixel_shader_id == 9) {
|
||||
diffuse = mesh_rgb * tex1.rgb * tex2.rgb * 2.0;
|
||||
discard_alpha = tex1.a * mesh_color.a;
|
||||
can_discard = 1.0;
|
||||
} else if (pixel_shader_id == 10) {
|
||||
diffuse = mesh_rgb * tex1.rgb;
|
||||
specular = tex2.rgb;
|
||||
discard_alpha = tex1.a * mesh_color.a;
|
||||
can_discard = 1.0;
|
||||
} else if (pixel_shader_id == 11) {
|
||||
diffuse = mesh_rgb * tex1.rgb * tex2.rgb;
|
||||
discard_alpha = tex1.a * mesh_color.a;
|
||||
can_discard = 1.0;
|
||||
} else if (pixel_shader_id == 12) {
|
||||
diffuse = mesh_rgb * mix(tex1.rgb * tex2.rgb * 2.0, tex1.rgb, vec3(tex1.a));
|
||||
} else if (pixel_shader_id == 13) {
|
||||
diffuse = mesh_rgb * tex1.rgb;
|
||||
specular = tex2.rgb * tex2.a;
|
||||
} else if (pixel_shader_id == 14) {
|
||||
diffuse = mesh_rgb * tex1.rgb;
|
||||
specular = tex2.rgb * tex2.a * (1.0 - tex1.a);
|
||||
} else if (pixel_shader_id == 15) {
|
||||
diffuse = mesh_rgb * mix(tex1.rgb * tex2.rgb * 2.0, tex1.rgb, vec3(tex1.a));
|
||||
specular = tex3.rgb * tex3.a * tex_sample_alpha.b;
|
||||
} else if (pixel_shader_id == 16) {
|
||||
diffuse = mesh_rgb * tex1.rgb;
|
||||
specular = tex2.rgb * tex2.a;
|
||||
discard_alpha = tex1.a * mesh_color.a;
|
||||
can_discard = 1.0;
|
||||
} else if (pixel_shader_id == 17) {
|
||||
diffuse = mesh_rgb * tex1.rgb;
|
||||
specular = tex2.rgb * tex2.a * (1.0 - tex1.a);
|
||||
discard_alpha = (tex1.a + tex2.a * dot(tex2.rgb, vec3(0.3, 0.59, 0.11))) * mesh_color.a;
|
||||
can_discard = 1.0;
|
||||
} else if (pixel_shader_id == 18) {
|
||||
diffuse = mesh_rgb * mix(mix(tex1.rgb, tex2.rgb, vec3(tex2.a)), tex1.rgb, vec3(tex1.a));
|
||||
} else if (pixel_shader_id == 19) {
|
||||
diffuse = mesh_rgb * mix(tex1.rgb * tex2.rgb * 2.0, tex3.rgb, vec3(tex3.a));
|
||||
} else if (pixel_shader_id == 21) {
|
||||
diffuse = mesh_rgb * tex1.rgb;
|
||||
specular = tex2.rgb * (1.0 - tex1.a);
|
||||
discard_alpha = (tex1.a + tex2.a) * mesh_color.a;
|
||||
can_discard = 1.0;
|
||||
} else if (pixel_shader_id == 22) {
|
||||
diffuse = mesh_rgb * mix(tex1.rgb * tex2.rgb, tex1.rgb, vec3(tex1.a));
|
||||
} else if (pixel_shader_id == 24) {
|
||||
diffuse = mesh_rgb * mix(tex1.rgb, tex2.rgb, vec3(tex2.a));
|
||||
specular = tex1.rgb * tex1.a * tex_sample_alpha.r;
|
||||
} else if (pixel_shader_id == 25) {
|
||||
float glow_opacity = clamp(tex3.a * tex_sample_alpha.b, 0.0, 1.0);
|
||||
diffuse = mesh_rgb * mix(tex1.rgb * tex2.rgb * 2.0, tex1.rgb, vec3(tex1.a)) * (1.0 - glow_opacity);
|
||||
specular = tex3.rgb * glow_opacity;
|
||||
} else if (pixel_shader_id == 26) {
|
||||
vec4 mixed = mix(mix(tex1, tex2, vec4(clamp(tex_sample_alpha.g, 0.0, 1.0))), tex3, vec4(clamp(tex_sample_alpha.b, 0.0, 1.0)));
|
||||
diffuse = mesh_rgb * mixed.rgb;
|
||||
discard_alpha = mixed.a * mesh_color.a;
|
||||
can_discard = 1.0;
|
||||
} else if (pixel_shader_id == 27) {
|
||||
diffuse = mesh_rgb * mix(mix(tex1.rgb * tex2.rgb * 2.0, tex3.rgb, vec3(tex3.a)), tex1.rgb, vec3(tex1.a));
|
||||
} else if (pixel_shader_id == 28) {
|
||||
vec4 mixed = mix(mix(tex1, tex2, vec4(clamp(tex_sample_alpha.g, 0.0, 1.0))), tex3, vec4(clamp(tex_sample_alpha.b, 0.0, 1.0)));
|
||||
diffuse = mesh_rgb * mixed.rgb;
|
||||
discard_alpha = mixed.a * tex4.a * mesh_color.a;
|
||||
can_discard = 1.0;
|
||||
} else if (pixel_shader_id == 29) {
|
||||
diffuse = mesh_rgb * mix(tex1.rgb, tex2.rgb, vec3(tex2.a));
|
||||
} else if (pixel_shader_id == 30) {
|
||||
diffuse = mesh_rgb * mix(tex1.rgb * tex2.rgb, tex3.rgb, vec3(tex3.a));
|
||||
discard_alpha = tex1.a * mesh_color.a;
|
||||
can_discard = 1.0;
|
||||
} else if (pixel_shader_id == 31) {
|
||||
diffuse = mesh_rgb * tex1.rgb * tex2.rgb;
|
||||
discard_alpha = tex1.a * mesh_color.a;
|
||||
can_discard = 1.0;
|
||||
} else if (pixel_shader_id == 32) {
|
||||
diffuse = mesh_rgb * mix(tex1.rgb * tex2.rgb, tex3.rgb, vec3(tex3.a));
|
||||
} else if (pixel_shader_id == 35) {
|
||||
vec4 combined = tex1 * tex2 * tex3;
|
||||
diffuse = mesh_rgb * combined.rgb;
|
||||
discard_alpha = combined.a * mesh_color.a;
|
||||
can_discard = 1.0;
|
||||
} else if (pixel_shader_id == 36) {
|
||||
diffuse = mesh_rgb * tex1.rgb * tex2.rgb;
|
||||
discard_alpha = tex1.a * tex2.a * mesh_color.a;
|
||||
can_discard = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec4 tex = use_texture ? texture(albedo_tex, stage_uv(0, world_normal)) : vec4(1.0);
|
||||
%s
|
||||
vec4 stage2 = use_stage2 ? texture(stage2_tex, stage_uv(1, world_normal)) : vec4(1.0);
|
||||
vec4 stage3 = use_stage3 ? texture(stage3_tex, stage_uv(2, world_normal)) : vec4(1.0);
|
||||
vec4 stage4 = use_stage4 ? texture(stage4_tex, stage_uv(3, world_normal)) : vec4(1.0);
|
||||
vec3 mat_diffuse;
|
||||
vec3 specular;
|
||||
float discard_alpha;
|
||||
float can_discard;
|
||||
resolve_m2_pixel(tex, stage2, stage3, stage4, mat_diffuse, specular, discard_alpha, can_discard);
|
||||
discard_alpha *= edge_fade;
|
||||
discard_alpha = pow(clamp(discard_alpha, 0.0, 1.0), max(effect_alpha_power, 0.01)) * effect_alpha_scale;
|
||||
if (uv_edge_fade_strength > 0.0) {
|
||||
float edge_width = 0.12;
|
||||
float edge_mask = smoothstep(0.0, edge_width, uv_interp.x) * smoothstep(0.0, edge_width, 1.0 - uv_interp.x);
|
||||
edge_mask *= smoothstep(0.0, edge_width, uv_interp.y) * smoothstep(0.0, edge_width, 1.0 - uv_interp.y);
|
||||
discard_alpha *= mix(1.0, edge_mask, clamp(uv_edge_fade_strength, 0.0, 1.0));
|
||||
}
|
||||
if (alpha_ref > 0.0 && discard_alpha < alpha_ref) {
|
||||
discard;
|
||||
}
|
||||
|
||||
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.76), max(wow_ambient_color.rgb, vec3(0.08)), 0.18);
|
||||
vec3 zone_light = mix(vec3(1.0), wow_light_color.rgb, 0.14);
|
||||
float day_strength = mix(0.48, 0.92, clamp(wow_sun_elevation, 0.0, 1.0));
|
||||
vec3 lit = zone_ambient * 0.82 + zone_light * ndl * diffuse_strength * 0.72 * day_strength;
|
||||
lit = clamp(lit, vec3(0.56), vec3(1.08));
|
||||
lit = mix(lit, vec3(1.0), clamp(unlit_strength, 0.0, 1.0));
|
||||
vec3 color = mat_diffuse * lit + specular * 0.45;
|
||||
color = mix(color, min(color * 2.0, vec3(1.0)), clamp(modulate_2x, 0.0, 1.0));
|
||||
color = clamp(color, vec3(0.0), vec3(1.0));
|
||||
color = apply_wow_fog(color);
|
||||
|
||||
ALBEDO = color;
|
||||
%s
|
||||
}
|
||||
""" % [
|
||||
", ".join(render_modes),
|
||||
repeat_hint,
|
||||
repeat_hint,
|
||||
repeat_hint,
|
||||
repeat_hint,
|
||||
"",
|
||||
"" if mode == "opaque" or mode == "cutout" else "\tALPHA = clamp(discard_alpha, 0.0, 1.0);",
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
uid://dbyyg7wwr0y3w
|
||||
@@ -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));",
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
uid://c1ju24mvhoi5e
|
||||
Reference in New Issue
Block a user