hy-farm/godot/FarmGodot/scripts/resources/egret_movie_clip_cache.gd
2026-05-29 19:54:56 +08:00

136 lines
4.0 KiB
GDScript

class_name EgretMovieClipCache
extends RefCounted
# Egret MovieClip atlas loader.
# It reads the old {mc,res} JSON files and returns frame textures plus offsets.
var _cache: Dictionary = {}
var _atlases: Dictionary = {}
func frames(atlas_path: String, clip_name: String) -> Array[Dictionary]:
return _frames(atlas_path, clip_name, "")
func frames_for_label(atlas_path: String, clip_name: String, label_name: String) -> Array[Dictionary]:
return _frames(atlas_path, clip_name, label_name)
func _frames(atlas_path: String, clip_name: String, label_name: String) -> Array[Dictionary]:
var cache_key := "%s:%s:%s" % [atlas_path, clip_name, label_name]
if _cache.has(cache_key):
var cached: Array[Dictionary] = _cache[cache_key]
return cached.duplicate(false)
var output: Array[Dictionary] = []
var atlas := _load_atlas(atlas_path)
if atlas == null:
_cache[cache_key] = output
return output
var json_path := _movie_clip_json_path(atlas_path)
var file := FileAccess.open(json_path, FileAccess.READ)
if file == null:
_cache[cache_key] = output
return output
var parsed = JSON.parse_string(file.get_as_text())
if not parsed is Dictionary:
_cache[cache_key] = output
return output
var clips = parsed.get("mc", {})
var resources = parsed.get("res", {})
if not clips is Dictionary or not resources is Dictionary:
_cache[cache_key] = output
return output
var clip = clips.get(clip_name, {})
if not clip is Dictionary:
_cache[cache_key] = output
return output
var raw_frames = clip.get("frames", [])
if not raw_frames is Array:
_cache[cache_key] = output
return output.duplicate(false)
var start_index := 0
var end_index: int = raw_frames.size() - 1
if not label_name.is_empty():
var label_range := _label_range(clip, label_name, raw_frames.size())
if label_range.is_empty():
_cache[cache_key] = output
return output.duplicate(false)
start_index = int(label_range.get("start", 0))
end_index = int(label_range.get("end", raw_frames.size() - 1))
var frame_rate := int(clip.get("frameRate", 12))
for index in range(start_index, end_index + 1):
var raw_frame = raw_frames[index]
if not raw_frame is Dictionary:
continue
var resource_key := str(raw_frame.get("res", ""))
var resource = resources.get(resource_key, {})
if not resource is Dictionary:
continue
var width := float(resource.get("w", resource.get("width", 1)))
var height := float(resource.get("h", resource.get("height", 1)))
var texture := AtlasTexture.new()
texture.atlas = atlas
texture.region = Rect2(
float(resource.get("x", 0)),
float(resource.get("y", 0)),
width,
height
)
output.append({
"texture": texture,
"offset": Vector2(float(raw_frame.get("x", 0)), float(raw_frame.get("y", 0))),
"size": Vector2(width, height),
"frame_rate": frame_rate,
})
_cache[cache_key] = output
return output.duplicate(false)
func _label_range(clip: Dictionary, label_name: String, frame_count: int) -> Dictionary:
var labels = clip.get("labels", [])
if not labels is Array:
return {}
for label in labels:
if not label is Dictionary:
continue
if str(label.get("name", "")) != label_name:
continue
var start_index := clampi(int(label.get("frame", 1)) - 1, 0, maxi(frame_count - 1, 0))
var end_index := clampi(int(label.get("end", start_index + 1)) - 1, start_index, maxi(frame_count - 1, 0))
return {
"start": start_index,
"end": end_index,
}
return {}
func _movie_clip_json_path(atlas_path: String) -> String:
var basename := atlas_path.get_basename()
var candidates := ["%s.json" % basename]
if basename.ends_with("_tex"):
candidates.append("%s_mc.json" % basename.trim_suffix("_tex"))
for candidate in candidates:
if ResourceLoader.exists(candidate) or FileAccess.file_exists(candidate):
return candidate
return candidates[0]
func _load_atlas(atlas_path: String) -> Texture2D:
if _atlases.has(atlas_path):
return _atlases[atlas_path]
if not ResourceLoader.exists(atlas_path):
return null
var atlas := load(atlas_path) as Texture2D
_atlases[atlas_path] = atlas
return atlas