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

This commit is contained in:
2026-07-07 10:45:43 +04:00
parent 8738c2495d
commit 44614a79e4
38 changed files with 4820 additions and 192 deletions
+58 -27
View File
@@ -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