Merge branch '138-toggle-free-look-with-hotkey' into main/gingo-test
[ryzomcore.git] / ryzom / client / src / gabarit.cpp
blobba934b06f206cc37369d1249fa440a254de4eec9
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "stdpch.h"
22 #include "gabarit.h"
23 #include "nel/3d/u_skeleton.h"
24 #include "nel/3d/u_bone.h"
25 #include "nel/3d/u_driver.h"
26 #include "nel/misc/algo.h"
27 #include "nel/misc/progress_callback.h"
29 #include "game_share/gender.h"
31 #include <algorithm>
33 using namespace NLMISC;
34 using namespace NL3D;
35 using namespace std;
36 //using CSkeletonGabarit::EBoneCategory;
39 H_AUTO_DECL(RZ_CSkeletonGabarit)
41 extern UDriver *Driver;
43 CGabaritSet GabaritSet;
45 static const struct
47 const char *Name;
48 CSkeletonGabarit::EBoneCategory Category;
49 } boneInfos[] =
52 { "Bip01 Head_Second", CSkeletonGabarit::Other },
53 { "Bip01 Ponytail1_Second", CSkeletonGabarit::Other },
54 { "Bip01 L Finger0_Second", CSkeletonGabarit::Arm },
55 { "Bip01 L Finger1_Second", CSkeletonGabarit::Arm },
56 { "Bip01 L Finger2_Second", CSkeletonGabarit::Arm },
57 { "Bip01 L Finger3_Second", CSkeletonGabarit::Arm },
58 { "Bip01 L Finger4_Second", CSkeletonGabarit::Arm },
59 { "Bip01 L Toe0_Second", CSkeletonGabarit::Legs },
60 { "Bip01 R Toe0_Second", CSkeletonGabarit::Legs }
62 { "Bip01 Pelvis", CSkeletonGabarit::Torso },
63 { "Bip01 Spine", CSkeletonGabarit::Torso },
64 { "Bip01 Spine1", CSkeletonGabarit::Torso },
65 { "Bip01 Spine2", CSkeletonGabarit::Torso },
66 { "Bip01 Neck", CSkeletonGabarit::Other },
67 { "Bip01 Head", CSkeletonGabarit::Other },
68 { "Bip01 Ponytail1", CSkeletonGabarit::Other },
69 { "Bip01 Ponytail11", CSkeletonGabarit::Other },
70 { "Bip01 L Clavicle", CSkeletonGabarit::Torso },
71 { "Bip01 L UpperArm", CSkeletonGabarit::Arm },
72 { "Bip01 L Forearm", CSkeletonGabarit::Arm },
73 { "Bip01 L Hand", CSkeletonGabarit::Arm },
74 { "Bip01 L Finger0", CSkeletonGabarit::Arm },
75 { "Bip01 L Finger01", CSkeletonGabarit::Arm },
76 { "Bip01 L Finger02", CSkeletonGabarit::Arm },
77 { "Bip01 L Finger1", CSkeletonGabarit::Arm },
78 { "Bip01 L Finger11", CSkeletonGabarit::Arm },
79 { "Bip01 L Finger12", CSkeletonGabarit::Arm },
80 { "Bip01 L Finger2", CSkeletonGabarit::Arm },
81 { "Bip01 L Finger21", CSkeletonGabarit::Arm },
82 { "Bip01 L Finger22", CSkeletonGabarit::Arm },
83 { "Bip01 L Finger3", CSkeletonGabarit::Arm },
84 { "Bip01 L Finger31", CSkeletonGabarit::Arm },
85 { "Bip01 L Finger32", CSkeletonGabarit::Arm },
86 { "Bip01 L Finger4", CSkeletonGabarit::Arm },
87 { "Bip01 L Finger41", CSkeletonGabarit::Arm },
88 { "Bip01 L Finger42", CSkeletonGabarit::Arm },
89 { "Bip01 R Clavicle", CSkeletonGabarit::Torso },
90 { "Bip01 R UpperArm", CSkeletonGabarit::Arm },
91 { "Bip01 R Forearm", CSkeletonGabarit::Arm },
92 { "Bip01 R Hand", CSkeletonGabarit::Arm },
93 { "Bip01 R Finger0", CSkeletonGabarit::Arm },
94 { "Bip01 R Finger01", CSkeletonGabarit::Arm },
95 { "Bip01 R Finger02", CSkeletonGabarit::Arm },
96 // TRAP : not present in the skeleton : no more used ? { "Bip01 R Finger0R", CSkeletonGabarit::Arm },
97 { "Bip01 R Finger1", CSkeletonGabarit::Arm },
98 { "Bip01 R Finger11", CSkeletonGabarit::Arm },
99 { "Bip01 R Finger12", CSkeletonGabarit::Arm },
100 // TRAP : not present in the skeleton : no more used ? { "Bip01 R Finger1R", CSkeletonGabarit::Arm },
101 { "Bip01 R Finger2", CSkeletonGabarit::Arm },
102 { "Bip01 R Finger21", CSkeletonGabarit::Arm },
103 { "Bip01 R Finger22", CSkeletonGabarit::Arm },
104 // TRAP : not present in the skeleton : no more used ? { "Bip01 R Finger2R", CSkeletonGabarit::Arm },
105 { "Bip01 R Finger3", CSkeletonGabarit::Arm },
106 { "Bip01 R Finger31", CSkeletonGabarit::Arm },
107 { "Bip01 R Finger32", CSkeletonGabarit::Arm },
108 // TRAP : not present in the skeleton : no more used ? { "Bip01 R Finger3R", CSkeletonGabarit::Arm },
109 { "Bip01 R Finger4", CSkeletonGabarit::Arm },
110 { "Bip01 R Finger41", CSkeletonGabarit::Arm },
111 { "Bip01 R Finger42", CSkeletonGabarit::Arm },
112 // TRAP : not present in the skeleton : no more used ? { "Bip01 R Finger4R", CSkeletonGabarit::Arm },
113 { "Bip01 L Thigh", CSkeletonGabarit::Legs },
114 { "Bip01 L Calf", CSkeletonGabarit::Legs },
115 { "Bip01 L Foot", CSkeletonGabarit::Legs },
116 { "Bip01 L Toe0", CSkeletonGabarit::Legs },
117 { "Bip01 R Thigh", CSkeletonGabarit::Legs },
118 { "Bip01 R Calf", CSkeletonGabarit::Legs },
119 { "Bip01 R Foot", CSkeletonGabarit::Legs },
120 { "Bip01 R Toe0", CSkeletonGabarit::Legs },
121 { "sein_droit", CSkeletonGabarit::Breast },
122 { "sein_gauche", CSkeletonGabarit::Breast }
125 //===================================================================================
126 CSkeletonGabarit::CSkeletonGabarit()
128 H_AUTO_USE(RZ_CSkeletonGabarit)
129 nlctassert(sizeof(boneInfos) / sizeof(boneInfos[0]) == NumBones);
130 std::fill(BoneScale, BoneScale + NumBones, CVector(1, 1, 1));
131 std::fill(BoneSkinScale, BoneSkinScale + NumBones, CVector(1, 1, 1));
135 //===================================================================================
136 void CSkeletonGabarit::buildFromSkeleton(NL3D::USkeleton src, const std::string &skelName, bool bMale)
138 H_AUTO_USE(RZ_CSkeletonGabarit)
139 nlassert(!src.empty());
140 // get the bones we need
141 for(uint k = 0; k < NumBones; ++k)
143 sint boneID = src.getBoneIdByName(boneIndexToName(k));
144 if (boneID == -1)
146 // If the skeleton is male and we try to get breast its normal that we dont found it
147 if ( ! ((boneIndexToCategory(k) == CSkeletonGabarit::Breast) && (bMale)) )
148 nlwarning("CSkeletonGabarit : can't get bone %s for skeleton %s", boneIndexToName(k), skelName.c_str());
149 BoneScale[k].set(1, 1, 1);
150 BoneSkinScale[k].set(1, 1, 1);
152 else
154 UBone bone = src.getBone((uint) boneID);
155 bone.setTransformMode(UTransformable::RotQuat);
156 BoneScale[k] = bone.getScale();
157 BoneSkinScale[k] = bone.getSkinScale();
160 // get size
161 /*NLMISC::CAABBox bbox;
162 src.computeCurrentBBox(bbox, NULL, 0.f, true);
163 HeightScale = 2.f * bbox.getHalfSize().z;
166 // yoyo patch
168 HeightScale= 1.f;
169 sint boneId = src.getBoneIdByName("Bip01 R Thigh");
170 if (boneId != -1)
172 UBone bone = src.getBone(boneId);
173 HeightScale = bone.getScale().x;
178 //===================================================================================
179 const char *CSkeletonGabarit::boneIndexToName(uint id)
181 H_AUTO_USE(RZ_CSkeletonGabarit)
182 nlassert(id < NumBones);
183 return boneInfos[id].Name;
186 //===================================================================================
187 CSkeletonGabarit::EBoneCategory CSkeletonGabarit::boneIndexToCategory(uint id)
189 H_AUTO_USE(RZ_CSkeletonGabarit)
190 nlassert(id < NumBones);
191 return boneInfos[id].Category;
194 //===================================================================================
195 //===================================================================================
197 void CGabaritSet::loadGabarits (NLMISC::IProgressCallback &progress)
199 H_AUTO_USE(RZ_CSkeletonGabarit)
200 static const char *genderPrefix[] = { "HOM_skel", "HOF_skel" };
201 static const char *racePrefix[] = { "TR_", "FY_", "MA_", "ZO_" };
202 static const char *heightPrefix[] = { "_Small", "_Mid", "_Big" };
203 static const char *widthPrefix[] = { "_Slim", "", "_Fat" };
205 // create a dummy scene to load the skeletons
206 UScene *scene = Driver->createScene(false);
207 if (!scene)
209 nlwarning("CGabaritSet::loadGabarits : can't create scene to load skeletons");
212 for(uint g = 0; g < NumGender; ++g)
214 // Progress bar
215 progress.progress ((float)g/(float)NumGender);
216 progress.pushCropedValues ((float)g/(float)NumGender, (float)(g+1)/(float)NumGender);
218 for(uint r = 0; r < NumRace; ++r)
220 // Progress bar
221 progress.progress ((float)r/(float)NumRace);
222 progress.pushCropedValues ((float)r/(float)NumRace, (float)(r+1)/(float)NumRace);
224 for(uint h = 0; h < NumHeights; ++h)
226 // Progress bar
227 progress.progress ((float)h/(float)NumHeights);
228 progress.pushCropedValues ((float)h/(float)NumHeights, (float)(h+1)/(float)NumHeights);
230 for(uint w = 0; w < NumWidths; ++w)
232 // Progress bar
233 progress.progress ((float)w/(float)NumWidths);
235 string skelName = string(racePrefix[r]) + genderPrefix[g];
236 if (h != 1 || w != 1)
238 skelName += string(heightPrefix[h]) + widthPrefix[w];
240 skelName += ".skel";
241 USkeleton skel = scene->createSkeleton(skelName);
242 if (skel.empty())
244 nlwarning("CGabaritSet::loadGabarits : can't load skeleton %s", skelName.c_str());
246 else
248 _Gabarit[g][r][h][w].buildFromSkeleton(skel, skelName, g == GSGENDER::male);
249 scene->deleteSkeleton(skel);
253 // Progress bar
254 progress.popCropedValues ();
257 // Progress bar
258 progress.popCropedValues ();
261 // Progress bar
262 progress.popCropedValues ();
264 Driver->deleteScene(scene);
267 //===================================================================================
268 sint CGabaritSet::peopleToIndex(EGSPD::CPeople::TPeople people)
270 H_AUTO_USE(RZ_CSkeletonGabarit)
271 sint race = -1;
272 switch (people)
274 case EGSPD::CPeople::Fyros: race = 1; break;
275 case EGSPD::CPeople::Tryker: race = 0; break;
276 case EGSPD::CPeople::Matis: race = 2; break;
277 case EGSPD::CPeople::Zorai: race = 3; break;
278 default:
279 nlwarning("CGabaritSet::peopleToIndex : not a supported race");
280 break;
282 return race;
285 //===================================================================================
286 void CGabaritSet::applyGabarit(NL3D::USkeleton dest, uint gender, EGSPD::CPeople::TPeople people, float height, float torsoWidth, float armsWidth, float legsWidth, float breastSize, float *finalHeightScale)
288 H_AUTO_USE(RZ_CSkeletonGabarit)
289 if (dest.empty())
291 nlwarning("<CGabaritSet::applyGabarit> Skeleton NULL (gender=%d People=%d)",gender,people);
292 return;
294 if (gender >= NumGender) return;
296 sint race = peopleToIndex(people);
297 if (race == -1) return;
299 clamp(height, -1, 1);
300 clamp(torsoWidth, -1, 1);
301 clamp(armsWidth, -1, 1);
302 clamp(legsWidth, -1, 1);
303 clamp(breastSize, -1, 1);
306 // we blend between the 4 nearest gabarits
307 // Slim -1 Normal 0 Fat 1
308 // +--------+--------+ Big 1
309 // | | |
310 // | | |
311 // | | |
312 // | | |
313 // +--------+--------+ Mid 0
314 // | | |
315 // | | |
316 // | | |
317 // | | |
318 // +--------+--------+ Small - 1
321 // a set of 4 gabarit between which to blend
322 // TL TR
323 // +------+
324 // | |
325 // | |
326 // | |
327 // +------+
328 // BL BR
329 struct CGabaritBlend
331 CSkeletonGabarit *TL;
332 CSkeletonGabarit *TR;
333 CSkeletonGabarit *BL;
334 CSkeletonGabarit *BR;
337 // Choose the right gabarits
338 float *widthsTab[CSkeletonGabarit::NumBoneCategory] = { &armsWidth, &torsoWidth, &legsWidth, &breastSize };
339 CGabaritBlend gbTab[CSkeletonGabarit::NumBoneCategory];
340 float blendTab[CSkeletonGabarit::NumBoneCategory];
342 // build each set of 4 gabarits
343 uint k;
344 for(k = 0; k < CSkeletonGabarit::NumBoneCategory; ++k)
346 float width = *widthsTab[k];
347 gbTab[k].BL = &_Gabarit[gender][race][height >= 0 ? 1 : 0][width >= 0 ? 1 : 0];
348 gbTab[k].BR = &_Gabarit[gender][race][height >= 0 ? 1 : 0][width >= 0 ? 2 : 1];
349 gbTab[k].TL = &_Gabarit[gender][race][height >= 0 ? 2 : 1][width >= 0 ? 1 : 0];
350 gbTab[k].TR = &_Gabarit[gender][race][height >= 0 ? 2 : 1][width >= 0 ? 2 : 1];
352 blendTab[k] = width >= 0 ? width : 1.f + width;
355 float heightBlend = height >= 0 ? height : 1.f + height;
358 // blend each bone
359 for(k = 0; k < CSkeletonGabarit::NumBones; ++k)
361 // get the bone in the dest skeleton
362 sint boneID = dest.getBoneIdByName(CSkeletonGabarit::boneIndexToName(k));
363 if (boneID != -1)
365 UBone bone = dest.getBone(boneID);
366 CSkeletonGabarit::EBoneCategory boneCategory = CSkeletonGabarit::boneIndexToCategory(k);
367 if (boneCategory != CSkeletonGabarit::Other)
369 const CGabaritBlend &gb = gbTab[boneCategory]; // takes the gabarit blend matching the bone category
370 bone.setTransformMode(UTransform::RotQuat);
372 // standard scale
373 CVector scale = NLMISC::computeBilinear(gb.BL->BoneScale[k],
374 gb.BR->BoneScale[k],
375 gb.TR->BoneScale[k],
376 gb.TL->BoneScale[k],
377 blendTab[boneCategory],
378 heightBlend);
379 bone.setScale(scale);
381 // skin scale
382 CVector skinScale= NLMISC::computeBilinear( gb.BL->BoneSkinScale[k],
383 gb.BR->BoneSkinScale[k],
384 gb.TR->BoneSkinScale[k],
385 gb.TL->BoneSkinScale[k],
386 blendTab[boneCategory],
387 heightBlend);
388 bone.setSkinScale(skinScale);
393 if (finalHeightScale)
395 *finalHeightScale = NLMISC::computeBilinear(gbTab[0].BL->HeightScale,
396 gbTab[0].BR->HeightScale,
397 gbTab[0].TR->HeightScale,
398 gbTab[0].TL->HeightScale,
399 0.f,
400 heightBlend);
406 //===================================================================================
407 float CGabaritSet::getRefHeightScale(uint gender, EGSPD::CPeople::TPeople people)
409 H_AUTO_USE(RZ_CSkeletonGabarit)
410 nlassert(gender < 2);
411 sint peopleIndex = peopleToIndex(people);
412 if (peopleIndex == -1) return 0.f;
413 return _Gabarit[gender][peopleIndex][1][1].HeightScale; // return mean size