скайбоксы, рендер воды, документация
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
extends RefCounted
|
||||
class_name ADTBuilder
|
||||
|
||||
const WOW_LIQUID_MATERIAL := preload("res://addons/mpq_extractor/loaders/wow_liquid_material.gd")
|
||||
|
||||
const TILE_SIZE := 533.33333
|
||||
const CHUNK_SIZE := 33.33333
|
||||
const UNIT_SIZE := CHUNK_SIZE / 8.0
|
||||
@@ -18,7 +20,6 @@ static var _single_texture_material_cache: Dictionary = {}
|
||||
static var _baked_texture_material_cache: Dictionary = {}
|
||||
static var _alpha_texture_cache: Dictionary = {}
|
||||
static var _layered_material_cache: Dictionary = {}
|
||||
static var _liquid_material_cache: Dictionary = {}
|
||||
|
||||
## Build a Node3D with 256 terrain chunk meshes.
|
||||
## `extracted_dir` must be an absolute path to the extracted/ folder so BLPs can be loaded.
|
||||
@@ -780,6 +781,7 @@ static func _build_tile_water_root(data: Dictionary, origin_offset: Vector3) ->
|
||||
var mi := MeshInstance3D.new()
|
||||
mi.mesh = mesh
|
||||
mi.name = "Liquid_%d" % int(liquid_id)
|
||||
mi.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
root.add_child(mi)
|
||||
|
||||
return root if root.get_child_count() > 0 else null
|
||||
@@ -817,6 +819,10 @@ static func _append_liquid_geometry(
|
||||
var y10 := liquid_heights[z * 9 + x + 1] - origin_offset.y
|
||||
var y01 := liquid_heights[(z + 1) * 9 + x] - origin_offset.y
|
||||
var y11 := liquid_heights[(z + 1) * 9 + x + 1] - origin_offset.y
|
||||
var u0 := x0 / TILE_SIZE
|
||||
var u1 := x1 / TILE_SIZE
|
||||
var v0 := z0 / TILE_SIZE
|
||||
var v1 := z1 / TILE_SIZE
|
||||
|
||||
verts.append(Vector3(x0, y00, z0))
|
||||
verts.append(Vector3(x0, y01, z1))
|
||||
@@ -828,10 +834,10 @@ static func _append_liquid_geometry(
|
||||
nrms.append(Vector3.UP)
|
||||
nrms.append(Vector3.UP)
|
||||
|
||||
uvs_arr.append(Vector2(float(x) / 8.0, float(z) / 8.0))
|
||||
uvs_arr.append(Vector2(float(x) / 8.0, float(z + 1) / 8.0))
|
||||
uvs_arr.append(Vector2(float(x + 1) / 8.0, float(z + 1) / 8.0))
|
||||
uvs_arr.append(Vector2(float(x + 1) / 8.0, float(z) / 8.0))
|
||||
uvs_arr.append(Vector2(u0, v0))
|
||||
uvs_arr.append(Vector2(u0, v1))
|
||||
uvs_arr.append(Vector2(u1, v1))
|
||||
uvs_arr.append(Vector2(u1, v0))
|
||||
|
||||
indices.append(base)
|
||||
indices.append(base + 1)
|
||||
@@ -912,6 +918,7 @@ static func _build_liquid_mesh(
|
||||
var mi := MeshInstance3D.new()
|
||||
mi.mesh = mesh
|
||||
mi.position = chunk_origin - origin_offset
|
||||
mi.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
mi.name = "Liquid_%d_%d_%d" % [
|
||||
chunk.get("index_x", 0),
|
||||
chunk.get("index_y", 0),
|
||||
@@ -938,33 +945,8 @@ static func _build_chunk_material(
|
||||
return _build_fallback_material()
|
||||
|
||||
|
||||
static func _build_liquid_material(liquid_id: int) -> StandardMaterial3D:
|
||||
if _liquid_material_cache.has(liquid_id):
|
||||
return _liquid_material_cache[liquid_id]
|
||||
|
||||
var color := _get_liquid_color(liquid_id)
|
||||
var mat := StandardMaterial3D.new()
|
||||
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||
mat.albedo_color = color
|
||||
mat.cull_mode = BaseMaterial3D.CULL_DISABLED
|
||||
mat.roughness = 0.08
|
||||
mat.metallic = 0.0
|
||||
mat.emission_enabled = true
|
||||
mat.emission = Color(color.r, color.g, color.b) * 0.08
|
||||
_liquid_material_cache[liquid_id] = mat
|
||||
return mat
|
||||
|
||||
|
||||
static func _get_liquid_color(liquid_id: int) -> Color:
|
||||
match liquid_id:
|
||||
2, 14:
|
||||
return Color(0.08, 0.20, 0.34, 0.62)
|
||||
3, 15, 19:
|
||||
return Color(0.75, 0.28, 0.03, 0.72)
|
||||
4, 20:
|
||||
return Color(0.24, 0.45, 0.14, 0.68)
|
||||
_:
|
||||
return Color(0.12, 0.33, 0.50, 0.58)
|
||||
static func _build_liquid_material(liquid_id: int) -> Material:
|
||||
return WOW_LIQUID_MATERIAL.build(liquid_id)
|
||||
|
||||
|
||||
static func _build_terrain_material(
|
||||
@@ -1287,18 +1269,20 @@ uniform float ambient = 0.62;
|
||||
uniform float diffuse = 0.38;
|
||||
uniform float hemi = 0.12;
|
||||
|
||||
vec3 sample_layer(float layer, vec2 local_uv) {
|
||||
return texture(terrain_tex, vec3(local_uv * uv_scale, layer), mip_bias).rgb;
|
||||
vec3 sample_layer(float layer, vec2 continuous_chunk_uv) {
|
||||
return texture(terrain_tex, vec3(continuous_chunk_uv * uv_scale, layer), mip_bias).rgb;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec2 tile_uv = clamp(UV, vec2(0.0), vec2(0.999999));
|
||||
vec2 chunk_f = floor(tile_uv * 16.0);
|
||||
vec2 local_uv = fract(tile_uv * 16.0);
|
||||
vec2 chunk_uv = tile_uv * 16.0;
|
||||
vec2 chunk_f = floor(chunk_uv);
|
||||
vec2 local_uv = chunk_uv - chunk_f;
|
||||
|
||||
vec4 packed_ids = COLOR;
|
||||
vec4 layers = floor(packed_ids * 255.0 + vec4(0.5));
|
||||
vec3 alpha = texture(alpha_atlas, (chunk_f * 64.0 + local_uv * 64.0 + vec2(0.5)) / 1024.0).rgb;
|
||||
vec2 alpha_px = clamp(local_uv * 64.0, vec2(0.5), vec2(63.5));
|
||||
vec3 alpha = texture(alpha_atlas, (chunk_f * 64.0 + alpha_px) / 1024.0).rgb;
|
||||
|
||||
float w1 = alpha.r;
|
||||
float w2 = alpha.g;
|
||||
@@ -1311,10 +1295,10 @@ void fragment() {
|
||||
w3 /= sum;
|
||||
|
||||
vec3 albedo =
|
||||
sample_layer(layers.x, local_uv) * w0 +
|
||||
sample_layer(layers.y, local_uv) * w1 +
|
||||
sample_layer(layers.z, local_uv) * w2 +
|
||||
sample_layer(layers.w, local_uv) * w3;
|
||||
sample_layer(layers.x, chunk_uv) * w0 +
|
||||
sample_layer(layers.y, chunk_uv) * w1 +
|
||||
sample_layer(layers.z, chunk_uv) * w2 +
|
||||
sample_layer(layers.w, chunk_uv) * w3;
|
||||
|
||||
vec3 n = normalize(NORMAL);
|
||||
if (!FRONT_FACING) {
|
||||
|
||||
@@ -21,8 +21,11 @@ static func build(data: Dictionary, extracted_dir: String = "") -> Node3D:
|
||||
return root
|
||||
|
||||
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 model_path: String = str(data.get("model_path", ""))
|
||||
|
||||
var mesh := ArrayMesh.new()
|
||||
|
||||
@@ -55,7 +58,17 @@ static func build(data: Dictionary, extracted_dir: String = "") -> Node3D:
|
||||
|
||||
var surf_idx := mesh.get_surface_count() - 1
|
||||
var mat_def: Dictionary = materials[mat_id] if mat_id >= 0 and mat_id < materials.size() else {}
|
||||
mesh.surface_set_material(surf_idx, _build_material(mat_def, tc_idx, textures, tex_combos, extracted_dir))
|
||||
mesh.surface_set_material(
|
||||
surf_idx,
|
||||
_build_material(
|
||||
mat_def,
|
||||
tc_idx,
|
||||
textures,
|
||||
texture_types,
|
||||
texture_flags,
|
||||
tex_combos,
|
||||
extracted_dir,
|
||||
model_path))
|
||||
|
||||
if mesh.get_surface_count() == 0:
|
||||
return root
|
||||
@@ -71,36 +84,91 @@ static func _build_material(
|
||||
mat_def: Dictionary,
|
||||
texture_combo_index: int,
|
||||
textures: PackedStringArray,
|
||||
texture_types: PackedInt32Array,
|
||||
texture_flags: PackedInt32Array,
|
||||
tex_combos: PackedInt32Array,
|
||||
extracted_dir: String) -> StandardMaterial3D:
|
||||
extracted_dir: String,
|
||||
model_path: String) -> StandardMaterial3D:
|
||||
|
||||
var mat := StandardMaterial3D.new()
|
||||
mat.cull_mode = BaseMaterial3D.CULL_DISABLED
|
||||
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
|
||||
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.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
return mat
|
||||
|
||||
|
||||
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)
|
||||
var rel_lower := rel_model.to_lower()
|
||||
if rel_lower.begins_with("item/objectcomponents/weapon/") and texture_type == 2:
|
||||
var stem := rel_model.get_file().get_basename()
|
||||
if stem.to_lower() == "axe_1h_horde_a_01":
|
||||
return "Item/ObjectComponents/Weapon/Axe_1H_Horde_A_01Gray.blp"
|
||||
if not stem.is_empty():
|
||||
return "Item/ObjectComponents/Weapon/%s.blp" % stem
|
||||
if rel_lower == "creature/wolf/wolf.m2" or rel_lower == "creature/wolf/wolf.mdx":
|
||||
if texture_type == 11:
|
||||
return "Creature/Wolf/WolfSkinCoyote.blp"
|
||||
if texture_type == 12:
|
||||
return "Creature/Wolf/WolfSkinCoyoteAlpha.blp"
|
||||
if rel_lower == "creature/boar/boar.m2" or rel_lower == "creature/boar/boar.mdx":
|
||||
if texture_type == 11:
|
||||
return "Creature/Boar/BoarSkinIvory.blp"
|
||||
if rel_lower == "creature/kobold/kobold.m2" or rel_lower == "creature/kobold/kobold.mdx":
|
||||
if texture_type == 11:
|
||||
return "Creature/Kobold/koboldskinAlbino.blp"
|
||||
if rel_lower == "creature/murloc/murloc.m2" or rel_lower == "creature/murloc/murloc.mdx":
|
||||
if texture_type == 11:
|
||||
return "Creature/Murloc/SahauginskinBlue.blp"
|
||||
return ""
|
||||
|
||||
|
||||
static func _relative_extracted_model_path(path: String) -> String:
|
||||
var marker := "/data/extracted/"
|
||||
var lower := path.to_lower()
|
||||
var marker_pos := lower.find(marker)
|
||||
if marker_pos >= 0:
|
||||
return path.substr(marker_pos + marker.length())
|
||||
return path
|
||||
|
||||
|
||||
static func _load_texture(rel_path: String, extracted_dir: String) -> Texture2D:
|
||||
if rel_path.is_empty() or extracted_dir.is_empty():
|
||||
return null
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
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 BUILD_OCCLUDERS := false
|
||||
const OCCLUDER_MIN_TRIANGLES := 16
|
||||
@@ -27,7 +28,7 @@ static func clear_caches() -> void:
|
||||
_m2_prototype_cache.clear()
|
||||
_m2_missing_cache.clear()
|
||||
_texture_cache.clear()
|
||||
_wmo_liquid_material_cache.clear()
|
||||
WOW_LIQUID_MATERIAL.clear_cache()
|
||||
|
||||
# Returns a Node3D containing one MeshInstance3D per WMO group.
|
||||
static func build(data: Dictionary, extracted_dir: String = "") -> Node3D:
|
||||
@@ -339,6 +340,7 @@ static func _build_material(
|
||||
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)
|
||||
|
||||
var blend_mode: int = mat_def.get("blend_mode", 0)
|
||||
match blend_mode:
|
||||
@@ -356,6 +358,7 @@ static func _build_material(
|
||||
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)
|
||||
|
||||
mat.set_meta("wow_flags", mat_def.get("flags", 0))
|
||||
mat.set_meta("wow_shader", mat_def.get("shader", 0))
|
||||
@@ -363,7 +366,6 @@ static func _build_material(
|
||||
return mat
|
||||
|
||||
|
||||
static var _wmo_liquid_material_cache: Dictionary = {}
|
||||
|
||||
# WMO MLIQ → MeshInstance3D in WMO-local coordinates.
|
||||
# Corner & vertex grid are in WMO-local (Z-up); convert to Godot Y-up using the
|
||||
@@ -384,6 +386,8 @@ static func _build_group_liquid(liquid: Dictionary) -> MeshInstance3D:
|
||||
return null
|
||||
|
||||
var verts := PackedVector3Array()
|
||||
var nrms := PackedVector3Array()
|
||||
var uvs := PackedVector2Array()
|
||||
var indices := PackedInt32Array()
|
||||
|
||||
# Pre-compute Godot-space position for each grid vertex.
|
||||
@@ -417,6 +421,14 @@ static func _build_group_liquid(liquid: Dictionary) -> MeshInstance3D:
|
||||
verts.append(pos[i10])
|
||||
verts.append(pos[i11])
|
||||
verts.append(pos[i01])
|
||||
nrms.append(Vector3.UP)
|
||||
nrms.append(Vector3.UP)
|
||||
nrms.append(Vector3.UP)
|
||||
nrms.append(Vector3.UP)
|
||||
uvs.append(Vector2(float(tx) / maxf(1.0, float(xtiles)), float(ty) / maxf(1.0, float(ytiles))))
|
||||
uvs.append(Vector2(float(tx + 1) / maxf(1.0, float(xtiles)), float(ty) / maxf(1.0, float(ytiles))))
|
||||
uvs.append(Vector2(float(tx + 1) / maxf(1.0, float(xtiles)), float(ty + 1) / maxf(1.0, float(ytiles))))
|
||||
uvs.append(Vector2(float(tx) / maxf(1.0, float(xtiles)), float(ty + 1) / maxf(1.0, float(ytiles))))
|
||||
# Two triangles, winding chosen to face up after the (-y, z, -x) flip.
|
||||
indices.append(base + 0)
|
||||
indices.append(base + 1)
|
||||
@@ -431,6 +443,8 @@ static func _build_group_liquid(liquid: Dictionary) -> MeshInstance3D:
|
||||
var arrays := []
|
||||
arrays.resize(Mesh.ARRAY_MAX)
|
||||
arrays[Mesh.ARRAY_VERTEX] = verts
|
||||
arrays[Mesh.ARRAY_NORMAL] = nrms
|
||||
arrays[Mesh.ARRAY_TEX_UV] = uvs
|
||||
arrays[Mesh.ARRAY_INDEX] = indices
|
||||
|
||||
var mesh := ArrayMesh.new()
|
||||
@@ -443,19 +457,8 @@ static func _build_group_liquid(liquid: Dictionary) -> MeshInstance3D:
|
||||
return mi
|
||||
|
||||
|
||||
static func _wmo_liquid_material(material_id: int) -> StandardMaterial3D:
|
||||
if _wmo_liquid_material_cache.has(material_id):
|
||||
return _wmo_liquid_material_cache[material_id]
|
||||
|
||||
var mat := StandardMaterial3D.new()
|
||||
mat.cull_mode = BaseMaterial3D.CULL_DISABLED
|
||||
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||
mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||
# Default: bluish water. Without DBC liquid_type lookup we can't pick magma/slime
|
||||
# precisely yet — material_id from MLIQ is the WMO MOMT slot, not the DBC id.
|
||||
mat.albedo_color = Color(0.2, 0.4, 0.7, 0.6)
|
||||
_wmo_liquid_material_cache[material_id] = mat
|
||||
return mat
|
||||
static func _wmo_liquid_material(material_id: int) -> Material:
|
||||
return WOW_LIQUID_MATERIAL.build_wmo(material_id)
|
||||
|
||||
|
||||
static func _load_texture(rel_path: String, extracted_dir: String) -> Texture2D:
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
extends RefCounted
|
||||
class_name WowLiquidMaterial
|
||||
|
||||
static var _shader: Shader
|
||||
static var _cache: Dictionary = {}
|
||||
|
||||
|
||||
static func clear_cache() -> void:
|
||||
_cache.clear()
|
||||
|
||||
|
||||
static func build(liquid_id: int) -> ShaderMaterial:
|
||||
var key := "adt:%d" % liquid_id
|
||||
if _cache.has(key):
|
||||
return _cache[key]
|
||||
|
||||
var style := _style_for_liquid(liquid_id)
|
||||
var mat := _create_material(style)
|
||||
_cache[key] = mat
|
||||
return mat
|
||||
|
||||
|
||||
static func build_wmo(material_id: int) -> ShaderMaterial:
|
||||
var key := "wmo:%d" % material_id
|
||||
if _cache.has(key):
|
||||
return _cache[key]
|
||||
|
||||
var style := {
|
||||
"color": Color(0.10, 0.30, 0.48, 0.60),
|
||||
"foam": Color(0.58, 0.80, 1.00, 0.22),
|
||||
"wave_strength": 0.045,
|
||||
"wave_scale": 0.095,
|
||||
"wave_speed": 0.38,
|
||||
"fresnel_power": 2.2,
|
||||
"emission_strength": 0.025,
|
||||
"magma_mode": 0.0,
|
||||
}
|
||||
var mat := _create_material(style)
|
||||
_cache[key] = mat
|
||||
return mat
|
||||
|
||||
|
||||
static func _create_material(style: Dictionary) -> ShaderMaterial:
|
||||
var mat := ShaderMaterial.new()
|
||||
mat.shader = _get_shader()
|
||||
mat.render_priority = 1
|
||||
mat.set_shader_parameter("liquid_color", style["color"])
|
||||
mat.set_shader_parameter("foam_color", style["foam"])
|
||||
mat.set_shader_parameter("wave_strength", float(style["wave_strength"]))
|
||||
mat.set_shader_parameter("wave_scale", float(style["wave_scale"]))
|
||||
mat.set_shader_parameter("wave_speed", float(style["wave_speed"]))
|
||||
mat.set_shader_parameter("alpha_base", (style["color"] as Color).a)
|
||||
mat.set_shader_parameter("fresnel_power", float(style["fresnel_power"]))
|
||||
mat.set_shader_parameter("emission_strength", float(style["emission_strength"]))
|
||||
mat.set_shader_parameter("magma_mode", float(style["magma_mode"]))
|
||||
return mat
|
||||
|
||||
|
||||
static func _style_for_liquid(liquid_id: int) -> Dictionary:
|
||||
match liquid_id:
|
||||
3, 15, 19:
|
||||
return {
|
||||
"color": Color(0.78, 0.22, 0.03, 0.78),
|
||||
"foam": Color(1.00, 0.66, 0.10, 0.38),
|
||||
"wave_strength": 0.025,
|
||||
"wave_scale": 0.12,
|
||||
"wave_speed": 0.22,
|
||||
"fresnel_power": 1.7,
|
||||
"emission_strength": 0.42,
|
||||
"magma_mode": 1.0,
|
||||
}
|
||||
4, 20:
|
||||
return {
|
||||
"color": Color(0.16, 0.38, 0.12, 0.66),
|
||||
"foam": Color(0.50, 0.80, 0.32, 0.24),
|
||||
"wave_strength": 0.035,
|
||||
"wave_scale": 0.11,
|
||||
"wave_speed": 0.28,
|
||||
"fresnel_power": 2.0,
|
||||
"emission_strength": 0.05,
|
||||
"magma_mode": 0.0,
|
||||
}
|
||||
2, 14:
|
||||
return {
|
||||
"color": Color(0.05, 0.16, 0.30, 0.64),
|
||||
"foam": Color(0.48, 0.72, 1.00, 0.20),
|
||||
"wave_strength": 0.060,
|
||||
"wave_scale": 0.075,
|
||||
"wave_speed": 0.32,
|
||||
"fresnel_power": 2.5,
|
||||
"emission_strength": 0.025,
|
||||
"magma_mode": 0.0,
|
||||
}
|
||||
_:
|
||||
return {
|
||||
"color": Color(0.08, 0.28, 0.46, 0.58),
|
||||
"foam": Color(0.58, 0.82, 1.00, 0.24),
|
||||
"wave_strength": 0.050,
|
||||
"wave_scale": 0.085,
|
||||
"wave_speed": 0.36,
|
||||
"fresnel_power": 2.3,
|
||||
"emission_strength": 0.025,
|
||||
"magma_mode": 0.0,
|
||||
}
|
||||
|
||||
|
||||
static func _get_shader() -> Shader:
|
||||
if _shader:
|
||||
return _shader
|
||||
|
||||
_shader = Shader.new()
|
||||
_shader.code = """
|
||||
shader_type spatial;
|
||||
render_mode blend_mix, cull_disabled, unshaded;
|
||||
|
||||
uniform vec4 liquid_color : source_color = vec4(0.08, 0.28, 0.46, 0.58);
|
||||
uniform vec4 foam_color : source_color = vec4(0.58, 0.82, 1.00, 0.24);
|
||||
uniform float wave_strength = 0.05;
|
||||
uniform float wave_scale = 0.085;
|
||||
uniform float wave_speed = 0.36;
|
||||
uniform float alpha_base = 0.58;
|
||||
uniform float fresnel_power = 2.3;
|
||||
uniform float emission_strength = 0.025;
|
||||
uniform float magma_mode = 0.0;
|
||||
|
||||
varying vec3 world_pos;
|
||||
|
||||
float hash21(vec2 p) {
|
||||
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
|
||||
}
|
||||
|
||||
float noise21(vec2 p) {
|
||||
vec2 i = floor(p);
|
||||
vec2 f = fract(p);
|
||||
vec2 u = f * f * (3.0 - 2.0 * f);
|
||||
float a = hash21(i);
|
||||
float b = hash21(i + vec2(1.0, 0.0));
|
||||
float c = hash21(i + vec2(0.0, 1.0));
|
||||
float d = hash21(i + vec2(1.0, 1.0));
|
||||
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
|
||||
}
|
||||
|
||||
void vertex() {
|
||||
world_pos = (MODEL_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);
|
||||
VERTEX.y += (wave_a + wave_b) * wave_strength;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec3 n = normalize(NORMAL);
|
||||
vec3 v = normalize(VIEW);
|
||||
float facing = clamp(abs(dot(n, v)), 0.0, 1.0);
|
||||
float fresnel = pow(1.0 - facing, fresnel_power);
|
||||
|
||||
vec2 p = world_pos.xz * wave_scale;
|
||||
float n1 = noise21(p * 0.70 + vec2(TIME * wave_speed, -TIME * wave_speed * 0.60));
|
||||
float n2 = noise21(p * 1.80 + vec2(-TIME * wave_speed * 0.35, TIME * wave_speed * 0.45));
|
||||
float ripple = smoothstep(0.52, 0.86, n1 * 0.65 + n2 * 0.35);
|
||||
|
||||
vec3 color = mix(liquid_color.rgb, foam_color.rgb, ripple * foam_color.a);
|
||||
color += fresnel * foam_color.rgb * 0.35;
|
||||
|
||||
if (magma_mode > 0.5) {
|
||||
float glow = smoothstep(0.45, 1.0, ripple + fresnel * 0.45);
|
||||
color += vec3(1.0, 0.35, 0.05) * glow * 0.45;
|
||||
EMISSION = color * max(emission_strength, 0.35);
|
||||
} else {
|
||||
EMISSION = color * emission_strength;
|
||||
}
|
||||
|
||||
ALBEDO = color;
|
||||
ALPHA = clamp(alpha_base + fresnel * 0.18 + ripple * 0.06, 0.0, 1.0);
|
||||
}
|
||||
"""
|
||||
return _shader
|
||||
@@ -0,0 +1 @@
|
||||
uid://d10ykrmo5h0ih
|
||||
Reference in New Issue
Block a user