mirror of
https://port.numenaute.org/aleajactaest/khanat-opennel-code.git
synced 2024-12-15 22:08:43 +00:00
581 lines
16 KiB
C++
581 lines
16 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/scene_group.h"
|
|
#include "nel/3d/seg_remanence.h"
|
|
#include "nel/3d/seg_remanence_shape.h"
|
|
#include "nel/3d/vertex_buffer.h"
|
|
#include "nel/3d/driver.h"
|
|
#include "nel/3d/scene.h"
|
|
#include "nel/3d/anim_detail_trav.h"
|
|
#include "nel/3d/skeleton_model.h"
|
|
#include "nel/3d/dru.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace NL3D
|
|
{
|
|
|
|
|
|
CVertexBuffer CSegRemanence::_VB;
|
|
CIndexBuffer CSegRemanence::_IB;
|
|
|
|
|
|
/// TODO : put this in a header (same code in ps_ribbon_base.cpp ..)
|
|
static inline void BuildHermiteVector(const NLMISC::CVector &P0,
|
|
const NLMISC::CVector &P1,
|
|
const NLMISC::CVector &T0,
|
|
const NLMISC::CVector &T1,
|
|
NLMISC::CVector &dest,
|
|
float lambda
|
|
)
|
|
{
|
|
const float lambda2 = lambda * lambda;
|
|
const float lambda3 = lambda2 * lambda;
|
|
const float h1 = 2 * lambda3 - 3 * lambda2 + 1;
|
|
const float h2 = - 2 * lambda3 + 3 * lambda2;
|
|
const float h3 = lambda3 - 2 * lambda2 + lambda;
|
|
const float h4 = lambda3 - lambda2;
|
|
/// just avoid some ctor calls here...
|
|
dest.set (h1 * P0.x + h2 * P1.x + h3 * T0.x + h4 * T1.x,
|
|
h1 * P0.y + h2 * P1.y + h3 * T0.y + h4 * T1.y,
|
|
h1 * P0.z + h2 * P1.z + h3 * T0.z + h4 * T1.z);
|
|
|
|
}
|
|
|
|
|
|
/// for test
|
|
static inline void BuildLinearVector(const NLMISC::CVector &P0,
|
|
const NLMISC::CVector &P1,
|
|
NLMISC::CVector &dest,
|
|
float lambda,
|
|
float oneMinusLambda
|
|
)
|
|
{
|
|
NL_PS_FUNC(BuildLinearVector)
|
|
dest.set (lambda * P1.x + oneMinusLambda * P0.x,
|
|
lambda * P1.y + oneMinusLambda * P0.y,
|
|
lambda * P1.z + oneMinusLambda * P0.z);
|
|
}
|
|
|
|
|
|
|
|
//===============================================================
|
|
CSegRemanence::CSegRemanence() : _NumSlices(0),
|
|
_Started(false),
|
|
_Stopping(false),
|
|
_Restarted(false),
|
|
_UnrollRatio(0),
|
|
_SliceTime(0.05f),
|
|
_AniMat(NULL),
|
|
_LastSampleFrame(0)
|
|
{
|
|
IAnimatable::resize(AnimValueLast);
|
|
|
|
// RenderFilter: We are a SegRemanece
|
|
_RenderFilterType= UScene::FilterSegRemanence;
|
|
}
|
|
|
|
//===============================================================
|
|
CSegRemanence::~CSegRemanence()
|
|
{
|
|
delete _AniMat;
|
|
// Auto detach me from skeleton. Must do it here, not in ~CTransform().
|
|
if(_FatherSkeletonModel)
|
|
{
|
|
// detach me from the skeleton.
|
|
// hrc and clip hierarchy is modified.
|
|
_FatherSkeletonModel->detachSkeletonSon(this);
|
|
nlassert(_FatherSkeletonModel==NULL);
|
|
}
|
|
}
|
|
|
|
//===============================================================
|
|
CSegRemanence::CSegRemanence(CSegRemanence &other) : CTransformShape(other), _AniMat(NULL)
|
|
{
|
|
copyFromOther(other);
|
|
}
|
|
|
|
//===============================================================
|
|
CSegRemanence &CSegRemanence::operator = (CSegRemanence &other)
|
|
{
|
|
if (this != &other)
|
|
{
|
|
(CTransformShape &) *this = (CTransformShape &) other; // copy base
|
|
copyFromOther(other);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
//===============================================================
|
|
void CSegRemanence::copyFromOther(CSegRemanence &other)
|
|
{
|
|
if (this == &other) return;
|
|
|
|
CAnimatedMaterial *otherMat = other._AniMat != NULL ? new CAnimatedMaterial(*other._AniMat)
|
|
: NULL;
|
|
delete _AniMat;
|
|
_AniMat = otherMat;
|
|
std::copy(other._Samples, other._Samples + 4, _Samples);
|
|
_HeadSample = other._HeadSample;
|
|
_HeadProgress = other._HeadProgress;
|
|
//
|
|
_Pos = other._Pos; // sampled positions at each extremities of segment
|
|
_NumSlices = other._NumSlices;
|
|
_NumCorners = other._NumCorners;
|
|
_Started = other._Started;
|
|
_Stopping = other._Stopping;
|
|
_Restarted = other._Restarted;
|
|
_StartDate = other._StartDate;
|
|
_CurrDate = other._CurrDate;
|
|
_UnrollRatio = other._UnrollRatio;
|
|
_SliceTime = other._SliceTime;
|
|
_LastSampleFrame = other._LastSampleFrame;
|
|
}
|
|
|
|
//===============================================================
|
|
void CSegRemanence::registerBasic()
|
|
{
|
|
CScene::registerModel(SegRemanenceShapeId, TransformShapeId, CSegRemanence::creator);
|
|
}
|
|
|
|
|
|
|
|
// helper functions to fill vb
|
|
static inline void vbPush(uint8 *&dest, const CVector &v)
|
|
{
|
|
*(CVector *) dest = v;
|
|
dest +=sizeof(CVector);
|
|
}
|
|
|
|
static inline void vbPush(uint8 *&dest, float f)
|
|
{
|
|
*(float *) dest = f;
|
|
dest +=sizeof(float);
|
|
}
|
|
|
|
//===============================================================
|
|
void CSegRemanence::render(IDriver *drv, CMaterial &mat)
|
|
{
|
|
nlassert(_NumSlices >= 2);
|
|
nlassert(_NumCorners >= 2);
|
|
CSegRemanenceShape *srs = NLMISC::safe_cast<CSegRemanenceShape *>((IShape *) Shape);
|
|
// resize before locking because of volatile vb
|
|
_VB.setPreferredMemory(CVertexBuffer::AGPVolatile, false);
|
|
_VB.setVertexFormat(CVertexBuffer::PositionFlag|CVertexBuffer::TexCoord0Flag);
|
|
_VB.setNumVertices(_NumCorners * (_NumSlices + 1));
|
|
const uint vertexSize = _VB.getVertexSize();
|
|
// Fill Vertex Buffer part
|
|
{
|
|
CVertexBufferReadWrite vba;
|
|
_VB.lock (vba);
|
|
uint8 *datas = (uint8 *) vba.getVertexCoordPointer();
|
|
const uint8 *endDatas = datas + vertexSize *_VB.getNumVertices();
|
|
//
|
|
const CVector *src = &_Pos[0];
|
|
|
|
float deltaV = 1.f / (_NumCorners - 1);
|
|
// first slice
|
|
for(uint k = 0; k < _NumCorners; ++k)
|
|
{
|
|
vbPush(datas, *src++);
|
|
vbPush(datas, 0.f); // U
|
|
vbPush(datas, k * deltaV); // V
|
|
nlassert(datas <= endDatas);
|
|
}
|
|
|
|
float deltaU = 1.f / _NumSlices;
|
|
float baseU = _HeadProgress * deltaU;
|
|
|
|
for (uint l = 1; l < _NumSlices; ++l)
|
|
{
|
|
float currU = baseU + (l - 1) * deltaU;
|
|
for(uint k = 0; k < _NumCorners; ++k)
|
|
{
|
|
vbPush(datas, *src++);
|
|
vbPush(datas, currU); // U
|
|
vbPush(datas, k * deltaV); // V
|
|
nlassert(datas <= endDatas);
|
|
}
|
|
}
|
|
// last slice
|
|
const CVector *prevRow = src - _NumCorners;
|
|
for(uint k = 0; k < _NumCorners; ++k)
|
|
{
|
|
vbPush(datas, (1.f - _HeadProgress) * *src + _HeadProgress * *prevRow);
|
|
++ src;
|
|
++ prevRow;
|
|
vbPush(datas, 1.f); // U
|
|
vbPush(datas, k * deltaV); // V
|
|
nlassert(datas <= endDatas);
|
|
}
|
|
}
|
|
//
|
|
uint numQuads = (_NumCorners - 1) * _NumSlices;
|
|
// Fill Index Buffer part
|
|
{
|
|
_IB.setPreferredMemory(CIndexBuffer::RAMVolatile, false);
|
|
_IB.setFormat(CIndexBuffer::Indices16);
|
|
_IB.setNumIndexes(numQuads * 6);
|
|
//
|
|
CIndexBufferReadWrite iba;
|
|
_IB.lock(iba);
|
|
uint16 *indexPtr = (uint16 *) iba.getPtr();
|
|
|
|
for (uint l = 0; l < _NumSlices; ++l)
|
|
{
|
|
for(uint k = 0; k < (_NumCorners - 1); ++k)
|
|
{
|
|
*indexPtr++ = l * _NumCorners + k;
|
|
*indexPtr++ = l * _NumCorners + k + 1;
|
|
*indexPtr++ = (l + 1) * _NumCorners + k;
|
|
//
|
|
*indexPtr++ = l * _NumCorners + k + 1;
|
|
*indexPtr++ = (l + 1) * _NumCorners + k + 1;
|
|
*indexPtr++ = (l + 1) * _NumCorners + k;
|
|
}
|
|
}
|
|
nlassert(indexPtr == (uint16 *) iba.getPtr() + _IB.getNumIndexes());
|
|
|
|
}
|
|
|
|
// roll / unroll using texture matrix
|
|
CMatrix texMat;
|
|
texMat.setPos(NLMISC::CVector(1.f - _UnrollRatio, 0, 0));
|
|
|
|
if (mat.getTexture(0) != NULL)
|
|
mat.setUserTexMat(0, texMat);
|
|
drv->setupModelMatrix(CMatrix::Identity);
|
|
|
|
drv->activeVertexBuffer(_VB);
|
|
drv->activeIndexBuffer(_IB);
|
|
drv->renderTriangles(mat, 0, numQuads * 2);
|
|
|
|
// draw wire frame version if needed
|
|
#ifdef DEBUG_SEG_REMANENCE_DISPLAY
|
|
static CMaterial unlitWF;
|
|
unlitWF.initUnlit();
|
|
unlitWF.setDoubleSided(true);
|
|
IDriver::TPolygonMode oldPM = drv->getPolygonMode();
|
|
drv->setPolygonMode(IDriver::Line);
|
|
drv->renderTriangles(unlitWF, 0, numQuads * 2);
|
|
drv->setPolygonMode(oldPM);
|
|
#endif
|
|
|
|
CScene *scene = getOwnerScene();
|
|
// change unroll ratio
|
|
if (!_Stopping)
|
|
{
|
|
if (_UnrollRatio != 1.f)
|
|
_UnrollRatio = std::min(1.f, _UnrollRatio + scene->getEllapsedTime() / (srs->getNumSlices() * _SliceTime));
|
|
}
|
|
else
|
|
{
|
|
_UnrollRatio = std::max(0.f, _UnrollRatio - srs->getRollupRatio() * scene->getEllapsedTime() / (srs->getNumSlices() * _SliceTime));
|
|
if (_UnrollRatio == 0.f)
|
|
{
|
|
_Stopping = false;
|
|
_Started = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
//===============================================================
|
|
void CSegRemanence::setupFromShape()
|
|
{
|
|
CSegRemanenceShape *srs = NLMISC::safe_cast<CSegRemanenceShape *>((IShape *) Shape);
|
|
if (srs->getNumCorners() != _NumCorners || srs->getNumSlices() != _NumSlices)
|
|
{
|
|
_NumCorners = srs->getNumCorners();
|
|
_NumSlices = srs->getNumSlices();
|
|
_Pos.resize(_NumCorners * (_NumSlices + 1));
|
|
for(uint k = 0; k < 4; ++k)
|
|
{
|
|
_Samples[k].Pos.resize(_NumSlices + 1);
|
|
}
|
|
}
|
|
updateOpacityFromShape();
|
|
}
|
|
|
|
//===============================================================
|
|
void CSegRemanence::samplePos(double date)
|
|
{
|
|
uint newHeadSample = (uint) floor(date / _SliceTime);
|
|
double sliceElapsedTime = date - (newHeadSample * _SliceTime);
|
|
_HeadProgress = (float) (sliceElapsedTime / _SliceTime);
|
|
NLMISC::clamp(_HeadProgress, 0.f, 1.f);
|
|
uint offset = newHeadSample - _HeadSample; // number of samples to remove
|
|
if(!_Restarted)
|
|
{
|
|
if (offset > 0)
|
|
{
|
|
offset = std::min(offset, (uint) _NumSlices);
|
|
_Samples[0].swap(_Samples[1]);
|
|
_Samples[1].swap(_Samples[2]);
|
|
_Samples[2].swap(_Samples[3]);
|
|
if (offset < _NumSlices + 1)
|
|
{
|
|
// make room for new position
|
|
memmove(&_Pos[_NumCorners * offset], &_Pos[0], sizeof(_Pos[0]) * _NumCorners * (_NumSlices + 1 - offset));
|
|
}
|
|
// else, too much time ellapsed, are sampled pos are invalidated
|
|
_HeadSample = newHeadSample;
|
|
}
|
|
}
|
|
_Samples[3].Date = date;
|
|
//
|
|
CSegRemanenceShape *srs = NLMISC::safe_cast<CSegRemanenceShape *>((IShape *) Shape);
|
|
// update positions for sample head
|
|
for(uint k = 0; k <_NumCorners;++k)
|
|
{
|
|
_Samples[3].Pos[k] = getWorldMatrix() * srs->getCorner(k);
|
|
}
|
|
if (_Restarted)
|
|
{
|
|
_HeadSample = newHeadSample;
|
|
_Samples[0] = _Samples[1] = _Samples[2] = _Samples[3];
|
|
CVector *head = &_Pos[0];
|
|
for(uint l = 0; l < _NumSlices + 1; ++l)
|
|
{
|
|
for(uint k = 0; k < _NumCorners;++k)
|
|
{
|
|
*head++ = _Samples[0].Pos[k];
|
|
}
|
|
}
|
|
_Restarted = false;
|
|
return;
|
|
}
|
|
// update head pos
|
|
CVector *head = &_Pos[0];
|
|
CVector *endPtr = head + _NumCorners * (_NumSlices + 1);
|
|
for(uint k = 0; k < _NumCorners;++k)
|
|
{
|
|
*head++ = _Samples[3].Pos[k];
|
|
}
|
|
// update current positions from sample pos
|
|
double currDate = _Samples[3].Date - sliceElapsedTime;
|
|
// interpolate linearly for 2 firstsamples
|
|
while (currDate > _Samples[2].Date && head != endPtr)
|
|
{
|
|
double dt = _Samples[3].Date - _Samples[2].Date;
|
|
float lambda = (float) (dt != 0 ? (currDate - _Samples[2].Date) / dt : 0);
|
|
for(uint k = 0; k < _NumCorners;++k)
|
|
{
|
|
*head++ = lambda * (_Samples[3].Pos[k] - _Samples[2].Pos[k]) + _Samples[2].Pos[k];
|
|
}
|
|
currDate -= _SliceTime;
|
|
}
|
|
if (head != endPtr)
|
|
{
|
|
// interpolate smoothly for remaining samples
|
|
while (currDate >= _Samples[1].Date)
|
|
{
|
|
double dt = _Samples[2].Date - _Samples[1].Date;
|
|
if (dt == 0)
|
|
{
|
|
for(uint k = 0; k < _NumCorners;++k)
|
|
{
|
|
*head++ = _Samples[2].Pos[k];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
double lambda = (currDate - _Samples[1].Date) / dt;
|
|
CVector T0, T1;
|
|
for(uint k = 0; k < _NumCorners;++k)
|
|
{
|
|
if (_Samples[2].Date != _Samples[0].Date)
|
|
{
|
|
T0 = (float) dt * (_Samples[2].Pos[k] - _Samples[0].Pos[k]) / (float) (_Samples[2].Date - _Samples[0].Date);
|
|
}
|
|
else
|
|
{
|
|
T0= NLMISC::CVector::Null;
|
|
}
|
|
if (_Samples[3].Date != _Samples[1].Date)
|
|
{
|
|
T1 = (float) dt * (_Samples[3].Pos[k] - _Samples[1].Pos[k]) / (float) (_Samples[3].Date - _Samples[1].Date);
|
|
}
|
|
else
|
|
{
|
|
T1= NLMISC::CVector::Null;
|
|
}
|
|
BuildHermiteVector(_Samples[1].Pos[k], _Samples[2].Pos[k], T0, T1, *head, (float) lambda);
|
|
++ head;
|
|
}
|
|
}
|
|
if (head == endPtr) break;
|
|
currDate -= _SliceTime;
|
|
}
|
|
/*
|
|
// Version with no time correction
|
|
while (currDate >= _Samples[1].Date)
|
|
{
|
|
float lambda = (float) ((currDate - _Samples[1].Date) / (_Samples[2].Date - _Samples[1].Date));
|
|
for(uint k = 0; k < _NumCorners;++k)
|
|
{
|
|
CVector T1 = 0.5f * (_Samples[3].Pos[k] - _Samples[1].Pos[k]);
|
|
CVector T0 = 0.5f * (_Samples[2].Pos[k] - _Samples[0].Pos[k]);
|
|
BuildHermiteVector(_Samples[1].Pos[k], _Samples[2].Pos[k], T0, T1, *head, lambda);
|
|
++ head;
|
|
}
|
|
if (head == endPtr) break;
|
|
currDate -= _SliceTime;
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
|
|
//===============================================================
|
|
void CSegRemanence::start()
|
|
{
|
|
if (_SliceTime == 0.f) return;
|
|
if (_Started && !_Stopping) return;
|
|
restart();
|
|
}
|
|
|
|
//===============================================================
|
|
void CSegRemanence::restart()
|
|
{
|
|
CSegRemanenceShape *srs = NLMISC::safe_cast<CSegRemanenceShape *>((IShape *) Shape);
|
|
if (!srs->getTextureShifting())
|
|
{
|
|
_UnrollRatio = 1.f;
|
|
}
|
|
else
|
|
{
|
|
if (!_Stopping)
|
|
_UnrollRatio = 0.f;
|
|
}
|
|
_Started = _Restarted = true;
|
|
_Stopping = false;
|
|
}
|
|
|
|
//===============================================================
|
|
void CSegRemanence::stop()
|
|
{
|
|
_Stopping = true;
|
|
}
|
|
|
|
//===============================================================
|
|
void CSegRemanence::stopNoUnroll()
|
|
{
|
|
_Started = _Restarted = _Stopping = false;
|
|
}
|
|
|
|
//===============================================================
|
|
void CSegRemanence::updateOpacityFromShape()
|
|
{
|
|
CSegRemanenceShape *srs = NLMISC::safe_cast<CSegRemanenceShape *>((IShape *) Shape);
|
|
bool transparent = srs->getMaterial().getBlend();
|
|
setTransparency(transparent);
|
|
setOpacity(!transparent);
|
|
}
|
|
|
|
//===============================================================
|
|
void CSegRemanence::setAnimatedMaterial(CAnimatedMaterial *mat)
|
|
{
|
|
if (mat == _AniMat) return;
|
|
delete _AniMat;
|
|
_AniMat = mat;
|
|
_AniMat->setFather(this, OwnerBit);
|
|
}
|
|
|
|
//===============================================================
|
|
void CSegRemanence::registerToChannelMixer(CChannelMixer *chanMixer, const std::string &prefix)
|
|
{
|
|
CTransformShape::registerToChannelMixer(chanMixer, prefix);
|
|
if (_AniMat)
|
|
{
|
|
_AniMat->registerToChannelMixer(chanMixer, prefix + _AniMat->getMaterialName() + ".") ;
|
|
}
|
|
}
|
|
|
|
//===============================================================
|
|
ITrack *CSegRemanence::getDefaultTrack (uint valueId)
|
|
{
|
|
CSegRemanenceShape *srs = NLMISC::safe_cast<CSegRemanenceShape *>((IShape *) Shape);
|
|
switch (valueId)
|
|
{
|
|
case PosValue: return srs->getDefaultPos();
|
|
case RotQuatValue: return srs->getDefaultRotQuat();
|
|
case ScaleValue: return srs->getDefaultScale();
|
|
}
|
|
return CTransformShape::getDefaultTrack(valueId);
|
|
return NULL;
|
|
|
|
}
|
|
|
|
//===============================================================
|
|
void CSegRemanence::traverseAnimDetail()
|
|
{
|
|
CTransformShape::traverseAnimDetail();
|
|
#ifndef DEBUG_SEG_REMANENCE_DISPLAY
|
|
if (isStarted())
|
|
#endif
|
|
{
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
CScene *scene = getOwnerScene();
|
|
if (scene->getNumRender() != (_LastSampleFrame + 1))
|
|
{
|
|
if (!isStopping())
|
|
{
|
|
// if wasn't visible at previous frame, must invalidate position
|
|
restart();
|
|
}
|
|
else
|
|
{
|
|
// ribbon started unrolling when it disapperaed from screen so simply remove it
|
|
stopNoUnroll();
|
|
}
|
|
}
|
|
_LastSampleFrame = scene->getNumRender();
|
|
setupFromShape();
|
|
samplePos(scene->getCurrentTime());
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
// test if animated material must be updated.
|
|
if(IAnimatable::isTouched(CSegRemanence::OwnerBit))
|
|
{
|
|
if (getAnimatedMaterial())
|
|
getAnimatedMaterial()->update();
|
|
clearAnimatedMatFlag();
|
|
}
|
|
}
|
|
}
|
|
|
|
//===============================================================
|
|
void CSegRemanence::setSliceTime(float duration)
|
|
{
|
|
if ( duration != _SliceTime )
|
|
{
|
|
stopNoUnroll();
|
|
_SliceTime = duration;
|
|
}
|
|
}
|
|
|
|
}
|