// Ryzom - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "stdpch.h" #include "nel/misc/path.h" #include "nel/misc/matrix.h" #include "nel/misc/vector.h" #include "nel/misc/vectord.h" #include "nel/misc/sheet_id.h" #include "nel/pacs/u_primitive_block.h" #include "nel/pacs/u_move_container.h" #include "nel/pacs/u_move_primitive.h" #include "nel/pacs/u_global_position.h" #include "nel/3d/u_scene.h" #include "nel/3d/u_instance.h" #include "ig_callback.h" #include "fix_season_data.h" #include "sheet_manager.h" #include "ig_enum.h" #include "weather.h" #include "pacs_client.h" // Client Sheets #include "client_sheets/plant_sheet.h" #include #define RZ_PRIM_ZEXT_BLOCK_AVOIDANCE 2.0f H_AUTO_DECL(RZ_IGCallback) ///=================================================================================== CIGCallback::CIGCallback() : _MoveContainer(NULL) { H_AUTO_USE(RZ_IGCallback) //nlinfo("**** YOYO: CREATING IG CALLBACK: %x", this); } ///=================================================================================== CIGCallback::~CIGCallback() { //nlinfo("**** YOYO: DELETING IG CALLBACK: %x", this); H_AUTO_USE(RZ_IGCallback) deleteIGs(); } ///=================================================================================== void CIGCallback::resetContainer() { H_AUTO_USE(RZ_IGCallback) if (!_MoveContainer) return; deleteIGs(); _MoveContainer = NULL; } ///=================================================================================== void CIGCallback::setMoveContainer(NLPACS::UMoveContainer *mc) { H_AUTO_USE(RZ_IGCallback) nlassert(!_MoveContainer); _MoveContainer = mc; } ///=================================================================================== void CIGCallback::addIG(NL3D::UInstanceGroup *ig) { H_AUTO_USE(RZ_IGCallback) nlassert(_MoveContainer); CIGInstance *igi; try { igi = new CIGInstance(ig, this); _IGInstances.push_back(igi); ig->setAddRemoveInstanceCallback(igi); ig->setTransformNameCallback(igi); ig->setIGAddBeginCallback(igi); } catch(...) { delete igi; throw; } } ///=================================================================================== void CIGCallback::addIGWithNumZC(NL3D::UInstanceGroup *ig, sint numZC) { H_AUTO_USE(RZ_IGCallback) // Check the ig is valid. if(ig == 0) return; nlassert(_MoveContainer); CIGInstance *igi; try { igi = new CIGInstance(ig, this); igi->numZC(numZC); _IGInstances.push_back(igi); ig->setAddRemoveInstanceCallback(igi); ig->setTransformNameCallback(igi); ig->setIGAddBeginCallback(igi); } catch(...) { delete igi; throw; } } ///=================================================================================== void CIGCallback::addIGs(const std::vector &igs) { H_AUTO_USE(RZ_IGCallback) _IGInstances.reserve(_IGInstances.size() + igs.size()); for(uint k = 0; k < igs.size(); ++k) { addIG(igs[k]); } } //----------------------------------------------- // addIGsWithNumZC : // Add a vector of instance groups with the num ZC associated. //----------------------------------------------- void CIGCallback::addIGsWithNumZC(std::vector > &igs) { H_AUTO_USE(RZ_IGCallback) _IGInstances.reserve(_IGInstances.size() + igs.size()); for(uint k = 0; k < igs.size(); ++k) { addIGWithNumZC(igs[k].first, igs[k].second); } } ///=================================================================================== CIGCallback::CIGInstance::CIGInstance(NL3D::UInstanceGroup *ig, CIGCallback *owner) : _Owner(owner), _IG(ig), _HasManagedFXs(false) { H_AUTO_USE(RZ_IGCallback) nlassert(owner); nlassert(ig); } ///=================================================================================== CIGCallback::CIGInstance::~CIGInstance() { H_AUTO_USE(RZ_IGCallback) releaseMovePrimitives(); if (_HasManagedFXs) { CTimedFXManager::getInstance().remove(_ManagedFXHandle); } if (_IG) { _IG->setAddRemoveInstanceCallback(NULL); _IG->setTransformNameCallback(NULL); _IG->setIGAddBeginCallback(NULL); } } ///=================================================================================== void CIGCallback::CIGInstance::instanceGroupAdded() { H_AUTO_USE(RZ_IGCallback) // Check the pointer on the IG. nlassert(_IG); nlassert(_Owner); // See what objects need collisions primitives if (!_MovePrimitives.empty()) return; // already added std::vector addedPrims; uint numInstances = _IG->getNumInstance(); for(uint k = 0; k < numInstances; ++k) { TPacsPrimMap::iterator pbIt = PacsPrims.find(NLMISC::strlwr(NLMISC::CFile::getFilenameWithoutExtension(_IG->getShapeName(k)))); if (pbIt != PacsPrims.end()) { // compute orientation and position NLMISC::CMatrix instanceMatrix; _IG->getInstanceMatrix(k, instanceMatrix); NLMISC::CVector pos; float angle; NLMISC::CVector scale = _IG->getInstanceScale(k); NLPACS::UMoveContainer::getPACSCoordsFromMatrix(pos, angle, instanceMatrix); // insert the matching primitive block addedPrims.clear(); _Owner->_MoveContainer->addCollisionnablePrimitiveBlock(pbIt->second, 0, 1, &addedPrims, angle, pos, true, scale); // Yoyo: For each primitive, increment its height, to avoid blocking bugs for(uint i=0;igetObstacle() && addedPrims[i]->getTriggerType()==NLPACS::UMovePrimitive::NotATrigger) { addedPrims[i]->setHeight(addedPrims[i]->getHeight() + RZ_PRIM_ZEXT_BLOCK_AVOIDANCE); } } // for future remove _MovePrimitives.insert(_MovePrimitives.end(), addedPrims.begin(), addedPrims.end()); } } // update additionnal datas from sheets updateFromSheets(); updateManagedFXs(); // free mem for sheet pointers NLMISC::contReset(_EntitySheets); _Owner->notifyIGAdded(_IG); } ///=================================================================================== void CIGCallback::CIGInstance::instanceGroupRemoved() { H_AUTO_USE(RZ_IGCallback) // If this zone is a ZC. releaseMovePrimitives(); if (_HasManagedFXs) { CTimedFXManager::getInstance().remove(_ManagedFXHandle); _HasManagedFXs = false; } } ///=================================================================================== void CIGCallback::CIGInstance::releaseMovePrimitives() { H_AUTO_USE(RZ_IGCallback) nlassert(_Owner); // remove all primitives from the move container nlassert(_Owner->_MoveContainer); for(TMovePrimitiveVect::iterator it = _MovePrimitives.begin(); it != _MovePrimitives.end(); ++it) { _Owner->_MoveContainer->removePrimitive(*it); } NLMISC::contReset(_MovePrimitives); } ///=================================================================================== void CIGCallback::deleteIGs() { H_AUTO_USE(RZ_IGCallback) for(TIGInstanceList::iterator it = _IGInstances.begin(); it != _IGInstances.end(); ++it) { delete *it; } _IGInstances.clear(); } ///=================================================================================== void CIGCallback::forceAddAll() { H_AUTO_USE(RZ_IGCallback) for(TIGInstanceList::iterator it = _IGInstances.begin(); it != _IGInstances.end(); ++it) { (*it)->forceAdd(); } } ///=================================================================================== void CIGCallback::CIGInstance::startAddingIG(uint numInstances) { H_AUTO_USE(RZ_IGCallback) // make room for sheets ptr _EntitySheets.resize(numInstances); std::fill(_EntitySheets.begin(), _EntitySheets.end(), (CEntitySheet *) NULL); } ///=================================================================================== void CIGCallback::CIGInstance::buildSheetVector() { H_AUTO_USE(RZ_IGCallback) uint numInstances = _IG->getNumInstance(); _EntitySheets.resize(numInstances); for(uint k = 0; k < numInstances; ++k) { _EntitySheets[k] = NULL; const std::string &name = _IG->getInstanceName(k); if (NLMISC::nlstricmp(NLMISC::CFile::getExtension(name), "plant") == 0) { NLMISC::CSheetId sheetId; if (sheetId.buildSheetId(name)) { _EntitySheets[k] = SheetMngr.get(sheetId); } } } } ///=================================================================================== void CIGCallback::CIGInstance::eraseSheetVector() { H_AUTO_USE(RZ_IGCallback) NLMISC::contReset(_EntitySheets); } ///=================================================================================== std::string CIGCallback::CIGInstance::transformName(uint instanceIndex, const std::string &instanceName, const std::string &shapeName) { H_AUTO_USE(RZ_IGCallback) /** we look if there'is a matching form for this instance name * If this is the case we can choose the shape we want to instanciate */ string ext = NLMISC::CFile::getExtension(instanceName); if (NLMISC::nlstricmp(ext, "pacs_prim") == 0) { return ""; // Don't instanciate pacs_prim } if (NLMISC::nlstricmp(ext, "plant") != 0) { return shapeName; // if there's no attached sheet, use the shape name } // We cache the last id static std::string lastName; static CEntitySheet *lastSheet = NULL; CEntitySheet *sh; if (instanceName == lastName) { sh = lastSheet; } else { NLMISC::CSheetId sheetId; if (sheetId.buildSheetId(instanceName)) { sh = SheetMngr.get(sheetId); lastSheet = sh; lastName = instanceName; } else { return shapeName; } } if (!sh) return shapeName; if (sh->type() == CEntitySheet::PLANT) { // store sheet in the sheet list _EntitySheets[instanceIndex] = sh; return ((CPlantSheet *) sh)->getShapeName(); } else { return shapeName; } } ///=================================================================================== void CIGCallback::CIGInstance::updateFromSheets() { H_AUTO_USE(RZ_IGCallback) nlassert(_EntitySheets.size() == _IG->getNumInstance()); // See for which objects distance should be overriden (object which use a .PLANT sheet) uint numInstances = (uint)_EntitySheets.size(); for(uint k = 0; k < numInstances; ++k) { if (_EntitySheets[k] && _EntitySheets[k]->Type == CEntitySheet::PLANT) { CPlantSheet *ps = NLMISC::safe_cast(_EntitySheets[k]); if (ps->getMaxDist() != -1) _IG->setDistMax(k, ps->getMaxDist()); if (ps->getCoarseMeshDist() != -1) _IG->setCoarseMeshDist(k, ps->getCoarseMeshDist()); } } } ///=================================================================================== void CIGCallback::CIGInstance::shutDownFXs() { H_AUTO_USE(RZ_IGCallback) if (!_HasManagedFXs) return; CTimedFXManager::getInstance().shutDown(_ManagedFXHandle); _HasManagedFXs = false; } ///=================================================================================== void CIGCallback::CIGInstance::updateManagedFXs() { H_AUTO_USE(RZ_IGCallback) nlassert(_EntitySheets.size() == _IG->getNumInstance()); // See for which objects distance should be overriden (object which use a .PLANT sheet) uint numInstances = (uint)_EntitySheets.size(); // vector of fx that should be managed by the dedicated manager. static for malloc perf static std::vector timedFXs; timedFXs.clear(); for(uint k = 0; k < numInstances; ++k) { if (_EntitySheets[k] && _EntitySheets[k]->Type == CEntitySheet::PLANT) { CPlantSheet *ps = NLMISC::safe_cast(_EntitySheets[k]); // check for managed fxs for the current season if (CurrSeason < EGSPD::CSeason::Invalid) { if (!ps->getFXSheet(CurrSeason).FXName.empty()) { timedFXs.push_back(CTimedFX()); timedFXs.back().SpawnPosition = _IG->getInstancePos(k); timedFXs.back().Rot = _IG->getInstanceRot(k); timedFXs.back().Scale = _IG->getInstanceScale(k); timedFXs.back().FXSheet = &ps->getFXSheet(CurrSeason); } } } } if (!timedFXs.empty()) { _ManagedFXHandle = CTimedFXManager::getInstance().add(timedFXs, CurrSeason); _HasManagedFXs = true; } } ///=================================================================================== bool CIGCallback::enumIGs(IIGEnum *callback) { H_AUTO_USE(RZ_IGCallback) nlassert(callback); for(TIGInstanceList::iterator it = _IGInstances.begin(); it != _IGInstances.end(); ++it) { if ((*it)->getIG() != NULL && (*it)->getIG() != (NL3D::UInstanceGroup *)-1) { bool res = callback->enumIG((*it)->getIG()); if (!res) return false; } } return true; } ///=================================================================================== void CIGCallback::changeSeason() { H_AUTO_USE(RZ_IGCallback) // for now, this only update managed fxs so that they are displayed the same way on both clients for(TIGInstanceList::iterator it = _IGInstances.begin(); it != _IGInstances.end(); ++it) { if ((*it)->getIG() != NULL && (*it)->getIG() != (NL3D::UInstanceGroup *)-1) { (*it)->buildSheetVector(); // the sheet vector is deleted after use, so need to rebuild it (*it)->shutDownFXs(); (*it)->updateManagedFXs(); (*it)->eraseSheetVector(); } } } ///=================================================================================== ///=================================================================================== void createInstancesFromMoveContainer(NL3D::UScene *scene, NLPACS::UMoveContainer *mc, std::vector *instances /*=NULL*/) { H_AUTO_USE(RZ_IGCallback) nlassert(scene); nlassert(mc); mc->evalCollision(0.1f, 0); // get the primitives std::vector prims; mc->getPrimitives(prims); if (instances) { instances->reserve(prims.size()); } for(uint k = 0; k < prims.size(); ++k) { NL3D::UInstance newInstance; float angle = 0.f; NLMISC::CVector scale; switch(prims[k]->getPrimitiveType()) { case NLPACS::UMovePrimitive::_2DOrientedBox: newInstance = scene->createInstance("unit_box.shape"); angle = (float) prims[k]->getOrientation(prims[k]->getFirstWorldImageV()); prims[k]->getSize(scale.x, scale.y); break; case NLPACS::UMovePrimitive::_2DOrientedCylinder: newInstance = scene->createInstance("unit_cylinder.shape"); scale.x = scale.y = prims[k]->getRadius(); break; default: nlwarning("createInstancesFromMoveContainer : unsupported type encountered"); continue; break; } if (!newInstance.empty()) { scale.z = prims[k]->getHeight(); NLMISC::CVectorD worldPos = prims[k]->getFinalPosition(prims[k]->getFirstWorldImageV()); newInstance.setPos(NLMISC::CVector((float) worldPos.x, (float) worldPos.y, (float) worldPos.z)); newInstance.setScale(scale); newInstance.setRotQuat(NLMISC::CQuat(NLMISC::CVector::K, angle)); if (instances) { instances->push_back(newInstance); } } else { nlwarning("createInstancesFromMoveContainer : couldn't create shape"); } } }