Files
mtgodot-poc/project/character_winding_test.gd

91 lines
3.4 KiB
GDScript

# Detect CPU animation dropping winding (including accidentally loading an old dylib).
extends SceneTree
var failures := 0
func _initialize() -> void:
call_deferred("run")
func indices(model: Node) -> Array:
var meshes := model.find_children("*", "MeshInstance3D", true, false)
var out := []
for mi in meshes:
for s in mi.mesh.get_surface_count():
out.append(mi.mesh.surface_get_arrays(s)[Mesh.ARRAY_INDEX])
return out
func check_reversed(a: Array, b: Array, stage: String) -> void:
if a.is_empty() or a.size() != b.size():
failures += 1
printerr("FAIL: missing surfaces at ", stage)
return
for s in a.size():
if a[s].size() != b[s].size():
failures += 1
continue
for t in range(0, a[s].size(), 3):
if a[s][t] != b[s][t] or a[s][t + 1] != b[s][t + 2] or a[s][t + 2] != b[s][t + 1]:
failures += 1
printerr("FAIL: winding lost at ", stage, " surface ", s)
break
func run() -> void:
var base := AssetRoot.path().path_join("PC/ymir work/pc/warrior")
var models := []
var animations := []
for flip in [false, true]:
var model: Node = ClassDB.instantiate("Metin2Model")
model.set("gr2_path", base.path_join("warrior_novice.gr2"))
model.set("hair_gr2", base.path_join("hair/hair_1_1.gr2"))
model.set("flip_winding", flip)
model.set("lod_enabled", false)
root.add_child(model)
models.append(model)
check_reversed(indices(models[0]), indices(models[1]), "bind")
for model in models:
var anim: Node = ClassDB.instantiate("Metin2AnimPlayer")
anim.set("model_path", model.get_path())
anim.set("anim_path", base.path_join("general/wait.msa"))
anim.set("playing", false)
root.add_child(anim)
animations.append(anim)
for time in [0.0, 0.5, 1.0]:
for anim in animations:
anim.set("time", time)
check_reversed(indices(models[0]), indices(models[1]), "animated %s" % time)
if indices(models[0]).size() != 4:
failures += 1
printerr("FAIL: body and hair surfaces must both be exercised")
# Changes must update the visible mesh immediately, even while animation is paused.
for model in models:
model.set("hair_gr2", "")
if indices(model).size() != 3:
failures += 1
printerr("FAIL: detached hair remains in rendered mesh")
model.set("hair_gr2", base.path_join("hair/hair_2_1.gr2"))
if indices(model).size() != 4:
failures += 1
printerr("FAIL: replacement hair missing")
model.set("hair_skin", base.path_join("warrior_hair_01.dds"))
var mi: MeshInstance3D = model.find_children("*", "MeshInstance3D", true, false)[0]
var hair: ShaderMaterial = mi.get_active_material(3)
var expected: Image = preload("res://ui/ui_assets.gd").load_dds_image(base.path_join("warrior_hair_01.dds"))
var actual: Image = hair.get_shader_parameter("albedo_tex").get_image()
actual.convert(Image.FORMAT_RGBA8)
expected.convert(Image.FORMAT_RGBA8)
actual.clear_mipmaps()
expected.clear_mipmaps()
if actual.get_data() != expected.get_data() or not hair.shader.code.contains("cull_disabled"):
failures += 1
printerr("FAIL: replacement hair texture or two-sided material not applied")
if OS.get_environment("MTGODOT_GPUSKIN") == "1":
var body: ShaderMaterial = mi.get_active_material(0)
if int(body.get_shader_parameter("mode")) != 0:
failures += 1
printerr("FAIL: body incorrectly reclassified as hair")
print("character_winding_test: failures=", failures)
for node in animations + models:
node.queue_free()
await process_frame
quit(1 if failures else 0)