完善客户端功能并同步差距文档

This commit is contained in:
shenlei
2026-09-03 18:40:01 +09:00
parent f93a172296
commit 13ccb8d02d
135 changed files with 21881 additions and 1259 deletions
+197
View File
@@ -0,0 +1,197 @@
# Mde (P5) —— ClientVS22 EffectMesh 的 .mde 二进制读取器。
#
# 40250/ClientVS22/source/EffectLib/EffectMesh.cpp 支持两种布局:
# EffectData —— v1:每个几何体固定顶点/索引/UV 数量,每帧只有 visibility
# MDEData002 —— v2:每帧带 changed/visibility 以及三组数量。
# 参考端把索引顶点展开成 TPTVertex,并把 UV.y 取反;这里同样展开,
# 同时把客户端厘米坐标换成 Godot 的米坐标。
extends RefCounted
const HEADER_SIZE := 11
const GEOMETRY_HEADER_SIZE := 160 # object name 32 + diffuse path 128
const MAX_GEOMETRIES := 256
const MAX_FRAMES := 4096
const MAX_ELEMENTS := 1_000_000
var last_error := ""
func parse_file(path: String) -> Dictionary:
last_error = ""
if not FileAccess.file_exists(path):
last_error = "no such file: " + path
return {}
return parse_bytes(FileAccess.get_file_as_bytes(path))
func parse_bytes(data: PackedByteArray) -> Dictionary:
last_error = ""
if data.size() < HEADER_SIZE + 8:
last_error = "truncated header"
return {}
var header := _fixed_string(data, 0, HEADER_SIZE)
var version := 0
if header == "EffectData":
version = 1
elif header == "MDEData002":
version = 2
else:
last_error = "unknown header: " + header
return {}
var offset := HEADER_SIZE
var geometry_count := int(data.decode_s32(offset))
offset += 4
var frame_count := int(data.decode_s32(offset))
offset += 4
if geometry_count < 1 or geometry_count > MAX_GEOMETRIES:
last_error = "invalid geometry count: %d" % geometry_count
return {}
if frame_count < 1 or frame_count > MAX_FRAMES:
last_error = "invalid frame count: %d" % frame_count
return {}
var geometries: Array = []
for geometry_index in geometry_count:
if not _has(data, offset, GEOMETRY_HEADER_SIZE):
last_error = "truncated geometry header %d" % geometry_index
return {}
var name := _fixed_string(data, offset, 32)
offset += 32
var diffuse := _fixed_string(data, offset, 128)
offset += 128
var fixed_vertex_count := 0
var fixed_index_count := 0
var fixed_uv_count := 0
if version == 1:
if not _has(data, offset, 12):
last_error = "truncated v1 geometry counts %d" % geometry_index
return {}
fixed_vertex_count = int(data.decode_u32(offset))
fixed_index_count = int(data.decode_u32(offset + 4))
fixed_uv_count = int(data.decode_u32(offset + 8))
offset += 12
if not _valid_counts(fixed_vertex_count, fixed_index_count, fixed_uv_count):
last_error = "invalid v1 geometry counts %d" % geometry_index
return {}
var frames: Array = []
for frame_index in frame_count:
var changed := 0
var visibility := 1.0
var vertex_count := fixed_vertex_count
var index_count := fixed_index_count
var uv_count := fixed_uv_count
if version == 2:
if not _has(data, offset, 17): # byte + float + 3 x DWORD
last_error = "truncated v2 frame header %d/%d" % [geometry_index, frame_index]
return {}
changed = int(data.decode_u8(offset))
offset += 1
visibility = float(data.decode_float(offset))
offset += 4
vertex_count = int(data.decode_u32(offset))
offset += 4
index_count = int(data.decode_u32(offset))
offset += 4
uv_count = int(data.decode_u32(offset))
offset += 4
else:
if not _has(data, offset, 4):
last_error = "truncated v1 frame header %d/%d" % [geometry_index, frame_index]
return {}
visibility = float(data.decode_float(offset))
offset += 4
if not _valid_counts(vertex_count, index_count, uv_count):
last_error = "invalid frame counts %d/%d" % [geometry_index, frame_index]
return {}
var bytes_needed := vertex_count * 12 + index_count * 4 + uv_count * 8 + index_count * 4
if not _has(data, offset, bytes_needed):
last_error = "truncated frame data %d/%d" % [geometry_index, frame_index]
return {}
var positions := PackedVector3Array()
positions.resize(vertex_count)
for i in vertex_count:
var base := offset + i * 12
positions[i] = Vector3(
float(data.decode_float(base)),
float(data.decode_float(base + 4)),
float(data.decode_float(base + 8))) * 0.01
offset += vertex_count * 12
var indices := PackedInt32Array()
indices.resize(index_count)
for i in index_count:
indices[i] = int(data.decode_s32(offset + i * 4))
offset += index_count * 4
var uvs := PackedVector2Array()
uvs.resize(uv_count)
for i in uv_count:
var base := offset + i * 8
uvs[i] = Vector2(
float(data.decode_float(base)),
-float(data.decode_float(base + 4)))
offset += uv_count * 8
var texture_indices := PackedInt32Array()
texture_indices.resize(index_count)
for i in index_count:
texture_indices[i] = int(data.decode_s32(offset + i * 4))
offset += index_count * 4
# EffectMesh.cpp renders a non-indexed TPTVertex array. Keep the
# same order so a frame is directly usable as a TriangleList.
var render_vertices := PackedVector3Array()
var render_uvs := PackedVector2Array()
render_vertices.resize(index_count)
render_uvs.resize(index_count)
for i in index_count:
var vi := indices[i]
var ti := texture_indices[i]
if vi < 0 or vi >= positions.size() or ti < 0 or ti >= uvs.size():
last_error = "index out of range %d/%d" % [geometry_index, frame_index]
return {}
render_vertices[i] = positions[vi]
render_uvs[i] = uvs[ti]
frames.append({
"changed": changed,
"visibility": visibility,
"vertex_count": vertex_count,
"index_count": index_count,
"uv_count": uv_count,
"vertices": render_vertices,
"uvs": render_uvs,
})
geometries.append({
"name": name,
"diffuse": diffuse,
"frames": frames,
})
return {
"version": version,
"geometry_count": geometry_count,
"frame_count": frame_count,
"geometries": geometries,
}
func _valid_counts(vertex_count: int, index_count: int, uv_count: int) -> bool:
return vertex_count >= 0 and index_count >= 0 and uv_count >= 0 \
and vertex_count <= MAX_ELEMENTS and index_count <= MAX_ELEMENTS \
and uv_count <= MAX_ELEMENTS
func _has(data: PackedByteArray, offset: int, length: int) -> bool:
return offset >= 0 and length >= 0 and offset <= data.size() - length
func _fixed_string(data: PackedByteArray, offset: int, length: int) -> String:
var out := ""
for i in length:
var value := int(data[offset + i])
if value == 0:
break
out += String.chr(value)
return out