новый рендер
This commit is contained in:
@@ -19,6 +19,11 @@
|
||||
|
||||
# Offline WMO render cache
|
||||
/data/cache/wmo_render_v1/
|
||||
/data/cache/terrain_splat_v1/
|
||||
/data/cache/terrain_control_splat_v1/
|
||||
/data/cache/terrain_control_splat_v2/
|
||||
/data/cache/terrain_control_splat_v3/
|
||||
/data/cache/splat_progress/
|
||||
|
||||
# Root cache dir (генерируется скриптом)
|
||||
/cache/
|
||||
|
||||
@@ -9,6 +9,7 @@ const IGNORE_TERRAIN_HOLES := false
|
||||
const USE_GEOMETRY_NORMALS := true
|
||||
|
||||
static var _terrain_shader: Shader
|
||||
static var _control_splat_shader: Shader
|
||||
static var _single_texture_shader: Shader
|
||||
static var _empty_alpha_texture: Texture2D
|
||||
static var _native_adt_baker: Object
|
||||
@@ -110,6 +111,35 @@ static func build_baked_tile_render_payload(
|
||||
return _build_baked_tile_payload(data, image_cache, extracted_dir, lod, texture_size)
|
||||
|
||||
|
||||
static func build_control_splat_tile_render_payload(
|
||||
data: Dictionary,
|
||||
image_cache: Dictionary,
|
||||
extracted_dir: String = "",
|
||||
lod: int = 0,
|
||||
texture_size: int = 256) -> Dictionary:
|
||||
return _build_control_splat_tile_payload(data, image_cache, extracted_dir, lod, texture_size)
|
||||
|
||||
|
||||
static func apply_control_splat_material(
|
||||
mesh: Mesh,
|
||||
texture_images: Array,
|
||||
alpha_atlas: Texture2D,
|
||||
layer_index_map: Texture2D) -> Mesh:
|
||||
|
||||
if mesh == null or texture_images.is_empty() or alpha_atlas == null or layer_index_map == null:
|
||||
return mesh
|
||||
var texture_array := _build_terrain_texture_array_from_images(texture_images)
|
||||
if texture_array == null:
|
||||
return mesh
|
||||
var array_mesh := mesh as ArrayMesh
|
||||
if array_mesh == null:
|
||||
return mesh
|
||||
array_mesh.surface_set_material(
|
||||
0,
|
||||
_build_control_splat_material(texture_array, alpha_atlas, layer_index_map))
|
||||
return array_mesh
|
||||
|
||||
|
||||
static func build_baked_tile_render_payload_pair(
|
||||
data: Dictionary,
|
||||
image_cache: Dictionary,
|
||||
@@ -128,6 +158,35 @@ static func build_baked_tile_render_payload_pair(
|
||||
coarse_texture_size)
|
||||
|
||||
|
||||
static func upgrade_cached_baked_mesh_materials(
|
||||
mesh: Mesh,
|
||||
uv_scale: float = 1.0,
|
||||
mip_bias: float = -1.25,
|
||||
sharpen_strength: float = 0.35) -> Mesh:
|
||||
|
||||
var array_mesh := mesh as ArrayMesh
|
||||
if array_mesh == null:
|
||||
return mesh
|
||||
|
||||
for surface_idx in array_mesh.get_surface_count():
|
||||
var mat := array_mesh.surface_get_material(surface_idx)
|
||||
if not (mat is ShaderMaterial):
|
||||
continue
|
||||
var shader_mat := mat as ShaderMaterial
|
||||
var tex: Texture2D = shader_mat.get_shader_parameter("tex0")
|
||||
if tex == null:
|
||||
continue
|
||||
var upgraded := ShaderMaterial.new()
|
||||
upgraded.shader = _get_single_texture_shader()
|
||||
upgraded.set_shader_parameter("uv_scale", uv_scale)
|
||||
upgraded.set_shader_parameter("mip_bias", mip_bias)
|
||||
upgraded.set_shader_parameter("sharpen_strength", sharpen_strength)
|
||||
upgraded.set_shader_parameter("tex0", tex)
|
||||
array_mesh.surface_set_material(surface_idx, upgraded)
|
||||
|
||||
return array_mesh
|
||||
|
||||
|
||||
static func get_tile_origin(data: Dictionary) -> Vector3:
|
||||
var found := false
|
||||
var min_x := 0.0
|
||||
@@ -469,6 +528,105 @@ static func _build_baked_tile_payload(
|
||||
return payload
|
||||
|
||||
|
||||
static func _build_control_splat_tile_payload(
|
||||
data: Dictionary,
|
||||
image_cache: Dictionary,
|
||||
extracted_dir: String,
|
||||
lod: int,
|
||||
texture_size: int) -> Dictionary:
|
||||
|
||||
var payload := _build_baked_tile_geometry_payload(data, lod)
|
||||
if payload.is_empty():
|
||||
return {}
|
||||
|
||||
var tex_names: PackedStringArray = data.get("textures", PackedStringArray())
|
||||
var texture_images := _build_terrain_texture_images(tex_names, image_cache, extracted_dir, texture_size)
|
||||
var texture_array := _build_terrain_texture_array_from_images(texture_images)
|
||||
if texture_array == null:
|
||||
return payload
|
||||
|
||||
payload = _build_control_splat_tile_geometry_payload(data, lod, tex_names.size())
|
||||
if payload.is_empty():
|
||||
return {}
|
||||
|
||||
var alpha_atlas := _build_tile_alpha_atlas(data)
|
||||
var layer_index_map := _build_tile_layer_index_map(data, tex_names.size())
|
||||
var material := _build_control_splat_material(texture_array, alpha_atlas, layer_index_map)
|
||||
var mesh: Mesh = payload.get("mesh", null)
|
||||
if mesh != null:
|
||||
mesh.surface_set_material(0, material)
|
||||
|
||||
payload["texture_array"] = texture_array
|
||||
payload["texture_images"] = texture_images
|
||||
payload["alpha_atlas"] = alpha_atlas
|
||||
payload["layer_index_map"] = layer_index_map
|
||||
payload["texture_names"] = tex_names
|
||||
return payload
|
||||
|
||||
|
||||
static func _build_control_splat_tile_geometry_payload(data: Dictionary, lod: int, texture_count: int) -> Dictionary:
|
||||
var tile_origin := get_tile_origin(data)
|
||||
var verts := PackedVector3Array()
|
||||
var nrms := PackedVector3Array()
|
||||
var uvs_arr := PackedVector2Array()
|
||||
var colors := PackedColorArray()
|
||||
var indices := PackedInt32Array()
|
||||
var max_id := mini(maxi(texture_count - 1, 0), 255)
|
||||
|
||||
for chunk in data.get("chunks", []) as Array:
|
||||
if chunk.is_empty():
|
||||
continue
|
||||
|
||||
var heights: PackedFloat32Array = chunk.get("heights", PackedFloat32Array())
|
||||
var positions := _build_chunk_positions(heights)
|
||||
if positions.is_empty():
|
||||
continue
|
||||
|
||||
var geometry := _build_chunk_geometry(
|
||||
positions,
|
||||
chunk.get("normals", PackedVector3Array()),
|
||||
chunk.get("holes", 0),
|
||||
lod)
|
||||
|
||||
var local_verts: PackedVector3Array = geometry["verts"]
|
||||
if local_verts.is_empty():
|
||||
continue
|
||||
|
||||
var layer_color := _get_chunk_layer_color(chunk, max_id)
|
||||
var offset: Vector3 = chunk.get("origin", Vector3.ZERO) - tile_origin
|
||||
var base := verts.size()
|
||||
|
||||
for v: Vector3 in local_verts:
|
||||
var tv := v + offset
|
||||
verts.append(tv)
|
||||
uvs_arr.append(Vector2(tv.x / TILE_SIZE, tv.z / TILE_SIZE))
|
||||
colors.append(layer_color)
|
||||
for n: Vector3 in geometry["nrms"]:
|
||||
nrms.append(n)
|
||||
for idx: int in geometry["indices"]:
|
||||
indices.append(base + idx)
|
||||
|
||||
if verts.is_empty():
|
||||
return {}
|
||||
|
||||
var arrays := []
|
||||
arrays.resize(Mesh.ARRAY_MAX)
|
||||
arrays[Mesh.ARRAY_VERTEX] = verts
|
||||
arrays[Mesh.ARRAY_NORMAL] = nrms
|
||||
arrays[Mesh.ARRAY_TEX_UV] = uvs_arr
|
||||
arrays[Mesh.ARRAY_COLOR] = colors
|
||||
arrays[Mesh.ARRAY_INDEX] = indices
|
||||
|
||||
var mesh := ArrayMesh.new()
|
||||
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
|
||||
|
||||
return {
|
||||
"mesh": mesh,
|
||||
"position": Vector3.ZERO,
|
||||
"name": "ControlSplatTileLOD%d" % lod,
|
||||
}
|
||||
|
||||
|
||||
static func _build_baked_tile_payload_pair(
|
||||
data: Dictionary,
|
||||
image_cache: Dictionary,
|
||||
@@ -874,6 +1032,8 @@ static func _build_baked_texture_material(tex: Texture2D) -> Material:
|
||||
var mat := ShaderMaterial.new()
|
||||
mat.shader = _get_single_texture_shader()
|
||||
mat.set_shader_parameter("uv_scale", 1.0)
|
||||
mat.set_shader_parameter("mip_bias", -1.25)
|
||||
mat.set_shader_parameter("sharpen_strength", 0.35)
|
||||
mat.set_shader_parameter("tex0", tex)
|
||||
if tex:
|
||||
_baked_texture_material_cache[tex.get_instance_id()] = mat
|
||||
@@ -887,6 +1047,8 @@ static func _build_single_texture_material(tex: Texture2D) -> Material:
|
||||
var mat := ShaderMaterial.new()
|
||||
mat.shader = _get_single_texture_shader()
|
||||
mat.set_shader_parameter("uv_scale", 8.0)
|
||||
mat.set_shader_parameter("mip_bias", -0.75)
|
||||
mat.set_shader_parameter("sharpen_strength", 0.0)
|
||||
mat.set_shader_parameter("tex0", tex)
|
||||
if tex:
|
||||
_single_texture_material_cache[tex.get_instance_id()] = mat
|
||||
@@ -897,6 +1059,7 @@ static func _build_layered_material(textures: Array, alpha_maps: Array) -> Shade
|
||||
var mat := ShaderMaterial.new()
|
||||
mat.shader = _get_terrain_shader()
|
||||
mat.set_shader_parameter("uv_scale", 8.0)
|
||||
mat.set_shader_parameter("mip_bias", -0.75)
|
||||
mat.set_shader_parameter("layer_count", textures.size())
|
||||
var base_tex: Texture2D = textures[0]
|
||||
|
||||
@@ -939,6 +1102,234 @@ static func _get_empty_alpha_texture() -> Texture2D:
|
||||
return _empty_alpha_texture
|
||||
|
||||
|
||||
static func _build_terrain_texture_images(
|
||||
tex_names: PackedStringArray,
|
||||
image_cache: Dictionary,
|
||||
extracted_dir: String,
|
||||
texture_size: int) -> Array[Image]:
|
||||
|
||||
if tex_names.is_empty():
|
||||
var fallback := Image.create(maxi(16, texture_size), maxi(16, texture_size), false, Image.FORMAT_RGBA8)
|
||||
fallback.fill(Color(0.4, 0.55, 0.3, 1.0))
|
||||
fallback.generate_mipmaps()
|
||||
return [fallback]
|
||||
|
||||
var target_size := maxi(16, texture_size)
|
||||
var images: Array[Image] = []
|
||||
for tex_name in tex_names:
|
||||
var source_img := _load_image(tex_name, image_cache, extracted_dir)
|
||||
var img: Image = null
|
||||
if source_img != null:
|
||||
img = source_img.duplicate()
|
||||
else:
|
||||
img = Image.create(target_size, target_size, false, Image.FORMAT_RGBA8)
|
||||
img.fill(Color(0.4, 0.55, 0.3, 1.0))
|
||||
|
||||
if img.get_format() != Image.FORMAT_RGBA8:
|
||||
img.convert(Image.FORMAT_RGBA8)
|
||||
if img.get_width() != target_size or img.get_height() != target_size:
|
||||
img.resize(target_size, target_size, Image.INTERPOLATE_LANCZOS)
|
||||
img.generate_mipmaps()
|
||||
images.append(img)
|
||||
|
||||
return images
|
||||
|
||||
|
||||
static func _build_terrain_texture_array_from_images(images: Array) -> Texture2DArray:
|
||||
if images.is_empty():
|
||||
return null
|
||||
|
||||
var fallback: Image = null
|
||||
for image_variant in images:
|
||||
var candidate := image_variant as Image
|
||||
if candidate != null and candidate.get_width() > 0 and candidate.get_height() > 0:
|
||||
fallback = candidate
|
||||
break
|
||||
if fallback == null:
|
||||
fallback = Image.create(16, 16, false, Image.FORMAT_RGBA8)
|
||||
fallback.fill(Color(0.4, 0.55, 0.3, 1.0))
|
||||
fallback.generate_mipmaps()
|
||||
|
||||
var valid_images: Array[Image] = []
|
||||
for image_variant in images:
|
||||
var img := image_variant as Image
|
||||
if img != null and img.get_width() > 0 and img.get_height() > 0:
|
||||
valid_images.append(img)
|
||||
continue
|
||||
valid_images.append(fallback)
|
||||
|
||||
if valid_images.is_empty():
|
||||
return null
|
||||
|
||||
var texture_array := Texture2DArray.new()
|
||||
var err := texture_array.create_from_images(valid_images)
|
||||
if err != OK:
|
||||
return null
|
||||
return texture_array
|
||||
|
||||
|
||||
static func _build_tile_alpha_atlas(data: Dictionary) -> Texture2D:
|
||||
var atlas_size := 16 * 64
|
||||
var image := Image.create(atlas_size, atlas_size, false, Image.FORMAT_RGBA8)
|
||||
image.fill(Color(0.0, 0.0, 0.0, 1.0))
|
||||
var tile_origin := get_tile_origin(data)
|
||||
|
||||
for chunk in data.get("chunks", []) as Array:
|
||||
if chunk.is_empty():
|
||||
continue
|
||||
var chunk_coord := _get_chunk_grid_coord(chunk, tile_origin)
|
||||
var base_x := chunk_coord.x * 64
|
||||
var base_y := chunk_coord.y * 64
|
||||
var alpha_maps: Array = chunk.get("alpha_maps", [])
|
||||
for py in range(64):
|
||||
for px in range(64):
|
||||
var a0 := 0
|
||||
var a1 := 0
|
||||
var a2 := 0
|
||||
if alpha_maps.size() > 0:
|
||||
var bytes0: PackedByteArray = alpha_maps[0]
|
||||
if bytes0.size() == 64 * 64:
|
||||
a0 = bytes0[py * 64 + px]
|
||||
if alpha_maps.size() > 1:
|
||||
var bytes1: PackedByteArray = alpha_maps[1]
|
||||
if bytes1.size() == 64 * 64:
|
||||
a1 = bytes1[py * 64 + px]
|
||||
if alpha_maps.size() > 2:
|
||||
var bytes2: PackedByteArray = alpha_maps[2]
|
||||
if bytes2.size() == 64 * 64:
|
||||
a2 = bytes2[py * 64 + px]
|
||||
image.set_pixel(
|
||||
base_x + px,
|
||||
base_y + py,
|
||||
Color8(a0, a1, a2, 255))
|
||||
|
||||
return ImageTexture.create_from_image(image)
|
||||
|
||||
|
||||
static func _build_tile_layer_index_map(data: Dictionary, texture_count: int) -> Texture2D:
|
||||
var image := Image.create(16, 16, false, Image.FORMAT_RGBA8)
|
||||
image.fill(Color8(0, 0, 0, 255))
|
||||
var tile_origin := get_tile_origin(data)
|
||||
var max_id := mini(maxi(texture_count - 1, 0), 255)
|
||||
|
||||
for chunk in data.get("chunks", []) as Array:
|
||||
if chunk.is_empty():
|
||||
continue
|
||||
var layers: Array = chunk.get("layers", [])
|
||||
if layers.is_empty():
|
||||
continue
|
||||
var ids := PackedByteArray([0, 0, 0, 0])
|
||||
for i in range(mini(4, layers.size())):
|
||||
var layer: Dictionary = layers[i]
|
||||
ids[i] = clampi(int(layer.get("texture_id", 0)), 0, max_id)
|
||||
|
||||
var chunk_coord := _get_chunk_grid_coord(chunk, tile_origin)
|
||||
image.set_pixel(
|
||||
chunk_coord.x,
|
||||
chunk_coord.y,
|
||||
Color8(ids[0], ids[1], ids[2], ids[3]))
|
||||
|
||||
return ImageTexture.create_from_image(image)
|
||||
|
||||
|
||||
static func _get_chunk_layer_color(chunk: Dictionary, max_id: int) -> Color:
|
||||
var layers: Array = chunk.get("layers", [])
|
||||
if layers.is_empty():
|
||||
return Color(0.0, 0.0, 0.0, 0.0)
|
||||
|
||||
var ids := PackedFloat32Array([0.0, 0.0, 0.0, 0.0])
|
||||
for i in range(mini(4, layers.size())):
|
||||
var layer: Dictionary = layers[i]
|
||||
ids[i] = float(clampi(int(layer.get("texture_id", 0)), 0, max_id)) / 255.0
|
||||
return Color(ids[0], ids[1], ids[2], ids[3])
|
||||
|
||||
|
||||
static func _get_chunk_grid_coord(chunk: Dictionary, tile_origin: Vector3) -> Vector2i:
|
||||
var origin: Vector3 = chunk.get("origin", Vector3.ZERO)
|
||||
var local := origin - tile_origin
|
||||
return Vector2i(
|
||||
clampi(int(round(local.x / CHUNK_SIZE)), 0, 15),
|
||||
clampi(int(round(local.z / CHUNK_SIZE)), 0, 15))
|
||||
|
||||
|
||||
static func _build_control_splat_material(
|
||||
texture_array: Texture2DArray,
|
||||
alpha_atlas: Texture2D,
|
||||
layer_index_map: Texture2D) -> ShaderMaterial:
|
||||
|
||||
var mat := ShaderMaterial.new()
|
||||
mat.shader = _get_control_splat_shader()
|
||||
mat.set_shader_parameter("terrain_tex", texture_array)
|
||||
mat.set_shader_parameter("alpha_atlas", alpha_atlas)
|
||||
mat.set_shader_parameter("layer_index_map", layer_index_map)
|
||||
mat.set_shader_parameter("uv_scale", 8.0)
|
||||
mat.set_shader_parameter("mip_bias", -0.75)
|
||||
return mat
|
||||
|
||||
|
||||
static func _get_control_splat_shader() -> Shader:
|
||||
if _control_splat_shader:
|
||||
return _control_splat_shader
|
||||
|
||||
_control_splat_shader = Shader.new()
|
||||
_control_splat_shader.code = """
|
||||
shader_type spatial;
|
||||
render_mode unshaded, cull_disabled, depth_draw_opaque;
|
||||
|
||||
uniform sampler2DArray terrain_tex : source_color, filter_linear_mipmap_anisotropic, repeat_enable;
|
||||
uniform sampler2D alpha_atlas : filter_linear, repeat_disable;
|
||||
uniform sampler2D layer_index_map : filter_nearest, repeat_disable;
|
||||
|
||||
uniform float uv_scale = 8.0;
|
||||
uniform float mip_bias = -0.75;
|
||||
uniform vec3 light_dir = vec3(-0.35, 0.82, -0.45);
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
float w1 = alpha.r;
|
||||
float w2 = alpha.g;
|
||||
float w3 = alpha.b;
|
||||
float w0 = max(0.0, 1.0 - w1 - w2 - w3);
|
||||
float sum = max(w0 + w1 + w2 + w3, 0.0001);
|
||||
w0 /= sum;
|
||||
w1 /= sum;
|
||||
w2 /= sum;
|
||||
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;
|
||||
|
||||
vec3 n = normalize(NORMAL);
|
||||
if (!FRONT_FACING) {
|
||||
n = -n;
|
||||
}
|
||||
vec3 l = normalize(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;
|
||||
}
|
||||
"""
|
||||
return _control_splat_shader
|
||||
|
||||
|
||||
static func _build_tile_baked_albedo(
|
||||
data: Dictionary,
|
||||
image_cache: Dictionary,
|
||||
@@ -1106,6 +1497,7 @@ render_mode unshaded, cull_disabled, depth_draw_opaque;
|
||||
|
||||
uniform int layer_count = 1;
|
||||
uniform float uv_scale = 8.0;
|
||||
uniform float mip_bias = -0.75;
|
||||
uniform vec3 light_dir = vec3(-0.35, 0.82, -0.45);
|
||||
uniform float ambient = 0.62;
|
||||
uniform float diffuse = 0.38;
|
||||
@@ -1114,10 +1506,10 @@ uniform float hemi = 0.12;
|
||||
void fragment() {
|
||||
vec2 tiled_uv = UV * uv_scale;
|
||||
|
||||
vec4 c0 = texture(tex0, tiled_uv);
|
||||
vec4 c1 = texture(tex1, tiled_uv);
|
||||
vec4 c2 = texture(tex2, tiled_uv);
|
||||
vec4 c3 = texture(tex3, tiled_uv);
|
||||
vec4 c0 = texture(tex0, tiled_uv, mip_bias);
|
||||
vec4 c1 = texture(tex1, tiled_uv, mip_bias);
|
||||
vec4 c2 = texture(tex2, tiled_uv, mip_bias);
|
||||
vec4 c3 = texture(tex3, tiled_uv, mip_bias);
|
||||
|
||||
float w1 = layer_count > 1 ? texture(alpha1, UV).r : 0.0;
|
||||
float w2 = layer_count > 2 ? texture(alpha2, UV).r : 0.0;
|
||||
@@ -1165,11 +1557,29 @@ render_mode unshaded, cull_disabled, depth_draw_opaque;
|
||||
|
||||
uniform sampler2D tex0 : source_color, filter_linear_mipmap_anisotropic, repeat_enable;
|
||||
uniform float uv_scale = 8.0;
|
||||
uniform float mip_bias = -0.75;
|
||||
uniform float sharpen_strength = 0.0;
|
||||
uniform vec3 light_dir = vec3(-0.35, 0.82, -0.45);
|
||||
uniform float ambient = 0.62;
|
||||
uniform float diffuse = 0.38;
|
||||
uniform float hemi = 0.12;
|
||||
|
||||
vec3 sample_sharp(sampler2D tex, vec2 uv) {
|
||||
vec3 center = texture(tex, uv, mip_bias).rgb;
|
||||
if (sharpen_strength <= 0.001) {
|
||||
return center;
|
||||
}
|
||||
vec2 texel = 1.0 / vec2(textureSize(tex, 0));
|
||||
vec2 min_uv = vec2(0.001);
|
||||
vec2 max_uv = vec2(0.999);
|
||||
vec3 side =
|
||||
texture(tex, clamp(uv + vec2(texel.x, 0.0), min_uv, max_uv), mip_bias).rgb +
|
||||
texture(tex, clamp(uv - vec2(texel.x, 0.0), min_uv, max_uv), mip_bias).rgb +
|
||||
texture(tex, clamp(uv + vec2(0.0, texel.y), min_uv, max_uv), mip_bias).rgb +
|
||||
texture(tex, clamp(uv - vec2(0.0, texel.y), min_uv, max_uv), mip_bias).rgb;
|
||||
return clamp(center * (1.0 + 4.0 * sharpen_strength) - side * sharpen_strength, vec3(0.0), vec3(1.0));
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec3 n = normalize(NORMAL);
|
||||
if (!FRONT_FACING) {
|
||||
@@ -1179,7 +1589,7 @@ void fragment() {
|
||||
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 = texture(tex0, UV * uv_scale).rgb * light;
|
||||
ALBEDO = sample_sharp(tex0, UV * uv_scale) * light;
|
||||
}
|
||||
"""
|
||||
return _single_texture_shader
|
||||
|
||||
@@ -1,410 +0,0 @@
|
||||
Godot Engine v4.6.1.stable.official.14d19694e - https://godotengine.org
|
||||
|
||||
Scanning ADT tiles...
|
||||
Found 368 unique WMO models from 687 tiles.
|
||||
WMOLoader: loaded 6 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_shipwreck/transport_shipwreck.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/collidabledoodads/darkshore/wreckedelvendestroyer/elvendestroyerwreckback.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/collidabledoodads/darkshore/wreckedelvendestroyer/elvendestroyerwreckfront.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/guardtower/guardtower.wmo
|
||||
WMOLoader: loaded 6 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/duskwoodabandoned_humantwostory/duskwoodabandoned_humantwostory.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/chapel/duskwoodchapel.wmo
|
||||
WMOLoader: loaded 12 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_spidermine/md_spidermine_nokobolds.wmo
|
||||
WMOLoader: loaded 5 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_cryptonerm/md_cryptonerm.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/duskwoodabandoned_human_farm/duskwoodabandoned_human_farm.wmo
|
||||
WMOLoader: loaded 12 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_spidermine/md_spidermine.wmo
|
||||
[10/368] baked=10 skipped=0 failed=0
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_shipwreck/shipwreck_a.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/duskwood_human_farm/duskwood_human_farm.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/duskwood_stable/duskwood_stable.wmo
|
||||
WMOLoader: loaded 11 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/duskwood_inn/duskwood_inn.wmo
|
||||
WMOLoader: loaded 10 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/ld_shadowfang/ld_shadowfang.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/greatwall/greatwall_wall_04.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/greatwall/greatwall_gate.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stormwind/sw_harbor_lighthouse.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_shipwreck/shipwreck_b.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/westfall_human_farm_burnt/westfallfarmhouseburnt.wmo
|
||||
[20/368] baked=20 skipped=0 failed=0
|
||||
WMOLoader: loaded 8 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_mountaincave/md_mountaincave_medium.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/human_barn_silo/silo.wmo
|
||||
WMOLoader: loaded 3 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/westfall_barn/westfall_barn.wmo
|
||||
WMOLoader: loaded 38 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/az_deadmines/az_deadmines_a.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/wetlands/wetlandshumandock01/wetlandshumandock01.wmo
|
||||
WMOLoader: loaded 9 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/az_deadmines/az_deadmines_c.wmo
|
||||
WMOLoader: loaded 3 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/duskwoodabandoned_barn/duskwoodabandoned_barn.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/westfall/westfallgrainsilo01/westfallgrainsilo01.wmo
|
||||
WMOLoader: loaded 6 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/duskwood_humantwostory/duskwood_humantwostory.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/duskwoodabandoned_blacksmith/duskwoodabandoned_blacksmith.wmo
|
||||
[30/368] baked=30 skipped=0 failed=0
|
||||
WMOLoader: loaded 3 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/duskwood_barn/duskwood_barn.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/duskwood/chasmbridge/duskwoodchasmbridge.wmo
|
||||
WMOLoader: loaded 5 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_goldmine/md_goldmine_1room.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/duskwood_blacksmith/duskwood_blacksmith.wmo
|
||||
WMOLoader: loaded 5 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/duskwood_townhall_nowall/duskwood_townhall_nowall.wmo
|
||||
WMOLoader: loaded 27 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/kz_gnomeragon/kz_gnomeragon.wmo
|
||||
WMOLoader: loaded 286 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stormwind/stormwind.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/keepwall/wallpiece01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/keepwall/wallpost01.wmo
|
||||
WMOLoader: loaded 4 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/transports/transport_ship/transportship.wmo
|
||||
[40/368] baked=40 skipped=0 failed=0
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stormwind/sw_harbor_lowmain.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stormwind/sw_harbor_lowstair.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stormwind/sw_harbor_lowtwr.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stormwind/sw_harbor_shipshelter.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stormwind/sw_harbor_docks.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stormwind/sw_harbor_lowstr.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/northrend/buildings/human/icebreaker/transport_icebreaker_ship_nomasts.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stormwind/stormwindhangar.wmo
|
||||
WMOLoader: loaded 10 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_goldmine/md_goldmine.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/westfall_human_farm/westfall_human_farm.wmo
|
||||
[50/368] baked=50 skipped=0 failed=0
|
||||
WMOLoader: loaded 6 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/westfall_humantwostory/westfall_humantwostory.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/moonbrook_human_farm/moonbrook_human_farm.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/westfall_stable/westfall_stable.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/westfall_lumbermill/westfall_lumbermill.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/prison_camp/prisonlonghouse.wmo
|
||||
WMOLoader: loaded 6 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/moonbrook_humantwostory/moonbrook_humantwostory.wmo
|
||||
WMOLoader: loaded 11 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/moonbrook_inn/moonbrook_inn.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/moonbrook_blacksmith/moonbrook_blacksmith.wmo
|
||||
WMOLoader: loaded 5 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/moonbrook_townhall_nowall/moonbrook_townhall_nowall.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/greatwall/greatwall_tower_01.wmo
|
||||
[60/368] baked=60 skipped=0 failed=0
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/swampofsorrows/swampdock01/swampdock01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/trollshop/trollshop01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/swampofsorrows/swampbridge01/swampbridge01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/trollshoppingmall/trollshoppingmall01.wmo
|
||||
WMOLoader: loaded 10 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_crypt/md_crypt.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/duskwood/coveredbridge/duskwoodcoveredbridge.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/guardtower_ruined/ruinedhumanguardtower01.wmo
|
||||
WMOLoader: loaded 206 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/undercity/undercity.wmo
|
||||
WMOLoader: loaded 26 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/duskwood_barracks/duskwood_barracks.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/buildings/tirisfalglade/fenriskeep/fenriskeep.wmo
|
||||
[70/368] baked=70 skipped=0 failed=0
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/duskwood_human_farm_burnt/duskwoodfarmhouseburnt.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/duskwoodabandoned_lumbermill/duskwoodabandoned_lumbermill.wmo
|
||||
WMOLoader: loaded 5 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/duskwood_townhall/duskwood_townhall.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/gnomehut/gnomehut.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/collidabledoodads/stonetalon/landingpad/landingpad01.wmo
|
||||
WMOLoader: loaded 7 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_mountaincave/md_icecave02.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/collidabledoodads/tanaris/trolltent/trolltentsand01.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/human_farm/farm.wmo
|
||||
WMOLoader: loaded 28 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/human_barracks/human_barracks.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/elwynn/widebridge/elwynnwidebridge.wmo
|
||||
[80/368] baked=80 skipped=0 failed=0
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/elwynn/lionbridge/elwynnlionbridge.wmo
|
||||
WMOLoader: loaded 5 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/westfall_magetower/westfall_magetower.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/stranglethorn/stranglethornruins16/stranglethornruins16.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/stranglethorn/stranglethornruins12/stranglethornruins12.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/stranglethorn/stranglethornruins11/stranglethornruins11.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/stranglethorn/stranglethornruins04/stranglethornruins04.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/collidabledoodads/darkshore/giantseaturtle02/giantseaturtle02.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stranglethorn_ruins/stranglethorn_ruins12.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/collidabledoodads/darkshore/snakespine/snakespinegiantarch.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stranglethorn_ruins/stranglethorn_ruins05.wmo
|
||||
[90/368] baked=90 skipped=0 failed=0
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stranglethorn_ruins/stranglethorn_ruins16.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stranglethorn_bootybay/bootybay_fortress2.wmo
|
||||
WMOLoader: loaded 46 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stranglethorn_bootybay/bootybay.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/buildings/tirisfalglade/mausoleum/mausoleumactive.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/undercity/undercitywall01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/buildings/tirisfalglade/lorderonzeppelintower/lorderonzeppelin.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/buildings/tirisfalglade/lorderonzeppelintower/lorderonzeppelin_nd_transport.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/keepwall/wallpieceruin02.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/keepwall/wallpostruined01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/keepwall/wallposttunnel01.wmo
|
||||
[100/368] baked=100 skipped=0 failed=0
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/collidabledoodads/arathihighlands/humanruinedbuildings/huruinedbuildingsmed01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/collidabledoodads/arathihighlands/humanruinedbuildings/huruinedbuildingssmall01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/collidabledoodads/arathihighlands/humanruinedbuildings/huruinedbuildingsmed02.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/keepwall/wallpiecestairs01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/collidabledoodads/arathihighlands/humanruinedbuildings/huruinedbuildingssmall02.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/collidabledoodads/arathihighlands/humanruinedbuildings/huruinedbuildingsbig03.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/collidabledoodads/arathihighlands/humanruinedbuildings/huruinedbuildingsbig02.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/northrend/dalaran/dalaran_sewer_oldworld_architecture.wmo
|
||||
WMOLoader: loaded 6 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/humantwostory/humantwostory.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/prison_camp/prisonguardtower.wmo
|
||||
[110/368] baked=110 skipped=0 failed=0
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/buildings/alterac/greenhouse01.wmo
|
||||
WMOLoader: loaded 5 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/townhall/townhall.wmo
|
||||
WMOLoader: loaded 3 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/prison_camp/prisonhq.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/goldshireblacksmith/goldshireblacksmith.wmo
|
||||
WMOLoader: loaded 11 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_goldmine/md_goldmine_variantb.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_animalden/animalden.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/gnomehut/gnomehuttwostory.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_caveden/md_iceden.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/trollhut/trollhut_desert.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/collidabledoodads/tanaris/trolltent/trolltentdesert01.wmo
|
||||
[120/368] baked=120 skipped=0 failed=0
|
||||
WMOLoader: loaded 13 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_anvilmarpass/anvilmarpass.wmo
|
||||
WMOLoader: loaded 6 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/anvilmar/anvilmar.wmo
|
||||
WMOLoader: loaded 13 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_mountaincave/md_icecave.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/elwynn/abbeygate/abbeygate01.wmo
|
||||
WMOLoader: loaded 12 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/goldshireinn/goldshireinn.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stable/stable.wmo
|
||||
WMOLoader: loaded 3 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/human_barn_silo/barn.wmo
|
||||
WMOLoader: loaded 22 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_goldmine/md_goldmine_varianta.wmo
|
||||
WMOLoader: loaded 13 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_crypt/md_crypt_c.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/duskwood/opengrave/duskwoodopengrave.wmo
|
||||
[130/368] baked=130 skipped=0 failed=0
|
||||
WMOLoader: loaded 11 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/duskwoodabandoned_inn/duskwoodabandoned_inn.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/collidabledoodads/darkshore/darkshoredocks/darkshoredocks01.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/orczeppelinhouse/orczeppelinhouse.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/orctower/orctower.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/orcblacksmith/orcblacksmith.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/stranglethorn/stranglethornarena/stranglegladiatorarena.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_caveden/md_trollden.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/stranglethorn/stranglethornarenagate/stranglearenagate01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/stranglethorn/ropebridge/ropebridgesmall.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stranglethorn_bootybay/bootybay_railing.wmo
|
||||
[140/368] baked=140 skipped=0 failed=0
|
||||
WMOLoader: loaded 3 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stranglethorn_bootybay/bootybay_house1.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stranglethorn_bootybay/bootybay_house2.wmo
|
||||
WMOLoader: loaded 10 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_pirateship/pirateship_plank.wmo
|
||||
WMOLoader: loaded 6 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/redridge_humantwostory/redridge_humantwostory.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/redridge/redridgedocks02/redridgedocks02.wmo
|
||||
WMOLoader: loaded 5 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/duskwoodabandoned_townhall_nowall/duskwoodabandoned_townhall_nowall.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/keepwall/wallpieceruin01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/keepwall/gateruined01.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/redridge_human_farm/redridge_human_farm.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_ogremound/md_snogremound1room.wmo
|
||||
[150/368] baked=150 skipped=0 failed=0
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_caveden/md_mushroomden.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/intactprisontower/intactprisontower.wmo
|
||||
WMOLoader: loaded 3 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_cavetunnels/pvp_alterac_ent01.wmo
|
||||
WMOLoader: loaded 4 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_mountaincave/md_mountaincave_medium_e.wmo
|
||||
WMOLoader: loaded 5 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/westfall_townhall_nowall/westfall_townhall_nowall.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/collidabledoodads/menethildocks/menethildocks.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/ironforge/footbridge/ironfootbridge.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/ironforge/ironforgeropebridge01/ironforgeropebridge01.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/dwarven_blacksmith/snow_blacksmith/snow_blacksmith.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/dwarven_1story_hovel/snow_hovel/hovel_machine.wmo
|
||||
[160/368] baked=160 skipped=0 failed=0
|
||||
WMOLoader: loaded 8 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_mountaincave/md_icecave03.wmo
|
||||
WMOLoader: loaded 7 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/dwarven_inn/snow_inn/snow_inn.wmo
|
||||
WMOLoader: loaded 7 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_mountaincave/md_mushroomcave02.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/redridge_stable/redridge_stable.wmo
|
||||
WMOLoader: loaded 14 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/nsabbey/nsabbey.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/elwynn/abbeygate02/abbeygate02.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/worldtree/duskworldtree.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/nightelfmoonwell/dsnightelfmoonwellornate.wmo
|
||||
WMOLoader: loaded 9 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_ogremound/md_ogremound.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/stranglethorn/ropebridge/ropebridge.wmo
|
||||
[170/368] baked=170 skipped=0 failed=0
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/oilplatform/oilplatformsmall.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/trollhut/trollhut01.wmo
|
||||
WMOLoader: loaded 7 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_mountaincave/md_mushroomcave03.wmo
|
||||
WMOLoader: loaded 13 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/scarlet_monestary/scarlet_monestary.wmo
|
||||
WMOLoader: loaded 9 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_ogremound/md_snogremound.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/collidabledoodads/wetlandsfootbridge/wetlandsfootbridge01.wmo
|
||||
WMOLoader: loaded 11 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/westfall_inn/westfall_inn.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/buildings/wetlands/bridgebigwhite/bridgebigwhite.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/keepwall/wallpiece03.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/keepwall/wallpiece02.wmo
|
||||
[180/368] baked=180 skipped=0 failed=0
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/keepwall/gate03.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/keepwall/wallpost02.wmo
|
||||
WMOLoader: loaded 4 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/transports/passengership/transportship_a.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/dwarven_stable/dwarven_stable.wmo
|
||||
WMOLoader: loaded 104 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/cities/ironforge/ironforge.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/dwarven_1story_hovel/snow_hovel/snow_hovel.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/dwarven_2story_hovel/snow_2story/snow_2story.wmo
|
||||
WMOLoader: loaded 13 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_goldmine/md_goldmine_variante.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/searinggorge/darkirontower.wmo
|
||||
WMOLoader: loaded 45 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/az_blackrock/blackrock.wmo
|
||||
[190/368] baked=190 skipped=0 failed=0
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/altarofstorms/altarofstorms.wmo
|
||||
WMOLoader: loaded 5 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/magetower/magetower.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/collidabledoodads/mulgore/taurentent01/taurentent01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/duskwood_lumbermill/duskwood_lumbermill.wmo
|
||||
WMOLoader: loaded 9 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_mountaincave/md_mushroomcave05.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/stranglethorn/holdingpen/holdingpenbamboo.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/orctower/swamporctower.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/zulgurub/zulgurubentrance.wmo
|
||||
WMOLoader: loaded 9 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_ogremound/md_grogremound.wmo
|
||||
WMOLoader: loaded 5 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/redridge_townhall_nowall/redridge_townhall_nowall.wmo
|
||||
[200/368] baked=200 skipped=0 failed=0
|
||||
WMOLoader: loaded 5 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/redridge_magetower/redridge_magetower.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/redridge_lumbermill/redridge_lumbermill.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/westfall_guardtower/westfall_guardtower.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/redridge_blacksmith/redridge_blacksmith.wmo
|
||||
WMOLoader: loaded 26 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/redridge_barracks/redridge_barracks.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/keepwall/gate01.wmo
|
||||
WMOLoader: loaded 11 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/duskwoodabandoned_inn/andorhalabandoned_inn.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/collidabledoodads/arathihighlands/humanruinedbuildings/huruinedbuildingsbig01.wmo
|
||||
WMOLoader: loaded 4 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_cavetunnels/md_cavetunnel01.wmo
|
||||
WMOLoader: loaded 3 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/guildhouses/guildhouseb.wmo
|
||||
[210/368] baked=210 skipped=0 failed=0
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/hillsbradterracewall/hillsbradterracewall.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/outland/humanbuildings/outlandruinedmagetower.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/outland/humanbuildings/outlandruinhumanbarracks01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/collidabledoodads/arathihighlands/durnholdedestroyedwalls/durnholdedestroyedwall.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/greatwall/greatwall_wall_02.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/greatwall/greatwall_wall_01.wmo
|
||||
WMOLoader: loaded 12 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/dwarven_barracks/wetlands_barracks/wet_dwarfbarracks.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/greatwall/greatwall_tower_02.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/collidabledoodads/arathihighlands/stromguard/stromguard.wmo
|
||||
WMOLoader: loaded 5 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/duskwood_magetower/duskwood_magetower.wmo
|
||||
[220/368] baked=220 skipped=0 failed=0
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/dwarven_1story_hovel/mud_hovel/mud_hovel.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/dwarven_2story_hovel/mud_2story/mud_2story.wmo
|
||||
WMOLoader: loaded 8 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_dwarven tunnels/smalltunnel.wmo
|
||||
WMOLoader: loaded 10 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_dwarven tunnels/mediumtunnel.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/dwarven_snowstable/dwarven_snowstable.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/collidable doodads/golbolarplatform/golbolarplatform.wmo
|
||||
WMOLoader: loaded 4 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_mountaincave/md_icecave04.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/searinggorge/darkironcrane.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/collidable doodads/searinggorge/searinggorgescaffold/searinggorgescaffold.wmo
|
||||
WMOLoader: loaded 4 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/az_blackrock/blackrock2.wmo
|
||||
[230/368] baked=230 skipped=0 failed=0
|
||||
WMOLoader: loaded 21 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/orcfortress/blackrockorcfortress.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/trollburrow/trollburrow.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/lumbermill/lumbermill.wmo
|
||||
WMOLoader: loaded 19 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_crypt/md_crypt_d.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/trollhut/trollhutsmall01.wmo
|
||||
WMOLoader: loaded 3 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/zulgurub/zulgurubcity.wmo
|
||||
WMOLoader: loaded 4 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stranglethorn_ruins/stranglethorn_ruins02.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/ziggurat/trollziggurat.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stranglethorn_ruins/stranglethorn_ruins04.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stranglethorn_ruins/stranglethorn_ruins08.wmo
|
||||
[240/368] baked=240 skipped=0 failed=0
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stranglethorn_ruins/stranglethorn_ruins09.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stranglethorn_ruins/stranglethorn_ruins11.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/stranglethorn/ropebridge/ropebridgeshort.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stranglethorn_ruins/stranglethorn_ruins15.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_ogremound/md_ogremound1room.wmo
|
||||
WMOLoader: loaded 12 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_cryptschool/md_cryptschool.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/buildings/tirisfalglade/uthershrine/uthershrine.wmo
|
||||
WMOLoader: loaded 5 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/dwarven_griffonaviary/dwarven_griffonaviary.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/dwarven_blacksmith/wetland_blacksmith/wetland_blacksmith.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/greatwall/greatwall_wall_03.wmo
|
||||
[250/368] baked=250 skipped=0 failed=0
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/collidabledoodads/arathihighlands/wreckedbuildinghcorner02/wreckedbuildinghcorner02.wmo
|
||||
WMOLoader: loaded 16 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_ruinedkeep/ruinedkeep.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/collidabledoodads/arathihighlands/wreckedbuildinghcorner01/wreckedbuildinghcorner01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/redridge/redridgedocks01/redridgedocks01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/redridge/redridgedocks03/redridgedocks03.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/dwarven_snowtower/dwarven_snowtower.wmo
|
||||
WMOLoader: loaded 22 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_dwarven tunnels/largetunnel.wmo
|
||||
WMOLoader: loaded 3 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/orcmedium/orcmedium.wmo
|
||||
WMOLoader: loaded 5 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/orcmagetower/orcmagetower.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/orckennel/orckennel.wmo
|
||||
[260/368] baked=260 skipped=0 failed=0
|
||||
WMOLoader: loaded 13 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_mountaincave/md_mushroomcave.wmo
|
||||
WMOLoader: loaded 12 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/az_karazahn/kharazan.wmo
|
||||
WMOLoader: loaded 7 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/collidabledoodads/arathihighlands/humanruinedbuildings/huruinedbuildingssm01_winecellar.wmo
|
||||
WMOLoader: loaded 4 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/collidabledoodads/arathihighlands/humanruinedbuildings/huruinedbuilding_collapsed.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/stranglethorn/strangleruinstair/strangleruinstair.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stranglethorn_ruins/stranglethorn_ruins17.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/trollhotel/troll_hotel01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stranglethorn_ruins/stranglethorn_ruins14.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/stranglethorn/stranglethornruins01/stranglethornruins01.wmo
|
||||
WMOLoader: loaded 25 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_stratholme_plaguewood_tunnel/md_stratholme_plaguewood_tunnel.wmo
|
||||
[270/368] baked=270 skipped=0 failed=0
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/collidabledoodads/plaguelandbridge/plaguelandsbridge.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/redridge/redridgedocks/redridgesmalldock01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/keepwall/lowwallpost01.wmo
|
||||
WMOLoader: loaded 15 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_ruinedkeep/ruinedkeep_crypt.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_ruinedkeep/ruinedkeep_crypt_facade.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/dwarven_guardtower/dwarven_guardtower.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/collidabledoodads/arathihighlands/bridge/arathistonebridge02.wmo
|
||||
WMOLoader: loaded 5 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/dwarven_tavern/wetlands_tavern/wet_tavern.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/collidabledoodads/dustwallowmarsh/crashedzippelin/final/crashedzippelin.wmo
|
||||
WMOLoader: loaded 7 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/thandolspan_bridge/thandolspan.wmo
|
||||
[280/368] baked=280 skipped=0 failed=0
|
||||
WMOLoader: loaded 8 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_mountaincave/md_mushroomcave04.wmo
|
||||
WMOLoader: loaded 8 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_dwarven tunnels/smalltunnel_snow.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/collidable doodads/lochmodan/dwarvensentinels/dwarvensentinel01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/collidable doodads/lochmodan/dwarvensentinels/dwarvensentinel02.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_caveden/md_warmden.wmo
|
||||
WMOLoader: loaded 9 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_ogremound/md_brogremound.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/orctower/blackrockorctower.wmo
|
||||
WMOLoader: loaded 11 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/redridge_inn/redridge_inn.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/redridge/redridgedamagedbridge/redridgedamagedbridge.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/redridge/redridgedocks/redridgesmalldock02.wmo
|
||||
[290/368] baked=290 skipped=0 failed=0
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_ogremound/md_grogremound1room.wmo
|
||||
WMOLoader: loaded 4 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/orcbarracks/abandonedorcbarracks.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/classicalelfruins/azrclassicalelfruin10.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/classicalelfruins/azrclassicalelfruin01.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/buildings/easternplaguelands/undeadziggurat/undeadziggurat.wmo
|
||||
WMOLoader: loaded 4 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_cavetunnels/md_warmcavetunnel01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/nightelfhunterslodge/nightelfhunterslodge.wmo
|
||||
WMOLoader: loaded 3 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/nightelfsmallhouse/nightelfsmallhouse.wmo
|
||||
WMOLoader: loaded 5 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_ogremound/md_ogremound_medium.wmo
|
||||
WMOLoader: loaded 3 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_mountaincave/md_mountaincave_medium_d.wmo
|
||||
[300/368] baked=300 skipped=0 failed=0
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/wetlands/wetlandswagon/final/wetlandsbustedwagon.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/collidable doodads/lochmodan/dwarvendam/dwarvendam01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/collidable doodads/lochmodan/dwarvendock/dwarvendock01.wmo
|
||||
WMOLoader: loaded 8 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/dwarven_inn/wetlands_inn/wet_inn.wmo
|
||||
WMOLoader: loaded 23 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/kz_uldaman/kz_uldaman_a.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/redridge/redridgefootbridge/redridgefootbridge.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/outland/draenibuildings/outland_draeni_hut_1.wmo
|
||||
WMOLoader: loaded 3 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/outland/draenibuildings/draenihuthooded01.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/trollburrow/swamptrollburrow.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/collidable doodads/swampofsorrows/bridgestranglethorn/bridgestranglethorn.wmo
|
||||
[310/368] baked=310 skipped=0 failed=0
|
||||
WMOLoader: loaded 13 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_goldmine/md_goldmine_variantd.wmo
|
||||
WMOLoader: loaded 3 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/orctower/abandonedorctower.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/outland/orcbuildings/outlandorctower04.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_ogremound/md_brogremound1room.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/outland/darkportal/darkportal.wmo
|
||||
WMOLoader: loaded 12 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/buildings/easternplaguelands/undeadslaughterhouse/slaughterhouse.wmo
|
||||
WMOLoader: loaded 5 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/ld_stratholme/stratholme_a.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/buildings/easternplaguelands/naxportal/epl_portal.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/worldtree/worldtree.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/trollhut/trolltent.wmo
|
||||
[320/368] baked=320 skipped=0 failed=0
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/collidabledoodads/hinterlands/trollcitypieces/aerietrollcitylevel7.wmo
|
||||
WMOLoader: loaded 5 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_mountaincave/md_mushroomcave08.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/buildings/wetlands_grimbatol/wetlands_grimbatol.wmo
|
||||
WMOLoader: loaded 5 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_cryptsimpleent/md_cryptsimpleent.wmo
|
||||
WMOLoader: loaded 6 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/kz_uldaman/kz_uldaman_c.wmo
|
||||
WMOLoader: loaded 36 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/sunkentemple/az_sunkentemple.wmo
|
||||
WMOLoader: loaded 3 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/orcmedium/swamporcmedium.wmo
|
||||
WMOLoader: loaded 4 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/orcgreathall/swamporcgreathall.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/orcblacksmith/swamporcblacksmith.wmo
|
||||
WMOLoader: loaded 6 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_mountaincave/md_mushroomcave07.wmo
|
||||
[330/368] baked=330 skipped=0 failed=0
|
||||
WMOLoader: loaded 26 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/westfall_barracks/westfall_barracks.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/westfall_blacksmith/westfall_blacksmith.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/prison_camp/prisonoubliette.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/westfall_stable/westfall_stableb.wmo
|
||||
WMOLoader: loaded 13 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_hordemine/md_hordemine.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/outland/orcbuildings/outlandorctower02.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/outland/orcbuildings/outlandorctower01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/outland/orcbuildings/outlandorcpigfarm01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/pvp/collidabledoodads/netents/nightelftent02_pvp.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/nightelfhunterslodge/dsnightelfhunterslodge.wmo
|
||||
[340/368] baked=340 skipped=0 failed=0
|
||||
WMOLoader: loaded 7 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/ld_stratholme/stratholme_c.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/classicalelfruins/classicalelfruin10.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/classicalelfruins/classicalelfruin01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/classicalelfruins/classicalelfruin02.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/classicalelfruins/classicalelfruin03.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/classicalelfruins/classicalelfruin05.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/collidabledoodads/hinterlands/trollcitypieces/aerietrollcitylevel1.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/collidabledoodads/hinterlands/trollcitypieces/aerietrollcitylevel2.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/collidabledoodads/hinterlands/trollcitypieces/aerietrollcitylevel3.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/collidabledoodads/hinterlands/trollcitypieces/aerietrollcitylevel4.wmo
|
||||
[350/368] baked=350 skipped=0 failed=0
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/collidabledoodads/hinterlands/trollcitypieces/aerietrollcitylevel5.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/collidabledoodads/hinterlands/trollcitypieces/aerietrollcitylevel6.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/collidabledoodads/hinterlands/trollcitypieces/aerietrollcityarena.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/collidabledoodads/hinterlands/trollcitypieces/aerietrollcityampatheater.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/collidable doodads/lochmodan/excavationsite/excavationplatform02.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/collidable doodads/lochmodan/excavationsite/excavationplatform01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/khazmodan/collidable doodads/lochmodan/excavationsite/excavationplatform03.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/collidabledoodads/darkshore/giantseaturtle01/giantseaturtle01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/kalimdor/buildings/trollhut/trollhut_burn01.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/stranglethorn_bootybay/ratchet_dock.wmo
|
||||
[360/368] baked=360 skipped=0 failed=0
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/md_shipwreck/shipwreck_c.wmo
|
||||
WMOLoader: loaded 6 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/nd_necropolis/deathknightnecropolis.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/duskwood_human_farm/duskwood_human_farm_closed.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/dungeon/nd_necropolis/nd_necropolisteleport02.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/duskwood_barn/duskwood_barn_closed.wmo
|
||||
WMOLoader: loaded 7 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/lorderon/buildings/easternplaguelands/cathedral/cathedral.wmo
|
||||
WMOLoader: loaded 2 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/chapel/redridgechapel.wmo
|
||||
WMOLoader: loaded 1 groups from C:/Users/sindo/open-wc/data/extracted/world/wmo/azeroth/buildings/guardtower/guardtower_damaged.wmo
|
||||
[368/368] baked=368 skipped=0 failed=0
|
||||
WMO render cache finished. baked=368 skipped=0 failed=0 elapsed=37.26s
|
||||
+1
-1
@@ -26,4 +26,4 @@ enabled=PackedStringArray("res://addons/mpq_extractor/plugin.cfg")
|
||||
[rendering]
|
||||
|
||||
rendering_device/driver.windows="d3d12"
|
||||
rendering/rendering_device/d3d12/max_resource_descriptors=524288
|
||||
rendering/rendering_device/d3d12/max_resource_descriptors=1048576
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
*
|
||||
!.gitignore
|
||||
!wmo_streaming_resource.gd
|
||||
!splat_adt_tile.gd
|
||||
!control_splat_adt_tile.gd
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
extends Resource
|
||||
class_name ControlSplatADTTile
|
||||
|
||||
const FORMAT_VERSION := 3
|
||||
|
||||
@export var format_version: int = FORMAT_VERSION
|
||||
@export var map_name: String = ""
|
||||
@export var tile_x: int = 0
|
||||
@export var tile_y: int = 0
|
||||
@export var tile_origin: Vector3 = Vector3.ZERO
|
||||
@export var lod: int = 0
|
||||
@export var texture_size: int = 256
|
||||
@export var terrain_mesh: Mesh
|
||||
@export var texture_images: Array[Image] = []
|
||||
@export var alpha_atlas: Texture2D
|
||||
@export var layer_index_map: Texture2D
|
||||
@export var texture_names: PackedStringArray = PackedStringArray()
|
||||
@@ -0,0 +1,12 @@
|
||||
extends Resource
|
||||
class_name SplatADTTile
|
||||
|
||||
const FORMAT_VERSION := 1
|
||||
|
||||
@export var format_version: int = FORMAT_VERSION
|
||||
@export var map_name: String = ""
|
||||
@export var tile_x: int = 0
|
||||
@export var tile_y: int = 0
|
||||
@export var tile_origin: Vector3 = Vector3.ZERO
|
||||
@export var lod: int = 3
|
||||
@export var terrain_mesh: Mesh
|
||||
@@ -0,0 +1,218 @@
|
||||
extends CharacterBody3D
|
||||
|
||||
const TILE_SIZE := 533.33333
|
||||
const CHUNK_SIZE := TILE_SIZE / 16.0
|
||||
const UNIT_SIZE := CHUNK_SIZE / 8.0
|
||||
|
||||
@export var extracted_dir: String = "res://data/extracted"
|
||||
@export var map_name: String = "Azeroth"
|
||||
@export var spawn_tile_x: int = 42
|
||||
@export var spawn_tile_y: int = 28
|
||||
@export var spawn_at_tile_center: bool = true
|
||||
|
||||
@export var run_speed: float = 7.0
|
||||
@export var backward_speed: float = 4.5
|
||||
@export var strafe_speed: float = 4.5
|
||||
@export var sprint_multiplier: float = 6.0
|
||||
@export var acceleration: float = 28.0
|
||||
@export var mouse_sensitivity: float = 0.003
|
||||
@export var camera_pitch_min: float = deg_to_rad(-65.0)
|
||||
@export var camera_pitch_max: float = deg_to_rad(35.0)
|
||||
@export var camera_height: float = 1.7
|
||||
@export var camera_distance: float = 8.0
|
||||
@export var ground_offset: float = 0.05
|
||||
@export var ground_snap_speed: float = 24.0
|
||||
|
||||
@export var camera_pivot_path: NodePath = NodePath("CameraPivot")
|
||||
@export var camera_path: NodePath = NodePath("CameraPivot/Camera3D")
|
||||
@export var visual_path: NodePath = NodePath("Visual")
|
||||
|
||||
var _camera_pivot: Node3D
|
||||
var _camera: Camera3D
|
||||
var _visual: Node3D
|
||||
var _captured := false
|
||||
var _yaw := 0.0
|
||||
var _pitch := deg_to_rad(-18.0)
|
||||
var _horizontal_velocity := Vector3.ZERO
|
||||
var _adt_cache: Dictionary = {}
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_camera_pivot = get_node_or_null(camera_pivot_path) as Node3D
|
||||
_camera = get_node_or_null(camera_path) as Camera3D
|
||||
_visual = get_node_or_null(visual_path) as Node3D
|
||||
if _camera:
|
||||
_camera.current = true
|
||||
_camera.far = 50000.0
|
||||
if spawn_at_tile_center:
|
||||
global_position.x = (float(spawn_tile_x) + 0.5) * TILE_SIZE
|
||||
global_position.z = (float(spawn_tile_y) + 0.5) * TILE_SIZE
|
||||
var ground := _sample_ground_height(global_position)
|
||||
if is_finite(ground):
|
||||
global_position.y = ground + ground_offset
|
||||
_yaw = rotation.y
|
||||
_apply_camera_transform()
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_RIGHT:
|
||||
_captured = event.pressed
|
||||
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED if _captured else Input.MOUSE_MODE_VISIBLE
|
||||
elif event is InputEventKey and event.pressed and event.keycode == KEY_ESCAPE:
|
||||
_captured = false
|
||||
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
||||
|
||||
if _captured and event is InputEventMouseMotion:
|
||||
_yaw -= event.relative.x * mouse_sensitivity
|
||||
_pitch = clampf(_pitch - event.relative.y * mouse_sensitivity, camera_pitch_min, camera_pitch_max)
|
||||
rotation.y = _yaw
|
||||
_apply_camera_transform()
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
var input_dir := _get_input_dir()
|
||||
var target_velocity := Vector3.ZERO
|
||||
if input_dir.length_squared() > 0.0:
|
||||
target_velocity = _movement_vector(input_dir)
|
||||
|
||||
_horizontal_velocity = _horizontal_velocity.move_toward(target_velocity, acceleration * delta)
|
||||
global_position += _horizontal_velocity * delta
|
||||
|
||||
var ground := _sample_ground_height(global_position)
|
||||
if is_finite(ground):
|
||||
var target_y := ground + ground_offset
|
||||
global_position.y = lerpf(global_position.y, target_y, clampf(ground_snap_speed * delta, 0.0, 1.0))
|
||||
|
||||
if _visual and _horizontal_velocity.length_squared() > 0.01:
|
||||
_visual.global_rotation.y = atan2(-_horizontal_velocity.x, -_horizontal_velocity.z)
|
||||
|
||||
_apply_camera_transform()
|
||||
|
||||
|
||||
func _get_input_dir() -> Vector2:
|
||||
var dir := Vector2.ZERO
|
||||
if Input.is_key_pressed(KEY_W):
|
||||
dir.y -= 1.0
|
||||
if Input.is_key_pressed(KEY_S):
|
||||
dir.y += 1.0
|
||||
if Input.is_key_pressed(KEY_A):
|
||||
dir.x -= 1.0
|
||||
if Input.is_key_pressed(KEY_D):
|
||||
dir.x += 1.0
|
||||
return dir.normalized()
|
||||
|
||||
|
||||
func _movement_vector(input_dir: Vector2) -> Vector3:
|
||||
var forward := -global_basis.z
|
||||
var right := global_basis.x
|
||||
forward.y = 0.0
|
||||
right.y = 0.0
|
||||
forward = forward.normalized()
|
||||
right = right.normalized()
|
||||
|
||||
var speed_z := run_speed if input_dir.y < 0.0 else backward_speed
|
||||
var speed_x := strafe_speed
|
||||
var sprint := sprint_multiplier if Input.is_key_pressed(KEY_SHIFT) else 1.0
|
||||
return (forward * -input_dir.y * speed_z + right * input_dir.x * speed_x) * sprint
|
||||
|
||||
|
||||
func _apply_camera_transform() -> void:
|
||||
if _camera_pivot:
|
||||
_camera_pivot.position = Vector3(0.0, camera_height, 0.0)
|
||||
_camera_pivot.rotation.x = _pitch
|
||||
if _camera:
|
||||
_camera.position = Vector3(0.0, 0.0, camera_distance)
|
||||
_camera.rotation = Vector3.ZERO
|
||||
|
||||
|
||||
func _sample_ground_height(world_pos: Vector3) -> float:
|
||||
var tx := int(floor(world_pos.x / TILE_SIZE))
|
||||
var ty := int(floor(world_pos.z / TILE_SIZE))
|
||||
var data := _load_adt(tx, ty)
|
||||
if data.is_empty():
|
||||
return NAN
|
||||
|
||||
var tile_origin := _get_tile_origin(data)
|
||||
var local := world_pos - tile_origin
|
||||
var chunk_x := clampi(int(floor(local.x / CHUNK_SIZE)), 0, 15)
|
||||
var chunk_y := clampi(int(floor(local.z / CHUNK_SIZE)), 0, 15)
|
||||
var chunk := _find_chunk(data, chunk_x, chunk_y, tile_origin)
|
||||
if chunk.is_empty():
|
||||
return NAN
|
||||
|
||||
var origin: Vector3 = chunk.get("origin", Vector3.ZERO)
|
||||
var chunk_local := world_pos - origin
|
||||
var gx := clampf(chunk_local.x / UNIT_SIZE, 0.0, 8.0)
|
||||
var gz := clampf(chunk_local.z / UNIT_SIZE, 0.0, 8.0)
|
||||
var x0 := clampi(int(floor(gx)), 0, 8)
|
||||
var z0 := clampi(int(floor(gz)), 0, 8)
|
||||
var x1 := mini(x0 + 1, 8)
|
||||
var z1 := mini(z0 + 1, 8)
|
||||
var fx := gx - float(x0)
|
||||
var fz := gz - float(z0)
|
||||
var heights: PackedFloat32Array = chunk.get("heights", PackedFloat32Array())
|
||||
if heights.size() < 145:
|
||||
return NAN
|
||||
|
||||
var h00 := heights[_outer(z0, x0)]
|
||||
var h10 := heights[_outer(z0, x1)]
|
||||
var h01 := heights[_outer(z1, x0)]
|
||||
var h11 := heights[_outer(z1, x1)]
|
||||
var hx0 := lerpf(h00, h10, fx)
|
||||
var hx1 := lerpf(h01, h11, fx)
|
||||
return origin.y + lerpf(hx0, hx1, fz)
|
||||
|
||||
|
||||
func _load_adt(tx: int, ty: int) -> Dictionary:
|
||||
var key := "%d_%d" % [tx, ty]
|
||||
if _adt_cache.has(key):
|
||||
return _adt_cache[key]
|
||||
if not ClassDB.class_exists("ADTLoader"):
|
||||
_adt_cache[key] = {}
|
||||
return {}
|
||||
|
||||
var res_path := extracted_dir.path_join("World/Maps/%s/%s_%d_%d.adt" % [map_name, map_name, tx, ty])
|
||||
var abs_path := ProjectSettings.globalize_path(res_path)
|
||||
if not FileAccess.file_exists(abs_path):
|
||||
_adt_cache[key] = {}
|
||||
return {}
|
||||
|
||||
var loader = ClassDB.instantiate("ADTLoader")
|
||||
var data: Dictionary = loader.call("load_adt", abs_path) if loader else {}
|
||||
_adt_cache[key] = data
|
||||
return data
|
||||
|
||||
|
||||
func _find_chunk(data: Dictionary, chunk_x: int, chunk_y: int, tile_origin: Vector3) -> Dictionary:
|
||||
for chunk in data.get("chunks", []) as Array:
|
||||
if chunk.is_empty():
|
||||
continue
|
||||
var origin: Vector3 = chunk.get("origin", Vector3.ZERO)
|
||||
var local := origin - tile_origin
|
||||
var cx := clampi(int(round(local.x / CHUNK_SIZE)), 0, 15)
|
||||
var cy := clampi(int(round(local.z / CHUNK_SIZE)), 0, 15)
|
||||
if cx == chunk_x and cy == chunk_y:
|
||||
return chunk
|
||||
return {}
|
||||
|
||||
|
||||
func _get_tile_origin(data: Dictionary) -> Vector3:
|
||||
var found := false
|
||||
var min_x := 0.0
|
||||
var min_z := 0.0
|
||||
for chunk in data.get("chunks", []) as Array:
|
||||
if chunk.is_empty():
|
||||
continue
|
||||
var origin: Vector3 = chunk.get("origin", Vector3.ZERO)
|
||||
if not found:
|
||||
min_x = origin.x
|
||||
min_z = origin.z
|
||||
found = true
|
||||
else:
|
||||
min_x = minf(min_x, origin.x)
|
||||
min_z = minf(min_z, origin.z)
|
||||
return Vector3(min_x, 0.0, min_z) if found else Vector3.ZERO
|
||||
|
||||
|
||||
func _outer(row: int, col: int) -> int:
|
||||
return row * 17 + col
|
||||
@@ -0,0 +1 @@
|
||||
uid://ltn2ko5kxvo4
|
||||
@@ -1,7 +1,7 @@
|
||||
[gd_scene format=3 uid="uid://f1nqi4emji47"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://yi6lawwjgocg" path="res://src/scenes/streaming/streaming_world_loader.gd" id="1_stream"]
|
||||
[ext_resource type="Script" uid="uid://bauggobg40psr" path="res://src/scenes/camera/fly_camera.gd" id="2_flycam"]
|
||||
[ext_resource type="Script" path="res://src/scenes/player/third_person_wow_controller.gd" id="2_player"]
|
||||
|
||||
[sub_resource type="Sky" id="Sky_1"]
|
||||
|
||||
@@ -12,41 +12,82 @@ ambient_light_source = 3
|
||||
ambient_light_color = Color(1, 1, 1, 1)
|
||||
ambient_light_energy = 0.5
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="PlayerMaterial_1"]
|
||||
albedo_color = Color(0.25, 0.42, 0.85, 1)
|
||||
roughness = 0.65
|
||||
|
||||
[sub_resource type="CapsuleMesh" id="PlayerCapsuleMesh_1"]
|
||||
material = SubResource("PlayerMaterial_1")
|
||||
radius = 0.45
|
||||
height = 2.1
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="PlayerCapsuleShape_1"]
|
||||
radius = 0.45
|
||||
height = 2.1
|
||||
|
||||
[node name="StreamingWorld" type="Node3D" unique_id=1063159974]
|
||||
script = ExtResource("1_stream")
|
||||
camera_path = NodePath("Camera3D")
|
||||
camera_path = NodePath("ThirdPersonPlayer/CameraPivot/Camera3D")
|
||||
quality_preset = "High"
|
||||
update_interval = 0.1
|
||||
tiles_per_tick = 1
|
||||
max_concurrent_tile_tasks = 1
|
||||
chunk_ops_per_tick = 16
|
||||
tile_lod_remove_ops_per_tick = 1
|
||||
tile_finalize_ops_per_tick = 1
|
||||
detail_asset_ops_per_tick = 1
|
||||
m2_build_groups_per_tick = 1
|
||||
m2_multimesh_batch_size = 32
|
||||
m2_mesh_finalize_ops_per_tick = 1
|
||||
wmo_build_instances_per_tick = 1
|
||||
wmo_render_group_ops_per_tick = 24
|
||||
wmo_max_runtime_scene_mb = 8.0
|
||||
prewarm_tile_margin = 2
|
||||
retain_tile_margin = 3
|
||||
boundary_prefetch_threshold = 0.4
|
||||
streaming_tile_cache_dir = "res://data/cache/baked_terrain_stream_v1"
|
||||
m2_multimesh_batch_size = 64
|
||||
wmo_render_group_ops_per_tick = 16
|
||||
cached_tile_mesh_limit = 48
|
||||
wmo_render_cache_dir = "res://data/cache/wmo_render_v1"
|
||||
terrain_quality_mesh_cache_limit = 128
|
||||
lod0_radius_chunks = 10
|
||||
lod1_radius_chunks = 20
|
||||
lod2_radius_chunks = 40
|
||||
lod2_tile_radius = 5
|
||||
prewarm_tile_margin = 3
|
||||
retain_tile_margin = 4
|
||||
full_lod_prewarm_tiles = 1.5
|
||||
boundary_prefetch_threshold = 0.42
|
||||
auto_position_camera = false
|
||||
enable_occlusion_culling = false
|
||||
terrain_full_quality_radius_tiles = 5
|
||||
terrain_control_splat_primary = true
|
||||
terrain_control_splat_hide_fallback = true
|
||||
terrain_control_splat_hide_fallback_radius_tiles = 0
|
||||
terrain_control_splat_radius_tiles = 5
|
||||
max_active_terrain_control_splat_tiles = 121
|
||||
terrain_control_splat_cache_finalize_ops_per_tick = 8
|
||||
terrain_splat_quality_enabled = false
|
||||
enable_water = true
|
||||
enable_wmo = true
|
||||
m2_tile_radius = 3
|
||||
wmo_tile_radius = 5
|
||||
m2_visibility_range = 1600.0
|
||||
wmo_visibility_range = 3600.0
|
||||
terrain_quality_log_enabled = true
|
||||
terrain_quality_log_interval = 1.0
|
||||
hitch_profiler_enabled = true
|
||||
hitch_profiler_threshold_ms = 20.0
|
||||
|
||||
[node name="Camera3D" type="Camera3D" parent="." unique_id=502573687]
|
||||
transform = Transform3D(1, 0, 0, 0, 0.707107, 0.707107, 0, -0.707107, 0.707107, 0, 300, 300)
|
||||
[node name="ThirdPersonPlayer" type="CharacterBody3D" parent="." unique_id=502573687]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 22666, 80, 15200)
|
||||
script = ExtResource("2_player")
|
||||
spawn_tile_x = 42
|
||||
spawn_tile_y = 28
|
||||
sprint_multiplier = 6.0
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="ThirdPersonPlayer"]
|
||||
position = Vector3(0, 1.05, 0)
|
||||
shape = SubResource("PlayerCapsuleShape_1")
|
||||
|
||||
[node name="Visual" type="MeshInstance3D" parent="ThirdPersonPlayer"]
|
||||
position = Vector3(0, 1.05, 0)
|
||||
mesh = SubResource("PlayerCapsuleMesh_1")
|
||||
|
||||
[node name="CameraPivot" type="Node3D" parent="ThirdPersonPlayer"]
|
||||
position = Vector3(0, 1.7, 0)
|
||||
|
||||
[node name="Camera3D" type="Camera3D" parent="ThirdPersonPlayer/CameraPivot"]
|
||||
position = Vector3(0, 0, 8)
|
||||
current = true
|
||||
far = 50000.0
|
||||
script = ExtResource("2_flycam")
|
||||
speed = 1200.0
|
||||
fast_mult = 8.0
|
||||
fov = 70.0
|
||||
|
||||
[node name="Sun" type="DirectionalLight3D" parent="." unique_id=1436804627]
|
||||
transform = Transform3D(0.866025, -0.353553, 0.353553, 0, 0.707107, 0.707107, -0.5, -0.612372, 0.612372, 0, 0, 0)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,263 @@
|
||||
extends SceneTree
|
||||
|
||||
## Builds ADT terrain resources using one Texture2DArray/control-map material per tile.
|
||||
##
|
||||
## Usage:
|
||||
## godot --headless --path <project> --script res://src/tools/build_adt_control_splat_cache.gd -- \
|
||||
## --map Azeroth --jobs auto --texture-size 256 --lod 0 --force
|
||||
|
||||
const ADT_BUILDER_SCRIPT := preload("res://addons/mpq_extractor/loaders/adt_builder.gd")
|
||||
const CONTROL_SPLAT_TILE_SCRIPT := preload("res://src/resources/control_splat_adt_tile.gd")
|
||||
|
||||
var _builder
|
||||
var _loader
|
||||
var _image_cache: Dictionary = {}
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
var args := OS.get_cmdline_user_args()
|
||||
var map_name := _get_arg_value(args, "--map", "Azeroth")
|
||||
var extracted_dir := _normalize_res_path(_get_arg_value(args, "--extracted", "res://data/extracted"))
|
||||
var output_dir := _normalize_res_path(_get_arg_value(args, "--output", "res://data/cache/terrain_control_splat_v3"))
|
||||
var lod := clampi(_get_arg_value(args, "--lod", "0").to_int(), 0, 3)
|
||||
var texture_size := clampi(_get_arg_value(args, "--texture-size", "256").to_int(), 16, 1024)
|
||||
var worker_index: Variant = _get_optional_int_arg(args, "--worker-index")
|
||||
var worker_count := maxi(1, _get_arg_value(args, "--worker-count", "1").to_int())
|
||||
var tile_x: Variant = _get_optional_int_arg(args, "--tile-x")
|
||||
var tile_y: Variant = _get_optional_int_arg(args, "--tile-y")
|
||||
var default_jobs := "1" if tile_x != null or tile_y != null else "auto"
|
||||
var jobs := _parse_jobs_arg(_get_arg_value(args, "--jobs", default_jobs))
|
||||
var force := args.has("--force")
|
||||
|
||||
if jobs > 1 and worker_index == null:
|
||||
_run_parallel_controller(args, jobs)
|
||||
return
|
||||
|
||||
_builder = ADT_BUILDER_SCRIPT.new()
|
||||
if not ClassDB.class_exists("ADTLoader"):
|
||||
push_error("ADTLoader not found. Rebuild GDExtension first.")
|
||||
quit(1)
|
||||
return
|
||||
|
||||
_loader = ClassDB.instantiate("ADTLoader")
|
||||
if _loader == null:
|
||||
push_error("Failed to instantiate ADTLoader.")
|
||||
quit(1)
|
||||
return
|
||||
|
||||
var map_dir := extracted_dir.path_join("World/Maps/%s" % map_name)
|
||||
var dir := DirAccess.open(map_dir)
|
||||
if dir == null:
|
||||
push_error("Cannot open map directory: %s" % map_dir)
|
||||
quit(1)
|
||||
return
|
||||
|
||||
DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path(output_dir.path_join(map_name)))
|
||||
|
||||
var built := 0
|
||||
var skipped := 0
|
||||
var failed := 0
|
||||
var tile_index := 0
|
||||
var started_ms := Time.get_ticks_msec()
|
||||
var files := dir.get_files()
|
||||
var total := _count_worker_tiles(files, map_name, tile_x, tile_y, worker_index, worker_count)
|
||||
print("Control splat cache started. map=%s tiles=%d lod=%d texture_size=%d" % [
|
||||
map_name, total, lod, texture_size])
|
||||
|
||||
for file_name in files:
|
||||
if not file_name.ends_with(".adt"):
|
||||
continue
|
||||
|
||||
var stem := file_name.trim_suffix(".adt")
|
||||
var parts := stem.split("_")
|
||||
if parts.size() != 3 or parts[0] != map_name:
|
||||
continue
|
||||
if not parts[1].is_valid_int() or not parts[2].is_valid_int():
|
||||
continue
|
||||
|
||||
var tx := parts[1].to_int()
|
||||
var ty := parts[2].to_int()
|
||||
if tile_x != null and tx != tile_x:
|
||||
continue
|
||||
if tile_y != null and ty != tile_y:
|
||||
continue
|
||||
if worker_index != null:
|
||||
if tile_index % worker_count != int(worker_index):
|
||||
tile_index += 1
|
||||
continue
|
||||
tile_index += 1
|
||||
|
||||
var output_res_path := output_dir.path_join(map_name).path_join("%s_%d_%d.res" % [map_name, tx, ty])
|
||||
var output_abs_path := ProjectSettings.globalize_path(output_res_path)
|
||||
if not force and FileAccess.file_exists(output_abs_path):
|
||||
skipped += 1
|
||||
continue
|
||||
|
||||
var source_res_path := map_dir.path_join(file_name)
|
||||
var source_abs_path := ProjectSettings.globalize_path(source_res_path)
|
||||
var data: Dictionary = _loader.call("load_adt", source_abs_path)
|
||||
if data.is_empty() or not data.has("chunks"):
|
||||
push_warning("Control splat failed to parse ADT: %s" % source_res_path)
|
||||
failed += 1
|
||||
continue
|
||||
|
||||
var payload: Dictionary = _builder.build_control_splat_tile_render_payload(
|
||||
data,
|
||||
_image_cache,
|
||||
ProjectSettings.globalize_path(extracted_dir),
|
||||
lod,
|
||||
texture_size)
|
||||
var mesh: Mesh = payload.get("mesh", null)
|
||||
var texture_images: Array = payload.get("texture_images", [])
|
||||
if mesh == null or texture_images.is_empty():
|
||||
push_warning("Control splat produced incomplete tile: %s" % source_res_path)
|
||||
failed += 1
|
||||
continue
|
||||
var array_mesh := mesh as ArrayMesh
|
||||
if array_mesh != null and array_mesh.get_surface_count() > 0:
|
||||
array_mesh.surface_set_material(0, null)
|
||||
|
||||
var tile: Resource = CONTROL_SPLAT_TILE_SCRIPT.new()
|
||||
tile.set("format_version", CONTROL_SPLAT_TILE_SCRIPT.FORMAT_VERSION)
|
||||
tile.set("map_name", map_name)
|
||||
tile.set("tile_x", tx)
|
||||
tile.set("tile_y", ty)
|
||||
tile.set("tile_origin", _builder.get_tile_origin_for_data(data))
|
||||
tile.set("lod", lod)
|
||||
tile.set("texture_size", texture_size)
|
||||
tile.set("terrain_mesh", mesh)
|
||||
tile.set("texture_images", texture_images)
|
||||
tile.set("alpha_atlas", payload.get("alpha_atlas", null))
|
||||
tile.set("layer_index_map", payload.get("layer_index_map", null))
|
||||
tile.set("texture_names", payload.get("texture_names", PackedStringArray()))
|
||||
|
||||
var save_err := ResourceSaver.save(tile, output_res_path)
|
||||
if save_err != OK:
|
||||
push_warning("Failed to save control splat tile: %s (err=%d)" % [output_res_path, save_err])
|
||||
failed += 1
|
||||
continue
|
||||
|
||||
built += 1
|
||||
if built % 25 == 0:
|
||||
print("Built %d control splat tiles..." % built)
|
||||
|
||||
var elapsed_ms := Time.get_ticks_msec() - started_ms
|
||||
print("Control splat cache finished. built=%d skipped=%d failed=%d elapsed=%.2fs" % [
|
||||
built, skipped, failed, float(elapsed_ms) / 1000.0])
|
||||
quit(0 if failed == 0 else 2)
|
||||
|
||||
|
||||
func _run_parallel_controller(args: PackedStringArray, jobs: int) -> void:
|
||||
var started_ms := Time.get_ticks_msec()
|
||||
var threads: Array[Thread] = []
|
||||
print("Starting parallel ADT control splat cache: jobs=%d" % jobs)
|
||||
for worker_index in jobs:
|
||||
var thread := Thread.new()
|
||||
var err := thread.start(_run_worker_process.bind(args, worker_index, jobs))
|
||||
if err != OK:
|
||||
push_error("Failed to start control splat worker thread %d (err=%d)" % [worker_index, err])
|
||||
quit(1)
|
||||
return
|
||||
threads.append(thread)
|
||||
|
||||
var failed := 0
|
||||
for thread in threads:
|
||||
var exit_code: int = int(thread.wait_to_finish())
|
||||
if exit_code != 0:
|
||||
failed += 1
|
||||
|
||||
var elapsed_ms := Time.get_ticks_msec() - started_ms
|
||||
print("Parallel control splat cache finished. jobs=%d failed_workers=%d elapsed=%.2fs" % [
|
||||
jobs, failed, float(elapsed_ms) / 1000.0])
|
||||
quit(0 if failed == 0 else 2)
|
||||
|
||||
|
||||
func _run_worker_process(args: PackedStringArray, worker_index: int, worker_count: int) -> int:
|
||||
var worker_args := PackedStringArray([
|
||||
"--headless",
|
||||
"--path",
|
||||
ProjectSettings.globalize_path("res://"),
|
||||
"--script",
|
||||
"res://src/tools/build_adt_control_splat_cache.gd",
|
||||
"--",
|
||||
])
|
||||
|
||||
var skip_next := false
|
||||
for i in args.size():
|
||||
if skip_next:
|
||||
skip_next = false
|
||||
continue
|
||||
var arg := args[i]
|
||||
if arg == "--jobs" or arg == "--worker-index" or arg == "--worker-count":
|
||||
skip_next = i + 1 < args.size()
|
||||
continue
|
||||
worker_args.append(arg)
|
||||
|
||||
worker_args.append("--jobs")
|
||||
worker_args.append("1")
|
||||
worker_args.append("--worker-index")
|
||||
worker_args.append(str(worker_index))
|
||||
worker_args.append("--worker-count")
|
||||
worker_args.append(str(worker_count))
|
||||
|
||||
print("Control splat worker %d/%d started" % [worker_index + 1, worker_count])
|
||||
var exit_code := OS.execute(OS.get_executable_path(), worker_args)
|
||||
print("Control splat worker %d/%d finished with exit=%d" % [worker_index + 1, worker_count, exit_code])
|
||||
return exit_code
|
||||
|
||||
|
||||
func _count_worker_tiles(
|
||||
files: PackedStringArray,
|
||||
map_name: String,
|
||||
tile_x: Variant,
|
||||
tile_y: Variant,
|
||||
worker_index: Variant,
|
||||
worker_count: int) -> int:
|
||||
|
||||
var tile_index := 0
|
||||
var count := 0
|
||||
for file_name in files:
|
||||
if not file_name.ends_with(".adt"):
|
||||
continue
|
||||
var stem := file_name.trim_suffix(".adt")
|
||||
var parts := stem.split("_")
|
||||
if parts.size() != 3 or parts[0] != map_name:
|
||||
continue
|
||||
if not parts[1].is_valid_int() or not parts[2].is_valid_int():
|
||||
continue
|
||||
var tx := parts[1].to_int()
|
||||
var ty := parts[2].to_int()
|
||||
if tile_x != null and tx != tile_x:
|
||||
continue
|
||||
if tile_y != null and ty != tile_y:
|
||||
continue
|
||||
if worker_index != null and tile_index % worker_count != int(worker_index):
|
||||
tile_index += 1
|
||||
continue
|
||||
tile_index += 1
|
||||
count += 1
|
||||
return count
|
||||
|
||||
|
||||
func _parse_jobs_arg(value: String) -> int:
|
||||
if value.to_lower() == "auto":
|
||||
return maxi(1, OS.get_processor_count() - 1)
|
||||
return maxi(1, value.to_int())
|
||||
|
||||
|
||||
func _get_arg_value(args: PackedStringArray, name: String, default_value: String) -> String:
|
||||
var idx := args.find(name)
|
||||
if idx >= 0 and idx + 1 < args.size():
|
||||
return args[idx + 1]
|
||||
return default_value
|
||||
|
||||
|
||||
func _get_optional_int_arg(args: PackedStringArray, name: String) -> Variant:
|
||||
var idx := args.find(name)
|
||||
if idx >= 0 and idx + 1 < args.size() and args[idx + 1].is_valid_int():
|
||||
return args[idx + 1].to_int()
|
||||
return null
|
||||
|
||||
|
||||
func _normalize_res_path(path: String) -> String:
|
||||
return path.rstrip("/")
|
||||
@@ -0,0 +1 @@
|
||||
uid://c3vxox0uwyyx7
|
||||
@@ -0,0 +1,377 @@
|
||||
extends SceneTree
|
||||
|
||||
## Builds lightweight terrain-splat resources for near-camera quality.
|
||||
##
|
||||
## Usage:
|
||||
## godot --headless --path <project> --script res://src/tools/build_adt_splat_cache.gd -- \
|
||||
## --map Azeroth --jobs auto --lod 0 --force
|
||||
|
||||
const ADT_BUILDER_SCRIPT := preload("res://addons/mpq_extractor/loaders/adt_builder.gd")
|
||||
const SPLAT_TILE_SCRIPT := preload("res://src/resources/splat_adt_tile.gd")
|
||||
const PROGRESS_DIR := "res://data/cache/splat_progress"
|
||||
const PROGRESS_REPORT_INTERVAL_MS := 2000
|
||||
|
||||
var _builder
|
||||
var _loader
|
||||
var _tex_cache: Dictionary = {}
|
||||
var _progress_path := ""
|
||||
var _progress_worker_index := -1
|
||||
var _progress_worker_count := 1
|
||||
var _progress_total := 0
|
||||
var _progress_processed := 0
|
||||
var _progress_built := 0
|
||||
var _progress_skipped := 0
|
||||
var _progress_failed := 0
|
||||
var _progress_last_tile := ""
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
var args := OS.get_cmdline_user_args()
|
||||
var map_name := _get_arg_value(args, "--map", "Azeroth")
|
||||
var extracted_dir := _normalize_res_path(_get_arg_value(args, "--extracted", "res://data/extracted"))
|
||||
var output_dir := _normalize_res_path(_get_arg_value(args, "--output", "res://data/cache/terrain_splat_v1"))
|
||||
var lod := clampi(_get_arg_value(args, "--lod", "0").to_int(), 0, 3)
|
||||
var worker_index: Variant = _get_optional_int_arg(args, "--worker-index")
|
||||
var worker_count := maxi(1, _get_arg_value(args, "--worker-count", "1").to_int())
|
||||
var tile_x: Variant = _get_optional_int_arg(args, "--tile-x")
|
||||
var tile_y: Variant = _get_optional_int_arg(args, "--tile-y")
|
||||
var default_jobs := "1" if tile_x != null or tile_y != null else "auto"
|
||||
var jobs := _parse_jobs_arg(_get_arg_value(args, "--jobs", default_jobs))
|
||||
var force := args.has("--force")
|
||||
|
||||
if jobs > 1 and worker_index == null:
|
||||
_run_parallel_controller(args, jobs)
|
||||
return
|
||||
|
||||
_builder = ADT_BUILDER_SCRIPT.new()
|
||||
if not ClassDB.class_exists("ADTLoader"):
|
||||
push_error("ADTLoader not found. Rebuild GDExtension first.")
|
||||
quit(1)
|
||||
return
|
||||
|
||||
_loader = ClassDB.instantiate("ADTLoader")
|
||||
if _loader == null:
|
||||
push_error("Failed to instantiate ADTLoader.")
|
||||
quit(1)
|
||||
return
|
||||
|
||||
var map_dir := extracted_dir.path_join("World/Maps/%s" % map_name)
|
||||
var dir := DirAccess.open(map_dir)
|
||||
if dir == null:
|
||||
push_error("Cannot open map directory: %s" % map_dir)
|
||||
quit(1)
|
||||
return
|
||||
|
||||
DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path(output_dir.path_join(map_name)))
|
||||
|
||||
var built := 0
|
||||
var skipped := 0
|
||||
var failed := 0
|
||||
var started_ms := Time.get_ticks_msec()
|
||||
var tile_index := 0
|
||||
var worker_total := _count_worker_tiles(dir.get_files(), map_name, tile_x, tile_y, worker_index, worker_count)
|
||||
_progress_setup(worker_index, worker_count, worker_total)
|
||||
|
||||
for file_name in dir.get_files():
|
||||
if not file_name.ends_with(".adt"):
|
||||
continue
|
||||
|
||||
var stem := file_name.trim_suffix(".adt")
|
||||
var parts := stem.split("_")
|
||||
if parts.size() != 3 or parts[0] != map_name:
|
||||
continue
|
||||
if not parts[1].is_valid_int() or not parts[2].is_valid_int():
|
||||
continue
|
||||
|
||||
var tx := parts[1].to_int()
|
||||
var ty := parts[2].to_int()
|
||||
if tile_x != null and tx != tile_x:
|
||||
continue
|
||||
if tile_y != null and ty != tile_y:
|
||||
continue
|
||||
if worker_index != null:
|
||||
if tile_index % worker_count != int(worker_index):
|
||||
tile_index += 1
|
||||
continue
|
||||
tile_index += 1
|
||||
|
||||
var output_res_path := output_dir.path_join(map_name).path_join("%s_%d_%d.res" % [map_name, tx, ty])
|
||||
var output_abs_path := ProjectSettings.globalize_path(output_res_path)
|
||||
if not force and FileAccess.file_exists(output_abs_path):
|
||||
skipped += 1
|
||||
_progress_update(tx, ty, built, skipped, failed)
|
||||
continue
|
||||
|
||||
var source_res_path := map_dir.path_join(file_name)
|
||||
var source_abs_path := ProjectSettings.globalize_path(source_res_path)
|
||||
var data: Dictionary = _loader.call("load_adt", source_abs_path)
|
||||
if data.is_empty() or not data.has("chunks"):
|
||||
push_warning("Splat cache failed to parse ADT: %s" % source_res_path)
|
||||
failed += 1
|
||||
_progress_update(tx, ty, built, skipped, failed)
|
||||
continue
|
||||
|
||||
var payload: Dictionary = _builder.build_tile_coarse_render_payload(
|
||||
data,
|
||||
_tex_cache,
|
||||
ProjectSettings.globalize_path(extracted_dir),
|
||||
lod)
|
||||
var mesh: Mesh = payload.get("mesh", null)
|
||||
if mesh == null:
|
||||
push_warning("Splat cache produced empty mesh: %s" % source_res_path)
|
||||
failed += 1
|
||||
_progress_update(tx, ty, built, skipped, failed)
|
||||
continue
|
||||
|
||||
var splat_tile: Resource = SPLAT_TILE_SCRIPT.new()
|
||||
splat_tile.set("format_version", SPLAT_TILE_SCRIPT.FORMAT_VERSION)
|
||||
splat_tile.set("map_name", map_name)
|
||||
splat_tile.set("tile_x", tx)
|
||||
splat_tile.set("tile_y", ty)
|
||||
splat_tile.set("tile_origin", _builder.get_tile_origin_for_data(data))
|
||||
splat_tile.set("lod", lod)
|
||||
splat_tile.set("terrain_mesh", mesh)
|
||||
|
||||
var save_err := ResourceSaver.save(splat_tile, output_res_path)
|
||||
if save_err != OK:
|
||||
push_warning("Failed to save splat tile: %s (err=%d)" % [output_res_path, save_err])
|
||||
failed += 1
|
||||
_progress_update(tx, ty, built, skipped, failed)
|
||||
continue
|
||||
|
||||
built += 1
|
||||
_progress_update(tx, ty, built, skipped, failed)
|
||||
if built % 25 == 0:
|
||||
print("Built %d splat tiles..." % built)
|
||||
|
||||
var elapsed_ms := Time.get_ticks_msec() - started_ms
|
||||
print("Splat cache finished. built=%d skipped=%d failed=%d elapsed=%.2fs" % [
|
||||
built, skipped, failed, float(elapsed_ms) / 1000.0])
|
||||
_progress_finish(built, skipped, failed)
|
||||
quit(0 if failed == 0 else 2)
|
||||
|
||||
|
||||
func _run_parallel_controller(args: PackedStringArray, jobs: int) -> void:
|
||||
var started_ms := Time.get_ticks_msec()
|
||||
var last_report_ms := 0
|
||||
var threads: Array[Thread] = []
|
||||
_prepare_progress_dir()
|
||||
|
||||
print("Starting parallel ADT splat cache: jobs=%d" % jobs)
|
||||
for worker_index in jobs:
|
||||
var thread := Thread.new()
|
||||
var err := thread.start(_run_worker_process.bind(args, worker_index, jobs))
|
||||
if err != OK:
|
||||
push_error("Failed to start splat worker thread %d (err=%d)" % [worker_index, err])
|
||||
quit(1)
|
||||
return
|
||||
threads.append(thread)
|
||||
|
||||
var finished := false
|
||||
while not finished:
|
||||
OS.delay_msec(250)
|
||||
var now := Time.get_ticks_msec()
|
||||
if now - last_report_ms >= PROGRESS_REPORT_INTERVAL_MS:
|
||||
_print_parallel_progress(jobs, started_ms)
|
||||
last_report_ms = now
|
||||
finished = true
|
||||
for thread in threads:
|
||||
if thread.is_alive():
|
||||
finished = false
|
||||
break
|
||||
|
||||
var failed := 0
|
||||
for thread in threads:
|
||||
var exit_code: int = int(thread.wait_to_finish())
|
||||
if exit_code != 0:
|
||||
failed += 1
|
||||
|
||||
var elapsed_ms := Time.get_ticks_msec() - started_ms
|
||||
_print_parallel_progress(jobs, started_ms)
|
||||
print("Parallel splat cache finished. jobs=%d failed_workers=%d elapsed=%.2fs" % [
|
||||
jobs, failed, float(elapsed_ms) / 1000.0])
|
||||
quit(0 if failed == 0 else 2)
|
||||
|
||||
|
||||
func _run_worker_process(args: PackedStringArray, worker_index: int, worker_count: int) -> int:
|
||||
var worker_args := PackedStringArray([
|
||||
"--headless",
|
||||
"--path",
|
||||
ProjectSettings.globalize_path("res://"),
|
||||
"--script",
|
||||
"res://src/tools/build_adt_splat_cache.gd",
|
||||
"--",
|
||||
])
|
||||
|
||||
var skip_next := false
|
||||
for i in args.size():
|
||||
if skip_next:
|
||||
skip_next = false
|
||||
continue
|
||||
var arg := args[i]
|
||||
if arg == "--jobs" or arg == "--worker-index" or arg == "--worker-count":
|
||||
skip_next = i + 1 < args.size()
|
||||
continue
|
||||
worker_args.append(arg)
|
||||
|
||||
worker_args.append("--jobs")
|
||||
worker_args.append("1")
|
||||
worker_args.append("--worker-index")
|
||||
worker_args.append(str(worker_index))
|
||||
worker_args.append("--worker-count")
|
||||
worker_args.append(str(worker_count))
|
||||
|
||||
print("Splat worker %d/%d started" % [worker_index + 1, worker_count])
|
||||
var exit_code := OS.execute(OS.get_executable_path(), worker_args)
|
||||
print("Splat worker %d/%d finished with exit=%d" % [worker_index + 1, worker_count, exit_code])
|
||||
return exit_code
|
||||
|
||||
|
||||
func _prepare_progress_dir() -> void:
|
||||
DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path(PROGRESS_DIR))
|
||||
var dir := DirAccess.open(PROGRESS_DIR)
|
||||
if dir == null:
|
||||
return
|
||||
for file_name in dir.get_files():
|
||||
if file_name.begins_with("splat_worker_") and file_name.ends_with(".json"):
|
||||
dir.remove(file_name)
|
||||
|
||||
|
||||
func _progress_setup(worker_index: Variant, worker_count: int, total: int) -> void:
|
||||
_progress_worker_index = int(worker_index) if worker_index != null else 0
|
||||
_progress_worker_count = worker_count
|
||||
_progress_total = total
|
||||
_progress_processed = 0
|
||||
_progress_built = 0
|
||||
_progress_skipped = 0
|
||||
_progress_failed = 0
|
||||
_progress_last_tile = ""
|
||||
_progress_path = PROGRESS_DIR.path_join("splat_worker_%d.json" % _progress_worker_index)
|
||||
_progress_write(false)
|
||||
|
||||
|
||||
func _progress_update(tx: int, ty: int, built: int, skipped: int, failed: int) -> void:
|
||||
_progress_processed += 1
|
||||
_progress_built = built
|
||||
_progress_skipped = skipped
|
||||
_progress_failed = failed
|
||||
_progress_last_tile = "%d_%d" % [tx, ty]
|
||||
_progress_write(false)
|
||||
|
||||
|
||||
func _progress_finish(built: int, skipped: int, failed: int) -> void:
|
||||
_progress_built = built
|
||||
_progress_skipped = skipped
|
||||
_progress_failed = failed
|
||||
_progress_processed = _progress_total
|
||||
_progress_write(true)
|
||||
|
||||
|
||||
func _progress_write(done: bool) -> void:
|
||||
if _progress_path.is_empty():
|
||||
return
|
||||
var file := FileAccess.open(_progress_path, FileAccess.WRITE)
|
||||
if file == null:
|
||||
return
|
||||
file.store_string(JSON.stringify({
|
||||
"worker": _progress_worker_index,
|
||||
"workers": _progress_worker_count,
|
||||
"total": _progress_total,
|
||||
"processed": _progress_processed,
|
||||
"built": _progress_built,
|
||||
"skipped": _progress_skipped,
|
||||
"failed": _progress_failed,
|
||||
"last_tile": _progress_last_tile,
|
||||
"done": done,
|
||||
}))
|
||||
|
||||
|
||||
func _print_parallel_progress(jobs: int, started_ms: int) -> void:
|
||||
var now := Time.get_ticks_msec()
|
||||
var total := 0
|
||||
var processed := 0
|
||||
var built := 0
|
||||
var skipped := 0
|
||||
var failed := 0
|
||||
var done := 0
|
||||
for worker_index in jobs:
|
||||
var path := PROGRESS_DIR.path_join("splat_worker_%d.json" % worker_index)
|
||||
if not FileAccess.file_exists(path):
|
||||
continue
|
||||
var file := FileAccess.open(path, FileAccess.READ)
|
||||
if file == null:
|
||||
continue
|
||||
var parsed: Variant = JSON.parse_string(file.get_as_text())
|
||||
if not (parsed is Dictionary):
|
||||
continue
|
||||
var row: Dictionary = parsed
|
||||
total += int(row.get("total", 0))
|
||||
processed += int(row.get("processed", 0))
|
||||
built += int(row.get("built", 0))
|
||||
skipped += int(row.get("skipped", 0))
|
||||
failed += int(row.get("failed", 0))
|
||||
if bool(row.get("done", false)):
|
||||
done += 1
|
||||
|
||||
var elapsed := maxf(0.001, float(now - started_ms) / 1000.0)
|
||||
var rate := float(processed) / elapsed
|
||||
print("Progress: %d/%d tiles, built=%d skipped=%d failed=%d, workers done=%d/%d, %.2f tiles/s" % [
|
||||
processed, total, built, skipped, failed, done, jobs, rate])
|
||||
|
||||
|
||||
func _count_worker_tiles(
|
||||
files: PackedStringArray,
|
||||
map_name: String,
|
||||
tile_x: Variant,
|
||||
tile_y: Variant,
|
||||
worker_index: Variant,
|
||||
worker_count: int) -> int:
|
||||
var count := 0
|
||||
var tile_index := 0
|
||||
for file_name in files:
|
||||
if not file_name.ends_with(".adt"):
|
||||
continue
|
||||
var parts := file_name.trim_suffix(".adt").split("_")
|
||||
if parts.size() != 3 or parts[0] != map_name:
|
||||
continue
|
||||
if not parts[1].is_valid_int() or not parts[2].is_valid_int():
|
||||
continue
|
||||
var tx := parts[1].to_int()
|
||||
var ty := parts[2].to_int()
|
||||
if tile_x != null and tx != tile_x:
|
||||
continue
|
||||
if tile_y != null and ty != tile_y:
|
||||
continue
|
||||
if worker_index != null and tile_index % worker_count != int(worker_index):
|
||||
tile_index += 1
|
||||
continue
|
||||
tile_index += 1
|
||||
count += 1
|
||||
return count
|
||||
|
||||
|
||||
func _get_arg_value(args: PackedStringArray, name: String, default_value: String) -> String:
|
||||
var index := args.find(name)
|
||||
if index >= 0 and index + 1 < args.size():
|
||||
return args[index + 1]
|
||||
return default_value
|
||||
|
||||
|
||||
func _get_optional_int_arg(args: PackedStringArray, name: String):
|
||||
var index := args.find(name)
|
||||
if index >= 0 and index + 1 < args.size() and args[index + 1].is_valid_int():
|
||||
return args[index + 1].to_int()
|
||||
return null
|
||||
|
||||
|
||||
func _parse_jobs_arg(value: String) -> int:
|
||||
var normalized := value.strip_edges().to_lower()
|
||||
if normalized == "auto":
|
||||
return clampi(OS.get_processor_count() - 2, 1, 8)
|
||||
return maxi(1, normalized.to_int())
|
||||
|
||||
|
||||
func _normalize_res_path(path: String) -> String:
|
||||
if path.begins_with("res://"):
|
||||
return path
|
||||
return "res://%s" % path.trim_prefix("./").trim_prefix("/")
|
||||
@@ -0,0 +1 @@
|
||||
uid://cr7v1un5okd3p
|
||||
Reference in New Issue
Block a user