136 lines
4.0 KiB
GDScript
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
|