mirror of https://github.com/axmolengine/axmol.git
423 lines
15 KiB
C++
423 lines
15 KiB
C++
/****************************************************************************
|
|
Copyright (C) 2013 Henry van Merode. All rights reserved.
|
|
Copyright (c) 2015-2016 Chukong Technologies Inc.
|
|
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
|
|
|
https://axmolengine.github.io/
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
****************************************************************************/
|
|
|
|
#include "extensions/Particle3D/PU/CCPURibbonTrail.h"
|
|
#include "extensions/Particle3D/PU/CCPUParticleSystem3D.h"
|
|
#include "base/CCDirector.h"
|
|
#include "renderer/CCMeshCommand.h"
|
|
#include "renderer/CCRenderer.h"
|
|
#include "renderer/CCTextureCache.h"
|
|
#include "renderer/backend/ProgramState.h"
|
|
#include "2d/CCCamera.h"
|
|
#include "3d/CCMeshRenderer.h"
|
|
|
|
NS_AX_BEGIN
|
|
|
|
PURibbonTrail::PURibbonTrail(std::string_view name,
|
|
std::string_view texFile,
|
|
size_t maxElements,
|
|
size_t numberOfChains,
|
|
bool useTextureCoords,
|
|
bool useColours)
|
|
: PUBillboardChain(name, texFile, maxElements, 0, useTextureCoords, useColours, true)
|
|
, _parentNode(nullptr)
|
|
, _needTimeUpdate(false)
|
|
{
|
|
setTrailLength(100);
|
|
setNumberOfChains(numberOfChains);
|
|
|
|
// use V as varying texture coord, so we can use 1D textures to 'smear'
|
|
setTextureCoordDirection(TCD_V);
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
PURibbonTrail::~PURibbonTrail() {}
|
|
//-----------------------------------------------------------------------
|
|
void PURibbonTrail::addNode(Node* n)
|
|
{
|
|
if (_nodeList.size() == _chainCount)
|
|
{
|
|
AXASSERT(false, " cannot monitor any more nodes, chain count exceeded");
|
|
}
|
|
|
|
// if (n->getListener())
|
|
//{
|
|
// OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
|
|
// mName + " cannot monitor node " + n->getName() + " since it already has a listener.",
|
|
// "RibbonTrail::addNode");
|
|
// }
|
|
|
|
// get chain index
|
|
size_t chainIndex = _freeChains.back();
|
|
_freeChains.pop_back();
|
|
_nodeToChainSegment.emplace_back(chainIndex);
|
|
_nodeToSegMap[n] = chainIndex;
|
|
|
|
// initialise the chain
|
|
resetTrail(chainIndex, n);
|
|
|
|
_nodeList.emplace_back(n);
|
|
// n->setListener(this);
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
size_t PURibbonTrail::getChainIndexForNode(const Node* n)
|
|
{
|
|
NodeToChainSegmentMap::const_iterator i = _nodeToSegMap.find(n);
|
|
if (i == _nodeToSegMap.end())
|
|
{
|
|
AXASSERT(false, "This node is not being tracked");
|
|
}
|
|
return i->second;
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
void PURibbonTrail::removeNode(Node* n)
|
|
{
|
|
NodeList::iterator i = std::find(_nodeList.begin(), _nodeList.end(), n);
|
|
if (i != _nodeList.end())
|
|
{
|
|
// also get matching chain segment
|
|
size_t index = std::distance(_nodeList.begin(), i);
|
|
IndexVector::iterator mi = _nodeToChainSegment.begin();
|
|
std::advance(mi, index);
|
|
size_t chainIndex = *mi;
|
|
PUBillboardChain::clearChain(chainIndex);
|
|
// mark as free now
|
|
_freeChains.emplace_back(chainIndex);
|
|
// n->setListener(0);
|
|
_nodeList.erase(i);
|
|
_nodeToChainSegment.erase(mi);
|
|
_nodeToSegMap.erase(_nodeToSegMap.find(n));
|
|
}
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
void PURibbonTrail::setTrailLength(float len)
|
|
{
|
|
_trailLength = len;
|
|
_elemLength = _trailLength / _maxElementsPerChain;
|
|
_squaredElemLength = _elemLength * _elemLength;
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
void PURibbonTrail::setMaxChainElements(size_t maxElements)
|
|
{
|
|
PUBillboardChain::setMaxChainElements(maxElements);
|
|
_elemLength = _trailLength / _maxElementsPerChain;
|
|
_squaredElemLength = _elemLength * _elemLength;
|
|
|
|
resetAllTrails();
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
void PURibbonTrail::setNumberOfChains(size_t numChains)
|
|
{
|
|
AXASSERT(numChains >= _nodeList.size(), "Can't shrink the number of chains less than number of tracking nodes");
|
|
|
|
size_t oldChains = getNumberOfChains();
|
|
|
|
PUBillboardChain::setNumberOfChains(numChains);
|
|
|
|
_initialColor.resize(numChains, Vec4::ONE);
|
|
_deltaColor.resize(numChains, Vec4::ZERO);
|
|
_initialWidth.resize(numChains, 10);
|
|
_deltaWidth.resize(numChains, 0);
|
|
|
|
if (oldChains > numChains)
|
|
{
|
|
// remove free chains
|
|
for (IndexVector::iterator i = _freeChains.begin(); i != _freeChains.end();)
|
|
{
|
|
if (*i >= numChains)
|
|
i = _freeChains.erase(i);
|
|
else
|
|
++i;
|
|
}
|
|
}
|
|
else if (oldChains < numChains)
|
|
{
|
|
// add new chains, at front to preserve previous ordering (pop_back)
|
|
for (size_t i = oldChains; i < numChains; ++i)
|
|
_freeChains.insert(_freeChains.begin(), i);
|
|
}
|
|
resetAllTrails();
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
void PURibbonTrail::clearChain(size_t chainIndex)
|
|
{
|
|
PUBillboardChain::clearChain(chainIndex);
|
|
|
|
// Reset if we are tracking for this chain
|
|
IndexVector::iterator i = std::find(_nodeToChainSegment.begin(), _nodeToChainSegment.end(), chainIndex);
|
|
if (i != _nodeToChainSegment.end())
|
|
{
|
|
size_t nodeIndex = std::distance(_nodeToChainSegment.begin(), i);
|
|
resetTrail(*i, _nodeList[nodeIndex]);
|
|
}
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
void PURibbonTrail::setInitialColour(size_t chainIndex, const Vec4& col)
|
|
{
|
|
setInitialColour(chainIndex, col.x, col.y, col.z, col.w);
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
void PURibbonTrail::setInitialColour(size_t chainIndex, float r, float g, float b, float a)
|
|
{
|
|
AXASSERT(chainIndex < _chainCount, "chainIndex out of bounds");
|
|
_initialColor[chainIndex].x = r;
|
|
_initialColor[chainIndex].y = g;
|
|
_initialColor[chainIndex].z = b;
|
|
_initialColor[chainIndex].w = a;
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
const Vec4& PURibbonTrail::getInitialColour(size_t chainIndex) const
|
|
{
|
|
AXASSERT(chainIndex < _chainCount, "chainIndex out of bounds");
|
|
return _initialColor[chainIndex];
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
void PURibbonTrail::setInitialWidth(size_t chainIndex, float width)
|
|
{
|
|
AXASSERT(chainIndex < _chainCount, "chainIndex out of bounds");
|
|
_initialWidth[chainIndex] = width;
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
float PURibbonTrail::getInitialWidth(size_t chainIndex) const
|
|
{
|
|
AXASSERT(chainIndex < _chainCount, "chainIndex out of bounds");
|
|
return _initialWidth[chainIndex];
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
void PURibbonTrail::setColourChange(size_t chainIndex, const Vec4& valuePerSecond)
|
|
{
|
|
setColourChange(chainIndex, valuePerSecond.x, valuePerSecond.y, valuePerSecond.z, valuePerSecond.w);
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
void PURibbonTrail::setColourChange(size_t chainIndex, float r, float g, float b, float a)
|
|
{
|
|
AXASSERT(chainIndex < _chainCount, "chainIndex out of bounds");
|
|
_deltaColor[chainIndex].x = r;
|
|
_deltaColor[chainIndex].y = g;
|
|
_deltaColor[chainIndex].z = b;
|
|
_deltaColor[chainIndex].w = a;
|
|
|
|
manageController();
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
const Vec4& PURibbonTrail::getColourChange(size_t chainIndex) const
|
|
{
|
|
AXASSERT(chainIndex < _chainCount, "chainIndex out of bounds");
|
|
return _deltaColor[chainIndex];
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
void PURibbonTrail::setWidthChange(size_t chainIndex, float widthDeltaPerSecond)
|
|
{
|
|
AXASSERT(chainIndex < _chainCount, "chainIndex out of bounds");
|
|
_deltaWidth[chainIndex] = widthDeltaPerSecond;
|
|
manageController();
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
float PURibbonTrail::getWidthChange(size_t chainIndex) const
|
|
{
|
|
AXASSERT(chainIndex < _chainCount, "chainIndex out of bounds");
|
|
return _deltaWidth[chainIndex];
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
void PURibbonTrail::manageController()
|
|
{
|
|
_needTimeUpdate = false;
|
|
for (size_t i = 0; i < _chainCount; ++i)
|
|
{
|
|
if (_deltaWidth[i] != 0 || _deltaColor[i] != Vec4::ZERO)
|
|
{
|
|
_needTimeUpdate = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
void PURibbonTrail::nodeUpdated(const Node* node)
|
|
{
|
|
size_t chainIndex = getChainIndexForNode(node);
|
|
updateTrail(chainIndex, node);
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
void PURibbonTrail::nodeDestroyed(const Node* node)
|
|
{
|
|
removeNode(const_cast<Node*>(node));
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
void PURibbonTrail::updateTrail(size_t index, const Node* node)
|
|
{
|
|
// Repeat this entire process if chain is stretched beyond its natural length
|
|
bool done = false;
|
|
while (!done)
|
|
{
|
|
// Node has changed somehow, we're only interested in the derived position
|
|
ChainSegment& seg = _chainSegmentList[index];
|
|
Element& headElem = _chainElementList[seg.start + seg.head];
|
|
size_t nextElemIdx = seg.head + 1;
|
|
// wrap
|
|
if (nextElemIdx == _maxElementsPerChain)
|
|
nextElemIdx = 0;
|
|
Element& nextElem = _chainElementList[seg.start + nextElemIdx];
|
|
|
|
// Vary the head elem, but bake new version if that exceeds element len
|
|
Vec3 newPos = node->getPosition3D();
|
|
if (_parentNode)
|
|
{
|
|
// Transform position to ourself space
|
|
_parentNode->getWorldToNodeTransform().transformPoint(newPos, &newPos);
|
|
}
|
|
Vec3 diff = newPos - nextElem.position;
|
|
float sqlen = diff.lengthSquared();
|
|
if (sqlen >= _squaredElemLength)
|
|
{
|
|
// Move existing head to mElemLength
|
|
Vec3 scaledDiff = diff * (_elemLength / sqrtf(sqlen));
|
|
headElem.position = nextElem.position + scaledDiff;
|
|
// Add a new element to be the new head
|
|
Element newElem(newPos, _initialWidth[index], 0.0f, _initialColor[index], node->getRotationQuat());
|
|
addChainElement(index, newElem);
|
|
// alter diff to represent new head size
|
|
diff = newPos - headElem.position;
|
|
// check whether another step is needed or not
|
|
if (diff.lengthSquared() <= _squaredElemLength)
|
|
done = true;
|
|
}
|
|
else
|
|
{
|
|
// Extend existing head
|
|
headElem.position = newPos;
|
|
done = true;
|
|
}
|
|
|
|
// Is this segment full?
|
|
if ((seg.tail + 1) % _maxElementsPerChain == seg.head)
|
|
{
|
|
// If so, shrink tail gradually to match head extension
|
|
Element& tailElem = _chainElementList[seg.start + seg.tail];
|
|
size_t preTailIdx;
|
|
if (seg.tail == 0)
|
|
preTailIdx = _maxElementsPerChain - 1;
|
|
else
|
|
preTailIdx = seg.tail - 1;
|
|
Element& preTailElem = _chainElementList[seg.start + preTailIdx];
|
|
|
|
// Measure tail diff from pretail to tail
|
|
Vec3 taildiff = tailElem.position - preTailElem.position;
|
|
float taillen = taildiff.length();
|
|
if (taillen > 1e-06)
|
|
{
|
|
float tailsize = _elemLength - diff.length();
|
|
taildiff *= tailsize / taillen;
|
|
tailElem.position = preTailElem.position + taildiff;
|
|
}
|
|
}
|
|
} // end while
|
|
|
|
_vertexContentDirty = true;
|
|
// Need to dirty the parent node, but can't do it using needUpdate() here
|
|
// since we're in the middle of the scene graph update (node listener),
|
|
// so re-entrant calls don't work. Queue.
|
|
// if (mParentNode)
|
|
//{
|
|
// Node::queueNeedUpdate(getParentSceneNode());
|
|
//}
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
void PURibbonTrail::timeUpdate(float time)
|
|
{
|
|
// Apply all segment effects
|
|
for (size_t s = 0; s < _chainSegmentList.size(); ++s)
|
|
{
|
|
ChainSegment& seg = _chainSegmentList[s];
|
|
if (seg.head != SEGMENT_EMPTY && seg.head != seg.tail)
|
|
{
|
|
|
|
for (size_t e = seg.head + 1;; ++e) // until break
|
|
{
|
|
e = e % _maxElementsPerChain;
|
|
|
|
Element& elem = _chainElementList[seg.start + e];
|
|
elem.width = elem.width - (time * _deltaWidth[s]);
|
|
elem.width = 0.0f < elem.width ? elem.width : 0.0f;
|
|
elem.color = elem.color - (_deltaColor[s] * time);
|
|
elem.color.clamp(Vec4(0.0f, 0.0f, 0.0f, 0.0f), Vec4(1.0f, 1.0f, 1.0f, 1.0f));
|
|
|
|
if (e == seg.tail)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
void PURibbonTrail::resetTrail(size_t index, const Node* node)
|
|
{
|
|
assert(index < _chainCount);
|
|
|
|
ChainSegment& seg = _chainSegmentList[index];
|
|
// set up this segment
|
|
seg.head = seg.tail = SEGMENT_EMPTY;
|
|
// Create new element, v coord is always 0.0f
|
|
// need to convert to take parent node's position into account
|
|
Vec3 position = node->getPosition3D();
|
|
if (_parentNode)
|
|
{
|
|
// Transform position to ourself space
|
|
_parentNode->getWorldToNodeTransform().transformPoint(position, &position);
|
|
}
|
|
Element e(position, _initialWidth[index], 0.0f, _initialColor[index], node->getRotationQuat());
|
|
// Add the start position
|
|
addChainElement(index, e);
|
|
// Add another on the same spot, this will extend
|
|
addChainElement(index, e);
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
void PURibbonTrail::resetAllTrails()
|
|
{
|
|
for (size_t i = 0; i < _nodeList.size(); ++i)
|
|
{
|
|
resetTrail(i, _nodeList[i]);
|
|
}
|
|
}
|
|
|
|
void PURibbonTrail::update(float deltaTime)
|
|
{
|
|
if (_needTimeUpdate)
|
|
{
|
|
static float lastUpdateTime = 0.0f;
|
|
if (0.5f < lastUpdateTime)
|
|
{
|
|
timeUpdate(deltaTime);
|
|
lastUpdateTime = 0.0f;
|
|
}
|
|
lastUpdateTime += deltaTime;
|
|
}
|
|
|
|
for (auto&& iter : _nodeToSegMap)
|
|
{
|
|
updateTrail(iter.second, iter.first);
|
|
}
|
|
}
|
|
|
|
NS_AX_END
|