1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 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>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
28 #include "entity_animation_manager.h"
30 #include "sheet_manager.h"
31 #include "interface_v3/interface_manager.h"
32 #include "continent_manager.h"
33 #include "world_database_manager.h"
34 #include "client_cfg.h"
35 #include "user_entity.h"
36 #include "net_manager.h"
37 #include "sky_render.h"
39 #include "client_sheets/item_sheet.h"
41 #include "nel/3d/u_instance.h"
42 #include "nel/3d/u_instance_material.h"
43 #include "nel/3d/u_scene.h"
44 #include "nel/3d/u_driver.h"
45 #include "nel/3d/material.h"
47 #include "nel/misc/file.h"
48 #include "nel/misc/i_xml.h"
49 #include "nel/misc/o_xml.h"
50 #include "nel/misc/fast_floor.h"
51 #include "nel/misc/noise_value.h"
52 #include "nel/misc/bitmap.h"
53 #include "nel/misc/system_utils.h"
55 #include "game_share/player_visual_properties.h"
56 #include "game_share/seeds.h"
58 #include "nel/georges/u_form.h"
59 #include "nel/georges/u_form_elm.h"
60 #include "nel/georges/u_form_loader.h"
66 using namespace NLMISC
;
68 using namespace NLGEORGES
;
73 extern CEntityAnimationManager
*EAM
;
74 extern UDriver
*Driver
;
75 extern UMaterial GenericMat
;
81 const uint RecordVersion
= 1;
82 std::set
<std::string
> LodCharactersNotFound
;
88 //-----------------------------------------------
90 //-----------------------------------------------
91 double keepIn_NegPi_Pi(double angle
)
99 }// keepIn_NegPi_Pi //
101 //-----------------------------------------------
102 // angleBetween2Vect :
103 //-----------------------------------------------
104 double angleBetween2Vect(const CVectorD
&from
, const CVectorD
&to
)
106 // Get the final local head Yaw.
113 mat
.setRot(vi
,vj
,vk
,true);
115 CVector localDir
= mat
.inverted() * to
;
116 return atan2(-localDir
.x
, localDir
.y
);
117 }// angleBetween2Vect //
119 //-----------------------------------------------
121 // \param face : pointer on the face to make up (must not be null).
122 // \param idMakeUp : index of the make-up to apply.
123 // \warning This function does not check if 'face' is valid.
124 //-----------------------------------------------
125 void makeUp(NL3D::UInstance face
, sint idMakeUp
)
127 static const char *tattooStr
= "visage_makeup";
129 // look for tattoo texture
130 uint numMat
= face
.getNumMaterials();
131 std::string texFilename
;
132 for(uint k
= 0; k
< numMat
; ++k
)
134 UInstanceMaterial im
= face
.getMaterial(k
);
135 sint numTex
= im
.getLastTextureStage();
136 for(sint l
= 0; l
<= numTex
; ++l
)
138 if (im
.isTextureFile(l
)) // one texture from a file ?
140 // see if it is the texture used for tattoos
141 texFilename
= im
.getTextureFileName(l
);
142 // nlinfo("visage tex = %s", texFilename.c_str());
143 std::string::size_type pos
= texFilename
.find(tattooStr
, 0);
144 if (pos
!= std::string::npos
)
146 uint charIndex
= (uint
)(pos
+ strlen(tattooStr
));
147 if (texFilename
.length() >= charIndex
+ 2)
149 texFilename
[charIndex
] = '0' + (unsigned char) (idMakeUp
/ 10);
150 texFilename
[charIndex
+ 1] = '0' + (unsigned char) (idMakeUp
% 10);
151 im
.setTextureFileName(texFilename
, l
);
159 //-----------------------------------------------
160 // qStart is the quaterion at t=0,
161 // qEnd is the quaterion at t=TAnimEnd,
162 // time is between 0 and 1. (1=> TAnimEnd)
163 //-----------------------------------------------
164 CQuat
applyRotationFactor(CQuat in
, float rotFactor
, CQuat qStart
, CQuat qEnd
, float time
)
166 H_AUTO ( RZ_Client_Apply_Rotation_Factor
)
168 CQuat qRotTotal1
, qRotTotal2
;
170 qRotTotal1
= qEnd
*qStart
;
171 // qRotTotal2.makeClosest(CQuat::Identity);
172 qRotTotal2
= CQuat::slerp(CQuat::Identity
, qRotTotal1
, rotFactor
);
174 // apply animation factor
175 // qRotTotal1.makeClosest(CQuat::Identity);
176 // qRotTotal2.makeClosest(CQuat::Identity);
177 CQuat qRotDelta1
= CQuat::slerp(CQuat::Identity
, qRotTotal1
, time
);
178 CQuat qRotDelta2
= CQuat::slerp(CQuat::Identity
, qRotTotal2
, time
);
180 // remove normal rotation, and apply rotFactor-ed one.
182 return qRotDelta2
* qRotDelta1
* in
;
183 }// applyRotationFactor //
186 //-----------------------------------------------
187 // computeShortestAngle :
188 // Compute the angle between a source and a destination using the shortest way.
189 // \param from : angle between -Pi and Pi;
190 // \param to : angle between -Pi and Pi;
191 //-----------------------------------------------
192 double computeShortestAngle(double from
, double to
)
194 double difAngle
= to
- from
;
195 if(difAngle
< -Pi
) // Angle in the wrong direction.
197 else if(difAngle
> Pi
) // Angle in the wrong direction.
201 }// computeShortestAngle //
203 //-----------------------------------------------
205 //-----------------------------------------------
206 void readStringArray(const std::string
&filename
, NLGEORGES::UFormLoader
*formLoader
, NLMISC::CSmartPtr
<NLGEORGES::UForm
> &form
, std::map
<std::string
, std::string
> &container
)
208 // Read an array of strings
211 form
= formLoader
->loadForm(filename
.c_str());
215 const UFormElm
& rootElmt
= form
->getRootNode();
217 const UFormElm
*elmt
= 0;
218 if(rootElmt
.getNodeByName(&elmt
, "array") == false)
219 nlwarning("readStringArray: the node 'array' is not referenced.");
220 // If the array is not empty (in fact exist).
223 // Get the array size
225 elmt
->getArraySize(arraySize
);
226 // If there is at least 1 animation.
229 // Get all animation for the State.
230 for(uint i
= 0; i
<arraySize
; ++i
)
233 std::string nodeName
;
234 std::string stringName
;
235 if(elmt
->getArrayNodeName(nodeName
, i
))
237 if(elmt
->getArrayValue(stringName
, i
))
238 container
.insert(make_pair(nodeName
, stringName
));
240 nlwarning("readStringArray: no string associated to the node '%u(%s)'.", i
, nodeName
.c_str());
243 nlwarning("readStringArray: node '%u', index valid.", i
);
248 nlwarning("readStringArray: array node not allocated.");
251 nlwarning("readStringArray: cannot create the form from file '%s'.", filename
.c_str());
254 nlwarning("readStringArray: the Loader is not allocated.");
255 }// readStringArray //
257 //-----------------------------------------------
259 // Return the animset base name corresponding to the mode.
260 // \param mode : the mode to convert.
261 // \param result : this will be filed with the mode animset name.
262 // \return bool : 'true' if 'result' is filled, 'false' if left untouched.
263 //-----------------------------------------------
264 bool mode2Anim(MBEHAV::EMode mode
, string
&result
)
266 static bool init
= false;
267 static string mode2AnimArray
[MBEHAV::NUMBER_OF_MODES
];
270 uint index
= (uint
)mode
;
271 if(index
>= MBEHAV::NUMBER_OF_MODES
)
277 // Read mode2animset file
278 std::map
<std::string
, std::string
> mode2Animset
;
279 const std::string filename
= "mode2animset.string_array";
280 NLGEORGES::UFormLoader
*formLoader
= UFormLoader::createLoader();
283 NLMISC::CSmartPtr
<NLGEORGES::UForm
> form
;
284 readStringArray(filename
, formLoader
, form
, mode2Animset
);
287 nlwarning("mode2Anim: cannot create de loader");
288 NLGEORGES::UFormLoader::releaseLoader(formLoader
);
289 // Initialize the static vector.
290 //-------------------------------
291 for(uint i
=0; i
<MBEHAV::NUMBER_OF_MODES
; ++i
)
293 const std::string modeName
= MBEHAV::modeToString((MBEHAV::EMode
)i
);
294 std::map
<std::string
, std::string
>::const_iterator it
= mode2Animset
.find(modeName
);
295 if(it
!= mode2Animset
.end())
297 mode2AnimArray
[i
] = (*it
).second
;
298 if(mode2AnimArray
[i
].empty())
299 nlwarning("mode2Anim: The mode '%d(%s)' has an empty animset associated.", i
, modeName
.c_str ());
301 // No animset for the mode.
304 mode2AnimArray
[i
].clear();
305 nlwarning("mode2Anim: no animset associated to the mode %d'%s'.", i
, modeName
.c_str());
314 if(mode2AnimArray
[index
].empty())
315 result
= mode2AnimArray
[MBEHAV::NORMAL
];
317 result
= mode2AnimArray
[index
];
322 //-----------------------------------------------
324 // Compute the animation set to use according to weapons, mode and race.
325 // \param animSet : result pointer.
326 // \param mode : the mode.
327 // \param animSetBaseName : basic name to construc the complet name of the animSet.
328 // \param leftHand : animSet name for the left hand.
329 // \param rightHand : animSet name for the right hand.
330 // \param lookAtItemsInHands : compute animset according to items in hands or not.
331 // \return bool : 'true' if the new animation set is the right one. 'false' if the one choosen is not the right one.
332 //-----------------------------------------------
333 bool computeAnimSet(const CAnimationSet
*&animSet
, MBEHAV::EMode mode
, const string
&animSetBaseName
, const CItemSheet
*itemLeftHand
, const CItemSheet
*itemRightHand
, bool lookAtItemsInHands
)
335 static std::set
<std::string
> UnknownAnimSet
;
341 nlwarning("computeAnimSet: EAM not allocated -> cannot compute the anim set.");
346 string animSetRight
, animSetLeft
;
348 animSetLeft
= itemLeftHand
->getAnimSet();
351 animSetRight
= itemRightHand
->getAnimSet();
353 // Get the animset name from the mode.
355 if(!mode2Anim(mode
, result
))
357 nlwarning("computeAnimSet: unknown mode '%d'.", mode
);
362 if(lookAtItemsInHands
)
363 result
= animSetBaseName
+ "_" + result
+ "_" + animSetRight
+ "_" + animSetLeft
;
365 result
= animSetBaseName
+ "_" + result
+ "__";
368 const CAnimationSet
*animSetTmp
= EAM
->getAnimSet(result
);
371 animSet
= animSetTmp
;
374 // Bad one try something else.
377 // Up to 100 missing anim set (security).
378 if(UnknownAnimSet
.size() < 100)
380 if(UnknownAnimSet
.insert(result
).second
)
381 nlwarning("computeAnimSet: unknown Anim Set '%s' ('%u' unkowns).", result
.c_str(), UnknownAnimSet
.size());
383 // Try to compute the default one
384 result
= animSetBaseName
+ "_" + "default" + "__";
385 animSetTmp
= EAM
->getAnimSet(result
);
387 animSet
= animSetTmp
;
390 // Up to 100 missing anim set (security).
391 if(UnknownAnimSet
.size() < 100)
393 if(UnknownAnimSet
.insert(result
).second
)
394 nlwarning("computeAnimSet: unknown Anim Set '%s' ('%u' unkowns).", result
.c_str(), UnknownAnimSet
.size());
401 }// computeAnimSet //
404 //-----------------------------------------------
406 // Create a file with information to debug.
407 //-----------------------------------------------
408 void dump(const std::string
&name
)
410 // Write information to start as the version
412 if(fStart
.open(name
+ "_start.rec", false, false))
414 CVectorD currentPos
= UserEntity
->pos();
415 fStart
.serialVersion(RecordVersion
);
416 fStart
.serial(currentPos
);
421 nlwarning("dump: cannot open/create the file '%s_start.rec'.", name
.c_str());
424 IngameDbMngr
.write(name
+ "_db.rec");
427 if(f
.open(name
+ ".rec", false, false))
430 EntitiesMngr
.dump(f
);
439 nlwarning("dump: cannot open/create the file '%s.rec'.", name
.c_str());
443 if(f
.open(name
+ ".xml", false, true))
445 // Create the XML stream
448 if(output
.init (&f
, "1.0"))
450 // Open the XML Dump.
451 output
.xmlPush("XML");
454 ClientCfg
.serial(output
);
457 EntitiesMngr
.dumpXML(output
);
459 // Close the XML Dump.
462 // Flush the stream, write all the output file
466 nlwarning("dump: cannot initialize '%s.xml'.", name
.c_str());
471 nlwarning("dump: cannot open/create the file '%s.xml'.", name
.c_str());
475 //-----------------------------------------------
477 // Create a file with the current state of the client (good to report a bug).
478 //-----------------------------------------------
479 void loadDump(const std::string
&name
)
483 // Load information to start as the version
485 if(fStart
.open(name
+ "_start.rec", false))
487 fStart
.serialVersion(RecordVersion
);
488 fStart
.serial(currentPos
);
493 nlwarning("loadDump: cannot open the file '%s_start.rec'.", name
.c_str());
495 // Update the position for the vision.
496 NetMngr
.setReferencePosition(currentPos
);
498 // Select the closest continent from the new position.
499 class CDummyProgress
: public IProgressCallback
501 void progress (float /* value */) {}
503 CDummyProgress dummy
;
504 ContinentMngr
.select(currentPos
, dummy
);
507 IngameDbMngr
.read(name
+ "_db.rec");
511 if(f
.open(name
+ ".rec", false))
514 EntitiesMngr
.dump(f
);
520 nlwarning("loadDump: cannot open '%s.rec'.", name
.c_str());
524 //-----------------------------------------------
526 // Get the lod character id from the scene, according to LodCharacterName. Cached.
527 // -1 if id not found.
528 //-----------------------------------------------
529 sint
getLodCharacterId(UScene
&scene
, const string
&lodCharacterName
)
531 sint lodCharacterId
= scene
.getCLodShapeIdByName(lodCharacterName
);
532 // display a warning for bad character Id, only if name was setup in the sheet
533 if(lodCharacterId
==-1 && !lodCharacterName
.empty() )
535 // Limited to 100 missing Lod to avoid memories problems
536 if(LodCharactersNotFound
.size() < 100)
538 // Insert and display a waring
539 if(LodCharactersNotFound
.insert(lodCharacterName
).second
)
540 nlwarning("getLodCharacterId: Not found A Character LodCharacter in the Manager: %s", lodCharacterName
.c_str());
544 return lodCharacterId
;
545 }// getLodCharacterId //
548 //-----------------------------------------------
550 //-----------------------------------------------
551 CItemSheet
*getItem(const CGenderInfo
&genderInfo
, SLOTTYPE::EVisualSlot slot
)
553 CEntitySheet
*faceItem
= SheetMngr
.get(CSheetId(genderInfo
.getItemName(slot
)));
554 return (dynamic_cast <CItemSheet
*> (faceItem
));
558 //-----------------------------------------------
560 //-----------------------------------------------
561 sint
getColorIndex(const CGenderInfo
&genderInfo
, SLOTTYPE::EVisualSlot slot
)
563 CItemSheet
*is
= getItem(genderInfo
, slot
);
570 //-----------------------------------------------
571 // buildPropVisualA :
572 //-----------------------------------------------
573 SPropVisualA
buildPropVisualA(const CGenderInfo
&genderInfo
)
578 dest
.PropertySubData
.JacketModel
= SheetMngr
.getVSIndex(genderInfo
.Items
[SLOTTYPE::CHEST_SLOT
], SLOTTYPE::CHEST_SLOT
);
579 dest
.PropertySubData
.TrouserModel
= SheetMngr
.getVSIndex(genderInfo
.Items
[SLOTTYPE::LEGS_SLOT
], SLOTTYPE::LEGS_SLOT
);
580 dest
.PropertySubData
.ArmModel
= SheetMngr
.getVSIndex(genderInfo
.Items
[SLOTTYPE::ARMS_SLOT
], SLOTTYPE::ARMS_SLOT
);
581 dest
.PropertySubData
.HatModel
= SheetMngr
.getVSIndex(genderInfo
.Items
[SLOTTYPE::HEAD_SLOT
], SLOTTYPE::HEAD_SLOT
);
583 // setup colors (fixed or user)
584 sint col
= getColorIndex(genderInfo
, SLOTTYPE::CHEST_SLOT
);
585 dest
.PropertySubData
.JacketColor
= (col
== -1) ? 0 : col
;
587 col
= getColorIndex(genderInfo
, SLOTTYPE::LEGS_SLOT
);
588 dest
.PropertySubData
.TrouserColor
= (col
== -1) ? 0 : col
;
590 col
= getColorIndex(genderInfo
, SLOTTYPE::ARMS_SLOT
);
591 dest
.PropertySubData
.ArmColor
= (col
== -1) ? 0 : col
;
593 col
= getColorIndex(genderInfo
, SLOTTYPE::HEAD_SLOT
);
594 dest
.PropertySubData
.HatColor
= (col
== -1) ? 0 : col
;
596 // sheath are not used
598 }// buildPropVisualA //
601 //-----------------------------------------------
602 // buildPropVisualB :
603 //-----------------------------------------------
604 SPropVisualB
buildPropVisualB(const CGenderInfo
&genderInfo
)
610 dest
.PropertySubData
.HandsModel
= SheetMngr
.getVSIndex(genderInfo
.Items
[SLOTTYPE::HANDS_SLOT
], SLOTTYPE::HANDS_SLOT
);
611 dest
.PropertySubData
.FeetModel
= SheetMngr
.getVSIndex(genderInfo
.Items
[SLOTTYPE::FEET_SLOT
], SLOTTYPE::FEET_SLOT
);
613 // setup colors (fixed or user)
614 sint col
= getColorIndex(genderInfo
, SLOTTYPE::HANDS_SLOT
);
615 dest
.PropertySubData
.HandsColor
= (col
== -1) ? 0 : col
;
617 col
= getColorIndex(genderInfo
, SLOTTYPE::FEET_SLOT
);
618 dest
.PropertySubData
.FeetColor
= (col
== -1) ? 0 : col
;
621 }// buildPropVisualB //
626 //-----------------------------------------------
627 // isUserColorSupported :
628 // Test whether user color is supported for this equipment
629 //-----------------------------------------------
630 bool isUserColorSupported(const CPlayerSheet::CEquipment
&equip
)
633 if(!si
.buildSheetId(equip
.Item
))
636 CItemSheet
*is
= dynamic_cast<CItemSheet
*>(SheetMngr
.get(si
));
640 return is
->Color
== -1; // user color supported by the item
641 }// isUserColorSupported //
643 //-----------------------------------------------
644 // isUserColorSupported :
645 // Test whether user color is supported for a given visual slot
646 //-----------------------------------------------
647 bool isUserColorSupported(const CPlayerSheet
&playerSheet
, SLOTTYPE::EVisualSlot vs
)
651 case SLOTTYPE::CHEST_SLOT
: return isUserColorSupported(playerSheet
.Body
);
652 case SLOTTYPE::LEGS_SLOT
: return isUserColorSupported(playerSheet
.Legs
);
653 case SLOTTYPE::HEAD_SLOT
: return isUserColorSupported(playerSheet
.Head
);
654 case SLOTTYPE::ARMS_SLOT
: return isUserColorSupported(playerSheet
.Arms
);
655 case SLOTTYPE::HANDS_SLOT
: return isUserColorSupported(playerSheet
.Hands
);
656 case SLOTTYPE::FEET_SLOT
: return isUserColorSupported(playerSheet
.Feet
);
657 case SLOTTYPE::RIGHT_HAND_SLOT
: return isUserColorSupported(playerSheet
.ObjectInRightHand
);
658 case SLOTTYPE::LEFT_HAND_SLOT
: return isUserColorSupported(playerSheet
.ObjectInLeftHand
);
662 }// isUserColorSupported //
665 //-----------------------------------------------
667 //-----------------------------------------------
668 sint8
getColor(const CPlayerSheet::CEquipment
&equip
)
671 if(!si
.buildSheetId(equip
.Item
))
674 CItemSheet
*is
= dynamic_cast<CItemSheet
*>(SheetMngr
.get(si
));
678 if(is
->Color
== -1) // user color supported by the item
688 //-----------------------------------------------
689 // buildPropVisualA :
690 //-----------------------------------------------
691 SPropVisualA
buildPropVisualA(const CPlayerSheet
&playerSheet
)
695 vp
.PropertySubData
.JacketModel
= SheetMngr
.getVSIndex(playerSheet
.Body
.Item
, SLOTTYPE::CHEST_SLOT
);
696 vp
.PropertySubData
.TrouserModel
= SheetMngr
.getVSIndex(playerSheet
.Legs
.Item
, SLOTTYPE::LEGS_SLOT
);
697 vp
.PropertySubData
.ArmModel
= SheetMngr
.getVSIndex(playerSheet
.Arms
.Item
, SLOTTYPE::ARMS_SLOT
);
698 vp
.PropertySubData
.HatModel
= SheetMngr
.getVSIndex(playerSheet
.Head
.Item
, SLOTTYPE::HEAD_SLOT
);
699 vp
.PropertySubData
.WeaponRightHand
= SheetMngr
.getVSIndex(playerSheet
.ObjectInRightHand
.Item
, SLOTTYPE::RIGHT_HAND_SLOT
);
700 vp
.PropertySubData
.WeaponLeftHand
= SheetMngr
.getVSIndex(playerSheet
.ObjectInLeftHand
.Item
, SLOTTYPE::LEFT_HAND_SLOT
);
703 vp
.PropertySubData
.JacketColor
= getColor(playerSheet
.Body
);
704 vp
.PropertySubData
.TrouserColor
= getColor(playerSheet
.Legs
);
705 vp
.PropertySubData
.ArmColor
= getColor(playerSheet
.Arms
);
706 vp
.PropertySubData
.HatColor
= getColor(playerSheet
.Head
);
709 }// buildPropVisualA //
711 //-----------------------------------------------
712 // buildPropVisualB :
713 //-----------------------------------------------
714 SPropVisualB
buildPropVisualB(const CPlayerSheet
&playerSheet
)
718 vp
.PropertySubData
.Name
= 0;
719 vp
.PropertySubData
.HandsModel
= SheetMngr
.getVSIndex(playerSheet
.Hands
.Item
, SLOTTYPE::HANDS_SLOT
);
720 vp
.PropertySubData
.FeetModel
= SheetMngr
.getVSIndex(playerSheet
.Feet
.Item
, SLOTTYPE::FEET_SLOT
);
723 vp
.PropertySubData
.HandsColor
= getColor(playerSheet
.Hands
);
724 vp
.PropertySubData
.FeetColor
= getColor(playerSheet
.Feet
);
727 }// buildPropVisualB //
730 //-----------------------------------------------
732 // Draw a Box from 2 vectors.
733 //-----------------------------------------------
734 void drawBox(const CVector
&vMin
, const CVector
&vMax
, const CRGBA
&color
)
740 line
= CLine(CVector(vMin
.x
,vMin
.y
,vMin
.z
), CVector(vMax
.x
,vMin
.y
,vMin
.z
));
741 Driver
->drawLine(line
, GenericMat
);
742 line
= CLine(CVector(vMax
.x
,vMin
.y
,vMin
.z
), CVector(vMax
.x
,vMax
.y
,vMin
.z
));
743 Driver
->drawLine(line
, GenericMat
);
744 line
= CLine(CVector(vMax
.x
,vMax
.y
,vMin
.z
), CVector(vMin
.x
,vMax
.y
,vMin
.z
));
745 Driver
->drawLine(line
, GenericMat
);
746 line
= CLine(CVector(vMin
.x
,vMax
.y
,vMin
.z
), CVector(vMin
.x
,vMin
.y
,vMin
.z
));
747 Driver
->drawLine(line
, GenericMat
);
749 line
= CLine(CVector(vMin
.x
,vMin
.y
,vMax
.z
), CVector(vMax
.x
,vMin
.y
,vMax
.z
));
750 Driver
->drawLine(line
, GenericMat
);
751 line
= CLine(CVector(vMax
.x
,vMin
.y
,vMax
.z
), CVector(vMax
.x
,vMax
.y
,vMax
.z
));
752 Driver
->drawLine(line
, GenericMat
);
753 line
= CLine(CVector(vMax
.x
,vMax
.y
,vMax
.z
), CVector(vMin
.x
,vMax
.y
,vMax
.z
));
754 Driver
->drawLine(line
, GenericMat
);
755 line
= CLine(CVector(vMin
.x
,vMax
.y
,vMax
.z
), CVector(vMin
.x
,vMin
.y
,vMax
.z
));
756 Driver
->drawLine(line
, GenericMat
);
758 line
= CLine(CVector(vMin
.x
,vMin
.y
,vMin
.z
), CVector(vMin
.x
,vMin
.y
,vMax
.z
));
759 Driver
->drawLine(line
, GenericMat
);
760 line
= CLine(CVector(vMax
.x
,vMin
.y
,vMin
.z
), CVector(vMax
.x
,vMin
.y
,vMax
.z
));
761 Driver
->drawLine(line
, GenericMat
);
762 line
= CLine(CVector(vMax
.x
,vMax
.y
,vMin
.z
), CVector(vMax
.x
,vMax
.y
,vMax
.z
));
763 Driver
->drawLine(line
, GenericMat
);
764 line
= CLine(CVector(vMin
.x
,vMax
.y
,vMin
.z
), CVector(vMin
.x
,vMax
.y
,vMax
.z
));
765 Driver
->drawLine(line
, GenericMat
);
769 //-----------------------------------------------
772 //-----------------------------------------------
773 void drawSphere(const NLMISC::CVector
¢er
, float radius
, const NLMISC::CRGBA
&color
)
775 const uint numSegs
= 12;
780 for(uint face
=0;face
<3;face
++)
782 for(uint i
=0;i
<numSegs
;i
++)
784 float angStart
= float(i
*2*Pi
/numSegs
);
785 float angEnd
= float(((i
+1)%numSegs
)*2*Pi
/numSegs
);
786 line
.V0
= radius
*CVector(cosf(angStart
), sinf(angStart
), 0.f
);
787 line
.V1
= radius
*CVector(cosf(angEnd
), sinf(angEnd
), 0.f
);
790 swap(line
.V0
.x
, line
.V0
.z
);
791 swap(line
.V1
.x
, line
.V1
.z
);
795 swap(line
.V0
.y
, line
.V0
.z
);
796 swap(line
.V1
.y
, line
.V1
.z
);
800 Driver
->drawLine(line
, GenericMat
);
806 //-----------------------------------------------
808 //-----------------------------------------------
809 void getSeedsFromDB(CSeeds
&dest
)
811 CInterfaceManager
*im
=CInterfaceManager::getInstance();
812 nlctassert(sizeof(CSeeds::TUInt
) == 4); // excpect that the number of each seed type is encoded on 32 bits
813 // if this assert at compile, change the following code
814 string ls
= CWidgetManager::getInstance()->getParser()->getDefine("money_1");
815 string ms
= CWidgetManager::getInstance()->getParser()->getDefine("money_2");
816 string bs
= CWidgetManager::getInstance()->getParser()->getDefine("money_3");
817 string vbs
= CWidgetManager::getInstance()->getParser()->getDefine("money_4");
819 dest
= CSeeds(NLGUI::CDBManager::getInstance()->getDbProp(ls
)->getValue32(),
820 NLGUI::CDBManager::getInstance()->getDbProp(ms
)->getValue32(),
821 NLGUI::CDBManager::getInstance()->getDbProp(bs
)->getValue32(),
822 NLGUI::CDBManager::getInstance()->getDbProp(vbs
)->getValue32());
823 } // getSeedsFromDB //
826 //-----------------------------------------------
828 // Change a 'direction' vector.
829 // \param vectToChange : the vector to change.
830 // \param vect : new vector to use.
831 // \param compute : adjust the param 'vect' to be valid or leave the old one unchanged if impossible.
832 // \param check : warning if the param 'vect' is not valid (vector Null) even with compute=true.
833 // \return bool : 'true' if the vectToChange has been filled, else 'false'.
834 //-----------------------------------------------
835 bool setVect(CVector
&vectToChange
, const CVector
&vect
, bool compute
, bool check
)
837 // Compute the vector.
840 CVector
vectTmp(vect
);
841 // No need of the Z component for the vector.
844 if(vectTmp
!= CVector::Null
)
847 vectToChange
= vectTmp
;
850 // Bad Vector -> vectToChange remains the same
855 nlwarning("setVect: cannot compute the vector, keep the old one.");
860 // Bad Vector -> vectToChange remains the same
861 if(vect
== CVector::Null
)
865 nlwarning("setVect: param is a vector Null, vectToChange remains the same.");
869 // Set the new front vector.
874 NLMISC::CRGBA
interpClientCfgColor(const string
&src
, string
&dest
)
876 CRGBA color
= CRGBA::White
;
881 string::size_type nextPos
= src
.find('&', 1);
882 if (nextPos
!= string::npos
)
884 std::string colorCode
;
885 colorCode
.resize(nextPos
- 1);
886 for(uint k
= 0; k
< nextPos
- 1; ++k
)
888 colorCode
[k
] = tolower((char) src
[k
+ 1]); // TODO: toLowerAscii
890 std::map
<std::string
, CClientConfig::SSysInfoParam
>::const_iterator it
= ClientCfg
.SystemInfoParams
.find(colorCode
);
891 if (it
!= ClientCfg
.SystemInfoParams
.end())
893 color
= it
->second
.Color
;
895 dest
= src
.substr(nextPos
+ 1);
914 std::string
getStringCategory(const string
&src
, string
&dest
, bool alwaysAddSysByDefault
)
916 std::string str
= getStringCategoryIfAny(src
, dest
);
917 if (alwaysAddSysByDefault
)
918 return str
.empty()?"SYS":str
;
924 std::string
getStringCategoryIfAny(const string
&src
, string
&dest
)
926 std::string colorCode
;
931 // Skip <NEW> or <CHG> if present at beginning
933 const size_t PreTagSize
= 5;
934 static const string newTag
= "<NEW>";
935 if ( (src
.size() >= PreTagSize
) && (src
.substr( 0, PreTagSize
) == newTag
) )
937 startPos
= PreTagSize
;
940 static const string chgTag
= "<CHG>";
941 if ( (src
.size() >= PreTagSize
) && (src
.substr( 0, PreTagSize
) == chgTag
) )
943 startPos
= PreTagSize
;
947 if (src
[startPos
] == '&')
949 string::size_type nextPos
= src
.find('&', startPos
+1);
950 if (nextPos
!= string::npos
)
952 size_t codeSize
= nextPos
- startPos
- 1;
953 colorCode
.resize( codeSize
);
954 for(ptrdiff_t k
= 0; k
< (ptrdiff_t)codeSize
; ++k
)
956 colorCode
[k
] = tolower((char) src
[k
+ startPos
+ 1]); // TODO: toLowerAscii
960 destTmp
= preTag
; // leave <NEW> or <CHG> in the dest string
961 destTmp
+= src
.substr(nextPos
+ 1);
982 // ***************************************************************************
983 sint
ucstrnicmp(const ucstring
&s0
, uint p0
, uint n0
, const ucstring
&s1
) // OLD
986 const ucchar
*start1
= s1
.c_str();
987 uint lenS1
= (uint
)s1
.size();
988 const ucchar
*start0
= s0
.c_str();
989 uint lenS0
= (uint
)s0
.size();
999 start0
+= lenS0
; // points to '\0'
1003 lenS0
= min(lenS0
, n0
);
1005 // compare character to character
1006 while(lenS0
>0 && lenS1
>0)
1008 ucchar c0
= toLower(*start0
++);
1009 ucchar c1
= toLower(*start1
++);
1016 // return -1 if s1>s0, 1 if s0>s1, or 0 if equals
1026 // *******************************************************************************************
1027 float computeUniformNoise(const NLMISC::CNoiseValue
&nv
, const CVector
&pos
)
1029 NLMISC::OptFastFloorBegin();
1030 float value
= nv
.eval(pos
);
1031 value
= 10.f
* fmodf(value
, 0.1f
); // make repartition more uniform
1032 NLMISC::OptFastFloorEnd();
1037 // ***************************************************************************
1038 void computeCurrentFovAspectRatio(float &fov
, float &ar
)
1041 fov
= (float)(ClientCfg
.FoV
*Pi
/180.0);
1043 // get the screen aspect ratio from CFG
1044 ar
= ClientCfg
.ScreenAspectRatio
;
1046 // if Driver is not created, we can't get current screen mode
1047 if (!Driver
) return;
1049 // if windowed, must modulate aspect ratio by (WindowResolution / ScreenResolution)
1050 if(ClientCfg
.Windowed
)
1053 Driver
->getWindowSize(wndW
, wndH
);
1054 UDriver::CMode mode
;
1055 Driver
->getCurrentScreenMode(mode
);
1058 // compute window aspect ratio
1059 float arWnd
= float(wndW
) / float(wndH
);
1062 // auto mode, we are using window aspect ratio
1065 else if (mode
.Width
&& mode
.Height
)
1067 // compute screen aspect ratio
1068 float arScreen
= float(mode
.Width
) / float(mode
.Height
);
1069 ar
*= arWnd
/ arScreen
;
1073 // if fullscreen, must modulate aspect ratio by ScreenResolution
1078 UDriver::CMode mode
;
1079 Driver
->getCurrentScreenMode(mode
);
1082 ar
= float(mode
.Width
) / float(mode
.Height
);
1088 // ***************************************************************************
1089 void drawDisc(CBitmap
&dest
, float x
, float y
, float radius
, const CRGBA
&color
, bool additif
/*= false*/, uint numSegs
/*= 127*/)
1092 poly
.Vertices
.resize(numSegs
);
1093 for(uint k
= 0; k
< numSegs
; ++k
)
1095 poly
.Vertices
[k
].set(x
+ radius
* (float) cos(k
/ (float) numSegs
* 2 * Pi
), y
+ radius
* (float) sin(k
/ (float) numSegs
* 2 * Pi
));
1097 CPolygon2D::TRasterVect rasters
;
1099 poly
.computeOuterBorders(rasters
, minY
);
1100 sint maxY
= std::min((sint
) dest
.getHeight(), (sint
) rasters
.size() + minY
);
1101 for (sint y
= std::max((sint
) 0, minY
); y
< maxY
; ++y
)
1103 nlassert(y
>= 0 && y
< (sint
) dest
.getHeight());
1104 sint minX
= std::max((sint
) 0, rasters
[y
- minY
].first
);
1105 sint maxX
= std::min((sint
) dest
.getWidth(), rasters
[y
- minY
].second
);
1108 CRGBA
*pt
= (CRGBA
*) &dest
.getPixels(0)[0];
1109 pt
+= y
* dest
.getWidth() + minX
;
1110 const CRGBA
*endPt
= pt
+ (maxX
- minX
);
1115 pt
->add(*pt
, color
);
1128 // *************************************************************************************************
1129 NL3D::UScene
*getSkyScene()
1131 if (ContinentMngr
.cur() && !ContinentMngr
.cur()->Indoor
)
1133 CSky
&sky
= ContinentMngr
.cur()->CurrentSky
;
1136 return sky
.getScene();
1140 return SkyScene
; // old sky rendering
1146 // *************************************************************************************************
1147 void setEmissive(NL3D::UInstance instance
, const NLMISC::CRGBA
&color
)
1149 if (instance
.empty()) return;
1150 for(uint k
= 0; k
< instance
.getNumMaterials(); ++k
)
1152 NL3D::UInstanceMaterial mat
= instance
.getMaterial(k
);
1153 mat
.setEmissive(color
);
1157 // *************************************************************************************************
1158 void setDiffuse(NL3D::UInstance instance
, bool onOff
, const NLMISC::CRGBA
&color
)
1160 if (instance
.empty()) return;
1161 for(uint k
= 0; k
< instance
.getNumMaterials(); ++k
)
1163 NL3D::UInstanceMaterial mat
= instance
.getMaterial(k
);
1164 if (mat
.isLighted())
1169 src
= mat
.getDiffuse();
1176 NL3D::UMaterial matShape
= instance
.getShape().getMaterial(k
);
1177 src
= matShape
.getDiffuse();
1178 src
.A
= mat
.getDiffuse().A
;
1180 mat
.setDiffuse(src
);
1187 src
= mat
.getColor();
1194 NL3D::UMaterial matShape
= instance
.getShape().getMaterial(k
);
1195 src
= matShape
.getColor();
1196 src
.A
= mat
.getColor().A
;
1205 // *************************************************************************************************
1206 void makeInstanceTransparent(UInstance
&inst
, uint8 opacity
, bool disableZWrite
)
1208 UShape shape
= inst
.getShape();
1211 uint numMats
= shape
.getNumMaterials();
1214 if(numMats
!=inst
.getNumMaterials())
1217 // instance transparent or not?
1220 // reset default shape opacity / transparency
1221 inst
.setOpacity(shape
.getDefaultOpacity());
1222 inst
.setTransparency(shape
.getDefaultTransparency());
1223 inst
.setBypassLODOpacityFlag(false);
1227 // Will have some blend material => sure not to be rendered in Opaque pass
1228 inst
.setOpacity(false);
1229 inst
.setTransparency(true);
1230 inst
.setBypassLODOpacityFlag(true); // these flags prevails over the current lods flags for multi-lod objects
1233 // set all materials
1234 for (uint32 j
= 0; j
< numMats
; ++j
)
1236 NL3D::UInstanceMaterial matInst
= inst
.getMaterial(j
);
1237 NL3D::UMaterial matShape
= shape
.getMaterial(j
);
1241 matInst
.setZWrite(false);
1243 matInst
.setZWrite(matShape
.getZWrite());
1245 // if no more transparent
1249 matInst
.setBlend(matShape
.getBlend());
1250 matInst
.setBlendFunc((NL3D::UInstanceMaterial::TBlend
)matShape
.getSrcBlend(),
1251 (NL3D::UInstanceMaterial::TBlend
)matShape
.getDstBlend());
1252 // if orginal material is opaque or additif and has no alpha test, then ensure restore last tex env if needed
1253 CMaterial
*destInternalMat
= matInst
.getObjectPtr();
1254 if (!matShape
.getBlend() && !matShape
.getAlphaTest())
1256 if (destInternalMat
->getShader() == CMaterial::Normal
)
1258 CMaterial
*srcInternalMat
= matShape
.getObjectPtr();
1260 for (;numTex
< 4 && srcInternalMat
->getTexture(numTex
) != NULL
; ++numTex
) {}
1263 if (srcInternalMat
->getTexEnvMode(numTex
- 1) != destInternalMat
->getTexEnvMode(numTex
- 1))
1265 destInternalMat
->setTexEnvMode(numTex
- 1, srcInternalMat
->getTexEnvMode(numTex
- 1));
1270 if (destInternalMat
->getShader() == CMaterial::Normal
)
1272 // if !lighted, restore color
1273 if (!destInternalMat
->isLighted())
1275 CMaterial
*srcInternalMat
= matShape
.getObjectPtr();
1276 // restore alpha in color
1277 CRGBA color
= destInternalMat
->getColor();
1278 color
.A
= srcInternalMat
->getColor().A
;
1279 destInternalMat
->setColor(color
);
1286 matInst
.setBlend(true);
1287 // If default is ???/one or , then use a srcalpha/one (eg: for Diamond-like weapons)
1288 if(matShape
.getBlend() && (sint32
)matShape
.getDstBlend()==(sint32
)NL3D::UInstanceMaterial::one
)
1289 matInst
.setBlendFunc(NL3D::UInstanceMaterial::srcalpha
, NL3D::UInstanceMaterial::one
);
1290 // else use a standard srcalpha/invsrcalpha
1292 matInst
.setBlendFunc(NL3D::UInstanceMaterial::srcalpha
, NL3D::UInstanceMaterial::invsrcalpha
);
1293 // if orginal material is opaque or additif and has no alpha test, then ensure that the alpha output is 'diffuse'
1294 CMaterial
*internalMat
= matInst
.getObjectPtr();
1295 if (!matShape
.getBlend() && !matShape
.getAlphaTest())
1297 if (internalMat
->getShader() == CMaterial::Normal
)
1300 for (;numTex
< 4 && internalMat
->getTexture(numTex
) != NULL
; ++numTex
) {}
1303 internalMat
->texEnvOpAlpha(numTex
- 1, CMaterial::Replace
);
1304 // if material is unlighted, then use the constant at this stage to set the alpha
1305 internalMat
->texEnvArg0Alpha(numTex
- 1, CMaterial::Diffuse
, CMaterial::SrcAlpha
);
1309 if (internalMat
->getShader() == CMaterial::Normal
)
1311 if (!internalMat
->isLighted())
1313 // replace alpha in color
1314 CRGBA color
= internalMat
->getColor();
1316 internalMat
->setColor(color
);
1321 // suppose that default opacity is always 255
1322 if (matInst
.isLighted())
1324 matInst
.setOpacity(opacity
);
1327 matInst
.setAlphaTestThreshold(matShape
.getAlphaTestThreshold()*((float)opacity
)/255.0f
);
1331 void setVideoMode(const UDriver::CMode
&mode
)
1333 UDriver::CMode oldMode
, newMode
= mode
;
1334 oldMode
.Windowed
= true; // getCurrentScreenMode may fail if first init ...
1335 Driver
->getCurrentScreenMode(oldMode
);
1336 bool wasMaximized
= isWindowMaximized();
1337 if (!Driver
->setMode(newMode
) && !newMode
.Windowed
)
1339 // failed to switch to mode, fall back to windowed
1340 newMode
.Windowed
= true;
1341 ClientCfg
.Windowed
= true;
1342 ClientCfg
.writeInt("FullScreen", 0);
1344 // set the window mode
1345 Driver
->setMode(newMode
);
1347 bool isMaximized
= isWindowMaximized();
1348 if (oldMode
.Windowed
&& !newMode
.Windowed
) // going to fullscreen ?
1350 /*CInterfaceManager *pIM = CInterfaceManager::getInstance();
1351 pIM->movePointerAbs((sint32) mode.Width / 2, (sint32) mode.Height / 2);
1352 Driver->setMousePos(0.5f, 0.5f);*/
1354 else if ((!oldMode
.Windowed
|| wasMaximized
) && (newMode
.Windowed
&& !isMaximized
)) // leaving fullscreen ?
1356 UDriver::CMode screenMode
;
1361 if (Driver
->getCurrentScreenMode(screenMode
))
1363 // position is not saved in config so center the window
1364 posX
= (screenMode
.Width
- Driver
->getWindowWidth())/2;
1365 posY
= (screenMode
.Height
- Driver
->getWindowHeight())/2;
1368 Driver
->setWindowPos(posX
, posY
);
1372 uint
getCurrentColorDepth()
1374 if (Driver
&& Driver
->isActive())
1376 UDriver::CMode videoMode
;
1377 Driver
->getCurrentScreenMode(videoMode
);
1378 if (!videoMode
.Windowed
)
1380 return videoMode
.Depth
;
1384 return CSystemUtils::getCurrentColorDepth();
1387 bool isWindowMaximized()
1389 UDriver::CMode screenMode
;
1390 uint32 width
, height
;
1392 Driver
->getWindowSize(width
, height
);
1394 return (Driver
->getCurrentScreenMode(screenMode
) && screenMode
.Windowed
&&
1395 screenMode
.Width
== width
&& screenMode
.Height
== height
);
1398 bool getRyzomModes(std::vector
<NL3D::UDriver::CMode
> &videoModes
, std::vector
<std::string
> &stringModeList
, std::vector
<std::string
> &stringFreqList
, sint
&nFoundStringMode
, sint
&nFoundStringFreq
)
1401 nFoundStringMode
= -1;
1402 nFoundStringFreq
= -1;
1404 // mode index in original video modes
1405 sint nFoundMode
= -1;
1407 // **** Init Video Modes
1408 Driver
->getModes(videoModes
);
1410 // Remove modes under 1024x768 (outgame ui limitation) and get the unique strings
1412 for (i
= 0; i
< (sint
)videoModes
.size(); ++i
)
1414 if ((videoModes
[i
].Width
< 1024) || (videoModes
[i
].Height
< 768))
1416 videoModes
.erase(videoModes
.begin()+i
);
1421 bool bFound
= false;
1423 // create string format with width and height
1424 string res
= toString(videoModes
[i
].Width
)+" x "+toString(videoModes
[i
].Height
);
1426 // check if video mode already found in list
1427 for (j
= 0; j
< (sint
)stringModeList
.size(); ++j
)
1429 if (stringModeList
[j
] == res
)
1439 // add it to the list
1440 stringModeList
.push_back(res
);
1442 // process all screen sizes less or equal to desired one
1443 if ((videoModes
[i
].Width
<= ClientCfg
.Width
) && (videoModes
[i
].Height
<= ClientCfg
.Height
))
1445 // take first one by default
1446 if (nFoundStringMode
== -1)
1448 nFoundStringMode
= j
;
1453 // then take the largest one
1454 if ((videoModes
[i
].Width
>= videoModes
[nFoundMode
].Width
) &&
1455 (videoModes
[i
].Height
>= videoModes
[nFoundMode
].Height
))
1457 nFoundStringMode
= j
;
1466 // If no modes are available, fallback to windowed mode
1467 if (nFoundStringMode
== -1)
1469 nlwarning("Mode %ux%u not found, fall back to windowed", (uint
)ClientCfg
.Width
, (uint
)ClientCfg
.Height
);
1470 ClientCfg
.Windowed
= true;
1471 ClientCfg
.writeInt("FullScreen", 0);
1475 // add frequencies to frequencies list
1476 for (i
= 0; i
< (sint
)videoModes
.size(); ++i
)
1478 // only take exact screen sizes
1479 if (videoModes
[i
].Width
== videoModes
[nFoundMode
].Width
&& videoModes
[i
].Height
== videoModes
[nFoundMode
].Height
)
1481 uint freq
= videoModes
[i
].Frequency
;
1482 std::string freqStr
= toString(freq
);
1484 bool bFound
= false;
1486 // check if frequency already found in list
1487 for (j
= 0; j
< (sint
)stringFreqList
.size(); ++j
)
1489 if (stringFreqList
[j
] == freqStr
)
1498 // if frequency is 0, take the first one else use the exact one
1499 if (nFoundStringFreq
== -1 && ((ClientCfg
.Frequency
== 0) || (freq
== ClientCfg
.Frequency
)))
1501 nFoundStringFreq
= stringFreqList
.size();
1504 stringFreqList
.push_back(freqStr
);
1510 return nFoundStringMode
> -1;
1513 // Get float value from string. Return true if the value is relatif ( src = "+15.5" for example )
1514 bool getRelativeFloatFromString(const std::string src
, float &dst
)
1521 return fromString(src
.substr(1), dst
);
1523 fromString(src
, dst
);
1528 void updateVector(const string part
, CVector
&dst
, float value
, bool add
/* = false */)
1531 if (part
.size() > 1)
1532 p
= part
.substr(part
.size()-1, 1);