khanat-opennel-code/code/nel/src/3d/skeleton_spawn_script.cpp

283 lines
7.8 KiB
C++

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// 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 <http://www.gnu.org/licenses/>.
#include "std3d.h"
#include "nel/3d/skeleton_spawn_script.h"
#include "nel/3d/skeleton_model.h"
#include "nel/3d/mesh_base_instance.h"
#include "nel/3d/scene.h"
#include "nel/3d/particle_system_model.h"
#include "nel/misc/common.h"
#include "nel/misc/algo.h"
using namespace std;
using namespace NLMISC;
namespace NL3D
{
// ***************************************************************************
CSkeletonSpawnScript::CSkeletonSpawnScript()
{
}
// ***************************************************************************
CSkeletonSpawnScript::~CSkeletonSpawnScript()
{
// the user has not called release
nlassert(_Instances.empty());
}
// ***************************************************************************
void CSkeletonSpawnScript::evaluate(CSkeletonModel *skeleton)
{
// if same cache, don't need to update parsed instances
if(_Cache!=skeleton->getSpawnScript())
{
_Cache=skeleton->getSpawnScript();
parseCache(skeleton->getOwnerScene(), skeleton);
}
// each frame, update PS UserMatrix
for(uint i=0;i<_Instances.size();i++)
{
CInstance &inst= _Instances[i];
if(inst.Model && inst.PS)
{
CMatrix userMat;
userMat.setRot(CVector::I, skeleton->getSSSWODir(), CVector::K);
userMat.normalize(CMatrix::YZX);
userMat.setPos(skeleton->getSSSWOPos());
inst.PS->setUserMatrix(userMat);
}
}
}
// ***************************************************************************
void CSkeletonSpawnScript::release(CScene *scene)
{
// act as if the skeleton has an empty script
if(!_Cache.empty())
{
_Cache.clear();
parseCache(scene, NULL);
}
}
// ***************************************************************************
void CSkeletonSpawnScript::parseCache(CScene *scene, CSkeletonModel *skeleton)
{
uint i;
nlassert(scene);
nlassert(_Cache.empty() || skeleton);
static std::vector<std::string> newLines;
newLines.clear();
splitString(_Cache,"\n",newLines);
// **** compare the 2 set of script line to know what to add, and what to remove.
// NB: this is an O(N2), but the number of spawned objects should be small (0, 1 or 2 surely)
static std::vector<bool> srcToRemove;
static std::vector<bool> dstToAdd;
srcToRemove.clear();
dstToAdd.clear();
srcToRemove.resize(_Instances.size(), true);
dstToAdd.resize(newLines.size(), true);
for(uint i=0;i<_Instances.size();i++)
{
for(uint j=0;j<newLines.size();j++)
{
// if the new line script is the same than the old one, then reuse!
// NB: the script line contains an "instance number", so there is no risk of "reuse same twice"
if(newLines[j] == _Instances[i].ScriptLine)
{
srcToRemove[i]= false;
dstToAdd[j]= false;
}
}
}
// **** remove the no more used instances
vector<CInstance>::iterator it= _Instances.begin();
for(i=0;i<srcToRemove.size();i++)
{
// if must remove this entry
if(srcToRemove[i])
{
// then erase the entry
if(it->Model)
scene->deleteInstance(it->Model);
it= _Instances.erase(it);
}
else
it++;
}
// **** create the new instances
for(i=0;i<dstToAdd.size();i++)
{
// if not reused
if(dstToAdd[i])
{
// parse the line
const std::string &line= newLines[i];
static std::vector<string> words;
words.clear();
splitString(line, " ", words);
if(words.size()>=3)
{
// command
if(words[0]=="objw")
{
// format: "objw inst shapeName"
// inst is a number only used to generate line difference (for cache comparison)
_Instances.push_back(CInstance());
CInstance &inst= _Instances.back();
inst.ScriptLine= line;
// Delay the model creation at end of CScene::render()
CSSSModelRequest req;
req.Skel= skeleton;
req.InstanceId= (uint)_Instances.size()-1;
req.Shape= words[2];
// World Spawned Objects are sticked to the root bone (for CLod hiding behavior)
req.BoneId= 0;
// but have a special feature to compute their world matrix
req.SSSWO= true;
addModelCreationRequest(req, scene);
}
else if(words[0]=="objl" && words.size()>=4)
{
// format: "objl inst shapeName boneName"
// inst is a number only used to generate line difference (for cache comparison)
// get the bone name, but may have space in its name => words[3] is not the correct name
uint pos= 0;
bool inWord= false;
uint wordId= 0;
// skip first spaces
while(pos<line.size() && line[pos]==' ')
pos++;
// count word until reach
while(pos<line.size())
{
if(line[pos]!=' ')
{
if(!inWord)
{
inWord= true;
wordId++;
// reach bone name! stop
if(wordId==4)
break;
}
}
else
{
inWord= false;
}
pos++;
}
// and get the bone name
string boneName= line.substr(pos);
// create
_Instances.push_back(CInstance());
CInstance &inst= _Instances.back();
inst.ScriptLine= line;
// try to get the bone id from name
sint32 boneId= skeleton->getBoneIdByName(boneName);
// if fails, then don't create the instance
if(boneId>=0)
{
// Delay the model creation at end of CScene::render()
CSSSModelRequest req;
req.Skel= skeleton;
req.InstanceId= (uint)_Instances.size()-1;
req.Shape= words[2];
req.BoneId= boneId;
req.SSSWO= false;
addModelCreationRequest(req, scene);
}
else
{
// avoid flooding (animation)
#ifdef NL_DEBUG
nlwarning("ERROR: SkeletonSpawnScript: boneId not found in: %s", line.c_str());
#endif
}
}
// unknown command
else
{
// avoid flooding (animation)
#ifdef NL_DEBUG
nlwarning("ERROR: SkeletonSpawnScript: error in command: %s", line.c_str());
#endif
}
}
}
}
}
// ***************************************************************************
void CSkeletonSpawnScript::addModelCreationRequest(CSSSModelRequest &req, CScene *scene)
{
// if the scene is currently rendering, then process the request at end of CScene::render()
if(scene->isRendering())
scene->addSSSModelRequest(req);
// else process it now!
else
req.execute();
}
// ***************************************************************************
void CSSSModelRequest::execute()
{
// If skeleton still exist
if(!Skel)
return;
CSkeletonSpawnScript &sss= Skel->getSSSScript();
CScene *scene= Skel->getOwnerScene();
// test validity of the instanceId
if(InstanceId>=sss._Instances.size())
return;
// it must not have been already created (else error???)
if(sss._Instances[InstanceId].Model)
return;
// OK, create the model
CSkeletonSpawnScript::CInstance &inst= sss._Instances[InstanceId];
inst.Model= scene->createInstance(Shape);
inst.PS= dynamic_cast<CParticleSystemModel*>((CTransformShape*)inst.Model);
if(inst.Model)
{
Skel->stickObject(inst.Model, BoneId);
inst.Model->setSSSWO(SSSWO);
}
}
} // NL3D