1 /* --------------------------------------------------------------------
2 EXTREME TUXRACER
3 
4 Copyright (C) 1999-2001 Jasmin F. Patry (Tuxracer)
5 Copyright (C) 2010 Extreme Tux Racer Team
6 
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
11 
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 ---------------------------------------------------------------------*/
17 
18 #ifdef HAVE_CONFIG_H
19 #include <etr_config.h>
20 #endif
21 
22 #include "keyframe.h"
23 #include "course.h"
24 #include "spx.h"
25 #include "tux.h"
26 #include "game_ctrl.h"
27 #include "physics.h"
28 #include <algorithm>
29 #include <iterator>
30 
31 static const int numJoints = 19;
32 
33 // The jointnames are shown on the tools screen and define the
34 // possible rotations. A joint can be rotated around 3 axis, so
35 // a joint can contain up to 3 joinnames.
36 static const std::string jointnames[numJoints] = {
37 	"time","pos.x","pos.y","pos.z","yaw","pitch","roll","neck","head",
38 	"l_shldr","r_shldr","l_arm","r_arm",
39 	"l_hip","r_hip","l_knee","r_knee","l_ankle","r_ankle"
40 };
41 
42 // The highlightnames must be official joint identifiers, defined in
43 // the character description. They are used to find the port nodes
44 // for highlighting
45 static const std::string highlightnames[numJoints] = {
46 	"","","","","","","","neck","head",
47 	"left_shldr","right_shldr","left_shldr","right_shldr",
48 	"left_hip","right_hip","left_knee","right_knee","left_ankle","right_ankle"
49 };
50 
51 CKeyframe TestFrame;
52 
CKeyframe()53 CKeyframe::CKeyframe() {
54 	keytime = 0;
55 	active = false;
56 	loaded = false;
57 	heightcorr = 0;
58 	keyidx = 0;
59 }
60 
interp(double frac,double v1,double v2)61 double CKeyframe::interp(double frac, double v1, double v2) {
62 	return frac * v1 + (1.0 - frac) * v2;
63 }
64 
Init(const TVector3d & ref_position,double height_correction)65 void CKeyframe::Init(const TVector3d& ref_position, double height_correction) {
66 	if (!loaded) return;
67 	g_game.character->shape->ResetNode("head");
68 	g_game.character->shape->ResetNode("neck");
69 	refpos = ref_position;
70 	heightcorr = height_correction;
71 	active = true;
72 	keyidx = 0;
73 	keytime = 0;
74 }
75 
Init(const TVector3d & ref_position,double height_correction,CCharShape * shape)76 void CKeyframe::Init(const TVector3d& ref_position, double height_correction, CCharShape *shape) {
77 	if (!loaded) return;
78 	shape->ResetNode("head");
79 	shape->ResetNode("neck");
80 	refpos = ref_position;
81 	heightcorr = height_correction;
82 	active = true;
83 	keyidx = 0;
84 	keytime = 0;
85 
86 }
87 
InitTest(const TVector3d & ref_position,CCharShape * shape)88 void CKeyframe::InitTest(const TVector3d& ref_position, CCharShape *shape) {
89 	if (!loaded) return;
90 	shape->ResetNode("head");
91 	shape->ResetNode("neck");
92 	refpos = ref_position;
93 	heightcorr = 0.0;
94 	active = true;
95 	keyidx = 0;
96 	keytime = 0;
97 }
98 
Reset()99 void CKeyframe::Reset() {
100 	loaded = false;
101 	active = false;
102 	loadedfile = "";
103 	keytime = 0;
104 	frames.clear();
105 }
106 
Load(const std::string & dir,const std::string & filename)107 bool CKeyframe::Load(const std::string& dir, const std::string& filename) {
108 	if (loaded && loadedfile == filename) return true;
109 	CSPList list;
110 
111 	if (list.Load(dir, filename)) {
112 		frames.resize(list.size());
113 		std::size_t i = 0;
114 		for (CSPList::const_iterator line = list.cbegin(); line != list.cend(); ++line, i++) {
115 			frames[i].val[0] = SPFloatN(*line, "time", 0);
116 			TVector3d posit = SPVector3d(*line, "pos");
117 			frames[i].val[1] = posit.x;
118 			frames[i].val[2] = posit.y;
119 			frames[i].val[3] = posit.z;
120 			frames[i].val[4] = SPFloatN(*line, "yaw", 0);
121 			frames[i].val[5] = SPFloatN(*line, "pitch", 0);
122 			frames[i].val[6] = SPFloatN(*line, "roll", 0);
123 			frames[i].val[7] = SPFloatN(*line, "neck", 0);
124 			frames[i].val[8] = SPFloatN(*line, "head", 0);
125 			TVector2d pp = SPVector2d(*line, "sh");
126 			frames[i].val[9] = pp.x;
127 			frames[i].val[10] = pp.y;
128 			pp = SPVector2d(*line, "arm");
129 			frames[i].val[11] = pp.x;
130 			frames[i].val[12] = pp.y;
131 			pp = SPVector2d(*line, "hip");
132 			frames[i].val[13] = pp.x;
133 			frames[i].val[14] = pp.y;
134 			pp = SPVector2d(*line, "knee");
135 			frames[i].val[15] = pp.x;
136 			frames[i].val[16] = pp.y;
137 			pp = SPVector2d(*line, "ankle");
138 			frames[i].val[17] = pp.x;
139 			frames[i].val[18] = pp.y;
140 		}
141 		loaded = true;
142 		loadedfile = filename;
143 		return true;
144 	} else {
145 		Message("keyframe not found:", filename);
146 		loaded = false;
147 		return false;
148 	}
149 }
150 
151 // there are more possibilities for rotating the parts of the body,
152 // that will be implemented later
153 
InterpolateKeyframe(std::size_t idx,double frac,CCharShape * shape)154 void CKeyframe::InterpolateKeyframe(std::size_t idx, double frac, CCharShape *shape) {
155 	double vv;
156 	vv = interp(frac, frames[idx].val[4], frames[idx+1].val[4]);
157 	shape->RotateNode("root", 2, vv);
158 
159 	vv = interp(frac, frames[idx].val[5], frames[idx+1].val[5]);
160 	shape->RotateNode("root", 1, vv);
161 
162 	vv = interp(frac, frames[idx].val[6], frames[idx+1].val[6]);
163 	shape->RotateNode("root", 3, vv);
164 
165 	vv = interp(frac, frames[idx].val[7], frames[idx+1].val[7]);
166 	shape->RotateNode("neck", 3, vv);
167 
168 	vv = interp(frac, frames[idx].val[8], frames[idx+1].val[8]);
169 	shape->RotateNode("head", 2, vv);
170 
171 	vv = interp(frac, frames[idx].val[9], frames[idx+1].val[9]);
172 	shape->RotateNode("left_shldr", 3, vv);
173 
174 	vv = interp(frac, frames[idx].val[10], frames[idx+1].val[10]);
175 	shape->RotateNode("right_shldr", 3, vv);
176 
177 	vv = interp(frac, frames[idx].val[11], frames[idx+1].val[11]);
178 	shape->RotateNode("left_shldr", 2, vv);
179 
180 	vv = interp(frac, frames[idx].val[12], frames[idx+1].val[12]);
181 	shape->RotateNode("right_shldr", 2, vv);
182 
183 	vv = interp(frac, frames[idx].val[13], frames[idx+1].val[13]);
184 	shape->RotateNode("left_hip", 3, vv);
185 
186 	vv = interp(frac, frames[idx].val[14], frames[idx+1].val[14]);
187 	shape->RotateNode("right_hip", 3, vv);
188 
189 	vv = interp(frac, frames[idx].val[15], frames[idx+1].val[15]);
190 	shape->RotateNode("left_knee", 3, vv);
191 
192 	vv = interp(frac, frames[idx].val[16], frames[idx+1].val[16]);
193 	shape->RotateNode("right_knee", 3, vv);
194 
195 	vv = interp(frac, frames[idx].val[17], frames[idx+1].val[17]);
196 	shape->RotateNode("left_ankle", 3, vv);
197 
198 	vv = interp(frac, frames[idx].val[18], frames[idx+1].val[18]);
199 	shape->RotateNode("right_ankle", 3, vv);
200 }
201 
CalcKeyframe(std::size_t idx,CCharShape * shape,const TVector3d & refpos_) const202 void CKeyframe::CalcKeyframe(std::size_t idx, CCharShape *shape, const TVector3d& refpos_) const {
203 	double vv;
204 	TVector3d pos;
205 
206 	pos.x = frames[idx].val[1] + refpos_.x;
207 	pos.z = frames[idx].val[3] + refpos_.z;
208 	pos.y = refpos_.y;
209 
210 	shape->ResetRoot();
211 	shape->ResetJoints();
212 	shape->TranslateNode(0, pos);
213 
214 	vv = frames[idx].val[4];
215 	shape->RotateNode("root", 2, vv);
216 
217 	vv = frames[idx].val[5];
218 	shape->RotateNode("root", 1, vv);
219 
220 	vv = frames[idx].val[6];
221 	shape->RotateNode("root", 3, vv);
222 
223 	vv = frames[idx].val[7];
224 	shape->RotateNode("neck", 3, vv);
225 
226 	vv = frames[idx].val[8];
227 	shape->RotateNode("head", 2, vv);
228 
229 	vv = frames[idx].val[9];
230 	shape->RotateNode("left_shldr", 3, vv);
231 
232 	vv = frames[idx].val[10];
233 	shape->RotateNode("right_shldr", 3, vv);
234 
235 	vv = frames[idx].val[11];
236 	shape->RotateNode("left_shldr", 2, vv);
237 
238 	vv = frames[idx].val[12];
239 	shape->RotateNode("right_shldr", 2, vv);
240 
241 	vv = frames[idx].val[13];
242 	shape->RotateNode("left_hip", 3, vv);
243 
244 	vv = frames[idx].val[14];
245 	shape->RotateNode("right_hip", 3, vv);
246 
247 	vv = frames[idx].val[15];
248 	shape->RotateNode("left_knee", 3, vv);
249 
250 	vv = frames[idx].val[16];
251 	shape->RotateNode("right_knee", 3, vv);
252 
253 	vv = frames[idx].val[17];
254 	shape->RotateNode("left_ankle", 3, vv);
255 
256 	vv = frames[idx].val[18];
257 	shape->RotateNode("right_ankle", 3, vv);
258 }
259 
Update(float timestep)260 void CKeyframe::Update(float timestep) {
261 	if (!loaded) return;
262 	if (!active) return;
263 
264 	keytime += timestep;
265 	if (keytime >= frames[keyidx].val[0]) {
266 		keyidx++;
267 		keytime = 0;
268 	}
269 
270 	if (keyidx >= frames.size()-1 || frames.size() < 2) {
271 		active = false;
272 		return;
273 	}
274 
275 	double frac;
276 	TVector3d pos;
277 	CCharShape *shape = g_game.character->shape;
278 
279 	if (std::fabs(frames[keyidx].val[0]) < 0.0001) frac = 1.0;
280 	else frac = (frames[keyidx].val[0] - keytime) / frames[keyidx].val[0];
281 
282 	pos.x = interp(frac, frames[keyidx].val[1], frames[keyidx+1].val[1]) + refpos.x;
283 	pos.z = interp(frac, frames[keyidx].val[3], frames[keyidx+1].val[3]) + refpos.z;
284 	pos.y = interp(frac, frames[keyidx].val[2], frames[keyidx+1].val[2]);
285 	pos.y += Course.FindYCoord(pos.x, pos.z);
286 
287 	shape->ResetRoot();
288 	shape->ResetJoints();
289 
290 	g_game.player->ctrl->cpos = pos;
291 	double disp_y = pos.y + TUX_Y_CORR + heightcorr;
292 	shape->ResetNode(0);
293 	shape->TranslateNode(0, TVector3d(pos.x, disp_y, pos.z));
294 	InterpolateKeyframe(keyidx, frac, shape);
295 }
296 
UpdateTest(float timestep,CCharShape * shape)297 void CKeyframe::UpdateTest(float timestep, CCharShape *shape) {
298 	if (!active) return;
299 
300 	keytime += timestep;
301 	if (keytime >= frames[keyidx].val[0]) {
302 		keyidx++;
303 		keytime = 0;
304 	}
305 
306 	if (keyidx >= frames.size()-1 || frames.size() < 2) {
307 		active = false;
308 		return;
309 	}
310 
311 	double frac;
312 	TVector3d pos;
313 
314 	if (std::fabs(frames[keyidx].val[0]) < 0.0001) frac = 1.0;
315 	else frac = (frames[keyidx].val[0] - keytime) / frames[keyidx].val[0];
316 
317 	pos.x = interp(frac, frames[keyidx].val[1], frames[keyidx+1].val[1]) + refpos.x;
318 	pos.z = interp(frac, frames[keyidx].val[3], frames[keyidx+1].val[3]) + refpos.z;
319 	pos.y = interp(frac, frames[keyidx].val[2], frames[keyidx+1].val[2]);
320 
321 	shape->ResetRoot();
322 	shape->ResetJoints();
323 	shape->TranslateNode(0, pos);
324 	InterpolateKeyframe(keyidx, frac, shape);
325 }
326 
ResetFrame2(TKeyframe * frame)327 void CKeyframe::ResetFrame2(TKeyframe *frame) {
328 	std::fill_n(frame->val + 1, MAX_FRAME_VALUES - 1, 0.0);
329 	frame->val[0] = 0.5; // time
330 }
331 
GetFrame(std::size_t idx)332 TKeyframe *CKeyframe::GetFrame(std::size_t idx) {
333 	if (idx >= frames.size()) return nullptr;
334 	return &frames[idx];
335 }
336 
GetJointName(std::size_t idx)337 const std::string& CKeyframe::GetJointName(std::size_t idx) {
338 	if (idx >= numJoints) return emptyString;
339 	return jointnames[idx];
340 }
341 
GetHighlightName(std::size_t idx)342 const std::string& CKeyframe::GetHighlightName(std::size_t idx) {
343 	if (idx >= numJoints) return emptyString;
344 	return highlightnames[idx];
345 }
346 
GetNumJoints()347 int CKeyframe::GetNumJoints() {
348 	return numJoints;
349 }
350 
SaveTest(const std::string & dir,const std::string & filename) const351 void CKeyframe::SaveTest(const std::string& dir, const std::string& filename) const {
352 	CSPList list;
353 
354 	for (std::size_t i=0; i<frames.size(); i++) {
355 		const TKeyframe* frame = &frames[i];
356 		std::string line = "*[time] " + Float_StrN(frame->val[0], 1);
357 		line += " [pos] " + Float_StrN(frame->val[1], 2);
358 		line += " " + Float_StrN(frame->val[2], 2);
359 		line += " " + Float_StrN(frame->val[3], 2);
360 		if (frame->val[4] != 0) line += " [yaw] " + Int_StrN((int)frame->val[4]);
361 		if (frame->val[5] != 0) line += " [pitch] " + Int_StrN((int)frame->val[5]);
362 		if (frame->val[6] != 0) line += " [roll] " + Int_StrN((int)frame->val[6]);
363 		if (frame->val[7] != 0) line += " [neck] " + Int_StrN((int)frame->val[7]);
364 		if (frame->val[8] != 0) line += " [head] " + Int_StrN((int)frame->val[8]);
365 
366 		double ll = frame->val[9];
367 		double rr = frame->val[10];
368 		if (ll != 0 || rr != 0)
369 			line += " [sh] " + Int_StrN((int)ll) + ' ' + Int_StrN((int)rr);
370 
371 		ll = frame->val[11];
372 		rr = frame->val[12];
373 		if (ll != 0 || rr != 0)
374 			line += " [arm] " + Int_StrN((int)ll) + ' ' + Int_StrN((int)rr);
375 
376 		ll = frame->val[13];
377 		rr = frame->val[14];
378 		if (ll != 0 || rr != 0)
379 			line += " [hip] " + Int_StrN((int)ll) + ' ' + Int_StrN((int)rr);
380 
381 		ll = frame->val[15];
382 		rr = frame->val[16];
383 		if (ll != 0 || rr != 0)
384 			line += " [knee] " + Int_StrN((int)ll) + ' ' + Int_StrN((int)rr);
385 
386 		ll = frame->val[17];
387 		rr = frame->val[18];
388 		if (ll != 0 || rr != 0)
389 			line += " [ankle] " + Int_StrN((int)ll) + ' ' + Int_StrN((int)rr);
390 
391 		list.Add(line);
392 	}
393 	list.Save(dir, filename);
394 }
395 
CopyFrame(std::size_t prim_idx,std::size_t sec_idx)396 void CKeyframe::CopyFrame(std::size_t prim_idx, std::size_t sec_idx) {
397 	TKeyframe *ppp = &frames[prim_idx];
398 	TKeyframe *sss = &frames[sec_idx];
399 	std::copy_n(ppp->val, MAX_FRAME_VALUES, sss->val);
400 }
401 
AddFrame()402 void CKeyframe::AddFrame() {
403 	frames.emplace_back();
404 }
405 
DeleteFrame(std::size_t idx)406 std::size_t CKeyframe::DeleteFrame(std::size_t idx) {
407 	if (frames.size() < 2) return idx;
408 	if (idx > frames.size() - 1) return 0;
409 
410 	std::vector<TKeyframe>::iterator i = frames.begin();
411 	std::advance(i, idx);
412 	frames.erase(i);
413 	return std::max(idx, frames.size() - 2);
414 }
415 
InsertFrame(std::size_t idx)416 void CKeyframe::InsertFrame(std::size_t idx) {
417 	if (idx > frames.size() - 1) return;
418 
419 	std::vector<TKeyframe>::iterator i = frames.begin();
420 	std::advance(i, idx);
421 	frames.emplace(i);
422 }
423 
CopyToClipboard(std::size_t idx)424 void CKeyframe::CopyToClipboard(std::size_t idx) {
425 	if (idx >= frames.size()) return;
426 	std::copy_n(frames[idx].val, MAX_FRAME_VALUES, clipboard.val);
427 }
428 
PasteFromClipboard(std::size_t idx)429 void CKeyframe::PasteFromClipboard(std::size_t idx) {
430 	if (idx >= frames.size()) return;
431 	std::copy_n(clipboard.val, MAX_FRAME_VALUES, frames[idx].val);
432 }
433 
ClearFrame(std::size_t idx)434 void CKeyframe::ClearFrame(std::size_t idx) {
435 	if (idx >= frames.size()) return;
436 	ResetFrame2(&frames[idx]);
437 }
438