]> git.jsancho.org Git - lugaru.git/blob - Source/Animation/Skeleton.cpp
91fdbd548441df9e216cb446e30631b03f70485d
[lugaru.git] / Source / Animation / Skeleton.cpp
1 /*
2 Copyright (C) 2003, 2010 - Wolfire Games
3 Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file)
4
5 This file is part of Lugaru.
6
7 Lugaru is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 Lugaru 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 You should have received a copy of the GNU General Public License
18 along with Lugaru.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "Animation/Skeleton.hpp"
22
23 #include "Animation/Animation.hpp"
24 #include "Audio/openal_wrapper.hpp"
25 #include "Game.hpp"
26 #include "Tutorial.hpp"
27 #include "Utils/Folders.hpp"
28
29 extern float multiplier;
30 extern float gravity;
31 extern Terrain terrain;
32 extern int environment;
33 extern float camerashake;
34 extern bool freeze;
35 extern int detail;
36
37 extern int whichjointstartarray[26];
38 extern int whichjointendarray[26];
39
40 Skeleton::Skeleton()
41     : selected(0)
42     , id(0)
43     , num_models(0)
44     , clothes(false)
45     , spinny(false)
46     , skinsize(0)
47     , checkdelay(0)
48     , longdead(0)
49     , broken(false)
50     , free(0)
51     , oldfree(0)
52     , freetime(0)
53     , freefall(false)
54 {
55     memset(forwardjoints, 0, sizeof(forwardjoints));
56     memset(lowforwardjoints, 0, sizeof(lowforwardjoints));
57     memset(jointlabels, 0, sizeof(jointlabels));
58     memset(skinText, 0, sizeof(skinText));
59 }
60
61 /* EFFECT
62  * sets forward, lowforward, specialforward[]
63  *
64  * USES:
65  * Skeleton::Load
66  * Person/Person::DoAnimations
67  * Person/Person::DrawSkeleton
68  */
69 void Skeleton::FindForwards()
70 {
71     //Find forward vectors
72     CrossProduct(joints[forwardjoints[1]].position - joints[forwardjoints[0]].position, joints[forwardjoints[2]].position - joints[forwardjoints[0]].position, &forward);
73     Normalise(&forward);
74
75     CrossProduct(joints[lowforwardjoints[1]].position - joints[lowforwardjoints[0]].position, joints[lowforwardjoints[2]].position - joints[lowforwardjoints[0]].position, &lowforward);
76     Normalise(&lowforward);
77
78     //Special forwards
79     specialforward[0] = forward;
80
81     specialforward[1] = jointPos(rightshoulder) + jointPos(rightwrist);
82     specialforward[1] = jointPos(rightelbow) - specialforward[1] / 2;
83     specialforward[1] += forward * .4;
84     Normalise(&specialforward[1]);
85     specialforward[2] = jointPos(leftshoulder) + jointPos(leftwrist);
86     specialforward[2] = jointPos(leftelbow) - specialforward[2] / 2;
87     specialforward[2] += forward * .4;
88     Normalise(&specialforward[2]);
89
90     specialforward[3] = jointPos(righthip) + jointPos(rightankle);
91     specialforward[3] = specialforward[3] / 2 - jointPos(rightknee);
92     specialforward[3] += lowforward * .4;
93     Normalise(&specialforward[3]);
94     specialforward[4] = jointPos(lefthip) + jointPos(leftankle);
95     specialforward[4] = specialforward[4] / 2 - jointPos(leftknee);
96     specialforward[4] += lowforward * .4;
97     Normalise(&specialforward[4]);
98 }
99
100 /* EFFECT
101  * TODO
102  *
103  * USES:
104  * Person/Person::RagDoll
105  * Person/Person::DoStuff
106  * Person/IKHelper
107  */
108 float Skeleton::DoConstraints(XYZ* coords, float* scale)
109 {
110     const float elasticity = .3;
111     XYZ bounceness;
112     const int numrepeats = 3;
113     float groundlevel = .15;
114     unsigned i;
115     XYZ temp;
116     XYZ terrainnormal;
117     int whichhit;
118     float frictionness;
119     XYZ terrainlight;
120     int whichpatchx;
121     int whichpatchz;
122     float damage = 0; // eventually returned from function
123     bool breaking = false;
124
125     if (free) {
126         freetime += multiplier;
127
128         whichpatchx = coords->x / (terrain.size / subdivision * terrain.scale);
129         whichpatchz = coords->z / (terrain.size / subdivision * terrain.scale);
130
131         terrainlight = *coords;
132         Object::SphereCheckPossible(&terrainlight, 1);
133
134         //Add velocity
135         for (i = 0; i < joints.size(); i++) {
136             joints[i].position = joints[i].position + joints[i].velocity * multiplier;
137
138             switch (joints[i].label) {
139                 case head:
140                     groundlevel = .8;
141                     break;
142                 case righthand:
143                 case rightwrist:
144                 case rightelbow:
145                 case lefthand:
146                 case leftwrist:
147                 case leftelbow:
148                     groundlevel = .2;
149                     break;
150                 default:
151                     groundlevel = .15;
152                     break;
153             }
154
155             joints[i].position.y -= groundlevel;
156             joints[i].oldvelocity = joints[i].velocity;
157         }
158
159         float tempmult = multiplier;
160         //multiplier/=numrepeats;
161
162         for (int j = 0; j < numrepeats; j++) {
163             float r = .05;
164             // right leg constraints?
165             if (!joint(rightknee).locked && !joint(righthip).locked) {
166                 temp = jointPos(rightknee) - (jointPos(righthip) + jointPos(rightankle)) / 2;
167                 while (normaldotproduct(temp, lowforward) > -.1 && !sphere_line_intersection(&jointPos(righthip), &jointPos(rightankle), &jointPos(rightknee), &r)) {
168                     jointPos(rightknee) -= lowforward * .05;
169                     if (spinny) {
170                         jointVel(rightknee) -= lowforward * .05 / multiplier / 4;
171                     } else {
172                         jointVel(rightknee) -= lowforward * .05;
173                     }
174                     jointPos(rightankle) += lowforward * .025;
175                     if (spinny) {
176                         jointVel(rightankle) += lowforward * .025 / multiplier / 4;
177                     } else {
178                         jointVel(rightankle) += lowforward * .25;
179                     }
180                     jointPos(righthip) += lowforward * .025;
181                     if (spinny) {
182                         jointVel(righthip) += lowforward * .025 / multiplier / 4;
183                     } else {
184                         jointVel(righthip) += lowforward * .025;
185                     }
186                     temp = jointPos(rightknee) - (jointPos(righthip) + jointPos(rightankle)) / 2;
187                 }
188             }
189
190             // left leg constraints?
191             if (!joint(leftknee).locked && !joint(lefthip).locked) {
192                 temp = jointPos(leftknee) - (jointPos(lefthip) + jointPos(leftankle)) / 2;
193                 while (normaldotproduct(temp, lowforward) > -.1 && !sphere_line_intersection(&jointPos(lefthip), &jointPos(leftankle), &jointPos(leftknee), &r)) {
194                     jointPos(leftknee) -= lowforward * .05;
195                     if (spinny) {
196                         jointVel(leftknee) -= lowforward * .05 / multiplier / 4;
197                     } else {
198                         jointVel(leftknee) -= lowforward * .05;
199                     }
200                     jointPos(leftankle) += lowforward * .025;
201                     if (spinny) {
202                         jointVel(leftankle) += lowforward * .025 / multiplier / 4;
203                     } else {
204                         jointVel(leftankle) += lowforward * .25;
205                     }
206                     jointPos(lefthip) += lowforward * .025;
207                     if (spinny) {
208                         jointVel(lefthip) += lowforward * .025 / multiplier / 4;
209                     } else {
210                         jointVel(lefthip) += lowforward * .025;
211                     }
212                     temp = jointPos(leftknee) - (jointPos(lefthip) + jointPos(leftankle)) / 2;
213                 }
214             }
215
216             for (i = 0; i < joints.size(); i++) {
217                 if (joints[i].locked && !spinny && findLengthfast(&joints[i].velocity) > 320) {
218                     joints[i].locked = 0;
219                 }
220                 if (spinny && findLengthfast(&joints[i].velocity) > 600) {
221                     joints[i].locked = 0;
222                 }
223                 if (joints[i].delay > 0) {
224                     bool freely = true;
225                     for (unsigned j = 0; j < joints.size(); j++) {
226                         if (joints[j].locked) {
227                             freely = false;
228                         }
229                     }
230                     if (freely) {
231                         joints[i].delay -= multiplier * 3;
232                     }
233                 }
234             }
235
236             for (i = 0; i < muscles.size(); i++) {
237                 //Length constraints
238                 muscles[i].DoConstraint(spinny);
239             }
240
241             float friction;
242             for (i = 0; i < joints.size(); i++) {
243                 //Length constraints
244                 //Ground constraint
245                 groundlevel = 0;
246                 if (joints[i].position.y * (*scale) + coords->y < terrain.getHeight(joints[i].position.x * (*scale) + coords->x, joints[i].position.z * (*scale) + coords->z) + groundlevel) {
247                     freefall = 0;
248                     friction = 1.5;
249                     if (joints[i].label == groin && !joints[i].locked && joints[i].delay <= 0) {
250                         joints[i].locked = 1;
251                         joints[i].delay = 1;
252                         if (!Tutorial::active || id == 0) {
253                             emit_sound_at(landsound1, joints[i].position * (*scale) + *coords, 128.);
254                         }
255                         breaking = true;
256                     }
257
258                     if (joints[i].label == head && !joints[i].locked && joints[i].delay <= 0) {
259                         joints[i].locked = 1;
260                         joints[i].delay = 1;
261                         if (!Tutorial::active || id == 0) {
262                             emit_sound_at(landsound2, joints[i].position * (*scale) + *coords, 128.);
263                         }
264                     }
265
266                     terrainnormal = terrain.getNormal(joints[i].position.x * (*scale) + coords->x, joints[i].position.z * (*scale) + coords->z);
267                     ReflectVector(&joints[i].velocity, &terrainnormal);
268                     bounceness = terrainnormal * findLength(&joints[i].velocity) * (abs(normaldotproduct(joints[i].velocity, terrainnormal)));
269                     if (!joints[i].locked) {
270                         damage += findLengthfast(&bounceness) / 4000;
271                     }
272                     if (findLengthfast(&joints[i].velocity) < findLengthfast(&bounceness)) {
273                         bounceness = 0;
274                     }
275                     frictionness = abs(normaldotproduct(joints[i].velocity, terrainnormal));
276                     joints[i].velocity -= bounceness;
277                     if (1 - friction * frictionness > 0) {
278                         joints[i].velocity *= 1 - friction * frictionness;
279                     } else {
280                         joints[i].velocity = 0;
281                     }
282
283                     if (!Tutorial::active || id == 0) {
284                         if (findLengthfast(&bounceness) > 8000 && breaking) {
285                             // FIXME: this crashes because k is not initialized!
286                             // to reproduce, type 'wolfie' in console and play a while
287                             // I'll just comment it out for now
288                             //Object::objects[k]->model.MakeDecal(breakdecal, DoRotation(temp - Object::objects[k]->position, 0, -Object::objects[k]->yaw, 0), .4, .5, Random() % 360);
289                             Sprite::MakeSprite(cloudsprite, joints[i].position * (*scale) + *coords, joints[i].velocity * .06, 1, 1, 1, 4, .2);
290                             breaking = false;
291                             camerashake += .6;
292
293                             emit_sound_at(breaksound2, joints[i].position * (*scale) + *coords);
294
295                             addEnvSound(*coords, 64);
296                         }
297                     }
298
299                     if (findLengthfast(&bounceness) > 2500) {
300                         Normalise(&bounceness);
301                         bounceness = bounceness * 50;
302                     }
303
304                     joints[i].velocity += bounceness * elasticity;
305
306                     if (findLengthfast(&joints[i].velocity) > findLengthfast(&joints[i].oldvelocity)) {
307                         bounceness = 0;
308                         joints[i].velocity = joints[i].oldvelocity;
309                     }
310
311                     if (joints[i].locked == 0) {
312                         if (findLengthfast(&joints[i].velocity) < 1) {
313                             joints[i].locked = 1;
314                         }
315                     }
316
317                     if (environment == snowyenvironment && findLengthfast(&bounceness) > 500 && terrain.getOpacity(joints[i].position.x * (*scale) + coords->x, joints[i].position.z * (*scale) + coords->z) < .2) {
318                         terrainlight = terrain.getLighting(joints[i].position.x * (*scale) + coords->x, joints[i].position.z * (*scale) + coords->z);
319                         Sprite::MakeSprite(cloudsprite, joints[i].position * (*scale) + *coords, joints[i].velocity * .06, terrainlight.x, terrainlight.y, terrainlight.z, .5, .7);
320                         if (detail == 2) {
321                             terrain.MakeDecal(bodyprintdecal, joints[i].position * (*scale) + *coords, .4, .4, 0);
322                         }
323                     } else if (environment == desertenvironment && findLengthfast(&bounceness) > 500 && terrain.getOpacity(joints[i].position.x * (*scale) + coords->x, joints[i].position.z * (*scale) + coords->z) < .2) {
324                         terrainlight = terrain.getLighting(joints[i].position.x * (*scale) + coords->x, joints[i].position.z * (*scale) + coords->z);
325                         Sprite::MakeSprite(cloudsprite, joints[i].position * (*scale) + *coords, joints[i].velocity * .06, terrainlight.x * 190 / 255, terrainlight.y * 170 / 255, terrainlight.z * 108 / 255, .5, .7);
326                     }
327
328                     else if (environment == grassyenvironment && findLengthfast(&bounceness) > 500 && terrain.getOpacity(joints[i].position.x * (*scale) + coords->x, joints[i].position.z * (*scale) + coords->z) < .2) {
329                         terrainlight = terrain.getLighting(joints[i].position.x * (*scale) + coords->x, joints[i].position.z * (*scale) + coords->z);
330                         Sprite::MakeSprite(cloudsprite, joints[i].position * (*scale) + *coords, joints[i].velocity * .06, terrainlight.x * 90 / 255, terrainlight.y * 70 / 255, terrainlight.z * 8 / 255, .5, .5);
331                     } else if (findLengthfast(&bounceness) > 500) {
332                         Sprite::MakeSprite(cloudsprite, joints[i].position * (*scale) + *coords, joints[i].velocity * .06, terrainlight.x, terrainlight.y, terrainlight.z, .5, .2);
333                     }
334
335                     joints[i].position.y = (terrain.getHeight(joints[i].position.x * (*scale) + coords->x, joints[i].position.z * (*scale) + coords->z) + groundlevel - coords->y) / (*scale);
336                     if (longdead > 100) {
337                         broken = 1;
338                     }
339                 }
340                 for (unsigned int m = 0; m < terrain.patchobjects[whichpatchx][whichpatchz].size(); m++) {
341                     unsigned int k = terrain.patchobjects[whichpatchx][whichpatchz][m];
342                     if (k < Object::objects.size()) {
343                         if (Object::objects[k]->possible) {
344                             friction = Object::objects[k]->friction;
345                             XYZ start = joints[i].realoldposition;
346                             XYZ end = joints[i].position * (*scale) + *coords;
347                             whichhit = Object::objects[k]->model.LineCheckPossible(&start, &end, &temp, &Object::objects[k]->position, &Object::objects[k]->yaw);
348                             if (whichhit != -1) {
349                                 if (joints[i].label == groin && !joints[i].locked && joints[i].delay <= 0) {
350                                     joints[i].locked = 1;
351                                     joints[i].delay = 1;
352                                     if (!Tutorial::active || id == 0) {
353                                         emit_sound_at(landsound1, joints[i].position * (*scale) + *coords, 128.);
354                                     }
355                                     breaking = true;
356                                 }
357
358                                 if (joints[i].label == head && !joints[i].locked && joints[i].delay <= 0) {
359                                     joints[i].locked = 1;
360                                     joints[i].delay = 1;
361                                     if (!Tutorial::active || id == 0) {
362                                         emit_sound_at(landsound2, joints[i].position * (*scale) + *coords, 128.);
363                                     }
364                                 }
365
366                                 terrainnormal = DoRotation(Object::objects[k]->model.Triangles[whichhit].facenormal, 0, Object::objects[k]->yaw, 0) * -1;
367                                 if (terrainnormal.y > .8) {
368                                     freefall = 0;
369                                 }
370                                 bounceness = terrainnormal * findLength(&joints[i].velocity) * (abs(normaldotproduct(joints[i].velocity, terrainnormal)));
371                                 if (findLengthfast(&joints[i].velocity) > findLengthfast(&joints[i].oldvelocity)) {
372                                     bounceness = 0;
373                                     joints[i].velocity = joints[i].oldvelocity;
374                                 }
375                                 if (!Tutorial::active || id == 0) {
376                                     if (findLengthfast(&bounceness) > 4000 && breaking) {
377                                         Object::objects[k]->model.MakeDecal(breakdecal, DoRotation(temp - Object::objects[k]->position, 0, -Object::objects[k]->yaw, 0), .4, .5, Random() % 360);
378                                         Sprite::MakeSprite(cloudsprite, joints[i].position * (*scale) + *coords, joints[i].velocity * .06, 1, 1, 1, 4, .2);
379                                         breaking = false;
380                                         camerashake += .6;
381
382                                         emit_sound_at(breaksound2, joints[i].position * (*scale) + *coords);
383
384                                         addEnvSound(*coords, 64);
385                                     }
386                                 }
387                                 if (Object::objects[k]->type == treetrunktype) {
388                                     Object::objects[k]->rotx += joints[i].velocity.x * multiplier * .4;
389                                     Object::objects[k]->roty += joints[i].velocity.z * multiplier * .4;
390                                     Object::objects[k + 1]->rotx += joints[i].velocity.x * multiplier * .4;
391                                     Object::objects[k + 1]->roty += joints[i].velocity.z * multiplier * .4;
392                                 }
393                                 if (!joints[i].locked) {
394                                     damage += findLengthfast(&bounceness) / 2500;
395                                 }
396                                 ReflectVector(&joints[i].velocity, &terrainnormal);
397                                 frictionness = abs(normaldotproduct(joints[i].velocity, terrainnormal));
398                                 joints[i].velocity -= bounceness;
399                                 if (1 - friction * frictionness > 0) {
400                                     joints[i].velocity *= 1 - friction * frictionness;
401                                 } else {
402                                     joints[i].velocity = 0;
403                                 }
404                                 if (findLengthfast(&bounceness) > 2500) {
405                                     Normalise(&bounceness);
406                                     bounceness = bounceness * 50;
407                                 }
408                                 joints[i].velocity += bounceness * elasticity;
409
410                                 if (!joints[i].locked) {
411                                     if (findLengthfast(&joints[i].velocity) < 1) {
412                                         joints[i].locked = 1;
413                                     }
414                                 }
415                                 if (findLengthfast(&bounceness) > 500) {
416                                     Sprite::MakeSprite(cloudsprite, joints[i].position * (*scale) + *coords, joints[i].velocity * .06, 1, 1, 1, .5, .2);
417                                 }
418                                 joints[i].position = (temp - *coords) / (*scale) + terrainnormal * .005;
419                                 if (longdead > 100) {
420                                     broken = 1;
421                                 }
422                             }
423                         }
424                     }
425                 }
426                 joints[i].realoldposition = joints[i].position * (*scale) + *coords;
427             }
428         }
429         multiplier = tempmult;
430
431         for (unsigned int m = 0; m < terrain.patchobjects[whichpatchx][whichpatchz].size(); m++) {
432             unsigned int k = terrain.patchobjects[whichpatchx][whichpatchz][m];
433             if (Object::objects[k]->possible) {
434                 for (i = 0; i < 26; i++) {
435                     //Make this less stupid
436                     XYZ start = joints[jointlabels[whichjointstartarray[i]]].position * (*scale) + *coords;
437                     XYZ end = joints[jointlabels[whichjointendarray[i]]].position * (*scale) + *coords;
438                     whichhit = Object::objects[k]->model.LineCheckSlidePossible(&start, &end, &Object::objects[k]->position, &Object::objects[k]->yaw);
439                     if (whichhit != -1) {
440                         joints[jointlabels[whichjointendarray[i]]].position = (end - *coords) / (*scale);
441                         for (unsigned j = 0; j < muscles.size(); j++) {
442                             if ((muscles[j].parent1->label == whichjointstartarray[i] && muscles[j].parent2->label == whichjointendarray[i]) || (muscles[j].parent2->label == whichjointstartarray[i] && muscles[j].parent1->label == whichjointendarray[i])) {
443                                 muscles[j].DoConstraint(spinny);
444                             }
445                         }
446                     }
447                 }
448             }
449         }
450
451         for (i = 0; i < joints.size(); i++) {
452             switch (joints[i].label) {
453                 case head:
454                     groundlevel = .8;
455                     break;
456                 case righthand:
457                 case rightwrist:
458                 case rightelbow:
459                 case lefthand:
460                 case leftwrist:
461                 case leftelbow:
462                     groundlevel = .2;
463                     break;
464                 default:
465                     groundlevel = .15;
466                     break;
467             }
468             joints[i].position.y += groundlevel;
469             joints[i].mass = 1;
470             if (joints[i].label == lefthip || joints[i].label == leftknee || joints[i].label == leftankle || joints[i].label == righthip || joints[i].label == rightknee || joints[i].label == rightankle) {
471                 joints[i].mass = 2;
472             }
473             if (joints[i].locked) {
474                 joints[i].mass = 4;
475             }
476         }
477
478         return damage;
479     }
480
481     if (!free) {
482         for (i = 0; i < muscles.size(); i++) {
483             if (muscles[i].type == boneconnect) {
484                 muscles[i].DoConstraint(0);
485             }
486         }
487     }
488
489     return 0;
490 }
491
492 /* EFFECT
493  * applies gravity to the skeleton
494  *
495  * USES:
496  * Person/Person::DoStuff
497  */
498 void Skeleton::DoGravity(float* scale)
499 {
500     for (unsigned i = 0; i < joints.size(); i++) {
501         if (
502             (
503                 ((joints[i].label != leftknee) && (joints[i].label != rightknee)) ||
504                 (lowforward.y > -.1) ||
505                 (joints[i].mass < 5)) &&
506             (((joints[i].label != leftelbow) && (joints[i].label != rightelbow)) ||
507              (forward.y < .3))) {
508             joints[i].velocity.y += gravity * multiplier / (*scale);
509         }
510     }
511 }
512
513 /* EFFECT
514  * set muscles[which].rotate1
515  *     .rotate2
516  *     .rotate3
517  *
518  * special case if animation == hanganim
519  */
520 void Skeleton::FindRotationMuscle(int which, int animation)
521 {
522     XYZ p1, p2, fwd;
523     float dist;
524
525     p1 = muscles[which].parent1->position;
526     p2 = muscles[which].parent2->position;
527     dist = findDistance(&p1, &p2);
528     if (p1.y - p2.y <= dist) {
529         muscles[which].rotate2 = asin((p1.y - p2.y) / dist);
530     }
531     if (p1.y - p2.y > dist) {
532         muscles[which].rotate2 = asin(1.f);
533     }
534     muscles[which].rotate2 *= 360.0 / 6.2831853;
535
536     p1.y = 0;
537     p2.y = 0;
538     dist = findDistance(&p1, &p2);
539     if (p1.z - p2.z <= dist) {
540         muscles[which].rotate1 = acos((p1.z - p2.z) / dist);
541     }
542     if (p1.z - p2.z > dist) {
543         muscles[which].rotate1 = acos(1.f);
544     }
545     muscles[which].rotate1 *= 360.0 / 6.2831853;
546     if (p1.x > p2.x) {
547         muscles[which].rotate1 = 360 - muscles[which].rotate1;
548     }
549     if (!isnormal(muscles[which].rotate1)) {
550         muscles[which].rotate1 = 0;
551     }
552     if (!isnormal(muscles[which].rotate2)) {
553         muscles[which].rotate2 = 0;
554     }
555
556     const int label1 = muscles[which].parent1->label;
557     const int label2 = muscles[which].parent2->label;
558     switch (label1) {
559         case head:
560             fwd = specialforward[0];
561             break;
562         case rightshoulder:
563         case rightelbow:
564         case rightwrist:
565         case righthand:
566             fwd = specialforward[1];
567             break;
568         case leftshoulder:
569         case leftelbow:
570         case leftwrist:
571         case lefthand:
572             fwd = specialforward[2];
573             break;
574         case righthip:
575         case rightknee:
576         case rightankle:
577         case rightfoot:
578             fwd = specialforward[3];
579             break;
580         case lefthip:
581         case leftknee:
582         case leftankle:
583         case leftfoot:
584             fwd = specialforward[4];
585             break;
586         default:
587             if (muscles[which].parent1->lower) {
588                 fwd = lowforward;
589             } else {
590                 fwd = forward;
591             }
592             break;
593     }
594
595     if (animation == hanganim) {
596         if (label1 == righthand || label2 == righthand) {
597             fwd = 0;
598             fwd.x = -1;
599         }
600         if (label1 == lefthand || label2 == lefthand) {
601             fwd = 0;
602             fwd.x = 1;
603         }
604     }
605
606     if (free == 0) {
607         if (label1 == rightfoot || label2 == rightfoot) {
608             fwd.y -= .3;
609         }
610         if (label1 == leftfoot || label2 == leftfoot) {
611             fwd.y -= .3;
612         }
613     }
614
615     fwd = DoRotation(fwd, 0, muscles[which].rotate1 - 90, 0);
616     fwd = DoRotation(fwd, 0, 0, muscles[which].rotate2 - 90);
617     fwd.y = 0;
618     fwd /= findLength(&fwd);
619     if (fwd.z <= 1 && fwd.z >= -1) {
620         muscles[which].rotate3 = acos(0 - fwd.z);
621     } else {
622         muscles[which].rotate3 = acos(-1.f);
623     }
624     muscles[which].rotate3 *= 360.0 / 6.2831853;
625     if (0 > fwd.x) {
626         muscles[which].rotate3 = 360 - muscles[which].rotate3;
627     }
628     if (!isnormal(muscles[which].rotate3)) {
629         muscles[which].rotate3 = 0;
630     }
631 }
632
633 /* EFFECT
634  * load skeleton
635  * takes filenames for three skeleton files and various models
636  */
637 void Skeleton::Load(const std::string& filename, const std::string& lowfilename, const std::string& clothesfilename,
638                     const std::string& modelfilename, const std::string& model2filename,
639                     const std::string& model3filename, const std::string& model4filename,
640                     const std::string& model5filename, const std::string& model6filename,
641                     const std::string& model7filename, const std::string& modellowfilename,
642                     const std::string& modelclothesfilename, bool clothes)
643 {
644     GLfloat M[16];
645     FILE* tfile;
646     float lSize;
647     int j, num_joints, num_muscles;
648
649     LOGFUNC;
650
651     num_models = 7;
652
653     // load various models
654     // rotate, scale, do normals, do texcoords for each as needed
655
656     model[0].loadnotex(modelfilename);
657     model[1].loadnotex(model2filename);
658     model[2].loadnotex(model3filename);
659     model[3].loadnotex(model4filename);
660     model[4].loadnotex(model5filename);
661     model[5].loadnotex(model6filename);
662     model[6].loadnotex(model7filename);
663
664     for (int i = 0; i < num_models; i++) {
665         model[i].Rotate(180, 0, 0);
666         model[i].Scale(.04, .04, .04);
667         model[i].CalculateNormals(0);
668     }
669
670     drawmodel.load(modelfilename);
671     drawmodel.Rotate(180, 0, 0);
672     drawmodel.Scale(.04, .04, .04);
673     drawmodel.FlipTexCoords();
674     if ((Tutorial::active) && (id != 0)) {
675         drawmodel.UniformTexCoords();
676         drawmodel.ScaleTexCoords(0.1);
677     }
678     drawmodel.CalculateNormals(0);
679
680     modellow.loadnotex(modellowfilename);
681     modellow.Rotate(180, 0, 0);
682     modellow.Scale(.04, .04, .04);
683     modellow.CalculateNormals(0);
684
685     drawmodellow.load(modellowfilename);
686     drawmodellow.Rotate(180, 0, 0);
687     drawmodellow.Scale(.04, .04, .04);
688     drawmodellow.FlipTexCoords();
689     if (Tutorial::active && id != 0) {
690         drawmodellow.UniformTexCoords();
691     }
692     if (Tutorial::active && id != 0) {
693         drawmodellow.ScaleTexCoords(0.1);
694     }
695     drawmodellow.CalculateNormals(0);
696
697     if (clothes) {
698         modelclothes.loadnotex(modelclothesfilename);
699         modelclothes.Rotate(180, 0, 0);
700         modelclothes.Scale(.041, .04, .041);
701         modelclothes.CalculateNormals(0);
702
703         drawmodelclothes.load(modelclothesfilename);
704         drawmodelclothes.Rotate(180, 0, 0);
705         drawmodelclothes.Scale(.04, .04, .04);
706         drawmodelclothes.FlipTexCoords();
707         drawmodelclothes.CalculateNormals(0);
708     }
709
710     // FIXME: three similar blocks follow, one for each of:
711     // filename, lowfilename, clothesfilename
712
713     // load skeleton
714
715     tfile = Folders::openMandatoryFile(Folders::getResourcePath(filename), "rb");
716
717     // read num_joints
718     funpackf(tfile, "Bi", &num_joints);
719
720     joints.resize(num_joints);
721
722     // read info for each joint
723     for (int i = 0; i < num_joints; i++) {
724         joints[i].load(tfile, joints);
725     }
726
727     // read num_muscles
728     funpackf(tfile, "Bi", &num_muscles);
729
730     // allocate memory
731     muscles.resize(num_muscles);
732
733     // for each muscle...
734     for (int i = 0; i < num_muscles; i++) {
735         muscles[i].load(tfile, model[0].vertexNum, joints);
736     }
737
738     // read forwardjoints (?)
739     for (j = 0; j < 3; j++) {
740         funpackf(tfile, "Bi", &forwardjoints[j]);
741     }
742     // read lowforwardjoints (?)
743     for (j = 0; j < 3; j++) {
744         funpackf(tfile, "Bi", &lowforwardjoints[j]);
745     }
746
747     // ???
748     for (j = 0; j < num_muscles; j++) {
749         for (unsigned i = 0; i < muscles[j].vertices.size(); i++) {
750             for (int k = 0; k < num_models; k++) {
751                 if (muscles[j].vertices[i] < model[k].vertexNum) {
752                     model[k].owner[muscles[j].vertices[i]] = j;
753                 }
754             }
755         }
756     }
757
758     // calculate some stuff
759     FindForwards();
760     for (int i = 0; i < num_muscles; i++) {
761         FindRotationMuscle(i, -1);
762     }
763     // this seems to use opengl purely for matrix calculations
764     for (int k = 0; k < num_models; k++) {
765         for (int i = 0; i < model[k].vertexNum; i++) {
766             model[k].vertex[i] = model[k].vertex[i] - (muscles[model[k].owner[i]].parent1->position + muscles[model[k].owner[i]].parent2->position) / 2;
767             glMatrixMode(GL_MODELVIEW);
768             glPushMatrix();
769             glLoadIdentity();
770             glRotatef(muscles[model[k].owner[i]].rotate3, 0, 1, 0);
771             glRotatef(muscles[model[k].owner[i]].rotate2 - 90, 0, 0, 1);
772             glRotatef(muscles[model[k].owner[i]].rotate1 - 90, 0, 1, 0);
773             glTranslatef(model[k].vertex[i].x, model[k].vertex[i].y, model[k].vertex[i].z);
774             glGetFloatv(GL_MODELVIEW_MATRIX, M);
775             model[k].vertex[i].x = M[12] * 1;
776             model[k].vertex[i].y = M[13] * 1;
777             model[k].vertex[i].z = M[14] * 1;
778             glPopMatrix();
779         }
780         model[k].CalculateNormals(0);
781     }
782     fclose(tfile);
783
784     // load ???
785
786     tfile = Folders::openMandatoryFile(Folders::getResourcePath(lowfilename), "rb");
787
788     // skip joints section
789
790     fseek(tfile, sizeof(num_joints), SEEK_CUR);
791     for (int i = 0; i < num_joints; i++) {
792         // skip joint info
793         lSize = sizeof(XYZ) + sizeof(float) + sizeof(float) + 1 //sizeof(bool)
794                 + 1                                             //sizeof(bool)
795                 + sizeof(int) + 1                               //sizeof(bool)
796                 + 1                                             //sizeof(bool)
797                 + sizeof(int) + sizeof(int) + 1                 //sizeof(bool)
798                 + sizeof(int);
799         fseek(tfile, lSize, SEEK_CUR);
800     }
801
802     // skip num_muscles
803     fseek(tfile, sizeof(num_muscles), SEEK_CUR);
804
805     for (int i = 0; i < num_muscles; i++) {
806         // skip muscle info
807         lSize = sizeof(float) + sizeof(float) + sizeof(float) + sizeof(float) + sizeof(float) + sizeof(int);
808         fseek(tfile, lSize, SEEK_CUR);
809
810         muscles[i].loadVerticesLow(tfile, modellow.vertexNum);
811
812         // skip more stuff
813         lSize = 1; //sizeof(bool);
814         fseek(tfile, lSize, SEEK_CUR);
815         lSize = sizeof(int);
816         fseek(tfile, lSize, SEEK_CUR);
817         fseek(tfile, lSize, SEEK_CUR);
818     }
819
820     for (j = 0; j < num_muscles; j++) {
821         for (unsigned i = 0; i < muscles[j].verticeslow.size(); i++) {
822             if (muscles[j].verticeslow[i] < modellow.vertexNum) {
823                 modellow.owner[muscles[j].verticeslow[i]] = j;
824             }
825         }
826     }
827
828     // use opengl for its matrix math
829     for (int i = 0; i < modellow.vertexNum; i++) {
830         modellow.vertex[i] = modellow.vertex[i] - (muscles[modellow.owner[i]].parent1->position + muscles[modellow.owner[i]].parent2->position) / 2;
831         glMatrixMode(GL_MODELVIEW);
832         glPushMatrix();
833         glLoadIdentity();
834         glRotatef(muscles[modellow.owner[i]].rotate3, 0, 1, 0);
835         glRotatef(muscles[modellow.owner[i]].rotate2 - 90, 0, 0, 1);
836         glRotatef(muscles[modellow.owner[i]].rotate1 - 90, 0, 1, 0);
837         glTranslatef(modellow.vertex[i].x, modellow.vertex[i].y, modellow.vertex[i].z);
838         glGetFloatv(GL_MODELVIEW_MATRIX, M);
839         modellow.vertex[i].x = M[12];
840         modellow.vertex[i].y = M[13];
841         modellow.vertex[i].z = M[14];
842         glPopMatrix();
843     }
844
845     modellow.CalculateNormals(0);
846
847     // load clothes
848
849     if (clothes) {
850         tfile = Folders::openMandatoryFile(Folders::getResourcePath(clothesfilename), "rb");
851
852         // skip num_joints
853         fseek(tfile, sizeof(num_joints), SEEK_CUR);
854
855         for (int i = 0; i < num_joints; i++) {
856             // skip joint info
857             lSize = sizeof(XYZ) + sizeof(float) + sizeof(float) + 1 //sizeof(bool)
858                     + 1                                             //sizeof(bool)
859                     + sizeof(int) + 1                               //sizeof(bool)
860                     + 1                                             //sizeof(bool)
861                     + sizeof(int) + sizeof(int) + 1                 //sizeof(bool)
862                     + sizeof(int);
863             fseek(tfile, lSize, SEEK_CUR);
864         }
865
866         // skip num_muscles
867         fseek(tfile, sizeof(num_muscles), SEEK_CUR);
868
869         for (int i = 0; i < num_muscles; i++) {
870             // skip muscle info
871             lSize = sizeof(float) + sizeof(float) + sizeof(float) + sizeof(float) + sizeof(float) + sizeof(int);
872             fseek(tfile, lSize, SEEK_CUR);
873
874             muscles[i].loadVerticesClothes(tfile, modelclothes.vertexNum);
875
876             // skip more stuff
877             lSize = 1; //sizeof(bool);
878             fseek(tfile, lSize, SEEK_CUR);
879             lSize = sizeof(int);
880             fseek(tfile, lSize, SEEK_CUR);
881             fseek(tfile, lSize, SEEK_CUR);
882         }
883
884         // ???
885         lSize = sizeof(int);
886         for (j = 0; j < num_muscles; j++) {
887             for (unsigned i = 0; i < muscles[j].verticesclothes.size(); i++) {
888                 if (muscles[j].verticesclothes.size() && muscles[j].verticesclothes[i] < modelclothes.vertexNum) {
889                     modelclothes.owner[muscles[j].verticesclothes[i]] = j;
890                 }
891             }
892         }
893
894         // use opengl for its matrix math
895         for (int i = 0; i < modelclothes.vertexNum; i++) {
896             modelclothes.vertex[i] = modelclothes.vertex[i] - (muscles[modelclothes.owner[i]].parent1->position + muscles[modelclothes.owner[i]].parent2->position) / 2;
897             glMatrixMode(GL_MODELVIEW);
898             glPushMatrix();
899             glLoadIdentity();
900             glRotatef(muscles[modelclothes.owner[i]].rotate3, 0, 1, 0);
901             glRotatef(muscles[modelclothes.owner[i]].rotate2 - 90, 0, 0, 1);
902             glRotatef(muscles[modelclothes.owner[i]].rotate1 - 90, 0, 1, 0);
903             glTranslatef(modelclothes.vertex[i].x, modelclothes.vertex[i].y, modelclothes.vertex[i].z);
904             glGetFloatv(GL_MODELVIEW_MATRIX, M);
905             modelclothes.vertex[i].x = M[12];
906             modelclothes.vertex[i].y = M[13];
907             modelclothes.vertex[i].z = M[14];
908             glPopMatrix();
909         }
910
911         modelclothes.CalculateNormals(0);
912     }
913     fclose(tfile);
914
915     for (int i = 0; i < num_joints; i++) {
916         for (j = 0; j < num_joints; j++) {
917             if (joints[i].label == j) {
918                 jointlabels[j] = i;
919             }
920         }
921     }
922
923     free = 0;
924 }