From 2c5f643dbe806e494d0e6d12f39b95de518cc2b4 Mon Sep 17 00:00:00 2001 From: Lukas Joergensen Date: Thu, 31 Jul 2014 00:22:45 +0200 Subject: [PATCH 1/7] Added PCNTT vertex type. This is a prerequisite change for the Ribbon implementation. --- Engine/source/gfx/gfxVertexTypes.cpp | 9 +++++++++ Engine/source/gfx/gfxVertexTypes.h | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/Engine/source/gfx/gfxVertexTypes.cpp b/Engine/source/gfx/gfxVertexTypes.cpp index ca4598270..dbee17550 100644 --- a/Engine/source/gfx/gfxVertexTypes.cpp +++ b/Engine/source/gfx/gfxVertexTypes.cpp @@ -99,6 +99,15 @@ GFXImplementVertexFormat( GFXVertexPNTT ) addElement( "TEXCOORD", GFXDeclType_Float2, 0 ); } +GFXImplementVertexFormat( GFXVertexPCNTT ) +{ + addElement( "POSITION", GFXDeclType_Float3 ); + addElement( "COLOR", GFXDeclType_Color ); + addElement( "NORMAL", GFXDeclType_Float3 ); + addElement( "TEXCOORD", GFXDeclType_Float2, 0 ); + addElement( "TEXCOORD", GFXDeclType_Float2, 1 ); +} + GFXImplementVertexFormat( GFXVertexPNTBT ) { addElement( "POSITION", GFXDeclType_Float3 ); diff --git a/Engine/source/gfx/gfxVertexTypes.h b/Engine/source/gfx/gfxVertexTypes.h index 9072f533c..8af285f5d 100644 --- a/Engine/source/gfx/gfxVertexTypes.h +++ b/Engine/source/gfx/gfxVertexTypes.h @@ -112,6 +112,14 @@ GFXDeclareVertexFormat( GFXVertexPNTT ) Point2F texCoord; }; +GFXDeclareVertexFormat( GFXVertexPCNTT ) +{ + Point3F point; + GFXVertexColor color; + Point3F normal; + Point2F texCoord[2]; +}; + GFXDeclareVertexFormat( GFXVertexPNTBT ) { Point3F point; From cb9cfea1c4aa5ae44664960ef58e7d0880cd591d Mon Sep 17 00:00:00 2001 From: Lukas Joergensen Date: Thu, 31 Jul 2014 00:25:52 +0200 Subject: [PATCH 2/7] 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. --- Engine/source/T3D/fx/ribbon.cpp | 701 ++++++++++++++++++++++++++++++++ Engine/source/T3D/fx/ribbon.h | 127 ++++++ 2 files changed, 828 insertions(+) create mode 100644 Engine/source/T3D/fx/ribbon.cpp create mode 100644 Engine/source/T3D/fx/ribbon.h diff --git a/Engine/source/T3D/fx/ribbon.cpp b/Engine/source/T3D/fx/ribbon.cpp new file mode 100644 index 000000000..d035c3491 --- /dev/null +++ b/Engine/source/T3D/fx/ribbon.cpp @@ -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( 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()); + + 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(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(); + 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 &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; +} \ No newline at end of file diff --git a/Engine/source/T3D/fx/ribbon.h b/Engine/source/T3D/fx/ribbon.h new file mode 100644 index 000000000..ce115d461 --- /dev/null +++ b/Engine/source/T3D/fx/ribbon.h @@ -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 mSegmentPoints; ///< The points in space where the ribbon has spawned segments. + BaseMatInstance *mRibbonMat; + MaterialParameterHandle* mRadiusSC; + MaterialParameterHandle* mRibbonProjSC; + GFXPrimitiveBufferHandle primBuffer; + GFXVertexBufferHandle 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 &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 + From a8a141e73c435d93a23fdd0f0d1aa86aa6f8ca90 Mon Sep 17 00:00:00 2001 From: Lukas Joergensen Date: Thu, 31 Jul 2014 00:31:02 +0200 Subject: [PATCH 3/7] RibbonNode class. Simple RibbonNode class for an implementation similar to that of ParticleEmitterNodes. Datablock currently has no static fields. --- Engine/source/T3D/fx/ribbonNode.cpp | 324 ++++++++++++++++++++++++++++ Engine/source/T3D/fx/ribbonNode.h | 108 ++++++++++ 2 files changed, 432 insertions(+) create mode 100644 Engine/source/T3D/fx/ribbonNode.cpp create mode 100644 Engine/source/T3D/fx/ribbonNode.h diff --git a/Engine/source/T3D/fx/ribbonNode.cpp b/Engine/source/T3D/fx/ribbonNode.cpp new file mode 100644 index 000000000..0343fc5b3 --- /dev/null +++ b/Engine/source/T3D/fx/ribbonNode.cpp @@ -0,0 +1,324 @@ +//----------------------------------------------------------------------------- +// 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 "ribbonNode.h" +#include "console/consoleTypes.h" +#include "core/stream/bitStream.h" +#include "T3D/fx/ribbon.h" +#include "math/mathIO.h" +#include "sim/netConnection.h" +#include "console/engineAPI.h" + +IMPLEMENT_CO_DATABLOCK_V1(RibbonNodeData); +IMPLEMENT_CO_NETOBJECT_V1(RibbonNode); + +ConsoleDocClass( RibbonNodeData, + "@brief Contains additional data to be associated with a RibbonNode." + "@ingroup FX\n" + ); + +ConsoleDocClass( RibbonNode, "" + ); + + +//----------------------------------------------------------------------------- +// RibbonNodeData +//----------------------------------------------------------------------------- +RibbonNodeData::RibbonNodeData() +{ +} + +RibbonNodeData::~RibbonNodeData() +{ + +} + +//----------------------------------------------------------------------------- +// initPersistFields +//----------------------------------------------------------------------------- +void RibbonNodeData::initPersistFields() +{ + Parent::initPersistFields(); +} + + +//----------------------------------------------------------------------------- +// RibbonNode +//----------------------------------------------------------------------------- +RibbonNode::RibbonNode() +{ + // Todo: ScopeAlways? + mNetFlags.set(Ghostable); + mTypeMask |= EnvironmentObjectType; + + mActive = true; + + mDataBlock = NULL; + mRibbonDatablock = NULL; + mRibbonDatablockId = 0; + mRibbon = NULL; +} + +//----------------------------------------------------------------------------- +// Destructor +//----------------------------------------------------------------------------- +RibbonNode::~RibbonNode() +{ + // +} + +//----------------------------------------------------------------------------- +// initPersistFields +//----------------------------------------------------------------------------- +void RibbonNode::initPersistFields() +{ + addField( "active", TYPEID< bool >(), Offset(mActive,RibbonNode), + "Controls whether ribbon is emitted from this node." ); + addField( "ribbon", TYPEID< RibbonData >(), Offset(mRibbonDatablock, RibbonNode), + "Datablock to use for the ribbon." ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- +// onAdd +//----------------------------------------------------------------------------- +bool RibbonNode::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + if( !mRibbonDatablock && mRibbonDatablockId != 0 ) + { + if( Sim::findObject(mRibbonDatablockId, mRibbonDatablock) == false ) + Con::errorf(ConsoleLogEntry::General, "RibbonNode::onAdd: Invalid packet, bad datablockId(mRibbonDatablock): %d", mRibbonDatablockId); + } + + if( isClientObject() ) + { + setRibbonDatablock( mRibbonDatablock ); + } + else + { + setMaskBits( StateMask | EmitterDBMask ); + } + + mObjBox.minExtents.set(-0.5, -0.5, -0.5); + mObjBox.maxExtents.set( 0.5, 0.5, 0.5); + resetWorldBox(); + addToScene(); + + return true; +} + +//----------------------------------------------------------------------------- +// onRemove +//----------------------------------------------------------------------------- +void RibbonNode::onRemove() +{ + removeFromScene(); + if( isClientObject() ) + { + if( mRibbon ) + { + mRibbon->deleteOnEnd(); + mRibbon = NULL; + } + } + + Parent::onRemove(); +} + +//----------------------------------------------------------------------------- +// onNewDataBlock +//----------------------------------------------------------------------------- +bool RibbonNode::onNewDataBlock( GameBaseData *dptr, bool reload ) +{ + mDataBlock = dynamic_cast( dptr ); + if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) + return false; + + // Todo: Uncomment if this is a "leaf" class + scriptOnNewDataBlock(); + return true; +} + +//----------------------------------------------------------------------------- +void RibbonNode::inspectPostApply() +{ + Parent::inspectPostApply(); + setMaskBits(StateMask | EmitterDBMask); +} + +//----------------------------------------------------------------------------- +// processTick +//----------------------------------------------------------------------------- +void RibbonNode::processTick(const Move* move) +{ + Parent::processTick(move); + + if ( isMounted() ) + { + MatrixF mat; + mMount.object->getMountTransform( mMount.node, mMount.xfm, &mat ); + setTransform( mat ); + } +} + +//----------------------------------------------------------------------------- +// advanceTime +//----------------------------------------------------------------------------- +void RibbonNode::advanceTime(F32 dt) +{ + Parent::advanceTime(dt); + + if(!mActive || mRibbon.isNull() || !mDataBlock) + return; + + MatrixF trans(getTransform()); + Point3F pos = getPosition(); + mRibbon->addSegmentPoint(pos, trans); +} + +//----------------------------------------------------------------------------- +// packUpdate +//----------------------------------------------------------------------------- +U32 RibbonNode::packUpdate(NetConnection* con, U32 mask, BitStream* stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + + if ( stream->writeFlag( mask & InitialUpdateMask ) ) + { + mathWrite(*stream, getTransform()); + mathWrite(*stream, getScale()); + } + + if ( stream->writeFlag( mask & EmitterDBMask ) ) + { + if( stream->writeFlag(mRibbonDatablock != NULL) ) + { + stream->writeRangedU32(mRibbonDatablock->getId(), DataBlockObjectIdFirst, + DataBlockObjectIdLast); + } + } + + if ( stream->writeFlag( mask & StateMask ) ) + { + stream->writeFlag( mActive ); + } + + return retMask; +} + +//----------------------------------------------------------------------------- +// unpackUpdate +//----------------------------------------------------------------------------- +void RibbonNode::unpackUpdate(NetConnection* con, BitStream* stream) +{ + Parent::unpackUpdate(con, stream); + + if ( stream->readFlag() ) + { + MatrixF temp; + Point3F tempScale; + mathRead(*stream, &temp); + mathRead(*stream, &tempScale); + + setScale(tempScale); + setTransform(temp); + } + + if ( stream->readFlag() ) + { + mRibbonDatablockId = stream->readFlag() ? + stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast) : 0; + + RibbonData *emitterDB = NULL; + Sim::findObject( mRibbonDatablockId, emitterDB ); + if ( isProperlyAdded() ) + setRibbonDatablock( emitterDB ); + } + + if ( stream->readFlag() ) + { + mActive = stream->readFlag(); + } +} + +//----------------------------------------------------------------------------- +// setRibbonDatablock +//----------------------------------------------------------------------------- +void RibbonNode::setRibbonDatablock(RibbonData* data) +{ + if ( isServerObject() ) + { + setMaskBits( EmitterDBMask ); + } + else + { + Ribbon* pRibbon = NULL; + if ( data ) + { + // Create emitter with new datablock + pRibbon = new Ribbon; + pRibbon->onNewDataBlock( data, false ); + if( pRibbon->registerObject() == false ) + { + Con::warnf(ConsoleLogEntry::General, "Could not register base ribbon of class: %s", data->getName() ? data->getName() : data->getIdString() ); + delete pRibbon; + return; + } + } + + // Replace emitter + if ( mRibbon ) + mRibbon->deleteOnEnd(); + + mRibbon = pRibbon; + } + + mRibbonDatablock = data; +} + +DefineEngineMethod(RibbonNode, setRibbonDatablock, void, (RibbonData* ribbonDatablock), (0), + "Assigns the datablock for this ribbon node.\n" + "@param ribbonDatablock RibbonData datablock to assign\n" + "@tsexample\n" + "// Assign a new emitter datablock\n" + "%emitter.setRibbonDatablock( %ribbonDatablock );\n" + "@endtsexample\n" ) +{ + if ( !ribbonDatablock ) + { + Con::errorf("RibbonData datablock could not be found when calling setRibbonDataBlock in ribbonNode."); + return; + } + + object->setRibbonDatablock(ribbonDatablock); +} + +DefineEngineMethod(RibbonNode, setActive, void, (bool active),, + "Turns the ribbon on or off.\n" + "@param active New ribbon state\n" ) +{ + object->setActive( active ); +} diff --git a/Engine/source/T3D/fx/ribbonNode.h b/Engine/source/T3D/fx/ribbonNode.h new file mode 100644 index 000000000..62b1158e4 --- /dev/null +++ b/Engine/source/T3D/fx/ribbonNode.h @@ -0,0 +1,108 @@ +//----------------------------------------------------------------------------- +// 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_NODE_H_ +#define _RIBBON_NODE_H_ + +#ifndef _GAMEBASE_H_ +#include "T3D/gameBase/gameBase.h" +#endif + +class RibbonData; +class Ribbon; + +//***************************************************************************** +// ParticleEmitterNodeData +//***************************************************************************** +class RibbonNodeData : public GameBaseData +{ + typedef GameBaseData Parent; + + //-------------------------------------- Console set variables +public: + F32 timeMultiple; + + //-------------------------------------- load set variables + +public: + RibbonNodeData(); + ~RibbonNodeData(); + + DECLARE_CONOBJECT(RibbonNodeData); + static void initPersistFields(); +}; + + +//***************************************************************************** +// ParticleEmitterNode +//***************************************************************************** +class RibbonNode : public GameBase +{ + typedef GameBase Parent; + + enum MaskBits + { + StateMask = Parent::NextFreeMask << 0, + EmitterDBMask = Parent::NextFreeMask << 1, + NextFreeMask = Parent::NextFreeMask << 2, + }; + + RibbonNodeData* mDataBlock; + +protected: + bool onAdd(); + void onRemove(); + bool onNewDataBlock( GameBaseData *dptr, bool reload ); + void inspectPostApply(); + + RibbonData* mRibbonDatablock; + S32 mRibbonDatablockId; + + SimObjectPtr mRibbon; + + bool mActive; + +public: + RibbonNode(); + ~RibbonNode(); + + Ribbon *getRibbonEmitter() {return mRibbon;} + + // Time/Move Management + + void processTick(const Move* move); + void advanceTime(F32 dt); + + DECLARE_CONOBJECT(RibbonNode); + static void initPersistFields(); + + U32 packUpdate (NetConnection *conn, U32 mask, BitStream* stream); + void unpackUpdate(NetConnection *conn, BitStream* stream); + + inline bool getActive( void ) { return mActive; }; + inline void setActive( bool active ) { mActive = active; setMaskBits( StateMask ); }; + + void setRibbonDatablock(RibbonData* data); +}; + +#endif // _RIBBON_NODE_H_ + From 35f88a77b16a4760fd3842c543e58b742c4fd930 Mon Sep 17 00:00:00 2001 From: LukasPJ Date: Thu, 18 Sep 2014 21:49:25 +0200 Subject: [PATCH 4/7] Script integration for Ribbons --- Templates/Empty/game/art/ribbons/materials.cs | 48 +++++++++++ .../Empty/game/art/ribbons/ribbonExec.cs | 23 ++++++ Templates/Empty/game/art/ribbons/ribbons.cs | 44 ++++++++++ .../Empty/game/core/scripts/server/game.cs | 1 + Templates/Empty/game/scripts/server/game.cs | 1 + .../common/ribbons/basicRibbonShaderP.hlsl | 18 ++++ .../common/ribbons/basicRibbonShaderV.hlsl | 34 ++++++++ .../worldEditor/gui/objectBuilderGui.ed.gui | 8 ++ .../worldEditor/scripts/editors/creator.ed.cs | 1 + Templates/Full/game/art/ribbons/materials.cs | 77 ++++++++++++++++++ Templates/Full/game/art/ribbons/ribTex.png | Bin 0 -> 111995 bytes Templates/Full/game/art/ribbons/ribbonExec.cs | 23 ++++++ Templates/Full/game/art/ribbons/ribbons.cs | 63 ++++++++++++++ .../Full/game/core/scripts/server/game.cs | 1 + Templates/Full/game/scripts/server/game.cs | 1 + .../common/ribbons/basicRibbonShaderP.hlsl | 18 ++++ .../common/ribbons/basicRibbonShaderV.hlsl | 34 ++++++++ .../common/ribbons/texRibbonShaderP.hlsl | 20 +++++ .../common/ribbons/texRibbonShaderV.hlsl | 34 ++++++++ .../worldEditor/gui/objectBuilderGui.ed.gui | 8 ++ .../worldEditor/scripts/editors/creator.ed.cs | 1 + 21 files changed, 458 insertions(+) create mode 100644 Templates/Empty/game/art/ribbons/materials.cs create mode 100644 Templates/Empty/game/art/ribbons/ribbonExec.cs create mode 100644 Templates/Empty/game/art/ribbons/ribbons.cs create mode 100644 Templates/Empty/game/shaders/common/ribbons/basicRibbonShaderP.hlsl create mode 100644 Templates/Empty/game/shaders/common/ribbons/basicRibbonShaderV.hlsl create mode 100644 Templates/Full/game/art/ribbons/materials.cs create mode 100644 Templates/Full/game/art/ribbons/ribTex.png create mode 100644 Templates/Full/game/art/ribbons/ribbonExec.cs create mode 100644 Templates/Full/game/art/ribbons/ribbons.cs create mode 100644 Templates/Full/game/shaders/common/ribbons/basicRibbonShaderP.hlsl create mode 100644 Templates/Full/game/shaders/common/ribbons/basicRibbonShaderV.hlsl create mode 100644 Templates/Full/game/shaders/common/ribbons/texRibbonShaderP.hlsl create mode 100644 Templates/Full/game/shaders/common/ribbons/texRibbonShaderV.hlsl diff --git a/Templates/Empty/game/art/ribbons/materials.cs b/Templates/Empty/game/art/ribbons/materials.cs new file mode 100644 index 000000000..a8ee7b19c --- /dev/null +++ b/Templates/Empty/game/art/ribbons/materials.cs @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- + +// This material should work fine for uniformly colored ribbons. + +//Basic ribbon shader///////////////////////////////////////////// + +new ShaderData( basicRibbonShader ) +{ + DXVertexShaderFile = "shaders/common/ribbons/basicRibbonShaderV.hlsl"; + DXPixelShaderFile = "shaders/common/ribbons/basicRibbonShaderP.hlsl"; + + pixVersion = 2.0; +}; + +singleton CustomMaterial( basicRibbonMat ) +{ + shader = basicRibbonShader; + version = 2.0; + + emissive[0] = true; + + doubleSided = true; + translucent = true; + BlendOp = AddAlpha; + translucentBlendOp = AddAlpha; + + preload = true; +}; \ No newline at end of file diff --git a/Templates/Empty/game/art/ribbons/ribbonExec.cs b/Templates/Empty/game/art/ribbons/ribbonExec.cs new file mode 100644 index 000000000..8193b1b8b --- /dev/null +++ b/Templates/Empty/game/art/ribbons/ribbonExec.cs @@ -0,0 +1,23 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- + +exec("./ribbons.cs"); \ No newline at end of file diff --git a/Templates/Empty/game/art/ribbons/ribbons.cs b/Templates/Empty/game/art/ribbons/ribbons.cs new file mode 100644 index 000000000..ce65e6fe8 --- /dev/null +++ b/Templates/Empty/game/art/ribbons/ribbons.cs @@ -0,0 +1,44 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- + +datablock RibbonNodeData(DefaultRibbonNodeData) +{ + timeMultiple = 1.0; +}; + +//ribbon data//////////////////////////////////////// + +datablock RibbonData(basicRibbon) +{ + size[0] = 0.5; + color[0] = "1.0 0.0 0.0 1.0"; + position[0] = 0.0; + + size[1] = 0.0; + color[1] = "1.0 0.0 0.0 0.0"; + position[1] = 1.0; + + RibbonLength = 40; + fadeAwayStep = 0.1; + UseFadeOut = true; + RibbonMaterial = basicRibbonMat; +}; \ No newline at end of file diff --git a/Templates/Empty/game/core/scripts/server/game.cs b/Templates/Empty/game/core/scripts/server/game.cs index d80dd4468..c135e6f99 100644 --- a/Templates/Empty/game/core/scripts/server/game.cs +++ b/Templates/Empty/game/core/scripts/server/game.cs @@ -34,6 +34,7 @@ function onServerCreated() // Load up any objects or datablocks saved to the editor managed scripts %datablockFiles = new ArrayObject(); + %datablockFiles.add( "art/ribbons/ribbonExec.cs" ); %datablockFiles.add( "art/particles/managedParticleData.cs" ); %datablockFiles.add( "art/particles/managedParticleEmitterData.cs" ); %datablockFiles.add( "art/decals/managedDecalData.cs" ); diff --git a/Templates/Empty/game/scripts/server/game.cs b/Templates/Empty/game/scripts/server/game.cs index 4826c0de2..d9529ca01 100644 --- a/Templates/Empty/game/scripts/server/game.cs +++ b/Templates/Empty/game/scripts/server/game.cs @@ -144,6 +144,7 @@ function onServerCreated() // Load up any objects or datablocks saved to the editor managed scripts %datablockFiles = new ArrayObject(); + %datablockFiles.add( "art/ribbons/ribbonExec.cs" ); %datablockFiles.add( "art/particles/managedParticleData.cs" ); %datablockFiles.add( "art/particles/managedParticleEmitterData.cs" ); %datablockFiles.add( "art/decals/managedDecalData.cs" ); diff --git a/Templates/Empty/game/shaders/common/ribbons/basicRibbonShaderP.hlsl b/Templates/Empty/game/shaders/common/ribbons/basicRibbonShaderP.hlsl new file mode 100644 index 000000000..7ce54e3aa --- /dev/null +++ b/Templates/Empty/game/shaders/common/ribbons/basicRibbonShaderP.hlsl @@ -0,0 +1,18 @@ +#define IN_HLSL +#include "../common/shdrConsts.h" + +struct v2f +{ + + float2 texCoord : TEXCOORD0; + float2 shiftdata : TEXCOORD1; + float4 color : COLOR0; +}; + +float4 main(v2f IN) : COLOR0 +{ + float fade = 1.0 - abs(IN.shiftdata.y - 0.5) * 2.0; + IN.color.xyz = IN.color.xyz + pow(fade, 4) / 10; + IN.color.a = IN.color.a * fade; + return IN.color; +} \ No newline at end of file diff --git a/Templates/Empty/game/shaders/common/ribbons/basicRibbonShaderV.hlsl b/Templates/Empty/game/shaders/common/ribbons/basicRibbonShaderV.hlsl new file mode 100644 index 000000000..5fd4ecbc0 --- /dev/null +++ b/Templates/Empty/game/shaders/common/ribbons/basicRibbonShaderV.hlsl @@ -0,0 +1,34 @@ +#define IN_HLSL +#include "../common/shdrConsts.h" + +struct a2v +{ + float2 texCoord : TEXCOORD0; + float2 shiftdata : TEXCOORD1; + float3 normal : NORMAL; + float4 position : POSITION; + float4 color : COLOR0; +}; + +struct v2f +{ + float4 hpos : POSITION; + float2 texCoord : TEXCOORD0; + float2 shiftdata : TEXCOORD1; + float4 color : COLOR0; +}; + +uniform float4x4 modelview; +uniform float3 eyePos; + +v2f main(a2v IN) +{ + v2f OUT; + + OUT.hpos = mul(modelview, IN.position); + OUT.color = IN.color; + OUT.texCoord = IN.texCoord; + OUT.shiftdata = IN.shiftdata; + + return OUT; +} \ No newline at end of file diff --git a/Templates/Empty/game/tools/worldEditor/gui/objectBuilderGui.ed.gui b/Templates/Empty/game/tools/worldEditor/gui/objectBuilderGui.ed.gui index 19c396a57..33cb5de75 100644 --- a/Templates/Empty/game/tools/worldEditor/gui/objectBuilderGui.ed.gui +++ b/Templates/Empty/game/tools/worldEditor/gui/objectBuilderGui.ed.gui @@ -862,6 +862,14 @@ function ObjectBuilderGui::buildParticleEmitterNode(%this) %this.process(); } +function ObjectBuilderGui::buildRibbonNode(%this) +{ + %this.objectClassName = "RibbonNode"; + %this.addField("dataBlock", "TypeDataBlock", "datablock", "RibbonNodeData"); + %this.addField("ribbon", "TypeDataBlock", "Ribbon data", "RibbonData"); + %this.process(); +} + function ObjectBuilderGui::buildParticleSimulation(%this) { %this.objectClassName = "ParticleSimulation"; diff --git a/Templates/Empty/game/tools/worldEditor/scripts/editors/creator.ed.cs b/Templates/Empty/game/tools/worldEditor/scripts/editors/creator.ed.cs index d63542d67..75d41eb53 100644 --- a/Templates/Empty/game/tools/worldEditor/scripts/editors/creator.ed.cs +++ b/Templates/Empty/game/tools/worldEditor/scripts/editors/creator.ed.cs @@ -46,6 +46,7 @@ function EWCreatorWindow::init( %this ) %this.registerMissionObject( "SFXEmitter", "Sound Emitter" ); %this.registerMissionObject( "Precipitation" ); %this.registerMissionObject( "ParticleEmitterNode", "Particle Emitter" ); + %this.registerMissionObject( "RibbonNode", "Ribbon" ); // Legacy features. Users should use Ground Cover and the Forest Editor. //%this.registerMissionObject( "fxShapeReplicator", "Shape Replicator" ); diff --git a/Templates/Full/game/art/ribbons/materials.cs b/Templates/Full/game/art/ribbons/materials.cs new file mode 100644 index 000000000..f9115356f --- /dev/null +++ b/Templates/Full/game/art/ribbons/materials.cs @@ -0,0 +1,77 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- + +// This material should work fine for uniformly colored ribbons. + +//Basic ribbon shader///////////////////////////////////////////// + +new ShaderData( basicRibbonShader ) +{ + DXVertexShaderFile = "shaders/common/ribbons/basicRibbonShaderV.hlsl"; + DXPixelShaderFile = "shaders/common/ribbons/basicRibbonShaderP.hlsl"; + + pixVersion = 2.0; +}; + +singleton CustomMaterial( basicRibbonMat ) +{ + shader = basicRibbonShader; + version = 2.0; + + emissive[0] = true; + + doubleSided = true; + translucent = true; + BlendOp = AddAlpha; + translucentBlendOp = AddAlpha; + + preload = true; +}; + +// This material can render a texture on top of a ribbon. + +//Texture ribbon shader///////////////////////////////////////////// + +new ShaderData( texRibbonShader ) +{ + DXVertexShaderFile = "shaders/common/ribbons/texRibbonShaderV.hlsl"; + DXPixelShaderFile = "shaders/common/ribbons/texRibbonShaderP.hlsl"; + + pixVersion = 2.0; +}; + +singleton CustomMaterial( texRibbonMat ) +{ + shader = texRibbonShader; + version = 2.0; + + emissive[0] = true; + + doubleSided = true; + translucent = true; + BlendOp = AddAlpha; + translucentBlendOp = AddAlpha; + + sampler["ribTex"] = "art/ribbons/ribTex.png"; + + preload = true; +}; \ No newline at end of file diff --git a/Templates/Full/game/art/ribbons/ribTex.png b/Templates/Full/game/art/ribbons/ribTex.png new file mode 100644 index 0000000000000000000000000000000000000000..82dffb96afab194b0960040ce5e4516dd4eb6c47 GIT binary patch literal 111995 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%0DrD`+a1mc6f)!GG8-qQDAIsWd*fd60e-aN*# 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 DLDPyXcFul~|6ee|{grcZqQcQ5|n`+hVV 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 b3IqNhkO7)w)nL>p00000NkvXXu0mjf36)ff literal 0 HcmV?d00001 diff --git a/Templates/Full/game/art/ribbons/ribbonExec.cs b/Templates/Full/game/art/ribbons/ribbonExec.cs new file mode 100644 index 000000000..8193b1b8b --- /dev/null +++ b/Templates/Full/game/art/ribbons/ribbonExec.cs @@ -0,0 +1,23 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- + +exec("./ribbons.cs"); \ No newline at end of file diff --git a/Templates/Full/game/art/ribbons/ribbons.cs b/Templates/Full/game/art/ribbons/ribbons.cs new file mode 100644 index 000000000..aef6204e4 --- /dev/null +++ b/Templates/Full/game/art/ribbons/ribbons.cs @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- + +datablock RibbonNodeData(DefaultRibbonNodeData) +{ + timeMultiple = 1.0; +}; + +//ribbon data//////////////////////////////////////// + +datablock RibbonData(basicRibbon) +{ + size[0] = 0.5; + color[0] = "1.0 0.0 0.0 1.0"; + position[0] = 0.0; + + size[1] = 0.0; + color[1] = "1.0 0.0 0.0 0.0"; + position[1] = 1.0; + + RibbonLength = 40; + fadeAwayStep = 0.1; + UseFadeOut = true; + RibbonMaterial = basicRibbonMat; +}; + +datablock RibbonData(texRibbon) +{ + RibbonMaterial = texRibbonMat; + size[0] = 0.5; + color[0] = "1.0 1.0 1.0 1.0"; + position[0] = 0.0; + + size[1] = 0.5; + color[1] = "1.0 1.0 1.0 1.0"; + position[1] = 1.0; + + RibbonLength = 40; + fadeAwayStep = 0.1; + UseFadeOut = true; + tileScale = 1; + fixedTexCoords = true; + TexcoordsRelativeToDistance = true; +}; \ No newline at end of file diff --git a/Templates/Full/game/core/scripts/server/game.cs b/Templates/Full/game/core/scripts/server/game.cs index d80dd4468..c135e6f99 100644 --- a/Templates/Full/game/core/scripts/server/game.cs +++ b/Templates/Full/game/core/scripts/server/game.cs @@ -34,6 +34,7 @@ function onServerCreated() // Load up any objects or datablocks saved to the editor managed scripts %datablockFiles = new ArrayObject(); + %datablockFiles.add( "art/ribbons/ribbonExec.cs" ); %datablockFiles.add( "art/particles/managedParticleData.cs" ); %datablockFiles.add( "art/particles/managedParticleEmitterData.cs" ); %datablockFiles.add( "art/decals/managedDecalData.cs" ); diff --git a/Templates/Full/game/scripts/server/game.cs b/Templates/Full/game/scripts/server/game.cs index ace3ac2f8..42d329cd2 100644 --- a/Templates/Full/game/scripts/server/game.cs +++ b/Templates/Full/game/scripts/server/game.cs @@ -50,6 +50,7 @@ function onServerCreated() // Load up any objects or datablocks saved to the editor managed scripts %datablockFiles = new ArrayObject(); + %datablockFiles.add( "art/ribbons/ribbonExec.cs" ); %datablockFiles.add( "art/particles/managedParticleData.cs" ); %datablockFiles.add( "art/particles/managedParticleEmitterData.cs" ); %datablockFiles.add( "art/decals/managedDecalData.cs" ); diff --git a/Templates/Full/game/shaders/common/ribbons/basicRibbonShaderP.hlsl b/Templates/Full/game/shaders/common/ribbons/basicRibbonShaderP.hlsl new file mode 100644 index 000000000..7ce54e3aa --- /dev/null +++ b/Templates/Full/game/shaders/common/ribbons/basicRibbonShaderP.hlsl @@ -0,0 +1,18 @@ +#define IN_HLSL +#include "../common/shdrConsts.h" + +struct v2f +{ + + float2 texCoord : TEXCOORD0; + float2 shiftdata : TEXCOORD1; + float4 color : COLOR0; +}; + +float4 main(v2f IN) : COLOR0 +{ + float fade = 1.0 - abs(IN.shiftdata.y - 0.5) * 2.0; + IN.color.xyz = IN.color.xyz + pow(fade, 4) / 10; + IN.color.a = IN.color.a * fade; + return IN.color; +} \ No newline at end of file diff --git a/Templates/Full/game/shaders/common/ribbons/basicRibbonShaderV.hlsl b/Templates/Full/game/shaders/common/ribbons/basicRibbonShaderV.hlsl new file mode 100644 index 000000000..5fd4ecbc0 --- /dev/null +++ b/Templates/Full/game/shaders/common/ribbons/basicRibbonShaderV.hlsl @@ -0,0 +1,34 @@ +#define IN_HLSL +#include "../common/shdrConsts.h" + +struct a2v +{ + float2 texCoord : TEXCOORD0; + float2 shiftdata : TEXCOORD1; + float3 normal : NORMAL; + float4 position : POSITION; + float4 color : COLOR0; +}; + +struct v2f +{ + float4 hpos : POSITION; + float2 texCoord : TEXCOORD0; + float2 shiftdata : TEXCOORD1; + float4 color : COLOR0; +}; + +uniform float4x4 modelview; +uniform float3 eyePos; + +v2f main(a2v IN) +{ + v2f OUT; + + OUT.hpos = mul(modelview, IN.position); + OUT.color = IN.color; + OUT.texCoord = IN.texCoord; + OUT.shiftdata = IN.shiftdata; + + return OUT; +} \ No newline at end of file diff --git a/Templates/Full/game/shaders/common/ribbons/texRibbonShaderP.hlsl b/Templates/Full/game/shaders/common/ribbons/texRibbonShaderP.hlsl new file mode 100644 index 000000000..a93c6162b --- /dev/null +++ b/Templates/Full/game/shaders/common/ribbons/texRibbonShaderP.hlsl @@ -0,0 +1,20 @@ +#define IN_HLSL +#include "../common/shdrConsts.h" +#include "shaders/common/torque.hlsl" + +uniform sampler2D ribTex : register(S0); + +struct v2f +{ + + float2 texCoord : TEXCOORD0; + float2 shiftdata : TEXCOORD1; + float4 color : COLOR0; +}; + +float4 main(v2f IN) : COLOR0 +{ + float4 Tex = tex2D(ribTex,IN.texCoord); + Tex.a *= IN.color.a; + return hdrEncode(Tex); +} \ No newline at end of file diff --git a/Templates/Full/game/shaders/common/ribbons/texRibbonShaderV.hlsl b/Templates/Full/game/shaders/common/ribbons/texRibbonShaderV.hlsl new file mode 100644 index 000000000..5fd4ecbc0 --- /dev/null +++ b/Templates/Full/game/shaders/common/ribbons/texRibbonShaderV.hlsl @@ -0,0 +1,34 @@ +#define IN_HLSL +#include "../common/shdrConsts.h" + +struct a2v +{ + float2 texCoord : TEXCOORD0; + float2 shiftdata : TEXCOORD1; + float3 normal : NORMAL; + float4 position : POSITION; + float4 color : COLOR0; +}; + +struct v2f +{ + float4 hpos : POSITION; + float2 texCoord : TEXCOORD0; + float2 shiftdata : TEXCOORD1; + float4 color : COLOR0; +}; + +uniform float4x4 modelview; +uniform float3 eyePos; + +v2f main(a2v IN) +{ + v2f OUT; + + OUT.hpos = mul(modelview, IN.position); + OUT.color = IN.color; + OUT.texCoord = IN.texCoord; + OUT.shiftdata = IN.shiftdata; + + return OUT; +} \ No newline at end of file diff --git a/Templates/Full/game/tools/worldEditor/gui/objectBuilderGui.ed.gui b/Templates/Full/game/tools/worldEditor/gui/objectBuilderGui.ed.gui index 19c396a57..33cb5de75 100644 --- a/Templates/Full/game/tools/worldEditor/gui/objectBuilderGui.ed.gui +++ b/Templates/Full/game/tools/worldEditor/gui/objectBuilderGui.ed.gui @@ -862,6 +862,14 @@ function ObjectBuilderGui::buildParticleEmitterNode(%this) %this.process(); } +function ObjectBuilderGui::buildRibbonNode(%this) +{ + %this.objectClassName = "RibbonNode"; + %this.addField("dataBlock", "TypeDataBlock", "datablock", "RibbonNodeData"); + %this.addField("ribbon", "TypeDataBlock", "Ribbon data", "RibbonData"); + %this.process(); +} + function ObjectBuilderGui::buildParticleSimulation(%this) { %this.objectClassName = "ParticleSimulation"; diff --git a/Templates/Full/game/tools/worldEditor/scripts/editors/creator.ed.cs b/Templates/Full/game/tools/worldEditor/scripts/editors/creator.ed.cs index d63542d67..75d41eb53 100644 --- a/Templates/Full/game/tools/worldEditor/scripts/editors/creator.ed.cs +++ b/Templates/Full/game/tools/worldEditor/scripts/editors/creator.ed.cs @@ -46,6 +46,7 @@ function EWCreatorWindow::init( %this ) %this.registerMissionObject( "SFXEmitter", "Sound Emitter" ); %this.registerMissionObject( "Precipitation" ); %this.registerMissionObject( "ParticleEmitterNode", "Particle Emitter" ); + %this.registerMissionObject( "RibbonNode", "Ribbon" ); // Legacy features. Users should use Ground Cover and the Forest Editor. //%this.registerMissionObject( "fxShapeReplicator", "Shape Replicator" ); From f5fb2fdf763e9b109f2e4a2a8eabdd976b29a622 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Wed, 24 Sep 2014 08:08:42 +1000 Subject: [PATCH 5/7] Improved code style in ribbon files. --- Engine/source/T3D/fx/ribbon.cpp | 44 +++++++++++-------- Engine/source/T3D/fx/ribbon.h | 67 ++++++++++++++++++----------- Engine/source/T3D/fx/ribbonNode.cpp | 2 +- Engine/source/T3D/fx/ribbonNode.h | 5 +-- 4 files changed, 68 insertions(+), 50 deletions(-) diff --git a/Engine/source/T3D/fx/ribbon.cpp b/Engine/source/T3D/fx/ribbon.cpp index d035c3491..49150711e 100644 --- a/Engine/source/T3D/fx/ribbon.cpp +++ b/Engine/source/T3D/fx/ribbon.cpp @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC +// Copyright (c) 2014 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 @@ -47,7 +47,7 @@ IMPLEMENT_CO_NETOBJECT_V1(Ribbon); // RibbonData::RibbonData() { - for (U8 i = 0; i < RIBBON_NUM_FIELDS; i++) { + for (U8 i = 0; i < NumFields; i++) { mSizes[i] = 0.0f; mColours[i].set(0.0f, 0.0f, 0.0f, 1.0f); mTimes[i] = -1.0f; @@ -70,32 +70,38 @@ void RibbonData::initPersistFields() { Parent::initPersistFields(); - addField("size", TypeF32, Offset(mSizes, RibbonData), RIBBON_NUM_FIELDS, + addGroup("Ribbon"); + + addField("size", TypeF32, Offset(mSizes, RibbonData), NumFields, "The size of the ribbon at the specified keyframe."); - addField("color", TypeColorF, Offset(mColours, RibbonData), RIBBON_NUM_FIELDS, + addField("color", TypeColorF, Offset(mColours, RibbonData), NumFields, "The colour of the ribbon at the specified keyframe."); - addField("position", TypeF32, Offset(mTimes, RibbonData), RIBBON_NUM_FIELDS, + addField("position", TypeF32, Offset(mTimes, RibbonData), NumFields, "The position of the keyframe along the lifetime of the ribbon."); - addField("RibbonLength", TypeS32, Offset(mRibbonLength, RibbonData), + + 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), + addField("skipAmount", TypeS32, Offset(mSegmentSkipAmount, RibbonData), + "The amount of segments to skip each update."); + + addField("useFadeOut", TypeBool, Offset(mUseFadeOut, RibbonData), + "If true, the ribbon will fade away after deletion."); + addField("fadeAwayStep", TypeF32, Offset(mFadeAwayStep, RibbonData), + "How much to fade the ribbon with each update, after deletion."); + addField("ribbonMaterial", TypeString, Offset(mMatName, RibbonData), + "The material the ribbon uses for rendering."); + 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), + addField("texcoordsRelativeToDistance", TypeBool, Offset(mTexcoordsRelativeToDistance, RibbonData), "If true, texture coordinates are scaled relative to distance, this prevents" "'stretched' textures."); + + endGroup("Ribbon"); } @@ -122,7 +128,7 @@ void RibbonData::packData(BitStream* stream) { Parent::packData(stream); - for (U8 i = 0; i < RIBBON_NUM_FIELDS; i++) { + for (U8 i = 0; i < NumFields; i++) { stream->write(mSizes[i]); stream->write(mColours[i]); stream->write(mTimes[i]); @@ -142,7 +148,7 @@ void RibbonData::unpackData(BitStream* stream) { Parent::unpackData(stream); - for (U8 i = 0; i < RIBBON_NUM_FIELDS; i++) { + for (U8 i = 0; i < NumFields; i++) { stream->read(&mSizes[i]); stream->read(&mColours[i]); stream->read(&mTimes[i]); @@ -558,7 +564,7 @@ void Ribbon::createBuffers(SceneRenderState *state, GFXVertexBufferHandlemSizes[0]; ColorF tColor = mDataBlock->mColours[0]; - for (U8 j = 0; j < RIBBON_NUM_FIELDS-1; j++) { + for (U8 j = 0; j < RibbonData::NumFields-1; j++) { F32 curPosition = mDataBlock->mTimes[j]; F32 curRadius = mDataBlock->mSizes[j]; diff --git a/Engine/source/T3D/fx/ribbon.h b/Engine/source/T3D/fx/ribbon.h index ce115d461..10ca8d40b 100644 --- a/Engine/source/T3D/fx/ribbon.h +++ b/Engine/source/T3D/fx/ribbon.h @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC +// Copyright (c) 2014 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 @@ -38,8 +38,6 @@ #include "materials/materialParameters.h" #include "math/util/matrixSet.h" -#define RIBBON_NUM_FIELDS 4 - //-------------------------------------------------------------------------- class RibbonData : public GameBaseData { @@ -50,19 +48,26 @@ protected: 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. + enum Constants + { + NumFields = 4 + }; + + F32 mSizes[NumFields]; ///< The radius for each keyframe. + ColorF mColours[NumFields]; ///< The colour of the ribbon for each keyframe. + F32 mTimes[NumFields]; ///< The relative time for each keyframe. + + U32 mRibbonLength; ///< The amount of segments that will make up the ribbon. + S32 segmentsPerUpdate; ///< Amount of segments to add each update. S32 mSegmentSkipAmount; ///< The amount of segments to skip each time segments are added. + bool mUseFadeOut; ///< If true, the ribbon will fade away after deletion. + F32 mFadeAwayStep; ///< How quickly the ribbons is faded away after deletion. + StringTableEntry mMatName; ///< The material for the ribbon. + 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. + RibbonData(); void packData(BitStream*); @@ -77,21 +82,25 @@ public: class Ribbon : public GameBase { typedef GameBase Parent; + RibbonData* mDataBlock; + + 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; + F32 mTravelledDistance; ///< How far the ribbon has travelled in it's lifetime. + Vector mSegmentPoints; ///< The points in space where the ribbon has spawned segments. + U32 mSegmentOffset; + U32 mSegmentIdx; + + bool mUpdateBuffers; ///< If true, the vertex buffers need to be updated. BaseMatInstance *mRibbonMat; MaterialParameterHandle* mRadiusSC; MaterialParameterHandle* mRibbonProjSC; GFXPrimitiveBufferHandle primBuffer; GFXVertexBufferHandle 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: @@ -102,10 +111,11 @@ protected: // Rendering void prepRenderImage(SceneRenderState *state); + void setShaderParams(); ///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 &verts, GFXPrimitiveBufferHandle &pb, U32 segments); @@ -116,11 +126,16 @@ public: 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(); + /// Used to add another segment to the ribbon. + void addSegmentPoint(Point3F &point, MatrixF &mat); + + /// Delete all segments. + void clearSegments() { mSegmentPoints.clear(); } + + /// Delete the ribbon when all segments have been deleted. + void deleteOnEnd(); }; #endif // _H_RIBBON diff --git a/Engine/source/T3D/fx/ribbonNode.cpp b/Engine/source/T3D/fx/ribbonNode.cpp index 0343fc5b3..2582fbe6f 100644 --- a/Engine/source/T3D/fx/ribbonNode.cpp +++ b/Engine/source/T3D/fx/ribbonNode.cpp @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC +// Copyright (c) 2014 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 diff --git a/Engine/source/T3D/fx/ribbonNode.h b/Engine/source/T3D/fx/ribbonNode.h index 62b1158e4..cdd86ef33 100644 --- a/Engine/source/T3D/fx/ribbonNode.h +++ b/Engine/source/T3D/fx/ribbonNode.h @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC +// Copyright (c) 2014 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 @@ -37,12 +37,9 @@ class RibbonNodeData : public GameBaseData { typedef GameBaseData Parent; - //-------------------------------------- Console set variables public: F32 timeMultiple; - //-------------------------------------- load set variables - public: RibbonNodeData(); ~RibbonNodeData(); From 2a9e6ef5e62c27b48852fe27e2ce91341b3876e4 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Wed, 24 Sep 2014 08:27:03 +1000 Subject: [PATCH 6/7] Renamed default ribbon datablocks. --- Templates/Empty/game/art/ribbons/materials.cs | 6 +++--- Templates/Empty/game/art/ribbons/ribbons.cs | 6 +++--- Templates/Full/game/art/ribbons/materials.cs | 16 ++++++++-------- Templates/Full/game/art/ribbons/ribbons.cs | 8 ++++---- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Templates/Empty/game/art/ribbons/materials.cs b/Templates/Empty/game/art/ribbons/materials.cs index a8ee7b19c..d53ada9e2 100644 --- a/Templates/Empty/game/art/ribbons/materials.cs +++ b/Templates/Empty/game/art/ribbons/materials.cs @@ -24,7 +24,7 @@ //Basic ribbon shader///////////////////////////////////////////// -new ShaderData( basicRibbonShader ) +new ShaderData( BasicRibbonShader ) { DXVertexShaderFile = "shaders/common/ribbons/basicRibbonShaderV.hlsl"; DXPixelShaderFile = "shaders/common/ribbons/basicRibbonShaderP.hlsl"; @@ -32,9 +32,9 @@ new ShaderData( basicRibbonShader ) pixVersion = 2.0; }; -singleton CustomMaterial( basicRibbonMat ) +singleton CustomMaterial( BasicRibbonMat ) { - shader = basicRibbonShader; + shader = BasicRibbonShader; version = 2.0; emissive[0] = true; diff --git a/Templates/Empty/game/art/ribbons/ribbons.cs b/Templates/Empty/game/art/ribbons/ribbons.cs index ce65e6fe8..b2184b74c 100644 --- a/Templates/Empty/game/art/ribbons/ribbons.cs +++ b/Templates/Empty/game/art/ribbons/ribbons.cs @@ -27,7 +27,7 @@ datablock RibbonNodeData(DefaultRibbonNodeData) //ribbon data//////////////////////////////////////// -datablock RibbonData(basicRibbon) +datablock RibbonData(BasicRibbon) { size[0] = 0.5; color[0] = "1.0 0.0 0.0 1.0"; @@ -40,5 +40,5 @@ datablock RibbonData(basicRibbon) RibbonLength = 40; fadeAwayStep = 0.1; UseFadeOut = true; - RibbonMaterial = basicRibbonMat; -}; \ No newline at end of file + RibbonMaterial = BasicRibbonMat; +}; diff --git a/Templates/Full/game/art/ribbons/materials.cs b/Templates/Full/game/art/ribbons/materials.cs index f9115356f..dce3e9cac 100644 --- a/Templates/Full/game/art/ribbons/materials.cs +++ b/Templates/Full/game/art/ribbons/materials.cs @@ -24,7 +24,7 @@ //Basic ribbon shader///////////////////////////////////////////// -new ShaderData( basicRibbonShader ) +new ShaderData( BasicRibbonShader ) { DXVertexShaderFile = "shaders/common/ribbons/basicRibbonShaderV.hlsl"; DXPixelShaderFile = "shaders/common/ribbons/basicRibbonShaderP.hlsl"; @@ -32,9 +32,9 @@ new ShaderData( basicRibbonShader ) pixVersion = 2.0; }; -singleton CustomMaterial( basicRibbonMat ) +singleton CustomMaterial( BasicRibbonMat ) { - shader = basicRibbonShader; + shader = BasicRibbonShader; version = 2.0; emissive[0] = true; @@ -51,17 +51,17 @@ singleton CustomMaterial( basicRibbonMat ) //Texture ribbon shader///////////////////////////////////////////// -new ShaderData( texRibbonShader ) +new ShaderData( TexturedRibbonShader ) { - DXVertexShaderFile = "shaders/common/ribbons/texRibbonShaderV.hlsl"; - DXPixelShaderFile = "shaders/common/ribbons/texRibbonShaderP.hlsl"; + DXVertexShaderFile = "shaders/common/ribbons/TexturedRibbonShaderV.hlsl"; + DXPixelShaderFile = "shaders/common/ribbons/TexturedRibbonShaderP.hlsl"; pixVersion = 2.0; }; -singleton CustomMaterial( texRibbonMat ) +singleton CustomMaterial( TexturedRibbonMat ) { - shader = texRibbonShader; + shader = TexturedRibbonShader; version = 2.0; emissive[0] = true; diff --git a/Templates/Full/game/art/ribbons/ribbons.cs b/Templates/Full/game/art/ribbons/ribbons.cs index aef6204e4..8660ec4d2 100644 --- a/Templates/Full/game/art/ribbons/ribbons.cs +++ b/Templates/Full/game/art/ribbons/ribbons.cs @@ -27,7 +27,7 @@ datablock RibbonNodeData(DefaultRibbonNodeData) //ribbon data//////////////////////////////////////// -datablock RibbonData(basicRibbon) +datablock RibbonData(BasicRibbon) { size[0] = 0.5; color[0] = "1.0 0.0 0.0 1.0"; @@ -40,12 +40,12 @@ datablock RibbonData(basicRibbon) RibbonLength = 40; fadeAwayStep = 0.1; UseFadeOut = true; - RibbonMaterial = basicRibbonMat; + RibbonMaterial = BasicRibbonMat; }; -datablock RibbonData(texRibbon) +datablock RibbonData(TexturedRibbon) { - RibbonMaterial = texRibbonMat; + RibbonMaterial = TexturedRibbonMat; size[0] = 0.5; color[0] = "1.0 1.0 1.0 1.0"; position[0] = 0.0; From c52731328442cd9443d0c20382327856e0d63152 Mon Sep 17 00:00:00 2001 From: unkown Date: Mon, 20 Oct 2014 21:09:41 -0600 Subject: [PATCH 7/7] Fixed a crash when ribbons are loaded --- .../Empty/game/shaders/common/ribbons/basicRibbonShaderP.hlsl | 2 +- .../Empty/game/shaders/common/ribbons/basicRibbonShaderV.hlsl | 2 +- Templates/Full/game/art/ribbons/materials.cs | 4 ++-- .../Full/game/shaders/common/ribbons/basicRibbonShaderP.hlsl | 2 +- .../Full/game/shaders/common/ribbons/basicRibbonShaderV.hlsl | 2 +- .../Full/game/shaders/common/ribbons/texRibbonShaderP.hlsl | 4 ++-- .../Full/game/shaders/common/ribbons/texRibbonShaderV.hlsl | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Templates/Empty/game/shaders/common/ribbons/basicRibbonShaderP.hlsl b/Templates/Empty/game/shaders/common/ribbons/basicRibbonShaderP.hlsl index 7ce54e3aa..5527f20cc 100644 --- a/Templates/Empty/game/shaders/common/ribbons/basicRibbonShaderP.hlsl +++ b/Templates/Empty/game/shaders/common/ribbons/basicRibbonShaderP.hlsl @@ -1,5 +1,5 @@ #define IN_HLSL -#include "../common/shdrConsts.h" +#include "../shdrConsts.h" struct v2f { diff --git a/Templates/Empty/game/shaders/common/ribbons/basicRibbonShaderV.hlsl b/Templates/Empty/game/shaders/common/ribbons/basicRibbonShaderV.hlsl index 5fd4ecbc0..933fbc0eb 100644 --- a/Templates/Empty/game/shaders/common/ribbons/basicRibbonShaderV.hlsl +++ b/Templates/Empty/game/shaders/common/ribbons/basicRibbonShaderV.hlsl @@ -1,5 +1,5 @@ #define IN_HLSL -#include "../common/shdrConsts.h" +#include "../shdrConsts.h" struct a2v { diff --git a/Templates/Full/game/art/ribbons/materials.cs b/Templates/Full/game/art/ribbons/materials.cs index dce3e9cac..a8ebc4a61 100644 --- a/Templates/Full/game/art/ribbons/materials.cs +++ b/Templates/Full/game/art/ribbons/materials.cs @@ -53,8 +53,8 @@ singleton CustomMaterial( BasicRibbonMat ) new ShaderData( TexturedRibbonShader ) { - DXVertexShaderFile = "shaders/common/ribbons/TexturedRibbonShaderV.hlsl"; - DXPixelShaderFile = "shaders/common/ribbons/TexturedRibbonShaderP.hlsl"; + DXVertexShaderFile = "shaders/common/ribbons/texRibbonShaderV.hlsl"; + DXPixelShaderFile = "shaders/common/ribbons/texRibbonShaderP.hlsl"; pixVersion = 2.0; }; diff --git a/Templates/Full/game/shaders/common/ribbons/basicRibbonShaderP.hlsl b/Templates/Full/game/shaders/common/ribbons/basicRibbonShaderP.hlsl index 7ce54e3aa..5527f20cc 100644 --- a/Templates/Full/game/shaders/common/ribbons/basicRibbonShaderP.hlsl +++ b/Templates/Full/game/shaders/common/ribbons/basicRibbonShaderP.hlsl @@ -1,5 +1,5 @@ #define IN_HLSL -#include "../common/shdrConsts.h" +#include "../shdrConsts.h" struct v2f { diff --git a/Templates/Full/game/shaders/common/ribbons/basicRibbonShaderV.hlsl b/Templates/Full/game/shaders/common/ribbons/basicRibbonShaderV.hlsl index 5fd4ecbc0..933fbc0eb 100644 --- a/Templates/Full/game/shaders/common/ribbons/basicRibbonShaderV.hlsl +++ b/Templates/Full/game/shaders/common/ribbons/basicRibbonShaderV.hlsl @@ -1,5 +1,5 @@ #define IN_HLSL -#include "../common/shdrConsts.h" +#include "../shdrConsts.h" struct a2v { diff --git a/Templates/Full/game/shaders/common/ribbons/texRibbonShaderP.hlsl b/Templates/Full/game/shaders/common/ribbons/texRibbonShaderP.hlsl index a93c6162b..ade2a1899 100644 --- a/Templates/Full/game/shaders/common/ribbons/texRibbonShaderP.hlsl +++ b/Templates/Full/game/shaders/common/ribbons/texRibbonShaderP.hlsl @@ -1,6 +1,6 @@ #define IN_HLSL -#include "../common/shdrConsts.h" -#include "shaders/common/torque.hlsl" +#include "../shdrConsts.h" +#include "../torque.hlsl" uniform sampler2D ribTex : register(S0); diff --git a/Templates/Full/game/shaders/common/ribbons/texRibbonShaderV.hlsl b/Templates/Full/game/shaders/common/ribbons/texRibbonShaderV.hlsl index 5fd4ecbc0..933fbc0eb 100644 --- a/Templates/Full/game/shaders/common/ribbons/texRibbonShaderV.hlsl +++ b/Templates/Full/game/shaders/common/ribbons/texRibbonShaderV.hlsl @@ -1,5 +1,5 @@ #define IN_HLSL -#include "../common/shdrConsts.h" +#include "../shdrConsts.h" struct a2v {