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) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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/>.
26 #include "nel/misc/path.h"
27 #include "nel/misc/matrix.h"
28 #include "nel/misc/vector.h"
29 #include "nel/misc/vectord.h"
30 #include "nel/misc/sheet_id.h"
32 #include "nel/pacs/u_primitive_block.h"
33 #include "nel/pacs/u_move_container.h"
34 #include "nel/pacs/u_move_primitive.h"
35 #include "nel/pacs/u_global_position.h"
37 #include "nel/3d/u_scene.h"
38 #include "nel/3d/u_instance.h"
41 #include "ig_callback.h"
42 #include "fix_season_data.h"
43 #include "sheet_manager.h"
46 #include "pacs_client.h"
48 #include "client_sheets/plant_sheet.h"
55 #define RZ_PRIM_ZEXT_BLOCK_AVOIDANCE 2.0f
58 H_AUTO_DECL(RZ_IGCallback
)
60 ///===================================================================================
61 CIGCallback::CIGCallback() : _MoveContainer(NULL
)
63 H_AUTO_USE(RZ_IGCallback
)
64 //nlinfo("**** YOYO: CREATING IG CALLBACK: %x", this);
67 ///===================================================================================
68 CIGCallback::~CIGCallback()
70 //nlinfo("**** YOYO: DELETING IG CALLBACK: %x", this);
71 H_AUTO_USE(RZ_IGCallback
)
75 ///===================================================================================
76 void CIGCallback::resetContainer()
78 H_AUTO_USE(RZ_IGCallback
)
79 if (!_MoveContainer
) return;
81 _MoveContainer
= NULL
;
84 ///===================================================================================
85 void CIGCallback::setMoveContainer(NLPACS::UMoveContainer
*mc
)
87 H_AUTO_USE(RZ_IGCallback
)
88 nlassert(!_MoveContainer
);
92 ///===================================================================================
93 void CIGCallback::addIG(NL3D::UInstanceGroup
*ig
)
95 H_AUTO_USE(RZ_IGCallback
)
96 nlassert(_MoveContainer
);
100 igi
= new CIGInstance(ig
, this);
101 _IGInstances
.push_back(igi
);
102 ig
->setAddRemoveInstanceCallback(igi
);
103 ig
->setTransformNameCallback(igi
);
104 ig
->setIGAddBeginCallback(igi
);
113 ///===================================================================================
114 void CIGCallback::addIGWithNumZC(NL3D::UInstanceGroup
*ig
, sint numZC
)
116 H_AUTO_USE(RZ_IGCallback
)
117 // Check the ig is valid.
121 nlassert(_MoveContainer
);
125 igi
= new CIGInstance(ig
, this);
127 _IGInstances
.push_back(igi
);
128 ig
->setAddRemoveInstanceCallback(igi
);
129 ig
->setTransformNameCallback(igi
);
130 ig
->setIGAddBeginCallback(igi
);
140 ///===================================================================================
141 void CIGCallback::addIGs(const std::vector
<NL3D::UInstanceGroup
*> &igs
)
143 H_AUTO_USE(RZ_IGCallback
)
144 _IGInstances
.reserve(_IGInstances
.size() + igs
.size());
145 for(uint k
= 0; k
< igs
.size(); ++k
)
151 //-----------------------------------------------
153 // Add a vector of instance groups with the num ZC associated.
154 //-----------------------------------------------
155 void CIGCallback::addIGsWithNumZC(std::vector
<std::pair
<NL3D::UInstanceGroup
*, sint
> > &igs
)
157 H_AUTO_USE(RZ_IGCallback
)
158 _IGInstances
.reserve(_IGInstances
.size() + igs
.size());
159 for(uint k
= 0; k
< igs
.size(); ++k
)
161 addIGWithNumZC(igs
[k
].first
, igs
[k
].second
);
166 ///===================================================================================
167 CIGCallback::CIGInstance::CIGInstance(NL3D::UInstanceGroup
*ig
, CIGCallback
*owner
)
168 : _Owner(owner
), _IG(ig
), _HasManagedFXs(false)
170 H_AUTO_USE(RZ_IGCallback
)
175 ///===================================================================================
176 CIGCallback::CIGInstance::~CIGInstance()
178 H_AUTO_USE(RZ_IGCallback
)
179 releaseMovePrimitives();
182 CTimedFXManager::getInstance().remove(_ManagedFXHandle
);
186 _IG
->setAddRemoveInstanceCallback(NULL
);
187 _IG
->setTransformNameCallback(NULL
);
188 _IG
->setIGAddBeginCallback(NULL
);
192 ///===================================================================================
193 void CIGCallback::CIGInstance::instanceGroupAdded()
195 H_AUTO_USE(RZ_IGCallback
)
196 // Check the pointer on the IG.
200 // See what objects need collisions primitives
201 if (!_MovePrimitives
.empty()) return; // already added
202 std::vector
<NLPACS::UMovePrimitive
*> addedPrims
;
203 uint numInstances
= _IG
->getNumInstance();
204 for(uint k
= 0; k
< numInstances
; ++k
)
206 TPacsPrimMap::iterator pbIt
= PacsPrims
.find(NLMISC::toLowerAscii(NLMISC::CFile::getFilenameWithoutExtension(_IG
->getShapeName(k
))));
207 if (pbIt
!= PacsPrims
.end())
209 // compute orientation and position
210 NLMISC::CMatrix instanceMatrix
;
211 _IG
->getInstanceMatrix(k
, instanceMatrix
);
214 NLMISC::CVector scale
= _IG
->getInstanceScale(k
);
215 NLPACS::UMoveContainer::getPACSCoordsFromMatrix(pos
, angle
, instanceMatrix
);
216 // insert the matching primitive block
218 _Owner
->_MoveContainer
->addCollisionnablePrimitiveBlock(pbIt
->second
, 0, 1, &addedPrims
, angle
, pos
, true, scale
);
219 // Yoyo: For each primitive, increment its height, to avoid blocking bugs
220 for(uint i
=0;i
<addedPrims
.size();i
++)
222 // do it only for obstacle, non trigerrable(?) prims
223 if(addedPrims
[i
] && addedPrims
[i
]->getObstacle() && addedPrims
[i
]->getTriggerType()==NLPACS::UMovePrimitive::NotATrigger
)
225 addedPrims
[i
]->setHeight(addedPrims
[i
]->getHeight() + RZ_PRIM_ZEXT_BLOCK_AVOIDANCE
);
229 _MovePrimitives
.insert(_MovePrimitives
.end(), addedPrims
.begin(), addedPrims
.end());
232 // update additionnal datas from sheets
235 // free mem for sheet pointers
236 NLMISC::contReset(_EntitySheets
);
237 _Owner
->notifyIGAdded(_IG
);
240 ///===================================================================================
241 void CIGCallback::CIGInstance::instanceGroupRemoved()
243 H_AUTO_USE(RZ_IGCallback
)
244 // If this zone is a ZC.
245 releaseMovePrimitives();
248 CTimedFXManager::getInstance().remove(_ManagedFXHandle
);
249 _HasManagedFXs
= false;
254 ///===================================================================================
255 void CIGCallback::CIGInstance::releaseMovePrimitives()
257 H_AUTO_USE(RZ_IGCallback
)
259 // remove all primitives from the move container
260 nlassert(_Owner
->_MoveContainer
);
261 for(TMovePrimitiveVect::iterator it
= _MovePrimitives
.begin(); it
!= _MovePrimitives
.end(); ++it
)
263 _Owner
->_MoveContainer
->removePrimitive(*it
);
265 NLMISC::contReset(_MovePrimitives
);
268 ///===================================================================================
269 void CIGCallback::deleteIGs()
271 H_AUTO_USE(RZ_IGCallback
)
272 for(TIGInstanceList::iterator it
= _IGInstances
.begin(); it
!= _IGInstances
.end(); ++it
)
276 _IGInstances
.clear();
279 ///===================================================================================
280 void CIGCallback::forceAddAll()
282 H_AUTO_USE(RZ_IGCallback
)
283 for(TIGInstanceList::iterator it
= _IGInstances
.begin(); it
!= _IGInstances
.end(); ++it
)
289 ///===================================================================================
290 void CIGCallback::CIGInstance::startAddingIG(uint numInstances
)
292 H_AUTO_USE(RZ_IGCallback
)
293 // make room for sheets ptr
294 _EntitySheets
.resize(numInstances
);
295 std::fill(_EntitySheets
.begin(), _EntitySheets
.end(), (CEntitySheet
*) NULL
);
298 ///===================================================================================
299 void CIGCallback::CIGInstance::buildSheetVector()
301 H_AUTO_USE(RZ_IGCallback
)
302 uint numInstances
= _IG
->getNumInstance();
303 _EntitySheets
.resize(numInstances
);
304 for(uint k
= 0; k
< numInstances
; ++k
)
306 _EntitySheets
[k
] = NULL
;
307 const std::string
&name
= _IG
->getInstanceName(k
);
308 if (NLMISC::nlstricmp(NLMISC::CFile::getExtension(name
), "plant") == 0)
310 NLMISC::CSheetId sheetId
;
311 if (sheetId
.buildSheetId(name
))
313 _EntitySheets
[k
] = SheetMngr
.get(sheetId
);
319 ///===================================================================================
320 void CIGCallback::CIGInstance::eraseSheetVector()
322 H_AUTO_USE(RZ_IGCallback
)
323 NLMISC::contReset(_EntitySheets
);
326 ///===================================================================================
327 std::string
CIGCallback::CIGInstance::transformName(uint instanceIndex
, const std::string
&instanceName
, const std::string
&shapeName
)
329 H_AUTO_USE(RZ_IGCallback
)
330 /** we look if there'is a matching form for this instance name
331 * If this is the case we can choose the shape we want to instanciate
334 string ext
= NLMISC::CFile::getExtension(instanceName
);
335 if (NLMISC::nlstricmp(ext
, "pacs_prim") == 0)
337 return ""; // Don't instanciate pacs_prim
339 if (NLMISC::nlstricmp(ext
, "plant") != 0)
341 return shapeName
; // if there's no attached sheet, use the shape name
343 // We cache the last id
344 static std::string lastName
;
345 static CEntitySheet
*lastSheet
= NULL
;
347 if (instanceName
== lastName
)
353 NLMISC::CSheetId sheetId
;
354 if (sheetId
.buildSheetId(instanceName
))
356 sh
= SheetMngr
.get(sheetId
);
358 lastName
= instanceName
;
365 if (!sh
) return shapeName
;
366 if (sh
->type() == CEntitySheet::PLANT
)
368 // store sheet in the sheet list
369 _EntitySheets
[instanceIndex
] = sh
;
370 return ((CPlantSheet
*) sh
)->getShapeName();
378 ///===================================================================================
379 void CIGCallback::CIGInstance::updateFromSheets()
381 H_AUTO_USE(RZ_IGCallback
)
382 nlassert(_EntitySheets
.size() == _IG
->getNumInstance());
383 // See for which objects distance should be overriden (object which use a .PLANT sheet)
384 uint numInstances
= (uint
)_EntitySheets
.size();
385 for(uint k
= 0; k
< numInstances
; ++k
)
387 if (_EntitySheets
[k
] && _EntitySheets
[k
]->Type
== CEntitySheet::PLANT
)
389 CPlantSheet
*ps
= NLMISC::safe_cast
<CPlantSheet
*>(_EntitySheets
[k
]);
390 if (ps
->getMaxDist() != -1) _IG
->setDistMax(k
, ps
->getMaxDist());
391 if (ps
->getCoarseMeshDist() != -1) _IG
->setCoarseMeshDist(k
, ps
->getCoarseMeshDist());
396 ///===================================================================================
397 void CIGCallback::CIGInstance::shutDownFXs()
399 H_AUTO_USE(RZ_IGCallback
)
400 if (!_HasManagedFXs
) return;
401 CTimedFXManager::getInstance().shutDown(_ManagedFXHandle
);
402 _HasManagedFXs
= false;
405 ///===================================================================================
406 void CIGCallback::CIGInstance::updateManagedFXs()
408 H_AUTO_USE(RZ_IGCallback
)
409 nlassert(_EntitySheets
.size() == _IG
->getNumInstance());
410 // See for which objects distance should be overriden (object which use a .PLANT sheet)
411 uint numInstances
= (uint
)_EntitySheets
.size();
412 // vector of fx that should be managed by the dedicated manager. static for malloc perf
413 static std::vector
<CTimedFX
> timedFXs
;
415 for(uint k
= 0; k
< numInstances
; ++k
)
417 if (_EntitySheets
[k
] && _EntitySheets
[k
]->Type
== CEntitySheet::PLANT
)
419 CPlantSheet
*ps
= NLMISC::safe_cast
<CPlantSheet
*>(_EntitySheets
[k
]);
420 // check for managed fxs for the current season
421 if (CurrSeason
< EGSPD::CSeason::Invalid
)
423 if (!ps
->getFXSheet(CurrSeason
).FXName
.empty())
425 timedFXs
.push_back(CTimedFX());
426 timedFXs
.back().SpawnPosition
= _IG
->getInstancePos(k
);
427 timedFXs
.back().Rot
= _IG
->getInstanceRot(k
);
428 timedFXs
.back().Scale
= _IG
->getInstanceScale(k
);
429 timedFXs
.back().FXSheet
= &ps
->getFXSheet(CurrSeason
);
434 if (!timedFXs
.empty())
436 _ManagedFXHandle
= CTimedFXManager::getInstance().add(timedFXs
, CurrSeason
);
437 _HasManagedFXs
= true;
443 ///===================================================================================
444 bool CIGCallback::enumIGs(IIGEnum
*callback
)
446 H_AUTO_USE(RZ_IGCallback
)
448 for(TIGInstanceList::iterator it
= _IGInstances
.begin(); it
!= _IGInstances
.end(); ++it
)
450 if ((*it
)->getIG() != NULL
&& (*it
)->getIG() != (NL3D::UInstanceGroup
*)-1)
452 bool res
= callback
->enumIG((*it
)->getIG());
453 if (!res
) return false;
459 ///===================================================================================
460 void CIGCallback::changeSeason()
462 H_AUTO_USE(RZ_IGCallback
)
463 // for now, this only update managed fxs so that they are displayed the same way on both clients
464 for(TIGInstanceList::iterator it
= _IGInstances
.begin(); it
!= _IGInstances
.end(); ++it
)
466 if ((*it
)->getIG() != NULL
&& (*it
)->getIG() != (NL3D::UInstanceGroup
*)-1)
468 (*it
)->buildSheetVector(); // the sheet vector is deleted after use, so need to rebuild it
469 (*it
)->shutDownFXs();
470 (*it
)->updateManagedFXs();
471 (*it
)->eraseSheetVector();
476 ///===================================================================================
477 ///===================================================================================
480 void createInstancesFromMoveContainer(NL3D::UScene
*scene
, NLPACS::UMoveContainer
*mc
, std::vector
<NL3D::UInstance
> *instances
/*=NULL*/)
482 H_AUTO_USE(RZ_IGCallback
)
485 mc
->evalCollision(0.1f
, 0);
486 // get the primitives
487 std::vector
<const NLPACS::UMovePrimitive
*> prims
;
488 mc
->getPrimitives(prims
);
491 instances
->reserve(prims
.size());
493 for(uint k
= 0; k
< prims
.size(); ++k
)
495 NL3D::UInstance newInstance
;
497 NLMISC::CVector scale
;
498 switch(prims
[k
]->getPrimitiveType())
500 case NLPACS::UMovePrimitive::_2DOrientedBox
:
501 newInstance
= scene
->createInstance("unit_box.shape");
502 angle
= (float) prims
[k
]->getOrientation(prims
[k
]->getFirstWorldImageV());
503 prims
[k
]->getSize(scale
.x
, scale
.y
);
505 case NLPACS::UMovePrimitive::_2DOrientedCylinder
:
506 newInstance
= scene
->createInstance("unit_cylinder.shape");
507 scale
.x
= scale
.y
= prims
[k
]->getRadius();
510 nlwarning("createInstancesFromMoveContainer : unsupported type encountered");
514 if (!newInstance
.empty())
516 scale
.z
= prims
[k
]->getHeight();
517 NLMISC::CVectorD worldPos
= prims
[k
]->getFinalPosition(prims
[k
]->getFirstWorldImageV());
518 newInstance
.setPos(NLMISC::CVector((float) worldPos
.x
, (float) worldPos
.y
, (float) worldPos
.z
));
519 newInstance
.setScale(scale
);
520 newInstance
.setRotQuat(NLMISC::CQuat(NLMISC::CVector::K
, angle
));
523 instances
->push_back(newInstance
);
528 nlwarning("createInstancesFromMoveContainer : couldn't create shape");