Files
mtgodot-poc/project/fx/mde.gd
T

198 lines
6.4 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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] = preload("res://fx/effect_space.gd").position(Vector3(
float(data.decode_float(base)),
float(data.decode_float(base + 4)),
float(data.decode_float(base + 8))))
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