Ribbon class implementation.

This class is based on Tim Newell from MaxGaming Technologies code, it's a
Ribbon class which is easy to use from other classes.
This commit is contained in:
Lukas Joergensen 2014-07-31 00:25:52 +02:00 committed by LukasPJ
parent 2c5f643dbe
commit cb9cfea1c4
2 changed files with 828 additions and 0 deletions

View file

@ -0,0 +1,701 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// 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 "console/consoleTypes.h"
#include "console/typeValidators.h"
#include "core/stream/bitStream.h"
#include "T3D/shapeBase.h"
#include "ts/tsShapeInstance.h"
#include "T3D/fx/ribbon.h"
#include "math/mathUtils.h"
#include "math/mathIO.h"
#include "sim/netConnection.h"
#include "gfx/primBuilder.h"
#include "gfx/gfxDrawUtil.h"
#include "materials/sceneData.h"
#include "materials/matInstance.h"
#include "gui/3d/guiTSControl.h"
#include "materials/materialManager.h"
#include "materials/processedShaderMaterial.h"
#include "gfx/gfxTransformSaver.h"
IMPLEMENT_CO_DATABLOCK_V1(RibbonData);
IMPLEMENT_CO_NETOBJECT_V1(Ribbon);
//--------------------------------------------------------------------------
//
RibbonData::RibbonData()
{
for (U8 i = 0; i < RIBBON_NUM_FIELDS; i++) {
mSizes[i] = 0.0f;
mColours[i].set(0.0f, 0.0f, 0.0f, 1.0f);
mTimes[i] = -1.0f;
}
mRibbonLength = 0;
mUseFadeOut = false;
mFadeAwayStep = 0.032f;
segmentsPerUpdate = 1;
mMatName = StringTable->insert("");
mTileScale = 1.0f;
mFixedTexcoords = false;
mSegmentSkipAmount = 0;
mTexcoordsRelativeToDistance = false;
}
//--------------------------------------------------------------------------
void RibbonData::initPersistFields()
{
Parent::initPersistFields();
addField("size", TypeF32, Offset(mSizes, RibbonData), RIBBON_NUM_FIELDS,
"The size of the ribbon at the specified keyframe.");
addField("color", TypeColorF, Offset(mColours, RibbonData), RIBBON_NUM_FIELDS,
"The colour of the ribbon at the specified keyframe.");
addField("position", TypeF32, Offset(mTimes, RibbonData), RIBBON_NUM_FIELDS,
"The position of the keyframe along the lifetime of the ribbon.");
addField("RibbonLength", TypeS32, Offset(mRibbonLength, RibbonData),
"The amount of segments the Ribbon can maximally have in length.");
addField("UseFadeOut", TypeBool, Offset(mUseFadeOut, RibbonData),
"If true, the ribbon will fade away after deletion.");
addField("RibbonMaterial", TypeString, Offset(mMatName, RibbonData),
"The material the ribbon uses for rendering.");
addField("fadeAwayStep", TypeF32, Offset(mFadeAwayStep, RibbonData),
"How much to fade the ribbon with each update, after deletion.");
addField("segmentsPerUpdate", TypeS32, Offset(segmentsPerUpdate, RibbonData),
"How many segments to add each update.");
addField("tileScale", TypeF32, Offset(mTileScale, RibbonData),
"How much to scale each 'tile' with, where 1 means the material is stretched"
"across the whole ribbon. (If TexcoordsRelativeToDistance is true, this is in meters.)");
addField("fixedTexcoords", TypeBool, Offset(mFixedTexcoords, RibbonData),
"If true, this prevents 'floating' texture coordinates.");
addField("skipAmount", TypeS32, Offset(mSegmentSkipAmount, RibbonData),
"The amount of segments to skip each update.");
addField("TexcoordsRelativeToDistance", TypeBool, Offset(mTexcoordsRelativeToDistance, RibbonData),
"If true, texture coordinates are scaled relative to distance, this prevents"
"'stretched' textures.");
}
//--------------------------------------------------------------------------
bool RibbonData::onAdd()
{
if(!Parent::onAdd())
return false;
return true;
}
bool RibbonData::preload(bool server, String &errorBuffer)
{
if (Parent::preload(server, errorBuffer) == false)
return false;
return true;
}
//--------------------------------------------------------------------------
void RibbonData::packData(BitStream* stream)
{
Parent::packData(stream);
for (U8 i = 0; i < RIBBON_NUM_FIELDS; i++) {
stream->write(mSizes[i]);
stream->write(mColours[i]);
stream->write(mTimes[i]);
}
stream->write(mRibbonLength);
stream->writeString(mMatName);
stream->writeFlag(mUseFadeOut);
stream->write(mFadeAwayStep);
stream->write(segmentsPerUpdate);
stream->write(mTileScale);
stream->writeFlag(mFixedTexcoords);
stream->writeFlag(mTexcoordsRelativeToDistance);
}
void RibbonData::unpackData(BitStream* stream)
{
Parent::unpackData(stream);
for (U8 i = 0; i < RIBBON_NUM_FIELDS; i++) {
stream->read(&mSizes[i]);
stream->read(&mColours[i]);
stream->read(&mTimes[i]);
}
stream->read(&mRibbonLength);
mMatName = StringTable->insert(stream->readSTString());
mUseFadeOut = stream->readFlag();
stream->read(&mFadeAwayStep);
stream->read(&segmentsPerUpdate);
stream->read(&mTileScale);
mFixedTexcoords = stream->readFlag();
mTexcoordsRelativeToDistance = stream->readFlag();
}
//--------------------------------------------------------------------------
//--------------------------------------
//
Ribbon::Ribbon()
{
mTypeMask |= StaticObjectType;
VECTOR_SET_ASSOCIATION(mSegmentPoints);
mSegmentPoints.clear();
mRibbonMat = NULL;
mUpdateBuffers = true;
mDeleteOnEnd = false;
mUseFadeOut = false;
mFadeAwayStep = 1.0f;
mFadeOut = 1.0f;
mNetFlags.clear(Ghostable);
mNetFlags.set(IsGhost);
mRadiusSC = NULL;
mRibbonProjSC = NULL;
mSegmentOffset = 0;
mSegmentIdx = 0;
mTravelledDistance = 0;
}
Ribbon::~Ribbon()
{
//Make sure we cleanup
SAFE_DELETE(mRibbonMat);
}
//--------------------------------------------------------------------------
void Ribbon::initPersistFields()
{
Parent::initPersistFields();
}
bool Ribbon::onAdd()
{
if(!Parent::onAdd())
return false;
// add to client side mission cleanup
SimGroup *cleanup = dynamic_cast<SimGroup *>( Sim::findObject( "ClientMissionCleanup") );
if( cleanup != NULL )
{
cleanup->addObject( this );
}
else
{
AssertFatal( false, "Error, could not find ClientMissionCleanup group" );
return false;
}
if (!isServerObject()) {
if(GFX->getPixelShaderVersion() >= 1.1 && dStrlen(mDataBlock->mMatName) > 0 )
{
mRibbonMat = MATMGR->createMatInstance( mDataBlock->mMatName );
GFXStateBlockDesc desc;
desc.setZReadWrite( true, false );
desc.cullDefined = true;
desc.cullMode = GFXCullNone;
desc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
desc.samplersDefined = true;
GFXSamplerStateDesc sDesc(GFXSamplerStateDesc::getClampLinear());
sDesc.addressModeV = GFXAddressWrap;
desc.samplers[0] = sDesc;
mRibbonMat->addStateBlockDesc( desc );
mRibbonMat->init(MATMGR->getDefaultFeatures(), getGFXVertexFormat<GFXVertexPCNTT>());
mRadiusSC = mRibbonMat->getMaterialParameterHandle( "$radius" );
mRibbonProjSC = mRibbonMat->getMaterialParameterHandle( "$ribbonProj" );
} else {
Con::warnf( "Invalid Material name: %s: for Ribbon", mDataBlock->mMatName );
#ifdef TORQUE_DEBUG
Con::warnf( "- This could be caused by having the shader data datablocks in server-only code." );
#endif
mRibbonMat = NULL;
}
}
mObjBox.minExtents.set( 1.0f, 1.0f, 1.0f );
mObjBox.maxExtents.set( 2.0f, 2.0f, 2.0f );
// Reset the World Box.
resetWorldBox();
// Set the Render Transform.
setRenderTransform(mObjToWorld);
addToScene();
return true;
}
void Ribbon::onRemove()
{
removeFromScene();
SAFE_DELETE(mRibbonMat);
Parent::onRemove();
}
bool Ribbon::onNewDataBlock(GameBaseData* dptr, bool reload)
{
mDataBlock = dynamic_cast<RibbonData*>(dptr);
if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload))
return false;
return true;
}
void Ribbon::processTick(const Move* move)
{
Parent::processTick(move);
if (mDeleteOnEnd) {
if (mUseFadeOut) {
if (mFadeOut <= 0.0f) {
mFadeOut = 0.0f;
//delete this class
mDeleteOnEnd = false;
safeDeleteObject();
return;
}
mFadeOut -= mFadeAwayStep;
if (mFadeOut < 0.0f) {
mFadeOut = 0.0f;
}
mUpdateBuffers = true;
} else {
//if (mSegmentPoints.size() == 0) {
//delete this class
mDeleteOnEnd = false;
safeDeleteObject();
return;
//}
//mSegmentPoints.pop_back();
}
}
}
void Ribbon::advanceTime(F32 dt)
{
Parent::advanceTime(dt);
}
void Ribbon::interpolateTick(F32 delta)
{
Parent::interpolateTick(delta);
}
void Ribbon::addSegmentPoint(Point3F &point, MatrixF &mat) {
//update our position
setRenderTransform(mat);
MatrixF xform(true);
xform.setColumn(3, point);
setTransform(xform);
if(mSegmentIdx < mDataBlock->mSegmentSkipAmount)
{
mSegmentIdx++;
return;
}
mSegmentIdx = 0;
U32 segmentsToDelete = checkRibbonDistance(mDataBlock->segmentsPerUpdate);
for (U32 i = 0; i < segmentsToDelete; i++) {
U32 last = mSegmentPoints.size() - 1;
if (last < 0)
break;
mTravelledDistance += last ? (mSegmentPoints[last] - mSegmentPoints[last-1]).len() : 0;
mSegmentPoints.pop_back();
mUpdateBuffers = true;
mSegmentOffset++;
}
//If there is no other points, just add a new one.
if (mSegmentPoints.size() == 0) {
mSegmentPoints.push_front(point);
mUpdateBuffers = true;
return;
}
Point3F startPoint = mSegmentPoints[0];
//add X points based on how many segments Per Update from last point to current point
for (U32 i = 0; i < mDataBlock->segmentsPerUpdate; i++) {
F32 interp = (F32(i+1) / (F32)mDataBlock->segmentsPerUpdate);
//(end - start) * percentage) + start
Point3F derivedPoint = ((point - startPoint) * interp) + startPoint;
mSegmentPoints.push_front(derivedPoint);
mUpdateBuffers = true;
}
if (mSegmentPoints.size() > 1) {
Point3F pointA = mSegmentPoints[mSegmentPoints.size()-1];
Point3F pointB = mSegmentPoints[0];
Point3F diffSize = pointA - pointB;
if (diffSize.x == 0.0f)
diffSize.x = 1.0f;
if (diffSize.y == 0.0f)
diffSize.y = 1.0f;
if (diffSize.z == 0.0f)
diffSize.z = 1.0f;
Box3F objBox;
objBox.minExtents.set( diffSize * -1 );
objBox.maxExtents.set( diffSize );
if (objBox.minExtents.x > objBox.maxExtents.x) {
F32 tmp = objBox.minExtents.x;
objBox.minExtents.x = objBox.maxExtents.x;
objBox.maxExtents.x = tmp;
}
if (objBox.minExtents.y > objBox.maxExtents.y) {
F32 tmp = objBox.minExtents.y;
objBox.minExtents.y = objBox.maxExtents.y;
objBox.maxExtents.y = tmp;
}
if (objBox.minExtents.z > objBox.maxExtents.z) {
F32 tmp = objBox.minExtents.z;
objBox.minExtents.z = objBox.maxExtents.z;
objBox.maxExtents.z = tmp;
}
if (objBox.isValidBox()) {
mObjBox = objBox;
// Reset the World Box.
resetWorldBox();
}
}
}
void Ribbon::deleteOnEnd() {
mDeleteOnEnd = true;
mUseFadeOut = mDataBlock->mUseFadeOut;
mFadeAwayStep = mDataBlock->mFadeAwayStep;
}
U32 Ribbon::checkRibbonDistance(S32 segments) {
S32 len = mSegmentPoints.size();
S32 difference = (mDataBlock->mRibbonLength/(mDataBlock->mSegmentSkipAmount+1)) - len;
if (difference < 0)
return mAbs(difference);
return 0; //do not delete any points
}
void Ribbon::setShaderParams() {
F32 numSegments = (F32)mSegmentPoints.size();
F32 length = (F32)mDataBlock->mRibbonLength;
Point3F radius(numSegments / length, numSegments, length);
MaterialParameters* matParams = mRibbonMat->getMaterialParameters();
matParams->setSafe( mRadiusSC, radius );
}
//--------------------------------------------------------------------------
//U32 Ribbon::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
//{
// U32 retMask = Parent::packUpdate(con, mask, stream);
// return retMask;
//}
//
//void Ribbon::unpackUpdate(NetConnection* con, BitStream* stream)
//{
// Parent::unpackUpdate(con, stream);
//}
//--------------------------------------------------------------------------
void Ribbon::prepRenderImage(SceneRenderState *state)
{
if (mFadeOut == 0.0f)
return;
if(!mRibbonMat)
return;
if (mDeleteOnEnd == true && mUseFadeOut == false) {
return;
}
// We only render during the normal diffuse render pass.
if( !state->isDiffusePass() )
return;
U32 segments = mSegmentPoints.size();
if (segments < 2)
return;
MeshRenderInst *ri = state->getRenderPass()->allocInst<MeshRenderInst>();
ri->type = RenderPassManager::RIT_Translucent;
ri->translucentSort = true;
ri->sortDistSq = ( mSegmentPoints[0] - state->getCameraPosition() ).lenSquared();
RenderPassManager *renderPass = state->getRenderPass();
MatrixF *proj = renderPass->allocUniqueXform(MatrixF( true ));
proj->mul(GFX->getProjectionMatrix());
proj->mul(GFX->getWorldMatrix());
ri->objectToWorld = &MatrixF::Identity;
ri->worldToCamera = &MatrixF::Identity;
ri->projection = proj;
ri->matInst = mRibbonMat;
// Set up our vertex buffer and primitive buffer
if(mUpdateBuffers)
createBuffers(state, verts, primBuffer, segments);
ri->vertBuff = &verts;
ri->primBuff = &primBuffer;
ri->visibility = 1.0f;
ri->prim = renderPass->allocPrim();
ri->prim->type = GFXTriangleList;
ri->prim->minIndex = 0;
ri->prim->startIndex = 0;
ri->prim->numPrimitives = (segments-1) * 2;
ri->prim->startVertex = 0;
ri->prim->numVertices = segments * 2;
if (mRibbonMat) {
ri->defaultKey = mRibbonMat->getStateHint();
} else {
ri->defaultKey = 1;
}
ri->defaultKey2 = (U32)ri->vertBuff; // Not 64bit safe!
state->getRenderPass()->addInst(ri);
}
void Ribbon::createBuffers(SceneRenderState *state, GFXVertexBufferHandle<GFXVertexPCNTT> &verts, GFXPrimitiveBufferHandle &pb, U32 segments) {
PROFILE_SCOPE( Ribbon_createBuffers );
Point3F cameraPos = state->getCameraPosition();
U32 count = 0;
U32 indexCount = 0;
verts.set(GFX, (segments*2), GFXBufferTypeDynamic);
// create index buffer based on that size
U32 indexListSize = (segments-1) * 6;
pb.set( GFX, indexListSize, 0, GFXBufferTypeDynamic );
U16 *indices = NULL;
verts.lock();
pb.lock( &indices );
F32 totalLength = 0;
if(mDataBlock->mTexcoordsRelativeToDistance)
{
for (U32 i = 0; i < segments; i++)
if (i != 0)
totalLength += (mSegmentPoints[i] - mSegmentPoints[i-1]).len();
}
U8 fixedAppend = 0;
F32 curLength = 0;
for (U32 i = 0; i < segments; i++) {
F32 interpol = ((F32)i / (F32)(segments-1));
Point3F leftvert = mSegmentPoints[i];
Point3F rightvert = mSegmentPoints[i];
F32 tRadius = mDataBlock->mSizes[0];
ColorF tColor = mDataBlock->mColours[0];
for (U8 j = 0; j < RIBBON_NUM_FIELDS-1; j++) {
F32 curPosition = mDataBlock->mTimes[j];
F32 curRadius = mDataBlock->mSizes[j];
ColorF curColor = mDataBlock->mColours[j];
F32 nextPosition = mDataBlock->mTimes[j+1];
F32 nextRadius = mDataBlock->mSizes[j+1];
ColorF nextColor = mDataBlock->mColours[j+1];
if ( curPosition < 0
|| curPosition > interpol )
break;
F32 positionDiff = (interpol - curPosition) / (nextPosition - curPosition);
tRadius = curRadius + (nextRadius - curRadius) * positionDiff;
tColor.interpolate(curColor, nextColor, positionDiff);
}
Point3F diff;
F32 length;
if (i == 0) {
diff = mSegmentPoints[i+1] - mSegmentPoints[i];
length = 0;
} else if (i == segments-1) {
diff = mSegmentPoints[i] - mSegmentPoints[i-1];
length = diff.len();
} else {
diff = mSegmentPoints[i+1] - mSegmentPoints[i-1];
length = (mSegmentPoints[i] - mSegmentPoints[i-1]).len();
}
//left point
Point3F eyeMinPos = cameraPos - leftvert;
Point3F perpendicular = mCross(diff, eyeMinPos);
perpendicular.normalize();
perpendicular = perpendicular * tRadius * -1.0f;
perpendicular += mSegmentPoints[i];
verts[count].point.set(perpendicular);
ColorF color = tColor;
if (mDataBlock->mUseFadeOut)
color.alpha *= mFadeOut;
F32 texCoords;
if(mDataBlock->mFixedTexcoords && !mDataBlock->mTexcoordsRelativeToDistance)
{
U32 fixedIdx = (i+mDataBlock->mRibbonLength-mSegmentOffset)%mDataBlock->mRibbonLength;
if(fixedIdx == 0 && i > 0)
fixedAppend++;
F32 fixedInterpol = (F32)fixedIdx / (F32)(mDataBlock->mRibbonLength);
fixedInterpol += fixedAppend;
texCoords = (1.0f - fixedInterpol)*mDataBlock->mTileScale;
}
else if(mDataBlock->mTexcoordsRelativeToDistance)
texCoords = (mTravelledDistance + (totalLength - (curLength + length)))*mDataBlock->mTileScale;
else
texCoords = (1.0f - interpol)*mDataBlock->mTileScale;
verts[count].color = color;
verts[count].texCoord[1] = Point2F(interpol, 0);
verts[count].texCoord[0] = Point2F(0.0f, texCoords);
verts[count].normal.set(diff);
//Triangle strip style indexing, so grab last 2
if (count > 1) {
indices[indexCount] = count-2;
indexCount++;
indices[indexCount] = count;
indexCount++;
indices[indexCount] = count-1;
indexCount++;
}
count++;
eyeMinPos = cameraPos - rightvert;
perpendicular = mCross(diff, eyeMinPos);
perpendicular.normalize();
perpendicular = perpendicular * tRadius;
perpendicular += mSegmentPoints[i];
verts[count].point.set(perpendicular);
color = tColor;
if (mDataBlock->mUseFadeOut)
color.alpha *= mFadeOut;
verts[count].color = color;
verts[count].texCoord[1] = Point2F(interpol, 1);
verts[count].texCoord[0] = Point2F(1.0f, texCoords);
verts[count].normal.set(diff);
//Triangle strip style indexing, so grab last 2
if (count > 1) {
indices[indexCount] = count-2;
indexCount++;
indices[indexCount] = count-1;
indexCount++;
indices[indexCount] = count;
indexCount++;
}
count++;
curLength += length;
}
Point3F pointA = verts[count-1].point;
Point3F pointB = verts[0].point;
verts.unlock();
pb.unlock();
Point3F diffSize = pointA - pointB;
Box3F objBox;
objBox.minExtents.set( diffSize * -1 );
objBox.maxExtents.set( diffSize );
if (objBox.minExtents.x > objBox.maxExtents.x) {
F32 tmp = objBox.minExtents.x;
objBox.minExtents.x = objBox.maxExtents.x;
objBox.maxExtents.x = tmp;
}
if (objBox.minExtents.y > objBox.maxExtents.y) {
F32 tmp = objBox.minExtents.y;
objBox.minExtents.y = objBox.maxExtents.y;
objBox.maxExtents.y = tmp;
}
if (objBox.minExtents.z > objBox.maxExtents.z) {
F32 tmp = objBox.minExtents.z;
objBox.minExtents.z = objBox.maxExtents.z;
objBox.maxExtents.z = tmp;
}
if (objBox.isValidBox()) {
mObjBox = objBox;
// Reset the World Box.
resetWorldBox();
}
mUpdateBuffers = false;
}

View file

@ -0,0 +1,127 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// 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.
//-----------------------------------------------------------------------------
#ifndef _RIBBON_H_
#define _RIBBON_H_
#ifndef _GAMEBASE_H_
#include "T3D/gameBase/gameBase.h"
#endif
#ifndef _GFXPRIMITIVEBUFFER_H_
#include "gfx/gfxPrimitiveBuffer.h"
#endif
#ifndef _GFXVERTEXBUFFER_H_
#include "gfx/gfxVertexBuffer.h"
#endif
#include "materials/materialParameters.h"
#include "math/util/matrixSet.h"
#define RIBBON_NUM_FIELDS 4
//--------------------------------------------------------------------------
class RibbonData : public GameBaseData
{
typedef GameBaseData Parent;
protected:
bool onAdd();
public:
U32 mRibbonLength; ///< The amount of segments that will make up the ribbon.
F32 mSizes[RIBBON_NUM_FIELDS]; ///< The radius for each keyframe.
ColorF mColours[RIBBON_NUM_FIELDS]; ///< The colour of the ribbon for each keyframe.
F32 mTimes[RIBBON_NUM_FIELDS]; ///< The relative time for each keyframe.
StringTableEntry mMatName; ///< The material for the ribbon.
bool mUseFadeOut; ///< If true, the ribbon will fade away after deletion.
F32 mFadeAwayStep; ///< How quickly the ribbons is faded away after deletion.
S32 segmentsPerUpdate; ///< Amount of segments to add each update.
F32 mTileScale; ///< A scalar to scale the texcoord.
bool mFixedTexcoords; ///< If true, texcoords will stay the same over the lifetime for each segment.
bool mTexcoordsRelativeToDistance; ///< If true, texcoords will not be stretched if the distance between 2 segments are long.
S32 mSegmentSkipAmount; ///< The amount of segments to skip each time segments are added.
RibbonData();
void packData(BitStream*);
void unpackData(BitStream*);
bool preload(bool server, String &errorBuffer);
static void initPersistFields();
DECLARE_CONOBJECT(RibbonData);
};
//--------------------------------------------------------------------------
class Ribbon : public GameBase
{
typedef GameBase Parent;
RibbonData* mDataBlock;
Vector<Point3F> mSegmentPoints; ///< The points in space where the ribbon has spawned segments.
BaseMatInstance *mRibbonMat;
MaterialParameterHandle* mRadiusSC;
MaterialParameterHandle* mRibbonProjSC;
GFXPrimitiveBufferHandle primBuffer;
GFXVertexBufferHandle<GFXVertexPCNTT> verts;
bool mUpdateBuffers; ///< If true, the vertex buffers need to be updated.
bool mDeleteOnEnd; ///< If true, the ribbon should delete itself as soon as the last segment is deleted
bool mUseFadeOut; ///< If true, the ribbon will fade away upon deletion
F32 mFadeAwayStep; ///< How quickly the ribbons is faded away after deletion.
F32 mFadeOut;
U32 mSegmentOffset;
U32 mSegmentIdx;
F32 mTravelledDistance; ///< How far the ribbon has travelled in it's lifetime.
protected:
bool onAdd();
void processTick(const Move*);
void advanceTime(F32);
void interpolateTick(F32 delta);
// Rendering
void prepRenderImage(SceneRenderState *state);
///Checks to see if ribbon is too long
U32 checkRibbonDistance(S32 segments);
void setShaderParams();
/// Construct the vertex and primitive buffers
void createBuffers(SceneRenderState *state, GFXVertexBufferHandle<GFXVertexPCNTT> &verts, GFXPrimitiveBufferHandle &pb, U32 segments);
public:
Ribbon();
~Ribbon();
DECLARE_CONOBJECT(Ribbon);
static void initPersistFields();
bool onNewDataBlock(GameBaseData*,bool);
void addSegmentPoint(Point3F &point, MatrixF &mat); ///< Used to add another segment to the ribbon.
void clearSegments() { mSegmentPoints.clear(); } ///< Delete all segments.
void deleteOnEnd(); ///< Delete the ribbon when all segments have been deleted.
void onRemove();
};
#endif // _H_RIBBON