File size: 6,815 Bytes
b6a38d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
--- Returns the camera eye position adjusted to be above the terrain.
-- @param EyePt point The camera eye position.
-- @param LookAtPt point The camera look at position.
-- @return point The adjusted camera eye position.
-- @return point The camera look at position.
function GetCameraEyeOverTerrain(EyePt, LookAtPt)
    local height = GetWalkableZ(EyePt) + const.CameraMinTerrainDist
    local EyePt = EyePt
    if height > EyePt:z() then
        EyePt = EyePt:SetZ(height)
    end
    return EyePt, LookAtPt
end

--- Moves camera look at and eye pos smoothly over the given period of time.
-- @cstyle int MoveCamera(function get_look_at, function get_eye, int time);
-- @param get_look_at function; a callback function that receives time as parameter and returns the camera look at position; the callback function is called every 33ms.
-- @param get_eye function; a callback function that receives time and look at position as parameter and returns the camera eye position; the callback function is called every 33ms.
-- @param time int.
-- @return int; orientation in minutes.

function MoveCamera(get_look_at, get_eye, time)
	if not camera3p.IsActive() then
		return
	end
	local sleep = 33
	local time_from_start = 0
	while true do
		local time_to_end = time - time_from_start
		local sleep_time = Min(sleep, time - time_from_start)
		local target_time = Min(time_from_start+sleep_time, time)

		local look_at = get_look_at(target_time)
		local eye     = get_eye    (look_at, target_time)
		eye = GetCameraEyeOverTerrain(eye, look_at)
		camera3p.SetLookAt(look_at, sleep_time)
		camera3p.SetEye   (eye    , sleep_time)
		if sleep_time > 0 then
			Sleep(sleep_time)
		end
		if not camera3p.IsActive() then
			return
		end
		time_from_start = time_from_start + sleep_time
		if time_from_start >= time then
			break
		end
	end
end

--- Return a callback function that is to be used as get_look_at parameter of MoveCamera function.
-- The callback will move the current look at position from the camera current look at postion to the target_pos.
-- 'observing' the target object's movement - the farther the target moves from his start position, the farther.
-- the camera look at will move away from its initial position and will approach the target_pos.
-- @cstyle function LookAtFollowCharacter(object target, point target_pos, int total_time).
-- @param target object.
-- @param target_pos point.
-- @param total_time int.
-- @return function.
function LookAtFollowCharacter(target, target_pos, total_time)
	if not camera3p.IsActive() then
		return
	end
	local start_pt = camera3p.GetLookAt()
	local last_dist = 0
	local max_dist = start_pt:Dist(target_pos)
	local pos_lerp = ValueLerp(start_pt, target_pos, max_dist)
	local height_lerp = ValueLerp(start_pt:z(), target_pos:z(), total_time)
	local last_pos
	return function(time)
		if IsValid(target) then
			local pos = GetPosFromPosSpot(target)
			local dist = Min(pos:Dist(start_pt), max_dist)
			if dist > last_dist then
				last_dist = dist
			end
		end
		return pos_lerp(last_dist):SetZ(height_lerp(time))
	end
end

--- Return a callback function that is to be used as get_eye parameter of MoveCamera function.
-- The callback will move smoothly the camera eye's z to the targetz, rotate the camera to the target_yaw, keeping the 2d distance from the eye to the look at to dist_eye_look_at.
-- @cstyle function RotateKeepDistEye(int target_eyez, int target_yaw, point dist_eye_look_at, int total_time).
-- @param target_eyez int.
-- @param target_yaw int.
-- @param dist_eye_look_at int.
-- @param total_time int.
-- @return function.
function RotateKeepDistEye(target_eyez, target_yaw, dist_eye_look_at, total_time)
	local pt = point(-dist_eye_look_at, 0, 0)
	local angle_lerp = AngleLerp(camera.GetYaw(), target_yaw, total_time)
	local eye_height_lerp = ValueLerp(camera.GetEye():z(), target_eyez, total_time)
	return function(look_at_pos, time)
		local eye = look_at_pos + Rotate(pt, angle_lerp(time))
		eye = eye:SetZ(eye_height_lerp(time))
		return eye
	end
end

--- This function will smoothly move/rotate the camera according the given parameters, mimicking the XCamera default behavior.
-- @cstyle void DefMoveCamera(point pos, int yaw, int pitch, int rot_speed, int move_speed, int move_time, int yaw_time, int pitch_time).
-- @param pos point; target camera look at position.
-- @param yaw int; targer camera yaw.
-- @param pitch int; target camera pitch.
-- @param rot_speed int; camera rotation speed in angular minutes per sec; can be omitted; used to calculate move_time in case move_time is omitted.
-- @param move_speed int; camera movement speed in angular minutes per sec; can be omitted; used to calculate yaw_time and pitch_time in case yaw_time or pitch_time are omitted.
-- @param move_time int; the time the camera should reach the target position; if omitted the time will be calculated from move_speed parameter.
-- @param yaw_time int; the time the camera should reach the target yaw; if omitted the time will be calculated from rot_speed parameter.
-- @param pitch_time int; the time the camera should reach the target position; if omitted the time will be calculated from rot_speed parameter.
-- @return void.
function DefMoveCamera(pos, yaw, dist_scale, pitch, rot_speed, move_speed, move_time, yaw_time, pitch_time)
	if not camera3p.IsActive() then
		return
	end
	if not pos:IsValidZ() then
		pos = pos:SetTerrainZ()
	end
	local start_look_at, start_pitch, start_yaw = camera3p.GetLookAt(), camera3p.GetPitch(), camera3p.GetYaw()
	local look_at_height_offset = (const.CameraScale*const.CameraVerticalOffset/100)*dist_scale/100

	rot_speed = rot_speed or const.CameraRotationDegreePerSec
	move_speed = move_speed or const.CameraResetMmPerSec
	
	local pitch_time	= pitch_time or abs(AngleDiff(start_pitch, pitch)/60)*1000/rot_speed
	local yaw_time		= yaw_time or abs(AngleDiff(start_yaw, yaw)/60)*1000/rot_speed
	local move_time		= move_time or pos:Dist(start_look_at)*1000/move_speed
	local yaw_lerp = AngleLerp(start_yaw, yaw, yaw_time, true)
	local pos_lerp = ValueLerp(start_look_at, pos:SetZ(look_at_height_offset + (pos:z() or terrain.GetHeight(pos))), move_time, true)
	local start_l, start_h = GetCameraLH(start_pitch, camera3p.DistanceAtPitch(start_pitch) * dist_scale / 100)
	local end_l  , end_h   = GetCameraLH(      pitch, camera3p.DistanceAtPitch(      pitch) * dist_scale / 100)
	
	local l_lerp, h_lerp = ValueLerp(start_l, end_l, pitch_time, true), ValueLerp(start_h, end_h, pitch_time, true)
	
	
	local function LookAt(t)
		return pos_lerp(t)
	end
	
	local function EyePt(look_at, t)
		local yaw = yaw_lerp(t)
		local l, h = l_lerp(t), h_lerp(t)

		local eye = (look_at+Rotate(point(-l, 0, 0), yaw)):SetZ(h+look_at:z())
		return eye
	end
	
	MoveCamera(LookAt, EyePt, Max(pitch_time, yaw_time, move_time))
end