aboutsummaryrefslogtreecommitdiff
path: root/client/player/camera_recorder.gd
blob: 8f398c2b72a1cd38e8d671c5a8740d28b6c7b22f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# Hurry Curry! - a game about cooking
# Copyright (C) 2025 Hurry Curry! contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, version 3 of the License only.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
#
extends Node

@export var record: bool = false
@export var replay: bool = false
@export_file var recording_path: String = ""

@onready var camera: Camera3D = $".."

var time = 0.
var keyframes = [] # Array<[float, Transform3D]>

func _ready() -> void:
	if record and replay: return push_error("concurrent ecord and replay is not supported")
	if replay: load_recording()

func _exit_tree() -> void:
	if record: save_recording()

func load_recording():
	print("Loading replay...")
	var file := FileAccess.open(recording_path, FileAccess.READ)
	var text := file.get_as_text()
	keyframes.clear()
	for line in text.split("\n"):
		if line == "": continue
		var toks = line.split(",")
		var time2 = float(toks[0])
		var transform = parse_transform(toks[1])
		var fov = float(toks[2])
		keyframes.push_back([time2, transform, fov])
	print("Done")

func save_recording():
	print("Saving replay...")
	var file := FileAccess.open(recording_path, FileAccess.WRITE)
	var out = ""
	for k in keyframes:
		out += "%f,%s,%f\n" % [k[0], serialize_transform(k[1]), k[2]]
	file.store_string(out)
	print("Done")

func serialize_transform(t: Transform3D) -> String:
	return "%f|%f|%f|%f|%f|%f|%f|%f|%f|%f|%f|%f" % [
		t.basis.x.x, t.basis.y.x, t.basis.z.x, t.origin.x,
		t.basis.x.y, t.basis.y.y, t.basis.z.y, t.origin.y,
		t.basis.x.z, t.basis.y.z, t.basis.z.z, t.origin.z,
	]

func parse_transform(s: String) -> Transform3D:
	var c := s.split("|")
	var t := Transform3D.IDENTITY
	t.basis.x.x = float(c[0])
	t.basis.y.x = float(c[1])
	t.basis.z.x = float(c[2])
	t.origin.x = float(c[3])
	t.basis.x.y = float(c[4])
	t.basis.y.y = float(c[5])
	t.basis.z.y = float(c[6])
	t.origin.y = float(c[7])
	t.basis.x.z = float(c[8])
	t.basis.y.z = float(c[9])
	t.basis.z.z = float(c[10])
	t.origin.z = float(c[11])
	return t

func _process(dt: float):
	if Global.game_paused: return
	time += dt
	if record: keyframes.push_back([time, camera.global_transform, camera.fov])
	if replay:
		var index = keyframes.bsearch_custom([time], func (a, b): return a[0] < b[0])
		if index >= keyframes.size(): return
		var from = keyframes[max(0, index - 1)]
		var to = keyframes[index]
		var t = (time - from[0]) / (to[0] - from[0])
		camera.global_transform = from[1].interpolate_with(to[1], t)
		camera.fov = from[2] * (1. - t) + to[2] * t