// 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 = _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 = _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");
}
}
}