OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / dc_level_texture_probe.gd

85 lines · gd ref:
extends SceneTree
## Crash bisect for _apply_textures. Runs the SAME sequence map_dc_level does,
## printing before AND after every step, so the last line printed names the step
## that died. Must run with a REAL renderer (headless has no GPU, and the crash is
## on the texture-upload path):
##
##   DISPLAY=:0 "$HYPER_TPS_GODOT" --rendering-driver vulkan --script scenes/test/dc_level_texture_probe.gd

const MAP_PATH := "/storage/projects/gamedev/quake/quakemash/START.MAP"
const WAD_PATH := "/storage/projects/gamedev/quake/quakemash/gfx/base.wad"
const PAL_PATH := "/storage/projects/gamedev/quake/darkyplafe/id1/gfx/palette.lmp"

var _lvl: Node3D = null
var _frame := 0

func _initialize() -> void:
	_lvl = ClassDB.instantiate("DCLevel") as Node3D
	root.add_child(_lvl)

func _step(msg: String) -> void:
	print("[probe] %s" % msg)
	# Flush matters: a hard crash loses buffered stdout, and then the last line
	# printed is NOT the last step reached.
	OS.delay_msec(1)

func _process(_delta: float) -> bool:
	_frame += 1
	if _frame < 3:
		return false

	_step("import START.MAP")
	var importer := QuakeMapImporter.new()
	var tex_dims: Dictionary = QuakeWadLoader.load_dims(WAD_PATH)
	var n: int = importer.import_file(_lvl, MAP_PATH, 0.028, Vector3.ZERO, 1.0, null, false, tex_dims, true)
	_step("imported %d brushes; %d materials; %d projection floats" % [
		n, importer.material_names.size(), importer.projections.size()])

	_step("rebuild")
	_lvl.rebuild()
	_step("rebuilt: %s" % str(_lvl.get_stats().get("triangles", 0)))

	_step("load shader")
	var shader: Shader = load("res://scenes/maps/dc_level.gdshader")
	_step("shader loaded: %s" % str(shader != null))

	var mat := ShaderMaterial.new()
	mat.shader = shader

	_step("QuakeWadLoader.load — %d names" % importer.material_names.size())
	var res: Dictionary = QuakeWadLoader.load(WAD_PATH, PAL_PATH,
		PackedStringArray(importer.material_names), 64)
	_step("wad load returned keys=%s" % str(res.keys()))

	if res.has("array"):
		_step("array type=%s" % str(typeof(res["array"])))
		mat.set_shader_parameter("tex_atlas", res["array"])
		_step("tex_atlas set")
	if res.has("layers"):
		_step("layers type=%s" % str(typeof(res["layers"])))
		# This is the exact expression map_dc_level used. If `layers` is not an
		# Array, `as Array` yields null and .size() on null is a hard failure.
		var layers_any: Variant = res["layers"]
		_step("layers size via duck-typing = %d" % int(layers_any.size()))

	var entries: int = importer.projections.size() / 16
	_step("projection entries=%d ; bytes=%d ; needed=%d" % [
		entries, importer.projections.to_byte_array().size(), 4 * entries * 16])
	if entries > 0:
		var img := Image.create_from_data(4, entries, false, Image.FORMAT_RGBAF,
										  importer.projections.to_byte_array())
		_step("image created: %dx%d fmt=%d" % [img.get_width(), img.get_height(), img.get_format()])
		var tex := ImageTexture.create_from_image(img)
		_step("ImageTexture created: %s" % str(tex != null))
		mat.set_shader_parameter("projection_table", tex)
		mat.set_shader_parameter("has_projection_table", true)
		mat.set_shader_parameter("projection_rows", entries)
		_step("projection uniforms set")

	mat.set_shader_parameter("use_textures", true)
	_step("assign material_override")
	var mi := _lvl.get_node_or_null("LevelMesh") as MeshInstance3D
	mi.material_override = mat
	_step("material assigned — SURVIVED THE WHOLE SEQUENCE")
	return true