1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2018 Winch Gate Property Limited
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>
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/>.
29 #include "entity_animation_manager.h"
31 #include "sheet_manager.h"
32 #include "interface_v3/interface_manager.h"
33 #include "continent_manager.h"
34 #include "world_database_manager.h"
35 #include "client_cfg.h"
36 #include "user_entity.h"
37 #include "net_manager.h"
38 #include "sky_render.h"
40 #include "client_sheets/item_sheet.h"
42 #include "nel/3d/u_instance.h"
43 #include "nel/3d/u_instance_material.h"
44 #include "nel/3d/u_scene.h"
45 #include "nel/3d/u_driver.h"
46 #include "nel/3d/material.h"
48 #include "nel/misc/file.h"
49 #include "nel/misc/i_xml.h"
50 #include "nel/misc/o_xml.h"
51 #include "nel/misc/fast_floor.h"
52 #include "nel/misc/noise_value.h"
53 #include "nel/misc/bitmap.h"
54 #include "nel/misc/system_utils.h"
56 #include "game_share/player_visual_properties.h"
57 #include "game_share/seeds.h"
59 #include "nel/georges/u_form.h"
60 #include "nel/georges/u_form_elm.h"
61 #include "nel/georges/u_form_loader.h"
67 using namespace NLMISC
;
69 using namespace NLGEORGES
;
74 extern CEntityAnimationManager
*EAM
;
75 extern UDriver
*Driver
;
76 extern UMaterial GenericMat
;
82 const uint RecordVersion
= 1;
83 std::set
<std::string
> LodCharactersNotFound
;
89 //-----------------------------------------------
91 //-----------------------------------------------
92 double keepIn_NegPi_Pi(double angle
)
100 }// keepIn_NegPi_Pi //
102 //-----------------------------------------------
103 // angleBetween2Vect :
104 //-----------------------------------------------
105 double angleBetween2Vect(const CVectorD
&from
, const CVectorD
&to
)
107 // Get the final local head Yaw.
114 mat
.setRot(vi
,vj
,vk
,true);
116 CVector localDir
= mat
.inverted() * to
;
117 return atan2(-localDir
.x
, localDir
.y
);
118 }// angleBetween2Vect //
120 //-----------------------------------------------
122 // \param face : pointer on the face to make up (must not be null).
123 // \param idMakeUp : index of the make-up to apply.
124 // \warning This function does not check if 'face' is valid.
125 //-----------------------------------------------
126 void makeUp(NL3D::UInstance face
, sint idMakeUp
)
128 static const char *tattooStr
= "visage_makeup";
130 // look for tattoo texture
131 uint numMat
= face
.getNumMaterials();
132 std::string texFilename
;
133 for(uint k
= 0; k
< numMat
; ++k
)
135 UInstanceMaterial im
= face
.getMaterial(k
);
136 sint numTex
= im
.getLastTextureStage();
137 for(sint l
= 0; l
<= numTex
; ++l
)
139 if (im
.isTextureFile(l
)) // one texture from a file ?
141 // see if it is the texture used for tattoos
142 texFilename
= im
.getTextureFileName(l
);
143 // nlinfo("visage tex = %s", texFilename.c_str());
144 std::string::size_type pos
= texFilename
.find(tattooStr
, 0);
145 if (pos
!= std::string::npos
)
147 uint charIndex
= (uint
)(pos
+ strlen(tattooStr
));
148 if (texFilename
.length() >= charIndex
+ 2)
150 texFilename
[charIndex
] = '0' + (unsigned char) (idMakeUp
/ 10);
151 texFilename
[charIndex
+ 1] = '0' + (unsigned char) (idMakeUp
% 10);
152 im
.setTextureFileName(texFilename
, l
);
160 //-----------------------------------------------
161 // qStart is the quaterion at t=0,
162 // qEnd is the quaterion at t=TAnimEnd,
163 // time is between 0 and 1. (1=> TAnimEnd)
164 //-----------------------------------------------
165 CQuat
applyRotationFactor(CQuat in
, float rotFactor
, CQuat qStart
, CQuat qEnd
, float time
)
167 H_AUTO ( RZ_Client_Apply_Rotation_Factor
)
169 CQuat qRotTotal1
, qRotTotal2
;
171 qRotTotal1
= qEnd
*qStart
;
172 // qRotTotal2.makeClosest(CQuat::Identity);
173 qRotTotal2
= CQuat::slerp(CQuat::Identity
, qRotTotal1
, rotFactor
);
175 // apply animation factor
176 // qRotTotal1.makeClosest(CQuat::Identity);
177 // qRotTotal2.makeClosest(CQuat::Identity);
178 CQuat qRotDelta1
= CQuat::slerp(CQuat::Identity
, qRotTotal1
, time
);
179 CQuat qRotDelta2
= CQuat::slerp(CQuat::Identity
, qRotTotal2
, time
);
181 // remove normal rotation, and apply rotFactor-ed one.
183 return qRotDelta2
* qRotDelta1
* in
;
184 }// applyRotationFactor //
187 //-----------------------------------------------
188 // computeShortestAngle :
189 // Compute the angle between a source and a destination using the shortest way.
190 // \param from : angle between -Pi and Pi;
191 // \param to : angle between -Pi and Pi;
192 //-----------------------------------------------
193 double computeShortestAngle(double from
, double to
)
195 double difAngle
= to
- from
;
196 if(difAngle
< -Pi
) // Angle in the wrong direction.
198 else if(difAngle
> Pi
) // Angle in the wrong direction.
202 }// computeShortestAngle //
204 //-----------------------------------------------
206 //-----------------------------------------------
207 void readStringArray(const std::string
&filename
, NLGEORGES::UFormLoader
*formLoader
, NLMISC::CSmartPtr
<NLGEORGES::UForm
> &form
, std::map
<std::string
, std::string
> &container
)
209 // Read an array of strings
212 form
= formLoader
->loadForm(filename
.c_str());
216 const UFormElm
& rootElmt
= form
->getRootNode();
218 const UFormElm
*elmt
= 0;
219 if(rootElmt
.getNodeByName(&elmt
, "array") == false)
220 nlwarning("readStringArray: the node 'array' is not referenced.");
221 // If the array is not empty (in fact exist).
224 // Get the array size
226 elmt
->getArraySize(arraySize
);
227 // If there is at least 1 animation.
230 // Get all animation for the State.
231 for(uint i
= 0; i
<arraySize
; ++i
)
234 std::string nodeName
;
235 std::string stringName
;
236 if(elmt
->getArrayNodeName(nodeName
, i
))
238 if(elmt
->getArrayValue(stringName
, i
))
239 container
.insert(make_pair(nodeName
, stringName
));
241 nlwarning("readStringArray: no string associated to the node '%u(%s)'.", i
, nodeName
.c_str());
244 nlwarning("readStringArray: node '%u', index valid.", i
);
249 nlwarning("readStringArray: array node not allocated.");
252 nlwarning("readStringArray: cannot create the form from file '%s'.", filename
.c_str());
255 nlwarning("readStringArray: the Loader is not allocated.");
256 }// readStringArray //
258 //-----------------------------------------------
260 // Return the animset base name corresponding to the mode.
261 // \param mode : the mode to convert.
262 // \param result : this will be filed with the mode animset name.
263 // \return bool : 'true' if 'result' is filled, 'false' if left untouched.
264 //-----------------------------------------------
265 bool mode2Anim(MBEHAV::EMode mode
, string
&result
)
267 static bool init
= false;
268 static string mode2AnimArray
[MBEHAV::NUMBER_OF_MODES
];
271 uint index
= (uint
)mode
;
272 if(index
>= MBEHAV::NUMBER_OF_MODES
)
278 // Read mode2animset file
279 std::map
<std::string
, std::string
> mode2Animset
;
280 const std::string filename
= "mode2animset.string_array";
281 NLGEORGES::UFormLoader
*formLoader
= UFormLoader::createLoader();
284 NLMISC::CSmartPtr
<NLGEORGES::UForm
> form
;
285 readStringArray(filename
, formLoader
, form
, mode2Animset
);
288 nlwarning("mode2Anim: cannot create de loader");
289 NLGEORGES::UFormLoader::releaseLoader(formLoader
);
290 // Initialize the static vector.
291 //-------------------------------
292 for(uint i
=0; i
<MBEHAV::NUMBER_OF_MODES
; ++i
)
294 const std::string modeName
= MBEHAV::modeToString((MBEHAV::EMode
)i
);
295 std::map
<std::string
, std::string
>::const_iterator it
= mode2Animset
.find(modeName
);
296 if(it
!= mode2Animset
.end())
298 mode2AnimArray
[i
] = (*it
).second
;
299 if(mode2AnimArray
[i
].empty())
300 nlwarning("mode2Anim: The mode '%d(%s)' has an empty animset associated.", i
, modeName
.c_str ());
302 // No animset for the mode.
305 mode2AnimArray
[i
].clear();
306 nlwarning("mode2Anim: no animset associated to the mode %d'%s'.", i
, modeName
.c_str());
315 if(mode2AnimArray
[index
].empty())
316 result
= mode2AnimArray
[MBEHAV::NORMAL
];
318 result
= mode2AnimArray
[index
];
323 //-----------------------------------------------
325 // Compute the animation set to use according to weapons, mode and race.
326 // \param animSet : result pointer.
327 // \param mode : the mode.
328 // \param animSetBaseName : basic name to construc the complet name of the animSet.
329 // \param leftHand : animSet name for the left hand.
330 // \param rightHand : animSet name for the right hand.
331 // \param lookAtItemsInHands : compute animset according to items in hands or not.
332 // \return bool : 'true' if the new animation set is the right one. 'false' if the one choosen is not the right one.
333 //-----------------------------------------------
334 bool computeAnimSet(const CAnimationSet
*&animSet
, MBEHAV::EMode mode
, const string
&animSetBaseName
, const CItemSheet
*itemLeftHand
, const CItemSheet
*itemRightHand
, bool lookAtItemsInHands
)
336 static std::set
<std::string
> UnknownAnimSet
;
342 nlwarning("computeAnimSet: EAM not allocated -> cannot compute the anim set.");
347 string animSetRight
, animSetLeft
;
349 animSetLeft
= itemLeftHand
->getAnimSet();
352 animSetRight
= itemRightHand
->getAnimSet();
354 // Get the animset name from the mode.
356 if(!mode2Anim(mode
, result
))
358 nlwarning("computeAnimSet: unknown mode '%d'.", mode
);
363 if(lookAtItemsInHands
)
364 result
= animSetBaseName
+ "_" + result
+ "_" + animSetRight
+ "_" + animSetLeft
;
366 result
= animSetBaseName
+ "_" + result
+ "__";
369 const CAnimationSet
*animSetTmp
= EAM
->getAnimSet(result
);
372 animSet
= animSetTmp
;
375 // Bad one try something else.
378 // Up to 100 missing anim set (security).
379 if(UnknownAnimSet
.size() < 100)
381 if(UnknownAnimSet
.insert(result
).second
)
382 nlwarning("computeAnimSet: unknown Anim Set '%s' ('%u' unkowns).", result
.c_str(), UnknownAnimSet
.size());
384 // Try to compute the default one
385 result
= animSetBaseName
+ "_" + "default" + "__";
386 animSetTmp
= EAM
->getAnimSet(result
);
388 animSet
= animSetTmp
;
391 // Up to 100 missing anim set (security).
392 if(UnknownAnimSet
.size() < 100)
394 if(UnknownAnimSet
.insert(result
).second
)
395 nlwarning("computeAnimSet: unknown Anim Set '%s' ('%u' unkowns).", result
.c_str(), UnknownAnimSet
.size());
402 }// computeAnimSet //
405 //-----------------------------------------------
407 // Create a file with information to debug.
408 //-----------------------------------------------
409 void dump(const std::string
&name
)
411 // Write information to start as the version
413 if(fStart
.open(name
+ "_start.rec", false, false))
415 CVectorD currentPos
= UserEntity
->pos();
416 fStart
.serialVersion(RecordVersion
);
417 fStart
.serial(currentPos
);
422 nlwarning("dump: cannot open/create the file '%s_start.rec'.", name
.c_str());
425 IngameDbMngr
.write(name
+ "_db.rec");
428 if(f
.open(name
+ ".rec", false, false))
431 EntitiesMngr
.dump(f
);
440 nlwarning("dump: cannot open/create the file '%s.rec'.", name
.c_str());
444 if(f
.open(name
+ ".xml", false, true))
446 // Create the XML stream
449 if(output
.init (&f
, "1.0"))
451 // Open the XML Dump.
452 output
.xmlPush("XML");
455 ClientCfg
.serial(output
);
458 EntitiesMngr
.dumpXML(output
);
460 // Close the XML Dump.
463 // Flush the stream, write all the output file
467 nlwarning("dump: cannot initialize '%s.xml'.", name
.c_str());
472 nlwarning("dump: cannot open/create the file '%s.xml'.", name
.c_str());
476 //-----------------------------------------------
478 // Create a file with the current state of the client (good to report a bug).
479 //-----------------------------------------------
480 void loadDump(const std::string
&name
)
484 // Load information to start as the version
486 if(fStart
.open(name
+ "_start.rec", false))
488 fStart
.serialVersion(RecordVersion
);
489 fStart
.serial(currentPos
);
494 nlwarning("loadDump: cannot open the file '%s_start.rec'.", name
.c_str());
496 // Update the position for the vision.
497 NetMngr
.setReferencePosition(currentPos
);
499 // Select the closest continent from the new position.
500 class CDummyProgress
: public IProgressCallback
502 void progress (float /* value */) {}
504 CDummyProgress dummy
;
505 ContinentMngr
.select(currentPos
, dummy
);
508 IngameDbMngr
.read(name
+ "_db.rec");
512 if(f
.open(name
+ ".rec", false))
515 EntitiesMngr
.dump(f
);
521 nlwarning("loadDump: cannot open '%s.rec'.", name
.c_str());
525 //-----------------------------------------------
527 // Get the lod character id from the scene, according to LodCharacterName. Cached.
528 // -1 if id not found.
529 //-----------------------------------------------
530 sint
getLodCharacterId(UScene
&scene
, const string
&lodCharacterName
)
532 sint lodCharacterId
= scene
.getCLodShapeIdByName(lodCharacterName
);
533 // display a warning for bad character Id, only if name was setup in the sheet
534 if(lodCharacterId
==-1 && !lodCharacterName
.empty() )
536 // Limited to 100 missing Lod to avoid memories problems
537 if(LodCharactersNotFound
.size() < 100)
539 // Insert and display a waring
540 if(LodCharactersNotFound
.insert(lodCharacterName
).second
)
541 nlwarning("getLodCharacterId: Not found A Character LodCharacter in the Manager: %s", lodCharacterName
.c_str());
545 return lodCharacterId
;
546 }// getLodCharacterId //
549 //-----------------------------------------------
551 //-----------------------------------------------
552 CItemSheet
*getItem(const CGenderInfo
&genderInfo
, SLOTTYPE::EVisualSlot slot
)
554 CEntitySheet
*faceItem
= SheetMngr
.get(CSheetId(genderInfo
.getItemName(slot
)));
555 return (dynamic_cast <CItemSheet
*> (faceItem
));
559 //-----------------------------------------------
561 //-----------------------------------------------
562 sint
getColorIndex(const CGenderInfo
&genderInfo
, SLOTTYPE::EVisualSlot slot
)
564 CItemSheet
*is
= getItem(genderInfo
, slot
);
571 //-----------------------------------------------
572 // buildPropVisualA :
573 //-----------------------------------------------
574 SPropVisualA
buildPropVisualA(const CGenderInfo
&genderInfo
)
579 dest
.PropertySubData
.JacketModel
= SheetMngr
.getVSIndex(genderInfo
.Items
[SLOTTYPE::CHEST_SLOT
], SLOTTYPE::CHEST_SLOT
);
580 dest
.PropertySubData
.TrouserModel
= SheetMngr
.getVSIndex(genderInfo
.Items
[SLOTTYPE::LEGS_SLOT
], SLOTTYPE::LEGS_SLOT
);
581 dest
.PropertySubData
.ArmModel
= SheetMngr
.getVSIndex(genderInfo
.Items
[SLOTTYPE::ARMS_SLOT
], SLOTTYPE::ARMS_SLOT
);
582 dest
.PropertySubData
.HatModel
= SheetMngr
.getVSIndex(genderInfo
.Items
[SLOTTYPE::HEAD_SLOT
], SLOTTYPE::HEAD_SLOT
);
584 // setup colors (fixed or user)
585 sint col
= getColorIndex(genderInfo
, SLOTTYPE::CHEST_SLOT
);
586 dest
.PropertySubData
.JacketColor
= (col
== -1) ? 0 : col
;
588 col
= getColorIndex(genderInfo
, SLOTTYPE::LEGS_SLOT
);
589 dest
.PropertySubData
.TrouserColor
= (col
== -1) ? 0 : col
;
591 col
= getColorIndex(genderInfo
, SLOTTYPE::ARMS_SLOT
);
592 dest
.PropertySubData
.ArmColor
= (col
== -1) ? 0 : col
;
594 col
= getColorIndex(genderInfo
, SLOTTYPE::HEAD_SLOT
);
595 dest
.PropertySubData
.HatColor
= (col
== -1) ? 0 : col
;
597 // sheath are not used
599 }// buildPropVisualA //
602 //-----------------------------------------------
603 // buildPropVisualB :
604 //-----------------------------------------------
605 SPropVisualB
buildPropVisualB(const CGenderInfo
&genderInfo
)
611 dest
.PropertySubData
.HandsModel
= SheetMngr
.getVSIndex(genderInfo
.Items
[SLOTTYPE::HANDS_SLOT
], SLOTTYPE::HANDS_SLOT
);
612 dest
.PropertySubData
.FeetModel
= SheetMngr
.getVSIndex(genderInfo
.Items
[SLOTTYPE::FEET_SLOT
], SLOTTYPE::FEET_SLOT
);
614 // setup colors (fixed or user)
615 sint col
= getColorIndex(genderInfo
, SLOTTYPE::HANDS_SLOT
);
616 dest
.PropertySubData
.HandsColor
= (col
== -1) ? 0 : col
;
618 col
= getColorIndex(genderInfo
, SLOTTYPE::FEET_SLOT
);
619 dest
.PropertySubData
.FeetColor
= (col
== -1) ? 0 : col
;
622 }// buildPropVisualB //
627 //-----------------------------------------------
628 // isUserColorSupported :
629 // Test whether user color is supported for this equipment
630 //-----------------------------------------------
631 bool isUserColorSupported(const CPlayerSheet::CEquipment
&equip
)
634 if(!si
.buildSheetId(equip
.Item
))
637 CItemSheet
*is
= dynamic_cast<CItemSheet
*>(SheetMngr
.get(si
));
641 return is
->Color
== -1; // user color supported by the item
642 }// isUserColorSupported //
644 //-----------------------------------------------
645 // isUserColorSupported :
646 // Test whether user color is supported for a given visual slot
647 //-----------------------------------------------
648 bool isUserColorSupported(const CPlayerSheet
&playerSheet
, SLOTTYPE::EVisualSlot vs
)
652 case SLOTTYPE::CHEST_SLOT
: return isUserColorSupported(playerSheet
.Body
);
653 case SLOTTYPE::LEGS_SLOT
: return isUserColorSupported(playerSheet
.Legs
);
654 case SLOTTYPE::HEAD_SLOT
: return isUserColorSupported(playerSheet
.Head
);
655 case SLOTTYPE::ARMS_SLOT
: return isUserColorSupported(playerSheet
.Arms
);
656 case SLOTTYPE::HANDS_SLOT
: return isUserColorSupported(playerSheet
.Hands
);
657 case SLOTTYPE::FEET_SLOT
: return isUserColorSupported(playerSheet
.Feet
);
658 case SLOTTYPE::RIGHT_HAND_SLOT
: return isUserColorSupported(playerSheet
.ObjectInRightHand
);
659 case SLOTTYPE::LEFT_HAND_SLOT
: return isUserColorSupported(playerSheet
.ObjectInLeftHand
);
663 }// isUserColorSupported //
666 //-----------------------------------------------
668 //-----------------------------------------------
669 sint8
getColor(const CPlayerSheet::CEquipment
&equip
)
672 if(!si
.buildSheetId(equip
.Item
))
675 CItemSheet
*is
= dynamic_cast<CItemSheet
*>(SheetMngr
.get(si
));
679 if(is
->Color
== -1) // user color supported by the item
689 //-----------------------------------------------
690 // buildPropVisualA :
691 //-----------------------------------------------
692 SPropVisualA
buildPropVisualA(const CPlayerSheet
&playerSheet
)
696 vp
.PropertySubData
.JacketModel
= SheetMngr
.getVSIndex(playerSheet
.Body
.Item
, SLOTTYPE::CHEST_SLOT
);
697 vp
.PropertySubData
.TrouserModel
= SheetMngr
.getVSIndex(playerSheet
.Legs
.Item
, SLOTTYPE::LEGS_SLOT
);
698 vp
.PropertySubData
.ArmModel
= SheetMngr
.getVSIndex(playerSheet
.Arms
.Item
, SLOTTYPE::ARMS_SLOT
);
699 vp
.PropertySubData
.HatModel
= SheetMngr
.getVSIndex(playerSheet
.Head
.Item
, SLOTTYPE::HEAD_SLOT
);
700 vp
.PropertySubData
.WeaponRightHand
= SheetMngr
.getVSIndex(playerSheet
.ObjectInRightHand
.Item
, SLOTTYPE::RIGHT_HAND_SLOT
);
701 vp
.PropertySubData
.WeaponLeftHand
= SheetMngr
.getVSIndex(playerSheet
.ObjectInLeftHand
.Item
, SLOTTYPE::LEFT_HAND_SLOT
);
704 vp
.PropertySubData
.JacketColor
= getColor(playerSheet
.Body
);
705 vp
.PropertySubData
.TrouserColor
= getColor(playerSheet
.Legs
);
706 vp
.PropertySubData
.ArmColor
= getColor(playerSheet
.Arms
);
707 vp
.PropertySubData
.HatColor
= getColor(playerSheet
.Head
);
710 }// buildPropVisualA //
712 //-----------------------------------------------
713 // buildPropVisualB :
714 //-----------------------------------------------
715 SPropVisualB
buildPropVisualB(const CPlayerSheet
&playerSheet
)
719 vp
.PropertySubData
.Name
= 0;
720 vp
.PropertySubData
.HandsModel
= SheetMngr
.getVSIndex(playerSheet
.Hands
.Item
, SLOTTYPE::HANDS_SLOT
);
721 vp
.PropertySubData
.FeetModel
= SheetMngr
.getVSIndex(playerSheet
.Feet
.Item
, SLOTTYPE::FEET_SLOT
);
724 vp
.PropertySubData
.HandsColor
= getColor(playerSheet
.Hands
);
725 vp
.PropertySubData
.FeetColor
= getColor(playerSheet
.Feet
);
728 }// buildPropVisualB //
731 //-----------------------------------------------
733 // Draw a Box from 2 vectors.
734 //-----------------------------------------------
735 void drawBox(const CVector
&vMin
, const CVector
&vMax
, const CRGBA
&color
)
741 line
= CLine(CVector(vMin
.x
,vMin
.y
,vMin
.z
), CVector(vMax
.x
,vMin
.y
,vMin
.z
));
742 Driver
->drawLine(line
, GenericMat
);
743 line
= CLine(CVector(vMax
.x
,vMin
.y
,vMin
.z
), CVector(vMax
.x
,vMax
.y
,vMin
.z
));
744 Driver
->drawLine(line
, GenericMat
);
745 line
= CLine(CVector(vMax
.x
,vMax
.y
,vMin
.z
), CVector(vMin
.x
,vMax
.y
,vMin
.z
));
746 Driver
->drawLine(line
, GenericMat
);
747 line
= CLine(CVector(vMin
.x
,vMax
.y
,vMin
.z
), CVector(vMin
.x
,vMin
.y
,vMin
.z
));
748 Driver
->drawLine(line
, GenericMat
);
750 line
= CLine(CVector(vMin
.x
,vMin
.y
,vMax
.z
), CVector(vMax
.x
,vMin
.y
,vMax
.z
));
751 Driver
->drawLine(line
, GenericMat
);
752 line
= CLine(CVector(vMax
.x
,vMin
.y
,vMax
.z
), CVector(vMax
.x
,vMax
.y
,vMax
.z
));
753 Driver
->drawLine(line
, GenericMat
);
754 line
= CLine(CVector(vMax
.x
,vMax
.y
,vMax
.z
), CVector(vMin
.x
,vMax
.y
,vMax
.z
));
755 Driver
->drawLine(line
, GenericMat
);
756 line
= CLine(CVector(vMin
.x
,vMax
.y
,vMax
.z
), CVector(vMin
.x
,vMin
.y
,vMax
.z
));
757 Driver
->drawLine(line
, GenericMat
);
759 line
= CLine(CVector(vMin
.x
,vMin
.y
,vMin
.z
), CVector(vMin
.x
,vMin
.y
,vMax
.z
));
760 Driver
->drawLine(line
, GenericMat
);
761 line
= CLine(CVector(vMax
.x
,vMin
.y
,vMin
.z
), CVector(vMax
.x
,vMin
.y
,vMax
.z
));
762 Driver
->drawLine(line
, GenericMat
);
763 line
= CLine(CVector(vMax
.x
,vMax
.y
,vMin
.z
), CVector(vMax
.x
,vMax
.y
,vMax
.z
));
764 Driver
->drawLine(line
, GenericMat
);
765 line
= CLine(CVector(vMin
.x
,vMax
.y
,vMin
.z
), CVector(vMin
.x
,vMax
.y
,vMax
.z
));
766 Driver
->drawLine(line
, GenericMat
);
770 //-----------------------------------------------
773 //-----------------------------------------------
774 void drawSphere(const NLMISC::CVector
¢er
, float radius
, const NLMISC::CRGBA
&color
)
776 const uint numSegs
= 12;
781 for(uint face
=0;face
<3;face
++)
783 for(uint i
=0;i
<numSegs
;i
++)
785 float angStart
= float(i
*2*Pi
/numSegs
);
786 float angEnd
= float(((i
+1)%numSegs
)*2*Pi
/numSegs
);
787 line
.V0
= radius
*CVector(cosf(angStart
), sinf(angStart
), 0.f
);
788 line
.V1
= radius
*CVector(cosf(angEnd
), sinf(angEnd
), 0.f
);
791 swap(line
.V0
.x
, line
.V0
.z
);
792 swap(line
.V1
.x
, line
.V1
.z
);
796 swap(line
.V0
.y
, line
.V0
.z
);
797 swap(line
.V1
.y
, line
.V1
.z
);
801 Driver
->drawLine(line
, GenericMat
);
807 //-----------------------------------------------
809 //-----------------------------------------------
810 void getSeedsFromDB(CSeeds
&dest
)
812 CInterfaceManager
*im
=CInterfaceManager::getInstance();
813 nlctassert(sizeof(CSeeds::TUInt
) == 4); // excpect that the number of each seed type is encoded on 32 bits
814 // if this assert at compile, change the following code
815 string ls
= CWidgetManager::getInstance()->getParser()->getDefine("money_1");
816 string ms
= CWidgetManager::getInstance()->getParser()->getDefine("money_2");
817 string bs
= CWidgetManager::getInstance()->getParser()->getDefine("money_3");
818 string vbs
= CWidgetManager::getInstance()->getParser()->getDefine("money_4");
820 dest
= CSeeds(NLGUI::CDBManager::getInstance()->getDbProp(ls
)->getValue32(),
821 NLGUI::CDBManager::getInstance()->getDbProp(ms
)->getValue32(),
822 NLGUI::CDBManager::getInstance()->getDbProp(bs
)->getValue32(),
823 NLGUI::CDBManager::getInstance()->getDbProp(vbs
)->getValue32());
824 } // getSeedsFromDB //
827 //-----------------------------------------------
829 // Change a 'direction' vector.
830 // \param vectToChange : the vector to change.
831 // \param vect : new vector to use.
832 // \param compute : adjust the param 'vect' to be valid or leave the old one unchanged if impossible.
833 // \param check : warning if the param 'vect' is not valid (vector Null) even with compute=true.
834 // \return bool : 'true' if the vectToChange has been filled, else 'false'.
835 //-----------------------------------------------
836 bool setVect(CVector
&vectToChange
, const CVector
&vect
, bool compute
, bool check
)
838 // Compute the vector.
841 CVector
vectTmp(vect
);
842 // No need of the Z component for the vector.
845 if(vectTmp
!= CVector::Null
)
848 vectToChange
= vectTmp
;
851 // Bad Vector -> vectToChange remains the same
856 nlwarning("setVect: cannot compute the vector, keep the old one.");
861 // Bad Vector -> vectToChange remains the same
862 if(vect
== CVector::Null
)
866 nlwarning("setVect: param is a vector Null, vectToChange remains the same.");
870 // Set the new front vector.
875 NLMISC::CRGBA
interpClientCfgColor(const string
&src
, string
&dest
)
877 CRGBA color
= CRGBA::White
;
882 string::size_type nextPos
= src
.find('&', 1);
883 if (nextPos
!= string::npos
)
885 std::string colorCode
;
886 colorCode
.resize(nextPos
- 1);
887 for(uint k
= 0; k
< nextPos
- 1; ++k
)
889 colorCode
[k
] = tolower((char) src
[k
+ 1]); // TODO: toLowerAscii
891 std::map
<std::string
, CClientConfig::SSysInfoParam
>::const_iterator it
= ClientCfg
.SystemInfoParams
.find(colorCode
);
892 if (it
!= ClientCfg
.SystemInfoParams
.end())
894 color
= it
->second
.Color
;
896 dest
= src
.substr(nextPos
+ 1);
915 std::string
getStringCategory(const string
&src
, string
&dest
, bool alwaysAddSysByDefault
)
917 std::string str
= getStringCategoryIfAny(src
, dest
);
918 if (alwaysAddSysByDefault
)
919 return str
.empty()?"SYS":str
;
925 std::string
getStringCategoryIfAny(const string
&src
, string
&dest
)
927 std::string colorCode
;
932 // Skip <NEW> or <CHG> if present at beginning
934 const size_t PreTagSize
= 5;
935 static const string newTag
= "<NEW>";
936 if ( (src
.size() >= PreTagSize
) && (src
.substr( 0, PreTagSize
) == newTag
) )
938 startPos
= PreTagSize
;
941 static const string chgTag
= "<CHG>";
942 if ( (src
.size() >= PreTagSize
) && (src
.substr( 0, PreTagSize
) == chgTag
) )
944 startPos
= PreTagSize
;
948 if (src
[startPos
] == '&')
950 string::size_type nextPos
= src
.find('&', startPos
+1);
951 if (nextPos
!= string::npos
)
953 size_t codeSize
= nextPos
- startPos
- 1;
954 colorCode
.resize( codeSize
);
955 for(ptrdiff_t k
= 0; k
< (ptrdiff_t)codeSize
; ++k
)
957 colorCode
[k
] = tolower((char) src
[k
+ startPos
+ 1]); // TODO: toLowerAscii
961 destTmp
= preTag
; // leave <NEW> or <CHG> in the dest string
962 destTmp
+= src
.substr(nextPos
+ 1);
983 // ***************************************************************************
984 sint
ucstrnicmp(const ucstring
&s0
, uint p0
, uint n0
, const ucstring
&s1
) // OLD
987 const ucchar
*start1
= s1
.c_str();
988 uint lenS1
= (uint
)s1
.size();
989 const ucchar
*start0
= s0
.c_str();
990 uint lenS0
= (uint
)s0
.size();
1000 start0
+= lenS0
; // points to '\0'
1004 lenS0
= min(lenS0
, n0
);
1006 // compare character to character
1007 while(lenS0
>0 && lenS1
>0)
1009 ucchar c0
= toLower(*start0
++);
1010 ucchar c1
= toLower(*start1
++);
1017 // return -1 if s1>s0, 1 if s0>s1, or 0 if equals
1027 // *******************************************************************************************
1028 float computeUniformNoise(const NLMISC::CNoiseValue
&nv
, const CVector
&pos
)
1030 NLMISC::OptFastFloorBegin();
1031 float value
= nv
.eval(pos
);
1032 value
= 10.f
* fmodf(value
, 0.1f
); // make repartition more uniform
1033 NLMISC::OptFastFloorEnd();
1038 // ***************************************************************************
1039 void computeCurrentFovAspectRatio(float &fov
, float &ar
)
1042 fov
= (float)(ClientCfg
.FoV
*Pi
/180.0);
1044 // get the screen aspect ratio from CFG
1045 ar
= ClientCfg
.ScreenAspectRatio
;
1047 // if Driver is not created, we can't get current screen mode
1048 if (!Driver
) return;
1050 // if windowed, must modulate aspect ratio by (WindowResolution / ScreenResolution)
1051 if(ClientCfg
.Windowed
)
1054 Driver
->getWindowSize(wndW
, wndH
);
1055 UDriver::CMode mode
;
1056 Driver
->getCurrentScreenMode(mode
);
1059 // compute window aspect ratio
1060 float arWnd
= float(wndW
) / float(wndH
);
1063 // auto mode, we are using window aspect ratio
1066 else if (mode
.Width
&& mode
.Height
)
1068 // compute screen aspect ratio
1069 float arScreen
= float(mode
.Width
) / float(mode
.Height
);
1070 ar
*= arWnd
/ arScreen
;
1074 // if fullscreen, must modulate aspect ratio by ScreenResolution
1079 UDriver::CMode mode
;
1080 Driver
->getCurrentScreenMode(mode
);
1083 ar
= float(mode
.Width
) / float(mode
.Height
);
1089 // ***************************************************************************
1090 void drawDisc(CBitmap
&dest
, float x
, float y
, float radius
, const CRGBA
&color
, bool additif
/*= false*/, uint numSegs
/*= 127*/)
1093 poly
.Vertices
.resize(numSegs
);
1094 for(uint k
= 0; k
< numSegs
; ++k
)
1096 poly
.Vertices
[k
].set(x
+ radius
* (float) cos(k
/ (float) numSegs
* 2 * Pi
), y
+ radius
* (float) sin(k
/ (float) numSegs
* 2 * Pi
));
1098 CPolygon2D::TRasterVect rasters
;
1100 poly
.computeOuterBorders(rasters
, minY
);
1101 sint maxY
= std::min((sint
) dest
.getHeight(), (sint
) rasters
.size() + minY
);
1102 for (sint y
= std::max((sint
) 0, minY
); y
< maxY
; ++y
)
1104 nlassert(y
>= 0 && y
< (sint
) dest
.getHeight());
1105 sint minX
= std::max((sint
) 0, rasters
[y
- minY
].first
);
1106 sint maxX
= std::min((sint
) dest
.getWidth(), rasters
[y
- minY
].second
);
1109 CRGBA
*pt
= (CRGBA
*) &dest
.getPixels(0)[0];
1110 pt
+= y
* dest
.getWidth() + minX
;
1111 const CRGBA
*endPt
= pt
+ (maxX
- minX
);
1116 pt
->add(*pt
, color
);
1129 // *************************************************************************************************
1130 NL3D::UScene
*getSkyScene()
1132 if (ContinentMngr
.cur() && !ContinentMngr
.cur()->Indoor
)
1134 CSky
&sky
= ContinentMngr
.cur()->CurrentSky
;
1137 return sky
.getScene();
1141 return SkyScene
; // old sky rendering
1147 // *************************************************************************************************
1148 void setEmissive(NL3D::UInstance instance
, const NLMISC::CRGBA
&color
)
1150 if (instance
.empty()) return;
1151 for(uint k
= 0; k
< instance
.getNumMaterials(); ++k
)
1153 NL3D::UInstanceMaterial mat
= instance
.getMaterial(k
);
1154 mat
.setEmissive(color
);
1158 // *************************************************************************************************
1159 void setDiffuse(NL3D::UInstance instance
, bool onOff
, const NLMISC::CRGBA
&color
)
1161 if (instance
.empty()) return;
1162 for(uint k
= 0; k
< instance
.getNumMaterials(); ++k
)
1164 NL3D::UInstanceMaterial mat
= instance
.getMaterial(k
);
1165 if (mat
.isLighted())
1170 src
= mat
.getDiffuse();
1177 NL3D::UMaterial matShape
= instance
.getShape().getMaterial(k
);
1178 src
= matShape
.getDiffuse();
1179 src
.A
= mat
.getDiffuse().A
;
1181 mat
.setDiffuse(src
);
1188 src
= mat
.getColor();
1195 NL3D::UMaterial matShape
= instance
.getShape().getMaterial(k
);
1196 src
= matShape
.getColor();
1197 src
.A
= mat
.getColor().A
;
1206 // *************************************************************************************************
1207 void makeInstanceTransparent(UInstance
&inst
, uint8 opacity
, bool disableZWrite
)
1209 UShape shape
= inst
.getShape();
1212 uint numMats
= shape
.getNumMaterials();
1215 if(numMats
!=inst
.getNumMaterials())
1218 // instance transparent or not?
1221 // reset default shape opacity / transparency
1222 inst
.setOpacity(shape
.getDefaultOpacity());
1223 inst
.setTransparency(shape
.getDefaultTransparency());
1224 inst
.setBypassLODOpacityFlag(false);
1228 // Will have some blend material => sure not to be rendered in Opaque pass
1229 inst
.setOpacity(false);
1230 inst
.setTransparency(true);
1231 inst
.setBypassLODOpacityFlag(true); // these flags prevails over the current lods flags for multi-lod objects
1234 // set all materials
1235 for (uint32 j
= 0; j
< numMats
; ++j
)
1237 NL3D::UInstanceMaterial matInst
= inst
.getMaterial(j
);
1238 NL3D::UMaterial matShape
= shape
.getMaterial(j
);
1242 matInst
.setZWrite(false);
1244 matInst
.setZWrite(matShape
.getZWrite());
1246 // if no more transparent
1250 matInst
.setBlend(matShape
.getBlend());
1251 matInst
.setBlendFunc((NL3D::UInstanceMaterial::TBlend
)matShape
.getSrcBlend(),
1252 (NL3D::UInstanceMaterial::TBlend
)matShape
.getDstBlend());
1253 // if orginal material is opaque or additif and has no alpha test, then ensure restore last tex env if needed
1254 CMaterial
*destInternalMat
= matInst
.getObjectPtr();
1255 if (!matShape
.getBlend() && !matShape
.getAlphaTest())
1257 if (destInternalMat
->getShader() == CMaterial::Normal
)
1259 CMaterial
*srcInternalMat
= matShape
.getObjectPtr();
1261 for (;numTex
< 4 && srcInternalMat
->getTexture(numTex
) != NULL
; ++numTex
) {}
1264 if (srcInternalMat
->getTexEnvMode(numTex
- 1) != destInternalMat
->getTexEnvMode(numTex
- 1))
1266 destInternalMat
->setTexEnvMode(numTex
- 1, srcInternalMat
->getTexEnvMode(numTex
- 1));
1271 if (destInternalMat
->getShader() == CMaterial::Normal
)
1273 // if !lighted, restore color
1274 if (!destInternalMat
->isLighted())
1276 CMaterial
*srcInternalMat
= matShape
.getObjectPtr();
1277 // restore alpha in color
1278 CRGBA color
= destInternalMat
->getColor();
1279 color
.A
= srcInternalMat
->getColor().A
;
1280 destInternalMat
->setColor(color
);
1287 matInst
.setBlend(true);
1288 // If default is ???/one or , then use a srcalpha/one (eg: for Diamond-like weapons)
1289 if(matShape
.getBlend() && (sint32
)matShape
.getDstBlend()==(sint32
)NL3D::UInstanceMaterial::one
)
1290 matInst
.setBlendFunc(NL3D::UInstanceMaterial::srcalpha
, NL3D::UInstanceMaterial::one
);
1291 // else use a standard srcalpha/invsrcalpha
1293 matInst
.setBlendFunc(NL3D::UInstanceMaterial::srcalpha
, NL3D::UInstanceMaterial::invsrcalpha
);
1294 // if orginal material is opaque or additif and has no alpha test, then ensure that the alpha output is 'diffuse'
1295 CMaterial
*internalMat
= matInst
.getObjectPtr();
1296 if (!matShape
.getBlend() && !matShape
.getAlphaTest())
1298 if (internalMat
->getShader() == CMaterial::Normal
)
1301 for (;numTex
< 4 && internalMat
->getTexture(numTex
) != NULL
; ++numTex
) {}
1304 internalMat
->texEnvOpAlpha(numTex
- 1, CMaterial::Replace
);
1305 // if material is unlighted, then use the constant at this stage to set the alpha
1306 internalMat
->texEnvArg0Alpha(numTex
- 1, CMaterial::Diffuse
, CMaterial::SrcAlpha
);
1310 if (internalMat
->getShader() == CMaterial::Normal
)
1312 if (!internalMat
->isLighted())
1314 // replace alpha in color
1315 CRGBA color
= internalMat
->getColor();
1317 internalMat
->setColor(color
);
1322 // suppose that default opacity is always 255
1323 if (matInst
.isLighted())
1325 matInst
.setOpacity(opacity
);
1328 matInst
.setAlphaTestThreshold(matShape
.getAlphaTestThreshold()*((float)opacity
)/255.0f
);
1332 void setVideoMode(const UDriver::CMode
&mode
)
1334 UDriver::CMode oldMode
, newMode
= mode
;
1335 oldMode
.Windowed
= true; // getCurrentScreenMode may fail if first init ...
1336 Driver
->getCurrentScreenMode(oldMode
);
1337 bool wasMaximized
= isWindowMaximized();
1338 if (!Driver
->setMode(newMode
) && !newMode
.Windowed
)
1340 // failed to switch to mode, fall back to windowed
1341 newMode
.Windowed
= true;
1342 ClientCfg
.Windowed
= true;
1343 ClientCfg
.writeInt("FullScreen", 0);
1345 // set the window mode
1346 Driver
->setMode(newMode
);
1348 bool isMaximized
= isWindowMaximized();
1349 if (oldMode
.Windowed
&& !newMode
.Windowed
) // going to fullscreen ?
1351 /*CInterfaceManager *pIM = CInterfaceManager::getInstance();
1352 pIM->movePointerAbs((sint32) mode.Width / 2, (sint32) mode.Height / 2);
1353 Driver->setMousePos(0.5f, 0.5f);*/
1355 else if ((!oldMode
.Windowed
|| wasMaximized
) && (newMode
.Windowed
&& !isMaximized
)) // leaving fullscreen ?
1357 UDriver::CMode screenMode
;
1362 if (Driver
->getCurrentScreenMode(screenMode
))
1364 // position is not saved in config so center the window
1365 posX
= (screenMode
.Width
- Driver
->getWindowWidth())/2;
1366 posY
= (screenMode
.Height
- Driver
->getWindowHeight())/2;
1369 Driver
->setWindowPos(posX
, posY
);
1373 uint
getCurrentColorDepth()
1375 if (Driver
&& Driver
->isActive())
1377 UDriver::CMode videoMode
;
1378 Driver
->getCurrentScreenMode(videoMode
);
1379 if (!videoMode
.Windowed
)
1381 return videoMode
.Depth
;
1385 return CSystemUtils::getCurrentColorDepth();
1388 bool isWindowMaximized()
1390 UDriver::CMode screenMode
;
1391 uint32 width
, height
;
1393 Driver
->getWindowSize(width
, height
);
1395 return (Driver
->getCurrentScreenMode(screenMode
) && screenMode
.Windowed
&&
1396 screenMode
.Width
== width
&& screenMode
.Height
== height
);
1399 bool getRyzomModes(std::vector
<NL3D::UDriver::CMode
> &videoModes
, std::vector
<std::string
> &stringModeList
, std::vector
<std::string
> &stringFreqList
, sint
&nFoundStringMode
, sint
&nFoundStringFreq
)
1402 nFoundStringMode
= -1;
1403 nFoundStringFreq
= -1;
1405 // mode index in original video modes
1406 sint nFoundMode
= -1;
1408 // **** Init Video Modes
1409 Driver
->getModes(videoModes
);
1411 // TODO: for resolutions below 1024x768, could use automatic UI scaling like in login/outgame
1412 // Remove modes under 1024x768 (outgame ui limitation) and get the unique strings
1414 for (i
= 0; i
< (sint
)videoModes
.size(); ++i
)
1416 if ((videoModes
[i
].Width
< 1024) || (videoModes
[i
].Height
< 768))
1418 videoModes
.erase(videoModes
.begin()+i
);
1423 bool bFound
= false;
1425 // create string format with width and height
1426 string res
= toString(videoModes
[i
].Width
)+" x "+toString(videoModes
[i
].Height
);
1428 if (!videoModes
[i
].DisplayDevice
.empty())
1429 res
+= toString(" (%s)", videoModes
[i
].DisplayDevice
.c_str());
1431 // check if video mode already found in list
1432 for (j
= 0; j
< (sint
)stringModeList
.size(); ++j
)
1434 if (stringModeList
[j
] == res
)
1444 // add it to the list
1445 stringModeList
.push_back(res
);
1447 // process all screen sizes less or equal to desired one
1448 if ((videoModes
[i
].Width
<= ClientCfg
.Width
) && (videoModes
[i
].Height
<= ClientCfg
.Height
))
1450 // take first one by default
1451 if (nFoundStringMode
== -1)
1453 nFoundStringMode
= j
;
1458 // then take the largest one
1459 if ((videoModes
[i
].Width
>= videoModes
[nFoundMode
].Width
) &&
1460 (videoModes
[i
].Height
>= videoModes
[nFoundMode
].Height
))
1462 nFoundStringMode
= j
;
1471 // If no modes are available, fallback to windowed mode
1472 if (nFoundStringMode
== -1)
1474 nlwarning("Mode %ux%u not found, fall back to windowed", (uint
)ClientCfg
.Width
, (uint
)ClientCfg
.Height
);
1475 ClientCfg
.Windowed
= true;
1476 ClientCfg
.writeInt("FullScreen", 0);
1480 // add frequencies to frequencies list
1481 for (i
= 0; i
< (sint
)videoModes
.size(); ++i
)
1483 // only take exact screen sizes
1484 if (videoModes
[i
].Width
== videoModes
[nFoundMode
].Width
&& videoModes
[i
].Height
== videoModes
[nFoundMode
].Height
)
1486 uint freq
= videoModes
[i
].Frequency
;
1487 std::string freqStr
= toString(freq
);
1489 bool bFound
= false;
1491 // check if frequency already found in list
1492 for (j
= 0; j
< (sint
)stringFreqList
.size(); ++j
)
1494 if (stringFreqList
[j
] == freqStr
)
1503 // if frequency is 0, take the first one else use the exact one
1504 if (nFoundStringFreq
== -1 && ((ClientCfg
.Frequency
== 0) || (freq
== ClientCfg
.Frequency
)))
1506 nFoundStringFreq
= stringFreqList
.size();
1509 stringFreqList
.push_back(freqStr
);
1515 return nFoundStringMode
> -1;
1518 // Get float value from string. Return true if the value is relatif ( src = "+15.5" for example )
1519 bool getRelativeFloatFromString(const std::string src
, float &dst
)
1526 return fromString(src
.substr(1), dst
);
1528 fromString(src
, dst
);
1533 void updateVector(const string part
, CVector
&dst
, float value
, bool add
/* = false */)
1536 if (part
.size() > 1)
1537 p
= part
.substr(part
.size()-1, 1);