Merge branch '138-toggle-free-look-with-hotkey' into main/gingo-test
[ryzomcore.git] / ryzom / client / src / interface_v3 / character_3d.cpp
blob30e592e7d768df46ed5a761749dd63413c885c2f
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2018 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 //
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 // ------------------------------------------------------------------------------------------------
24 #include "stdpch.h"
25 #include "character_3d.h"
26 #include "interface_manager.h"
28 #include "../color_slot_manager.h"
29 #include "../sheet_manager.h"
30 #include "../gabarit.h"
31 #include "../misc.h"
32 #include "../time_client.h"
33 #include "../player_cl.h"
34 #include "../player_r2_cl.h"
35 #include "../entities.h"
37 #include "../r2/editor.h"
38 #include "../client_cfg.h"
40 // ------------------------------------------------------------------------------------------------
41 using namespace std;
42 using namespace NLMISC;
43 using namespace NL3D;
44 using namespace R2;
46 extern CEntityAnimationManager *EAM;
49 // ------------------------------------------------------------------------------------------------
50 // SCharacter3DSetup
51 // ------------------------------------------------------------------------------------------------
53 // ------------------------------------------------------------------------------------------------
54 SCharacter3DSetup::SCharacter3DSetup ()
56 // Setup a naked male fyros
57 LeftHandItemIsShield = false;
58 People = EGSPD::CPeople::Fyros;
59 Male = true;
60 Skeleton = "fy_hom_skel.skel";
61 AnimPlayed = 0;
62 Parts[Char3DPart_Chest].Name = "TR_HOM_underwear_gilet.shape";
63 Parts[Char3DPart_Legs].Name = "FY_HOM_underwear_pantabottes.shape";
64 Parts[Char3DPart_Arms].Name = "TR_HOM_underwear_armpad.shape";
65 Parts[Char3DPart_Feet].Name = "TR_HOM_underwear_bottes.shape";
66 Parts[Char3DPart_Face].Name = "TR_HOM_visage.shape";
67 Parts[Char3DPart_Head].Name = "FY_HOM_cheveux_medium01.shape";
68 Parts[Char3DPart_Hands].Name = "TR_HOM_underwear_hand.shape";
69 Parts[Char3DPart_HandRightItem].Name.clear();
70 Parts[Char3DPart_HandLeftItem].Name.clear();
71 for (uint32 i = 0; i < NB_CHARACTER3D_PARTS; ++i)
73 Parts[i].Color = 0;
74 Parts[i].Quality = -1;
76 Tattoo = 0;
77 EyesColor = 0;
78 HairColor = 0;
79 CharHeight = 0.0f;
80 ChestWidth = 0.0f;
81 ArmsWidth = 0.0f;
82 LegsWidth = 0.0f;
83 BreastSize = 0.0f;
84 HideFace = false;
86 for (uint32 i = 0; i < NB_MORPH_TARGETS; ++i)
87 MorphTarget[i] = 0.0f;
90 // ------------------------------------------------------------------------------------------------
91 static CGenderInfo *getGenderInfo (EGSPD::CPeople::TPeople ePeople, bool bMale)
93 // Read in the race_stats forms the default equipement
94 CSheetId RSid;
95 switch (ePeople)
97 case EGSPD::CPeople::Tryker: RSid = CSheetId("tryker.race_stats"); break;
98 case EGSPD::CPeople::Matis: RSid = CSheetId("matis.race_stats"); break;
99 case EGSPD::CPeople::Zorai: RSid = CSheetId("zorai.race_stats"); break;
100 case EGSPD::CPeople::Fyros:
101 default:
102 RSid = CSheetId("fyros.race_stats"); break;
104 CRaceStatsSheet *pRSS = dynamic_cast<CRaceStatsSheet*>(SheetMngr.get (RSid));
106 if (pRSS == NULL)
108 nlwarning ("cannot find sheet for people:%d male:%d", ePeople, bMale);
109 return NULL;
112 // Choose default stuff is we are male or female
113 CGenderInfo *pGI;
114 if (bMale)
115 pGI = &pRSS->GenderInfos[0];
116 else
117 pGI = &pRSS->GenderInfos[1];
119 return pGI;
122 // ------------------------------------------------------------------------------------------------
123 void SCharacter3DSetup::setupDefault (EGSPD::CPeople::TPeople eRace, bool bMale)
125 People = eRace;
126 Male = bMale;
128 CGenderInfo *pGI = getGenderInfo(eRace, bMale);
129 if (pGI == NULL) return;
131 Skeleton = pGI->Skelfilename;
133 // Read all the default equipement
134 for (sint32 i = 0; i < SLOTTYPE::NB_SLOT; ++i)
136 string ISstr = pGI->Items[i]; // All the strings are items
137 if (!ISstr.empty())
139 CItemSheet *pIS = dynamic_cast<CItemSheet*>(SheetMngr.get(CSheetId(ISstr)));
140 if (pIS != NULL)
142 sint32 cpIndex = convert_VisualSlot_To_Char3DPart ((SLOTTYPE::EVisualSlot)i);
143 if (cpIndex != Char3DPart_INVALID)
145 Parts[cpIndex].Quality = pIS->MapVariant;
146 if (Male)
147 Parts[cpIndex].Name = pIS->getShape();
148 else
149 Parts[cpIndex].Name = pIS->getShapeFemale();
156 // ------------------------------------------------------------------------------------------------
157 void SCharacter3DSetup::setupFromCharacterSummary (const CCharacterSummary &cs)
159 const SPropVisualA::SPropSubData &rPVA = cs.VisualPropA.PropertySubData;
160 const SPropVisualB::SPropSubData &rPVB = cs.VisualPropB.PropertySubData;
161 const SPropVisualC::SPropSubData &rPVC = cs.VisualPropC.PropertySubData;
163 setupDefault (cs.People, (rPVA.Sex == 0));
165 // Setup char3dParts with additionnal info
166 setupFromCS_ModelCol (SLOTTYPE::CHEST_SLOT, rPVA.JacketModel, rPVA.JacketColor);
167 setupFromCS_ModelCol (SLOTTYPE::LEGS_SLOT, rPVA.TrouserModel, rPVA.TrouserColor);
168 setupFromCS_ModelCol (SLOTTYPE::HEAD_SLOT, rPVA.HatModel, rPVA.HatColor);
170 // Should have to hide the face ?
172 // TChar3DPart part = convert_VisualSlot_To_Char3DPart (SLOTTYPE::HEAD_SLOT);
173 CItemSheet *item = SheetMngr.getItem (SLOTTYPE::HEAD_SLOT, rPVA.HatModel);
174 if ((item != NULL) && ((item->Family == ITEMFAMILY::ARMOR) || (item->Family == ITEMFAMILY::SHIELD)))
175 HideFace = true;
176 else
177 HideFace = false;
180 setupFromCS_ModelCol (SLOTTYPE::ARMS_SLOT, rPVA.ArmModel, rPVA.ArmColor);
181 // setupFromCS_Model (SLOTTYPE::FACE_SLOT, ?????????????);
182 setupFromCS_ModelCol (SLOTTYPE::FEET_SLOT, rPVB.FeetModel, rPVB.FeetColor);
183 setupFromCS_ModelCol (SLOTTYPE::RIGHT_HAND_SLOT, rPVA.WeaponRightHand, 0);
184 setupFromCS_ModelCol (SLOTTYPE::LEFT_HAND_SLOT, rPVA.WeaponLeftHand, 0);
185 // armor gloves are not displayed if character has the 'weapon' magician gloves
187 CItemSheet *item = SheetMngr.getItem (SLOTTYPE::RIGHT_HAND_SLOT, rPVA.WeaponRightHand);
188 if( ! ((item != NULL)&&(item->ItemType == ITEM_TYPE::MAGICIAN_STAFF) ) )
189 setupFromCS_ModelCol (SLOTTYPE::HANDS_SLOT, rPVB.HandsModel, rPVB.HandsColor);
191 Tattoo = rPVC.Tattoo;
192 HairColor = rPVA.HatColor; // TODO : For the moment no diff between hair color and head color !!!
193 EyesColor = rPVC.EyesColor;
194 CharHeight = (rPVC.CharacterHeight - 7.0f) / 7.0f;
195 ChestWidth = (rPVC.TorsoWidth - 7.0f) / 7.0f;
196 ArmsWidth = (rPVC.ArmsWidth - 7.0f) / 7.0f;
197 LegsWidth = (rPVC.LegsWidth - 7.0f) / 7.0f;
198 BreastSize = (rPVC.BreastSize - 7.0f) / 7.0f;
200 float MTmin, MTmax;
201 CGenderInfo *pGI = getGenderInfo (cs.People, (rPVA.Sex == 0));
202 if (pGI == NULL)
203 return;
204 MTmin = pGI->BlendShapeMin[0];
205 MTmax = pGI->BlendShapeMax[0];
206 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
207 MorphTarget[0] = rPVC.MorphTarget1 / 7.0f * (MTmax - MTmin) + MTmin;
208 MTmin = pGI->BlendShapeMin[1];
209 MTmax = pGI->BlendShapeMax[1];
210 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
211 MorphTarget[1] = rPVC.MorphTarget2 / 7.0f * (MTmax - MTmin) + MTmin;
212 MTmin = pGI->BlendShapeMin[2];
213 MTmax = pGI->BlendShapeMax[2];
214 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
215 MorphTarget[2] = rPVC.MorphTarget3 / 7.0f * (MTmax - MTmin) + MTmin;
216 MTmin = pGI->BlendShapeMin[3];
217 MTmax = pGI->BlendShapeMax[3];
218 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
219 MorphTarget[3] = rPVC.MorphTarget4 / 7.0f * (MTmax - MTmin) + MTmin;
220 MTmin = pGI->BlendShapeMin[4];
221 MTmax = pGI->BlendShapeMax[4];
222 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
223 MorphTarget[4] = rPVC.MorphTarget5 / 7.0f * (MTmax - MTmin) + MTmin;
224 MTmin = pGI->BlendShapeMin[5];
225 MTmax = pGI->BlendShapeMax[5];
226 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
227 MorphTarget[5] = rPVC.MorphTarget6 / 7.0f * (MTmax - MTmin) + MTmin;
228 MTmin = pGI->BlendShapeMin[6];
229 MTmax = pGI->BlendShapeMax[6];
230 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
231 MorphTarget[6] = rPVC.MorphTarget7 / 7.0f * (MTmax - MTmin) + MTmin;
232 MTmin = pGI->BlendShapeMin[7];
233 MTmax = pGI->BlendShapeMax[7];
234 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
235 MorphTarget[7] = rPVC.MorphTarget8 / 7.0f * (MTmax - MTmin) + MTmin;
238 // ------------------------------------------------------------------------------------------------
239 void SCharacter3DSetup::setupFromDataBase (const std::string &branchName)
241 CCharacterSummary CS;
242 setupCharacterSummaryFromDB(CS, branchName);
243 setupFromCharacterSummary (CS);
246 // ------------------------------------------------------------------------------------------------
247 void SCharacter3DSetup::setupFromSERVERDataBase (uint8 nEntity)
249 CCharacterSummary CS;
250 setupCharacterSummaryFromSERVERDB(CS, nEntity);
251 setupFromCharacterSummary (CS);
254 // ------------------------------------------------------------------------------------------------
255 void SCharacter3DSetup::setupDBFromCharacterSummary (const string &branchName, const CCharacterSummary &CS)
257 setDB (branchName+":PEOPLE", CS.People);
259 setDB (branchName+":VPA:SEX", CS.VisualPropA.PropertySubData.Sex);
260 setDB (branchName+":VPA:JACKETMODEL", CS.VisualPropA.PropertySubData.JacketModel);
261 setDB (branchName+":VPA:JACKETCOLOR", CS.VisualPropA.PropertySubData.JacketColor);
262 setDB (branchName+":VPA:TROUSERMODEL", CS.VisualPropA.PropertySubData.TrouserModel);
263 setDB (branchName+":VPA:TROUSERCOLOR", CS.VisualPropA.PropertySubData.TrouserColor);
264 setDB (branchName+":VPA:WEAPONRIGHTHAND", CS.VisualPropA.PropertySubData.WeaponRightHand);
265 setDB (branchName+":VPA:WEAPONLEFTHAND", CS.VisualPropA.PropertySubData.WeaponLeftHand);
266 setDB (branchName+":VPA:ARMMODEL", CS.VisualPropA.PropertySubData.ArmModel);
267 setDB (branchName+":VPA:ARMCOLOR", CS.VisualPropA.PropertySubData.ArmColor);
268 setDB (branchName+":VPA:HATMODEL", CS.VisualPropA.PropertySubData.HatModel);
269 setDB (branchName+":VPA:HATCOLOR", CS.VisualPropA.PropertySubData.HatColor);
271 setDB (branchName+":VPB:NAME", CS.VisualPropB.PropertySubData.Name);
272 setDB (branchName+":VPB:HANDSMODEL", CS.VisualPropB.PropertySubData.HandsModel);
273 setDB (branchName+":VPB:HANDSCOLOR", CS.VisualPropB.PropertySubData.HandsColor);
274 setDB (branchName+":VPB:FEETMODEL", CS.VisualPropB.PropertySubData.FeetModel);
275 setDB (branchName+":VPB:FEETCOLOR", CS.VisualPropB.PropertySubData.FeetColor);
277 setDB (branchName+":VPC:MORPHTARGET1", CS.VisualPropC.PropertySubData.MorphTarget1);
278 setDB (branchName+":VPC:MORPHTARGET2", CS.VisualPropC.PropertySubData.MorphTarget2);
279 setDB (branchName+":VPC:MORPHTARGET3", CS.VisualPropC.PropertySubData.MorphTarget3);
280 setDB (branchName+":VPC:MORPHTARGET4", CS.VisualPropC.PropertySubData.MorphTarget4);
281 setDB (branchName+":VPC:MORPHTARGET5", CS.VisualPropC.PropertySubData.MorphTarget5);
282 setDB (branchName+":VPC:MORPHTARGET6", CS.VisualPropC.PropertySubData.MorphTarget6);
283 setDB (branchName+":VPC:MORPHTARGET7", CS.VisualPropC.PropertySubData.MorphTarget7);
284 setDB (branchName+":VPC:MORPHTARGET8", CS.VisualPropC.PropertySubData.MorphTarget8);
285 setDB (branchName+":VPC:EYESCOLOR", CS.VisualPropC.PropertySubData.EyesColor);
286 setDB (branchName+":VPC:TATTOO", CS.VisualPropC.PropertySubData.Tattoo);
287 setDB (branchName+":VPC:CHARACTERHEIGHT", CS.VisualPropC.PropertySubData.CharacterHeight);
288 setDB (branchName+":VPC:TORSOWIDTH", CS.VisualPropC.PropertySubData.TorsoWidth);
289 setDB (branchName+":VPC:ARMSWIDTH", CS.VisualPropC.PropertySubData.ArmsWidth);
290 setDB (branchName+":VPC:LEGSWIDTH", CS.VisualPropC.PropertySubData.LegsWidth);
291 setDB (branchName+":VPC:BREASTSIZE", CS.VisualPropC.PropertySubData.BreastSize);
294 // ------------------------------------------------------------------------------------------------
295 void SCharacter3DSetup::setupCharacterSummaryFromDB (CCharacterSummary &CS, const string &branchName)
297 CS.People = (EGSPD::CPeople::TPeople)getDB (branchName+":PEOPLE");
299 CS.VisualPropA.PropertySubData.Sex = getDB (branchName+":VPA:SEX");
300 CS.VisualPropA.PropertySubData.JacketModel = getDB (branchName+":VPA:JACKETMODEL");
301 CS.VisualPropA.PropertySubData.JacketColor = getDB (branchName+":VPA:JACKETCOLOR");
302 CS.VisualPropA.PropertySubData.TrouserModel = getDB (branchName+":VPA:TROUSERMODEL");
303 CS.VisualPropA.PropertySubData.TrouserColor = getDB (branchName+":VPA:TROUSERCOLOR");
304 CS.VisualPropA.PropertySubData.WeaponRightHand = getDB (branchName+":VPA:WEAPONRIGHTHAND");
305 CS.VisualPropA.PropertySubData.WeaponLeftHand = getDB (branchName+":VPA:WEAPONLEFTHAND");
306 CS.VisualPropA.PropertySubData.ArmModel = getDB (branchName+":VPA:ARMMODEL");
307 CS.VisualPropA.PropertySubData.ArmColor = getDB (branchName+":VPA:ARMCOLOR");
308 CS.VisualPropA.PropertySubData.HatModel = getDB (branchName+":VPA:HATMODEL");
309 CS.VisualPropA.PropertySubData.HatColor = getDB (branchName+":VPA:HATCOLOR");
311 CS.VisualPropB.PropertySubData.Name = getDB (branchName+":VPB:NAME");
312 CS.VisualPropB.PropertySubData.HandsModel = getDB (branchName+":VPB:HANDSMODEL");
313 CS.VisualPropB.PropertySubData.HandsColor = getDB (branchName+":VPB:HANDSCOLOR");
314 CS.VisualPropB.PropertySubData.FeetModel = getDB (branchName+":VPB:FEETMODEL");
315 CS.VisualPropB.PropertySubData.FeetColor = getDB (branchName+":VPB:FEETCOLOR");
317 CS.VisualPropC.PropertySubData.MorphTarget1 = getDB (branchName+":VPC:MORPHTARGET1");
318 CS.VisualPropC.PropertySubData.MorphTarget2 = getDB (branchName+":VPC:MORPHTARGET2");
319 CS.VisualPropC.PropertySubData.MorphTarget3 = getDB (branchName+":VPC:MORPHTARGET3");
320 CS.VisualPropC.PropertySubData.MorphTarget4 = getDB (branchName+":VPC:MORPHTARGET4");
321 CS.VisualPropC.PropertySubData.MorphTarget5 = getDB (branchName+":VPC:MORPHTARGET5");
322 CS.VisualPropC.PropertySubData.MorphTarget6 = getDB (branchName+":VPC:MORPHTARGET6");
323 CS.VisualPropC.PropertySubData.MorphTarget7 = getDB (branchName+":VPC:MORPHTARGET7");
324 CS.VisualPropC.PropertySubData.MorphTarget8 = getDB (branchName+":VPC:MORPHTARGET8");
325 CS.VisualPropC.PropertySubData.EyesColor = getDB (branchName+":VPC:EYESCOLOR");
326 CS.VisualPropC.PropertySubData.Tattoo = getDB (branchName+":VPC:TATTOO");
327 CS.VisualPropC.PropertySubData.CharacterHeight = getDB (branchName+":VPC:CHARACTERHEIGHT");
328 CS.VisualPropC.PropertySubData.TorsoWidth = getDB (branchName+":VPC:TORSOWIDTH");
329 CS.VisualPropC.PropertySubData.ArmsWidth = getDB (branchName+":VPC:ARMSWIDTH");
330 CS.VisualPropC.PropertySubData.LegsWidth = getDB (branchName+":VPC:LEGSWIDTH");
331 CS.VisualPropC.PropertySubData.BreastSize = getDB (branchName+":VPC:BREASTSIZE");
334 // ------------------------------------------------------------------------------------------------
336 void SCharacter3DSetup::setupCharacterSummaryFromSERVERDB (CCharacterSummary &cs, uint8 entityID)
340 cs.VisualPropA = getDB ("SERVER:Entities:E"+NLMISC::toString(entityID)+
341 ":P"+NLMISC::toString(CLFECOMMON::PROPERTY_VPA));
343 cs.VisualPropB = getDB ("SERVER:Entities:E"+NLMISC::toString(entityID)+
344 ":P"+NLMISC::toString(CLFECOMMON::PROPERTY_VPB));
346 cs.VisualPropC = getDB ("SERVER:Entities:E"+NLMISC::toString(entityID)+
347 ":P"+NLMISC::toString(CLFECOMMON::PROPERTY_VPC));
349 cs.People = EGSPD::CPeople::Fyros;
350 CPlayerCL *pp = NULL;
352 if ((pp=dynamic_cast<CPlayerCL*>(EntitiesMngr.entity(entityID))) == NULL)
354 pp=(CPlayerCL*)dynamic_cast<CPlayerR2CL*>(EntitiesMngr.entity(entityID));
356 if(pp)
358 cs.People = pp->people();
359 cs.VisualPropA.PropertySubData.Sex = (pp->getGender() == GSGENDER::female);
363 // ------------------------------------------------------------------------------------------------
364 TChar3DPart SCharacter3DSetup::convert_VisualSlot_To_Char3DPart (SLOTTYPE::EVisualSlot vs)
366 switch (vs)
368 case SLOTTYPE::HIDDEN_SLOT: return Char3DPart_INVALID;
369 case SLOTTYPE::CHEST_SLOT: return Char3DPart_Chest;
370 case SLOTTYPE::LEGS_SLOT: return Char3DPart_Legs;
371 case SLOTTYPE::HEAD_SLOT: return Char3DPart_Head;
372 case SLOTTYPE::ARMS_SLOT: return Char3DPart_Arms;
373 case SLOTTYPE::FACE_SLOT: return Char3DPart_Face;
374 case SLOTTYPE::HANDS_SLOT: return Char3DPart_Hands;
375 case SLOTTYPE::FEET_SLOT: return Char3DPart_Feet;
376 case SLOTTYPE::RIGHT_HAND_SLOT: return Char3DPart_HandRightItem;
377 case SLOTTYPE::LEFT_HAND_SLOT: return Char3DPart_HandLeftItem;
378 case SLOTTYPE::NB_SLOT: return Char3DPart_INVALID;
379 default: break;
381 return Char3DPart_INVALID;
384 // ------------------------------------------------------------------------------------------------
385 SLOTTYPE::EVisualSlot SCharacter3DSetup::convert_Char3DPart_To_VisualSlot (TChar3DPart cp)
387 switch (cp)
389 case Char3DPart_Chest: return SLOTTYPE::CHEST_SLOT;
390 case Char3DPart_Legs: return SLOTTYPE::LEGS_SLOT;
391 case Char3DPart_Head: return SLOTTYPE::HEAD_SLOT;
392 case Char3DPart_Arms: return SLOTTYPE::ARMS_SLOT;
393 case Char3DPart_Face: return SLOTTYPE::FACE_SLOT;
394 case Char3DPart_Hands: return SLOTTYPE::HANDS_SLOT;
395 case Char3DPart_Feet: return SLOTTYPE::FEET_SLOT;
396 case Char3DPart_HandRightItem: return SLOTTYPE::RIGHT_HAND_SLOT;
397 case Char3DPart_HandLeftItem: return SLOTTYPE::LEFT_HAND_SLOT;
398 case Char3DPart_INVALID: return SLOTTYPE::NB_SLOT;
399 default: break;
401 return SLOTTYPE::HIDDEN_SLOT;
404 // ------------------------------------------------------------------------------------------------
405 string SCharacter3DSetup::convert_VisualSlot_To_String (SLOTTYPE::EVisualSlot vs)
407 switch (vs)
409 case SLOTTYPE::HIDDEN_SLOT: return string("Hidden");
410 case SLOTTYPE::CHEST_SLOT: return string("Chest");
411 case SLOTTYPE::LEGS_SLOT: return string("Legs");
412 case SLOTTYPE::HEAD_SLOT: return string("Head");
413 case SLOTTYPE::ARMS_SLOT: return string("Arms");
414 case SLOTTYPE::FACE_SLOT: return string("Face");
415 case SLOTTYPE::HANDS_SLOT: return string("Hands");
416 case SLOTTYPE::FEET_SLOT: return string("Feet");
417 case SLOTTYPE::RIGHT_HAND_SLOT: return string("Hand Right Item");
418 case SLOTTYPE::LEFT_HAND_SLOT: return string("Hand Left Item");
419 case SLOTTYPE::NB_SLOT: return string("Number Of Slot");
420 default: break;
422 return string("Invalid");
425 // ------------------------------------------------------------------------------------------------
426 void SCharacter3DSetup::setupFromCS_ModelCol (SLOTTYPE::EVisualSlot s, sint32 model, sint32 col)
428 TChar3DPart part = convert_VisualSlot_To_Char3DPart (s);
429 if (part == Char3DPart_INVALID) return;
431 CItemSheet *item = SheetMngr.getItem (s, model);
432 if (item != NULL)
434 // magician gloves are a weapon but displayed in hands slot(armor gloves)
435 if( (s == SLOTTYPE::RIGHT_HAND_SLOT) && (item->ItemType == ITEM_TYPE::MAGICIAN_STAFF) )
437 Parts[part].Name = "none.shape";
438 part = convert_VisualSlot_To_Char3DPart (SLOTTYPE::HANDS_SLOT);
441 Parts[part].Quality = item->MapVariant;
442 if (Male)
444 switch(People)
446 case EGSPD::CPeople::Fyros:
447 Parts[part].Name = item->getShapeFyros();
448 break;
449 case EGSPD::CPeople::Matis:
450 Parts[part].Name = item->getShapeMatis();
451 break;
452 case EGSPD::CPeople::Tryker:
453 Parts[part].Name = item->getShapeTryker();
454 break;
455 case EGSPD::CPeople::Zorai:
456 Parts[part].Name = item->getShapeZorai();
457 break;
459 if (Parts[part].Name.empty())
460 Parts[part].Name = item->getShape();
462 else
464 switch(People)
466 case EGSPD::CPeople::Fyros:
467 Parts[part].Name = item->getShapeFyrosFemale();
468 break;
469 case EGSPD::CPeople::Matis:
470 Parts[part].Name = item->getShapeMatisFemale();
471 break;
472 case EGSPD::CPeople::Tryker:
473 Parts[part].Name = item->getShapeTrykerFemale();
474 break;
475 case EGSPD::CPeople::Zorai:
476 Parts[part].Name = item->getShapeZoraiFemale();
477 break;
479 if (Parts[part].Name.empty())
480 Parts[part].Name = item->getShapeFemale();
483 // use the right type of boots if wearing a caster dress
484 if ((s == SLOTTYPE::FEET_SLOT) && (item->ItemType == ITEM_TYPE::LIGHT_BOOTS || item->ItemType == ITEM_TYPE::MEDIUM_BOOTS || item->ItemType == ITEM_TYPE::HEAVY_BOOTS))
486 std::string shapeLegs = Parts[Char3DPart_Legs].Name;
488 if (shapeLegs.find("_caster01_") != std::string::npos)
490 std::string tmpName = toLowerAscii(Parts[part].Name);
492 std::string::size_type posBottes = tmpName.find("_bottes");
494 if (posBottes != std::string::npos)
496 std::string orgType = tmpName.substr(7, posBottes-7); // underwear, caster01, armor00 or armor01
498 tmpName.replace(posBottes+7, 0, "_" + orgType);
499 tmpName.replace(7, orgType.length(), "caster01");
501 if (CPath::exists(tmpName))
503 // use fixed shape name only if file is present
504 Parts[part].Name = tmpName;
506 else
508 // temporary hack because Fyros light boots don't respect conventions
509 if (tmpName[0] == 'f' && (item->ItemType == ITEM_TYPE::LIGHT_BOOTS))
511 if (tmpName[5] == 'f')
513 tmpName = "fy_hof_caster01_bottes_civil.shape";
515 else
517 tmpName = "fy_hom_caster01_civil01_bottes.shape";
520 // use fixed shape name only if file is present
521 if (CPath::exists(tmpName))
523 Parts[part].Name = tmpName;
525 else
527 nlwarning("File %s doesn't exist, use %s", tmpName.c_str(), Parts[part].Name.c_str());
530 else
532 nlwarning("File %s doesn't exist, use %s", tmpName.c_str(), Parts[part].Name.c_str());
539 // FX
541 Parts[part].AdvFx = item->FX.getAdvantageFX();
542 Parts[part].StatFxNames.clear();
543 Parts[part].StatFxBones.clear();
544 Parts[part].StatFxOffss.clear();
545 for (uint32 fx = 0; fx < item->FX.getNumStaticFX(); ++fx)
547 Parts[part].StatFxNames.push_back(item->FX.getStaticFXName(fx));
548 Parts[part].StatFxBones.push_back(item->FX.getStaticFXBone(fx));
549 Parts[part].StatFxOffss.push_back(item->FX.getStaticFXOffset(fx));
553 if (part == Char3DPart_HandLeftItem)
555 if ((item->ItemType == ITEM_TYPE::SHIELD) || (item->ItemType == ITEM_TYPE::BUCKLER))
556 LeftHandItemIsShield = true;
557 else
558 LeftHandItemIsShield = false;
561 else
563 // fix underwears color
564 if (model == 0) col = 6; // white
566 if ((part == Char3DPart_HandLeftItem) || (part == Char3DPart_HandRightItem))
567 Parts[part].Name = "none.shape";
570 Parts[part].Color = col;
573 // ------------------------------------------------------------------------------------------------
574 uint64 SCharacter3DSetup::getDB (const string &name)
576 CInterfaceManager *pIM = CInterfaceManager::getInstance();
577 CCDBNodeLeaf *pNL = NLGUI::CDBManager::getInstance()->getDbProp(name);
578 if (pNL == NULL) return 0;
579 return pNL->getValue64();
582 // ------------------------------------------------------------------------------------------------
583 void SCharacter3DSetup::setDB (const string &name, uint64 val)
585 CInterfaceManager *pIM = CInterfaceManager::getInstance();
586 CCDBNodeLeaf *pNL = NLGUI::CDBManager::getInstance()->getDbProp(name);
587 if (pNL == NULL) return;
588 pNL->setValue64(val);
591 // ------------------------------------------------------------------------------------------------
592 void DEBUG_DumpClothes()
594 for (uint cp = 0; cp < NB_CHARACTER3D_PARTS; ++cp)
595 if (cp != Char3DPart_Face)
597 SLOTTYPE::EVisualSlot vs = SCharacter3DSetup::convert_Char3DPart_To_VisualSlot((TChar3DPart)cp);
598 string sTmp = SCharacter3DSetup::convert_VisualSlot_To_String(vs);
599 nlinfo("*** PART *** : %s", sTmp.c_str());
600 uint nNbItems = 0;
601 if (cp == Char3DPart_HandRightItem)
602 nNbItems = 1<<11;
603 if ((cp == Char3DPart_Chest) || (cp == Char3DPart_Hands) || (cp == Char3DPart_Feet))
604 nNbItems = 1<<9;
605 if ((cp == Char3DPart_Legs) || (cp == Char3DPart_Arms) || (cp == Char3DPart_HandLeftItem))
606 nNbItems = 1<<8;
607 if (cp == Char3DPart_Head)
608 nNbItems = 1<<7;
609 for (uint it = 0; it < nNbItems; ++it)
611 CItemSheet *item = SheetMngr.getItem (vs, it);
612 if (item == NULL)
614 //nlinfo(" val:%d UNKNOWN",it);
616 else
618 //nlinfo(" val:%d M[%s] F[%s]", it, item->Shape.c_str(), item->ShapeFemale.c_str());
619 const CSheetManager::TEntitySheetMap &esm = SheetMngr.getSheets();
620 CSheetManager::TEntitySheetMap::const_iterator esmit = esm.begin();
621 while (esmit != esm.end())
623 if (esmit->second.EntitySheet == item)
625 nlinfo(" val:%d item[%s]", it, esmit->first.toString().c_str() );
626 break;
628 esmit++;
632 nlSleep(1);
637 // ------------------------------------------------------------------------------------------------
638 // CCharacter3D
639 // ------------------------------------------------------------------------------------------------
641 // ------------------------------------------------------------------------------------------------
643 CCharacter3D::CCharacter3D()
645 _ClusterSystem = NULL;
646 _Scene = NULL;
647 _PlayListManager = NULL;
648 _AnimationSet = NULL;
649 _PlayList = NULL;
650 _FacePlayList = NULL;
651 // Clear the cache to make it work with 1st init
652 _CurrentSetup.Skeleton.clear();
653 _CurrentSetup.AnimPlayed = -1;
654 for (uint32 i = 0; i < NB_CHARACTER3D_PARTS; ++i)
656 _CurrentSetup.Parts[i].Name.clear();
657 _CurrentSetup.Parts[i].Color = -1;
658 _CurrentSetup.Parts[i].Quality = -1;
660 _CurrentSetup.Tattoo = -1;
661 _CurrentSetup.EyesColor = -1;
662 _CurrentSetup.CharHeight = _CurrentSetup.ChestWidth = -20.0f;
663 _CurrentSetup.ArmsWidth = _CurrentSetup.LegsWidth = _CurrentSetup.BreastSize = -20.0f;
664 _PelvisPos.set(0.f,0.f,-20.0f);
665 _CurPosX = _CurPosY = _CurPosZ = 0.0f;
666 _CurRotX = _CurRotY = _CurRotZ = 0.0f;
667 _NextBlinkTime = 0;
668 _CopyAnim=false;
671 // ------------------------------------------------------------------------------------------------
672 CCharacter3D::~CCharacter3D()
674 if (_Scene == NULL) return;
676 // Delete animations first
677 if (_PlayListManager != NULL)
679 if (_PlayList != NULL)
681 _PlayList->resetAllChannels();
682 _PlayListManager->deletePlayList(_PlayList);
684 if (_FacePlayList != NULL)
686 _FacePlayList->resetAllChannels();
687 _PlayListManager->deletePlayList(_FacePlayList);
689 _Scene->deletePlayListManager(_PlayListManager);
691 _AnimationSet= NULL;
693 // delete instances
694 for (uint32 i = 0; i < NB_CHARACTER3D_PARTS; ++i)
696 if (!_Instances[i].empty())
697 _Scene->deleteInstance (_Instances[i]);
699 for (uint32 fx = 0; fx < _InstancesFx[i].StaticFx.size(); ++fx)
700 if (!_InstancesFx[i].StaticFx[fx].empty())
701 _Scene->deleteInstance(_InstancesFx[i].StaticFx[fx]);
702 _InstancesFx[i].StaticFx.clear();
704 if (!_InstancesFx[i].AdvantageFx.empty())
705 _Scene->deleteInstance (_InstancesFx[i].AdvantageFx);
707 // delete skeleton
708 if(!_Skeleton.empty())
709 _Scene->deleteSkeleton(_Skeleton);
711 _Scene= NULL;
714 // ------------------------------------------------------------------------------------------------
715 bool CCharacter3D::init (UScene *pScene)
717 // DEBUG_DumpClothes();
719 if (_Scene != NULL) return true;
720 _Scene = pScene;
722 _PlayListManager = _Scene->createPlayListManager();
723 if (!_PlayListManager)
725 nlwarning ("CCharacter3D : couldn't create playlist manager");
726 return false;
730 // ANIMATIONS
731 CInterfaceManager *pIM = CInterfaceManager::getInstance();
732 COptionsAnimationSet *pOAS= dynamic_cast<COptionsAnimationSet*>(CWidgetManager::getInstance()->getOptions("character_animations"));
733 if(!pOAS || !pOAS->AnimationSet)
735 nlwarning("Not found <options> 'character_animations', or not of type 'animation_set'");
736 return false;
739 if(ClientCfg.Light || !ClientCfg.EAMEnabled)
740 _CopyAnim = false;
742 // Retrieve the animation info
743 if(!_CopyAnim)
745 resetAnimation (pOAS->AnimationSet);
746 _AnimMale= pOAS->AnimMale;
747 _AnimFemale= pOAS->AnimFemale;
749 else
751 if (EAM)
753 resetAnimation (EAM->getAnimationSet());
757 return true;
760 // ------------------------------------------------------------------------------------------------
761 void CCharacter3D::resetAnimation (UAnimationSet *animSet)
763 nlassert(animSet);
765 if (_PlayList)
766 _PlayList->resetAllChannels();
767 if (_FacePlayList)
768 _FacePlayList->resetAllChannels();
769 // if (_PlayList != NULL) _PlayListManager->deletePlayList (_PlayList);
770 // if (_FacePlayList != NULL) _PlayListManager->deletePlayList (_FacePlayList);
772 _AnimationSet= animSet;
774 if (_PlayList == NULL)
775 _PlayList = _PlayListManager->createPlayList(_AnimationSet);
776 if (!_PlayList)
778 nlwarning ("CCharacter3D : couldn't create play list");
779 _Scene->deletePlayListManager (_PlayListManager);
780 _PlayListManager = NULL;
781 _AnimationSet = NULL;
782 return;
785 if (_FacePlayList == NULL)
786 _FacePlayList = _PlayListManager->createPlayList (_AnimationSet);
787 if (!_FacePlayList)
789 nlwarning ("CCharacter3D : couldn't create face play list");
790 // no face anim, but body anim is still available
791 return;
795 // ------------------------------------------------------------------------------------------------
796 void CCharacter3D::disableFaceMorphAndBlinks()
798 if(_FacePlayList && _AnimationSet)
800 // disable eye blink animation (handled by ourselves)
801 uint id= _AnimationSet->getChannelIdByName("visage_100MorphFactor");
802 if(id!=UAnimationSet::NotFound)
803 _FacePlayList->enableChannel(id, false);
804 // disable morph target (handled by ourselves)
805 for(uint i=0;i<NB_MORPH_TARGETS;i++)
807 static const string baseName = "visage_00";
808 id= _AnimationSet->getChannelIdByName(baseName + toString(i) + "MorphFactor");
809 if(id!=UAnimationSet::NotFound)
810 _FacePlayList->enableChannel(id, false);
815 // ------------------------------------------------------------------------------------------------
816 void CCharacter3D::setup (const SCharacter3DSetup &c3ds)
818 bool bSkeletonRebuilt = false;
819 // Test with cache and call dressing/loading functions
820 if (!c3ds.Skeleton.empty())
821 if (c3ds.Skeleton != _CurrentSetup.Skeleton)
823 setSkeleton (c3ds.Skeleton);
824 _CurrentSetup.Skeleton = c3ds.Skeleton;
825 bSkeletonRebuilt = true;
826 _Skeleton.setClusterSystem (_ClusterSystem);
827 if (_PlayList)
829 _PlayList->registerTransform (_Skeleton);
830 // disable pos animation
831 uint id= _AnimationSet->getChannelIdByName("pos");
832 if(id!=UAnimationSet::NotFound)
833 _PlayList->enableChannel(id, false);
837 // Information that are additionnal
838 _CurrentSetup.LeftHandItemIsShield = c3ds.LeftHandItemIsShield;
839 _CurrentSetup.Male = c3ds.Male;
841 // Setup instances
842 uint32 i;
844 for (i = 0; i < NB_CHARACTER3D_PARTS; ++i)
846 bool bInstanceRebuilt = false;
847 bool bQualityRebuilt = false;
849 // Create Instance
850 if ((c3ds.Parts[i].Name != _CurrentSetup.Parts[i].Name) || (c3ds.Parts[i].AdvFx != _CurrentSetup.Parts[i].AdvFx))
852 // If face, unregister FacePlayList
853 if(i==Char3DPart_Face && _FacePlayList)
854 _FacePlayList->resetAllChannels();
856 // rebuild this instance
857 bInstanceRebuilt = true;
858 createInstance ((TChar3DPart)i, c3ds.Parts[i]);
859 _CurrentSetup.Parts[i].Name = c3ds.Parts[i].Name;
860 _CurrentSetup.Parts[i].AdvFx = c3ds.Parts[i].AdvFx;
861 _CurrentSetup.Parts[i].StatFxNames = c3ds.Parts[i].StatFxNames;
862 _CurrentSetup.Parts[i].StatFxBones = c3ds.Parts[i].StatFxBones;
863 _CurrentSetup.Parts[i].StatFxOffss = c3ds.Parts[i].StatFxOffss;
865 // If face and instance created, reassign FacePlayList
866 if(i==Char3DPart_Face && _FacePlayList && !_Instances[Char3DPart_Face].empty())
868 _FacePlayList->registerTransform(_Instances[Char3DPart_Face]);
869 disableFaceMorphAndBlinks();
873 // Quality
874 if (c3ds.Parts[i].Quality != -1)
875 if ((c3ds.Parts[i].Quality != _CurrentSetup.Parts[i].Quality) || bInstanceRebuilt)
877 if (!_Instances[i].empty())
879 _Instances[i].selectTextureSet((uint)c3ds.Parts[i].Quality);
880 bQualityRebuilt = true;
882 _CurrentSetup.Parts[i].Quality = c3ds.Parts[i].Quality;
885 // Instance user color
886 if (c3ds.Parts[i].Color != -1)
887 if ((c3ds.Parts[i].Color != _CurrentSetup.Parts[i].Color) || bInstanceRebuilt || bQualityRebuilt)
889 if (!_Instances[i].empty())
891 ColorSlotManager.setInstanceSlot ( _Instances[i],
892 1u, // Slot 1 is for user color
893 c3ds.Parts[i].Color);
895 _CurrentSetup.Parts[i].Color = c3ds.Parts[i].Color;
898 // Instance skin color
899 if (c3ds.People != EGSPD::CPeople::Undefined)
900 if ((c3ds.People != _CurrentSetup.People) || bInstanceRebuilt || bQualityRebuilt)
902 if (!_Instances[i].empty() && i != Char3DPart_HandRightItem && i != Char3DPart_HandLeftItem)
904 ColorSlotManager.setInstanceSlot ( _Instances[i],
905 0u, // Slot 0 is for skin
906 peopleToSkin(c3ds.People));
908 // Here we do not update current setup people value to let other instances colorize too
911 // Special cases
912 switch(i)
914 case Char3DPart_Face:
915 // Setup tatoo
916 if (c3ds.Tattoo != -1)
917 if ((c3ds.Tattoo != _CurrentSetup.Tattoo) || bInstanceRebuilt)
919 if (!_Instances[Char3DPart_Face].empty())
920 makeUp (_Instances[Char3DPart_Face], c3ds.Tattoo);
921 _CurrentSetup.Tattoo = c3ds.Tattoo;
923 // Setup eyes color
924 if (c3ds.EyesColor != -1)
925 if ((c3ds.EyesColor != _CurrentSetup.EyesColor) ||bInstanceRebuilt)
927 if (!_Instances[Char3DPart_Face].empty())
928 ColorSlotManager.setInstanceSlot ( _Instances[Char3DPart_Face],
929 (uint)3, // slot 3 is for eyes colors
930 c3ds.EyesColor );
931 _CurrentSetup.EyesColor = c3ds.EyesColor;
933 // Setup morph targets
934 if ((c3ds.MorphTarget[0] != _CurrentSetup.MorphTarget[0]) ||
935 (c3ds.MorphTarget[1] != _CurrentSetup.MorphTarget[1]) ||
936 (c3ds.MorphTarget[2] != _CurrentSetup.MorphTarget[2]) ||
937 (c3ds.MorphTarget[3] != _CurrentSetup.MorphTarget[3]) ||
938 (c3ds.MorphTarget[4] != _CurrentSetup.MorphTarget[4]) ||
939 (c3ds.MorphTarget[5] != _CurrentSetup.MorphTarget[5]) ||
940 (c3ds.MorphTarget[6] != _CurrentSetup.MorphTarget[6]) ||
941 (c3ds.MorphTarget[7] != _CurrentSetup.MorphTarget[7]) ||
942 bInstanceRebuilt || bSkeletonRebuilt)
944 if (!_Instances[Char3DPart_Face].empty())
946 for(uint k = 0; k < NB_MORPH_TARGETS; ++k)
948 static const char *baseName = "visage_00";
949 _Instances[Char3DPart_Face].setBlendShapeFactor (baseName + toString(k),
950 c3ds.MorphTarget[k], true);
951 _CurrentSetup.MorphTarget[k] = c3ds.MorphTarget[k];
956 if (!_Instances[Char3DPart_Face].empty())
958 if (c3ds.HideFace)
959 _Instances[Char3DPart_Face].hide();
960 else
961 _Instances[Char3DPart_Face].show();
964 _CurrentSetup.HideFace = c3ds.HideFace;
966 // Setup hair color (for both part)
967 if (c3ds.HairColor != -1)
968 if ((c3ds.HairColor!= _CurrentSetup.HairColor) || bInstanceRebuilt)
970 if (!_Instances[Char3DPart_Face].empty())
971 ColorSlotManager.setInstanceSlot ( _Instances[Char3DPart_Face],
972 (uint)2, // slot 2 is for hair color
973 c3ds.HairColor );
974 //_CurrentSetup.HairColor = c3ds.HairColor;
976 break;
977 case Char3DPart_Head:
978 // Setup hair color
979 if (c3ds.HairColor != -1)
980 if ((c3ds.HairColor!= _CurrentSetup.HairColor) || bInstanceRebuilt)
982 if (!_Instances[Char3DPart_Head].empty())
983 ColorSlotManager.setInstanceSlot ( _Instances[Char3DPart_Head],
984 (uint)2, // slot 2 is for hair color
985 c3ds.HairColor );
986 _CurrentSetup.HairColor = c3ds.HairColor;
988 break;
989 default:
990 break;
993 // Bind instance to skeleton
994 if (bInstanceRebuilt || bSkeletonRebuilt)
996 bindToSkeleton ((TChar3DPart)i);
999 _CurrentSetup.People = c3ds.People; // Because not done for each instance
1001 // Setup gabarit
1002 bool bGabaritChanged = false;
1003 if ((c3ds.CharHeight != _CurrentSetup.CharHeight) ||
1004 (c3ds.ChestWidth != _CurrentSetup.ChestWidth) ||
1005 (c3ds.ArmsWidth != _CurrentSetup.ArmsWidth) ||
1006 (c3ds.LegsWidth != _CurrentSetup.LegsWidth) ||
1007 (c3ds.BreastSize != _CurrentSetup.BreastSize) ||
1009 bSkeletonRebuilt)
1011 uint gender = _CurrentSetup.Male ? 0 : 1;
1012 float heightScale;
1013 GabaritSet.applyGabarit ( _Skeleton, gender, _CurrentSetup.People,
1014 c3ds.CharHeight, c3ds.ChestWidth, c3ds.ArmsWidth, c3ds.LegsWidth, c3ds.BreastSize,
1015 &heightScale );
1016 float refHeightScale = GabaritSet.getRefHeightScale(gender, _CurrentSetup.People);
1017 // dummy code, to avoid 1 frame big swap
1018 _PelvisPos.z = 1.f * heightScale;
1020 _CurrentSetup.CharHeight = c3ds.CharHeight;
1021 _CurrentSetup.ChestWidth = c3ds.ChestWidth;
1022 _CurrentSetup.ArmsWidth = c3ds.ArmsWidth;
1023 _CurrentSetup.LegsWidth = c3ds.LegsWidth;
1024 _CurrentSetup.BreastSize = c3ds.BreastSize;
1026 if(refHeightScale != 0.f)
1027 _CustomScalePos = heightScale/refHeightScale;
1028 else
1029 _CustomScalePos = 1.f;
1031 bGabaritChanged = true;
1034 // Play an animation
1036 if (c3ds.AnimPlayed != -1)
1037 if ((c3ds.AnimPlayed != _CurrentSetup.AnimPlayed) || (bSkeletonRebuilt) || _CopyAnim)
1038 setAnim (c3ds.AnimPlayed);
1040 if (bSkeletonRebuilt || bGabaritChanged)
1041 animate(0.0);
1043 // If skeleton or gabarit has changed replace correctly the skeleton from feet reference point
1044 setPos (_CurPosX, _CurPosY, _CurPosZ);
1045 setRotEuler (_CurRotX, _CurRotY, _CurRotZ);
1047 // update skeleton pelvis pos
1048 if (!_Skeleton.empty())
1049 _Skeleton.setPos(_PelvisPos);
1052 // ------------------------------------------------------------------------------------------------
1053 void CCharacter3D::setAnim (uint animID)
1055 CCharacterCL * character = NULL;
1057 if(!_CopyAnim)
1059 _CurrentSetup.AnimPlayed = animID;
1060 if (_CurrentSetup.Male)
1062 if (animID >= _AnimMale.size()) return;
1063 animID = _AnimMale[animID].AnimId;
1065 else
1067 if (animID >= _AnimFemale.size()) return;
1068 animID = _AnimFemale[animID].AnimId;
1071 else
1073 CInstance * selectedInst = getEditor().getSelectedInstance();
1074 if(!selectedInst) return;
1075 CEntityCL * entity = selectedInst->getEntity();
1076 if(!(entity && ((character=dynamic_cast<CCharacterCL*>(entity))!=NULL)))
1077 return;
1079 animID = character->playList()->getAnimation(MOVE);
1080 _CurrentSetup.AnimPlayed = animID;
1084 float animSpeedFactor = 0.9f + 0.2f * NLMISC::frand(1);
1085 if (_PlayList)
1087 if(_CopyAnim)
1089 _PlayList->setTimeOrigin(MOVE, character->playList()->getTimeOrigin(MOVE));
1090 if(character->playList()->getAnimation(MOVE)!=_PlayList->getAnimation(MOVE))
1092 _PlayList->setAnimation(MOVE, animID);
1093 _PlayList->setSpeedFactor(MOVE, character->playList()->getSpeedFactor(MOVE));
1094 _PlayList->setWrapMode(MOVE, character->playList()->getWrapMode(MOVE));
1097 else
1099 _PlayList->setAnimation(MOVE, animID);
1100 _PlayList->setSpeedFactor(MOVE, animSpeedFactor);
1101 _PlayList->setTimeOrigin(MOVE, TimeInSec);
1102 _PlayList->setWrapMode(MOVE, UPlayList::Repeat);
1106 if (_FacePlayList)
1108 uint faceAnimId=UPlayList::empty;
1109 if(_AnimationSet && animID<_AnimationSet->getNumAnimation())
1111 // build the anim name of the face
1112 string faceAnimName= COptionsAnimationSet::getFaceAnimName(_AnimationSet->getAnimationName(animID));
1113 // find the face anim for this name
1114 faceAnimId= _AnimationSet->getAnimationIdByName(faceAnimName);
1115 if(faceAnimId==UAnimationSet::NotFound)
1116 faceAnimId= UPlayList::empty;
1119 _FacePlayList->setAnimation(MOVE, faceAnimId);
1120 if(faceAnimId!=UPlayList::empty)
1122 if(_CopyAnim)
1124 _FacePlayList->setTimeOrigin(MOVE, character->facePlayList()->getTimeOrigin(MOVE));
1125 _FacePlayList->setSpeedFactor(MOVE, character->facePlayList()->getSpeedFactor(MOVE));
1126 _FacePlayList->setWrapMode(MOVE, character->facePlayList()->getWrapMode(MOVE));
1128 else
1130 _FacePlayList->setSpeedFactor(MOVE, animSpeedFactor);
1131 _FacePlayList->setTimeOrigin(MOVE, TimeInSec);
1132 _FacePlayList->setWrapMode(MOVE, UPlayList::Repeat);
1138 // ------------------------------------------------------------------------------------------------
1139 void CCharacter3D::animate (double globalTime)
1141 if (!_AnimationSet) return;
1142 _PlayListManager->animate (globalTime);
1144 animblink (globalTime);
1146 if (_CurrentSetup.AnimPlayed == -1) return;
1147 // take correct 3D animId
1148 uint animID= _CurrentSetup.AnimPlayed;
1149 bool applyRaceScalePos= true;
1151 if(!_CopyAnim)
1153 if (_CurrentSetup.Male && animID < _AnimMale.size())
1155 applyRaceScalePos = _AnimMale[animID].ApplyRaceScalePos;
1156 // animId is now the correct 3D animId
1157 animID = _AnimMale[animID].AnimId;
1159 else if (!_CurrentSetup.Male && animID < _AnimFemale.size())
1161 applyRaceScalePos = _AnimFemale[animID].ApplyRaceScalePos;
1162 // animId is now the correct 3D animId
1163 animID = _AnimFemale[animID].AnimId;
1165 else
1166 return;
1169 // get the animation
1170 if(animID==UAnimationSet::NotFound)
1171 return;
1172 UAnimation *pAnim = _AnimationSet->getAnimation (animID);
1173 if (pAnim == NULL) return;
1174 UTrack *pTrack = pAnim->getTrackByName("pos");
1175 CVector animPos;
1176 if (pTrack == NULL) return;
1178 // Compute animation time (wrapped)
1179 double wrappedTime=(globalTime-_PlayList->getTimeOrigin(0))*_PlayList->getSpeedFactor(0);
1180 // Mod repeat the time
1182 float length=pAnim->getEndTime ()-pAnim->getBeginTime();
1183 if (wrappedTime>=0)
1184 wrappedTime=pAnim->getBeginTime()+(float)fmod ((float)wrappedTime, length);
1185 else
1186 wrappedTime=pAnim->getBeginTime()+(float)fmod ((float)wrappedTime, length)+length;
1188 pTrack->interpolate((float)wrappedTime, animPos);
1190 // apply race scale pos only if animation need it
1191 if(applyRaceScalePos)
1192 animPos *= getGenderInfo(_CurrentSetup.People, _CurrentSetup.Male)->CharacterScalePos;
1193 // always apply custom scale pos
1194 animPos *= _CustomScalePos;
1195 _PelvisPos = animPos;
1196 // update skeleton pelvis pos
1197 if (!_Skeleton.empty())
1199 _Skeleton.setPos(_PelvisPos);
1200 // update skeleton spawn script pos
1201 _Skeleton.setSSSWOPos(_Root.getMatrix().getPos());
1202 _Skeleton.setSSSWODir(_Root.getMatrix().getJ());
1206 // ------------------------------------------------------------------------------------------------
1207 void CCharacter3D::setPos (float x, float y, float z)
1209 _CurPosX = x;
1210 _CurPosY = y;
1211 _CurPosZ = z;
1212 if (!_Root.empty())
1213 _Root.setPos (x, y, z);
1216 // ------------------------------------------------------------------------------------------------
1217 void CCharacter3D::setClusterSystem (NL3D::UInstanceGroup *pIG)
1219 _ClusterSystem = pIG;
1220 if (!_Skeleton.empty())
1221 _Skeleton.setClusterSystem(pIG);
1224 // ------------------------------------------------------------------------------------------------
1225 void CCharacter3D::setRotEuler (float rx, float ry, float rz)
1227 _CurRotX = rx;
1228 _CurRotY = ry;
1229 _CurRotZ = rz;
1230 if (!_Root.empty())
1232 _Root.setTransformMode (UTransformable::RotEuler);
1233 _Root.setRotEuler (_CurRotX, _CurRotY, _CurRotZ);
1237 // ------------------------------------------------------------------------------------------------
1238 void CCharacter3D::getHeadPos (float &x, float &y, float &z)
1240 x = y = z = 0;
1241 if (!_Skeleton.empty())
1243 sint boneId = _Skeleton.getBoneIdByName("Bip01 Head");
1244 if (boneId == -1)
1246 nlwarning ("bad bone name");
1247 return;
1249 _Skeleton.forceComputeBone(boneId);
1250 UBone rBone = _Skeleton.getBone(boneId);
1251 const CMatrix &rM = rBone.getLastWorldMatrixComputed();
1252 CVector v;
1253 rM.getPos(v);
1254 x = v.x;
1255 y = v.y;
1256 z = v.z;
1258 else
1260 nlwarning ("no skeleton");
1264 // ------------------------------------------------------------------------------------------------
1265 void CCharacter3D::setSkeleton (const string &filename)
1267 // Remove the old skeleton.
1268 if (!_Skeleton.empty())
1270 // Must remove first any channels of _Skeleton registered into _PlayList
1271 if (_PlayList)
1272 _PlayList->resetAllChannels();
1273 _Scene->deleteSkeleton(_Skeleton);
1274 _Skeleton = NULL;
1275 _Root = NULL;
1277 if (!_Root.empty())
1278 _Scene->deleteTransform(_Root);
1279 _Root = _Scene->createTransform();
1280 // Create the skeleton.
1281 _Skeleton = _Scene->createSkeleton(filename);
1282 if (_Skeleton.empty())
1284 nlwarning ("CCharacter3D::setSkeleton : Skeleton %s can't be created.", filename.c_str());
1285 return;
1288 _Skeleton.setPos (_PelvisPos);
1289 _Skeleton.changeMRMDistanceSetup (100.0f, 150.0f, 200.0f);
1290 _Skeleton.parent(_Root);
1293 // ------------------------------------------------------------------------------------------------
1294 void CCharacter3D::createInstance (TChar3DPart i, const SCharacter3DSetup::SCharacterPart &part)
1296 if (_Scene == NULL)
1298 nlwarning ("CCharacter3D::createInstance : no scene setup.");
1299 return;
1302 if (!_Instances[i].empty())
1303 _Scene->deleteInstance (_Instances[i]);
1305 if ((!part.Name.empty()) && (part.Name != "none.shape"))
1306 _Instances[i] = _Scene->createInstance (part.Name);
1308 // if cannot create output some errors
1309 if (_Instances[i].empty())
1311 if ((i != Char3DPart_HandRightItem) && (i != Char3DPart_HandLeftItem))
1312 nlwarning ("CCharacter3D::createInstance : cannot create the instance : %s.", part.Name.c_str());
1313 return;
1316 // FX Management
1318 // Advantage Fx
1319 if (!_InstancesFx[i].AdvantageFx.empty())
1320 _Scene->deleteInstance (_InstancesFx[i].AdvantageFx);
1322 if ((!part.AdvFx.empty()) && (part.AdvFx != "none.shape"))
1324 _InstancesFx[i].AdvantageFx = _Scene->createInstance (part.AdvFx);
1325 if (_InstancesFx[i].AdvantageFx.empty())
1327 nlwarning ("CCharacter3D::createInstance : cannot create the fx : %s.", part.AdvFx.c_str());
1329 else
1331 CMatrix mat = _Instances[i].getMatrix();
1332 mat.invert();
1333 mat *= _InstancesFx[i].AdvantageFx.getMatrix();
1334 _InstancesFx[i].AdvantageFx.setTransformMode(UTransformable::DirectMatrix);
1335 _InstancesFx[i].AdvantageFx.setMatrix(mat);
1336 _InstancesFx[i].AdvantageFx.parent(_Instances[i]);
1340 // Static Fx
1341 uint32 fx;
1342 for (fx = 0; fx < _InstancesFx[i].StaticFx.size(); ++fx)
1343 if (!_InstancesFx[i].StaticFx[fx].empty())
1344 _Scene->deleteInstance(_InstancesFx[i].StaticFx[fx]);
1345 _InstancesFx[i].StaticFx.clear();
1347 for (fx = 0; fx < part.StatFxNames.size(); ++fx)
1348 if ((!part.StatFxNames[fx].empty()) && (part.StatFxNames[fx] != "none.shape") &&
1349 (!part.StatFxBones[fx].empty()) && (part.StatFxBones[fx] != "none.shape"))
1351 sint boneID = _Skeleton.getBoneIdByName(part.StatFxBones[fx]);
1352 if (boneID != -1)
1354 UInstance instance = _Scene->createInstance(part.StatFxNames[fx]);
1355 if (!instance.empty())
1357 instance.setTransformMode(UTransform::DirectMatrix);
1358 CMatrix mat;
1359 mat.setPos(part.StatFxOffss[fx]);
1360 instance.setMatrix(mat);
1361 _Skeleton.stickObject(instance, boneID);
1362 _InstancesFx[i].StaticFx.push_back(instance);
1364 else
1366 nlwarning("Can't create static fx %s sticked on bone %s", part.StatFxNames[fx].c_str(), part.StatFxBones[fx].c_str());
1369 else
1371 nlwarning("Can't find bone %s for static fx %s", part.StatFxBones[fx].c_str(), part.StatFxNames[fx].c_str());
1376 // ------------------------------------------------------------------------------------------------
1377 void CCharacter3D::bindToSkeleton (TChar3DPart i)
1379 if (_Skeleton.empty())
1381 nlwarning ("CCharacter3D::bindToSkeleton : no skeleton setup");
1382 return;
1385 if (_Instances[i].empty())
1387 if ((i != Char3DPart_HandRightItem) && (i != Char3DPart_HandLeftItem))
1388 nlinfo ("CCharacter3D::bindToSkeleton : no character part for %d", i);
1389 return;
1392 switch (i)
1394 case Char3DPart_HandRightItem:
1396 sint rightHandBoneID = _Skeleton.getBoneIdByName ("box_arme");
1397 if (rightHandBoneID != -1)
1398 _Skeleton.stickObject (_Instances[i], rightHandBoneID);
1400 break;
1402 case Char3DPart_HandLeftItem:
1404 sint leftHandBoneID;
1405 // If this is a shield.
1406 if (_CurrentSetup.LeftHandItemIsShield)
1407 leftHandBoneID = _Skeleton.getBoneIdByName ("Box_bouclier");
1408 else
1409 leftHandBoneID = _Skeleton.getBoneIdByName ("box_arme_gauche");
1410 if (leftHandBoneID != -1)
1411 _Skeleton.stickObject (_Instances[i], leftHandBoneID);
1413 break;
1415 default:
1416 if (!_Skeleton.bindSkin(_Instances[i]))
1418 nlwarning ("CCharacter3D::bindToSkeleton: Cannot bind the instance : %d.", i);
1419 return;
1421 break;
1425 // ------------------------------------------------------------------------------------------------
1426 uint32 CCharacter3D::peopleToSkin (EGSPD::CPeople::TPeople people) const
1428 switch (people)
1430 case EGSPD::CPeople::Matis:
1431 return 1;
1433 case EGSPD::CPeople::Tryker:
1434 return 2;
1436 case EGSPD::CPeople::Zorai:
1437 return 3;
1439 case EGSPD::CPeople::Fyros:
1440 default:
1441 return 0;
1445 // ------------------------------------------------------------------------------------------------
1446 void CCharacter3D::animblink (double globalTime)
1448 float blend;
1450 // Some parameters
1451 static const double blinkTime = 0.1f;
1452 static const double minBlinkLength = 0.5f;
1453 static const double maxBlinkLength = 5.0f;
1455 // Next blink time is valid ?
1456 bool validTime = (_NextBlinkTime + blinkTime >= globalTime) && (_NextBlinkTime <= (globalTime + maxBlinkLength));
1458 // Blink end ?
1459 bool blinkEnd = (globalTime >= _NextBlinkTime + blinkTime);
1461 // Blink is finished or next blink time is invalid ?
1462 if ( blinkEnd || !validTime )
1464 blend = 0;
1466 // Compute next time
1467 _NextBlinkTime = (((double)rand () / (double)RAND_MAX) * (maxBlinkLength - minBlinkLength) + minBlinkLength + (double)globalTime);
1469 else
1471 // Blink time ?
1472 if (globalTime >= _NextBlinkTime)
1474 blend = 100.f;
1476 else
1478 // Do nothing
1479 return;
1483 // Set the blend shape
1484 if(!_Instances[Char3DPart_Face].empty())
1485 _Instances[Char3DPart_Face].setBlendShapeFactor ("visage_100", blend, true);
1488 // ------------------------------------------------------------------------------------------------
1489 CVector CCharacter3D::getBonePos (const string &boneName)
1491 CVector ret=CVector(0,0,0);
1492 sint boneId = _Skeleton.getBoneIdByName(boneName);
1493 if (boneId == -1) return ret;
1494 _Skeleton.forceComputeBone(boneId);
1495 UBone rBone = _Skeleton.getBone(boneId);
1496 const CMatrix &rM = rBone.getLastWorldMatrixComputed();
1497 rM.getPos(ret);
1498 return ret;
1501 // ------------------------------------------------------------------------------------------------