mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-19 20:24:49 +00:00
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:
parent
2c5f643dbe
commit
cb9cfea1c4
701
Engine/source/T3D/fx/ribbon.cpp
Normal file
701
Engine/source/T3D/fx/ribbon.cpp
Normal 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;
|
||||
}
|
||||
127
Engine/source/T3D/fx/ribbon.h
Normal file
127
Engine/source/T3D/fx/ribbon.h
Normal 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
|
||||
|
||||
Loading…
Reference in a new issue