From 21be97d206d96ff20cba70cb6f01c7efb13736e8 Mon Sep 17 00:00:00 2001 From: Areloch Date: Tue, 1 Dec 2015 00:49:37 -0600 Subject: [PATCH] Missed a few files. --- Engine/source/environment/VolumetricFog.cpp | 1319 +++++++++++++++++ Engine/source/environment/VolumetricFog.h | 243 +++ .../environment/VolumetricFogRTManager.cpp | 299 ++++ .../environment/VolumetricFogRTManager.h | 92 ++ 4 files changed, 1953 insertions(+) create mode 100644 Engine/source/environment/VolumetricFog.cpp create mode 100644 Engine/source/environment/VolumetricFog.h create mode 100644 Engine/source/environment/VolumetricFogRTManager.cpp create mode 100644 Engine/source/environment/VolumetricFogRTManager.h diff --git a/Engine/source/environment/VolumetricFog.cpp b/Engine/source/environment/VolumetricFog.cpp new file mode 100644 index 000000000..e6be112dc --- /dev/null +++ b/Engine/source/environment/VolumetricFog.cpp @@ -0,0 +1,1319 @@ +//----------------------------------------------------------------------------- +// 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 "environment/VolumetricFog.h" +#include "windowManager/platformWindowMgr.h" +#include "gfx/gfxTransformSaver.h" +#include "renderInstance/renderPassManager.h" +#include "math/mathIO.h" +#include "materials/shaderData.h" +#include "math/util/matrixSet.h" +#include "core/resourceManager.h" +#include "core/stream/bitStream.h" +#include "T3D/gameBase/gameConnection.h" +#include "T3D/shapeBase.h" +#include "ts/tsShapeInstance.h" +#include "console/engineAPI.h" +#include "gui/core/guiCanvas.h" +#include "VolumetricFogRTManager.h" +#include "lighting/lightInfo.h" +#include "lighting/lightManager.h" + +#define COLBOX_SCALE Point3F(1.02f, 1.02f, 1.02f) + +IMPLEMENT_CO_NETOBJECT_V1(VolumetricFog); + +ConsoleDocClass(VolumetricFog, +"@brief Volumetric Fog Object class. Main class defining the Volumetric\n" +"Fog objects in the scene. Used in conjunction with the VolumetricFogRTManager\n" +"class which is responsible for the required rendertargets.\n\n" +"Methods (exposed to script):\n" +" setFogColorF(color) Changes the overall fog color (color.rgba range 0.0 - 1.0).\n;" +" setFogColor(color) Changes the overall fog color color.rgba range 0 - 255).\n;" +" setFogDensity(density) Changes the overall fog density.\n" +" setFogModulation(strength, speed1, speed2) changes the strength\n" +" and the speeds of the 2 animation layers.\n\n" +"Callbacks:\n" +"onEnterFog triggered whenever the controlobject (Player or Camera) enters the Fog.\n" +" (current Fog object and the controlobject are exposed to script.\n" +"onLeaveFog triggered whenever the controlobject (Player or Camera) left the Fog.\n" +" (current Fog object and the controlobject are exposed to script.\n\n" +"@tsexample\n" +" new VolumetricFog()\n" +" {\n" +" shapeName = \"art/environment/FogRCube.dts\";\n" +" fogColor = \"200 200 200 128\";\n" +" fogDensity = \"0.2\";\n" +" ignoreWater = \"0\";\n" +" MinSize = \"250\";\n" +" FadeSize = \"750\";\n" +" texture = \"art/environment/FogMod_heavy.dds\";\n" +" tiles = \"1.5\";\n" +" modStrength = \"0.2\";\n" +" PrimSpeed = \"-0.01 0.04\";\n" +" SecSpeed = \"0.02 -0.02\";\n" +" position = \"748.644 656.371 65.3506\"; \n" +" rotation = \"0 0 1 20.354\";\n" +" scale = \"40 30 6\";\n" +" };\n" +"@endtsexample\n" +); + +IMPLEMENT_CALLBACK(VolumetricFog, onEnterFog, void, (SimObjectId obj), (obj), +"@brief Called when an object enters the volume of the Fog instance.\n\n" + +"@param obj the controlobject entering the fog."); + +IMPLEMENT_CALLBACK(VolumetricFog, onLeaveFog, void, (SimObjectId obj), (obj), +"@brief Called when an object left the volume of the Fog instance.\n\n" + +"@param obj the controlobject leaving the fog."); + + +VolumetricFog::VolumetricFog() +{ + AssertFatal(VFRTM != NULL, "VolumetricFog Fatal Error: No Manager found"); + + if (!VFRTM->IsInitialized()) + VFRTM->Init(); + + mNetFlags.set(Ghostable | ScopeAlways); + + mTypeMask |= EnvironmentObjectType | StaticObjectType; + + mPrepassTarget = NULL; + mDepthBufferTarget = NULL; + mFrontBufferTarget = NULL; + + z_buf = NULL; + mTexture = NULL; + + mIsVBDirty = false; + mIsPBDirty = false; + + mFogColor.set(200, 200, 200, 255); + mFogDensity = 0.3f; + mIgnoreWater = false; + mReflect = false; + mCamInFog = false; + mResizing = false; + mFogReflStrength = 20.0; + mUseGlow = false; + mGlowStrength = 0.3f; + mGlowing = 0; + mModifLightRays = false; + mLightRayMod = 1.0f; + mOldLightRayStrength = 0.1f; + + mShapeName = ""; + mShapeLoaded = false; + mMinDisplaySize = 10.0f; + mFadeSize = 0.0f; + mCurDetailLevel = 0; + mNumDetailLevels = 0; + det_size.clear(); + + mTextureName = ""; + mIsTextured = false; + mStrength = 0.5f; + mTexTiles = 1.0f; + mSpeed1.set(0.5f, 0.0f); + mSpeed2.set(0.1f, 0.1f); +} + +VolumetricFog::~VolumetricFog() +{ + if (isClientObject()) + return; + + for (S32 i = 0; i < det_size.size(); i++) + { + if (det_size[i].indices != NULL) + delete(det_size[i].indices); + if (det_size[i].piArray != NULL) + delete(det_size[i].piArray); + if (det_size[i].verts != NULL) + delete(det_size[i].verts); + } + det_size.clear(); + + if (z_buf.isValid()) + SAFE_DELETE(z_buf); + + if (!mTexture.isNull()) + mTexture.free(); +} + +void VolumetricFog::initPersistFields() +{ + addGroup("VolumetricFogData"); + addField("shapeName", TypeShapeFilename, Offset(mShapeName, VolumetricFog), + "Path and filename of the model file (.DTS, .DAE) to use for this Volume."); + addField("FogColor", TypeColorI, Offset(mFogColor, VolumetricFog), + "Fog color RGBA (Alpha is ignored)"); + addField("FogDensity", TypeF32, Offset(mFogDensity, VolumetricFog), + "Overal fog density value (0 disables the fog)."); + addField("IgnoreWater", TypeBool, Offset(mIgnoreWater, VolumetricFog), + "Set to true if volumetric fog should continue while submerged."); + addField("MinSize", TypeF32, Offset(mMinDisplaySize, VolumetricFog), + "Min size (in pixels) for fog to be rendered."); + addField("FadeSize", TypeF32, Offset(mFadeSize, VolumetricFog), + "Object size in pixels at which the FX-fading kicks in (0 disables fading)."); + endGroup("VolumetricFogData"); + + addGroup("VolumetricFogModulation"); + addField("texture", TypeImageFilename, Offset(mTextureName, VolumetricFog), + "A texture which contains Fogdensity modulator in the red channel and color with 1-green channel. No texture disables modulation."); + addField("tiles", TypeF32, Offset(mTexTiles, VolumetricFog), + "How many times the texture is mapped to the object."); + addField("modStrength", TypeF32, Offset(mStrength, VolumetricFog), + "Overall strength of the density modulation (0 disables modulation)."); + addField("PrimSpeed", TypePoint2F, Offset(mSpeed1, VolumetricFog), + "Overall primary speed of the density modulation (x-speed(u) y-speed(v))"); + addField("SecSpeed", TypePoint2F, Offset(mSpeed2, VolumetricFog), + "Overall secundary speed of the density modulation (x-speed(u) y-speed(v))"); + endGroup("VolumetricFogModulation"); + + addGroup("Reflections"); + addField("Reflectable", TypeBool, Offset(mReflect, VolumetricFog), + "Set to true if volumetric fog should be reflected."); + addField("ReflectStrength", TypeF32, Offset(mFogReflStrength, VolumetricFog), + "Strength of the reflections (0 disables the fog)."); + endGroup("Reflections"); + + addGroup("PostFX"); + addField("useGlow", TypeBool, Offset(mUseGlow, VolumetricFog), + "Set to true if volumetric fog should use glow PostFX."); + addField("glowStrength", TypeF32, Offset(mGlowStrength, VolumetricFog), + "Overall strength of the glow PostFX."); + addField("modLightRay", TypeBool, Offset(mModifLightRays, VolumetricFog), + "Set to true if volumetric fog should modify the brightness of the Lightrays."); + addField("lightRayMod", TypeF32, Offset(mLightRayMod, VolumetricFog), + "Modifier for LightRay PostFX when inside Fog."); + endGroup("PostFX"); + Parent::initPersistFields(); +} + +void VolumetricFog::inspectPostApply() +{ + Parent::inspectPostApply(); + mSpeed.set(mSpeed1.x, mSpeed1.y, mSpeed2.x, mSpeed2.y); + setMaskBits(VolumetricFogMask | FogColorMask | FogDensityMask | FogModulationMask | FogPostFXMask | FogShapeMask); +} + +bool VolumetricFog::onAdd() +{ + if (!Parent::onAdd()) + return false; + + if (!VFRTM->IsInitialized()) + { + Con::errorf("No VolumetricFogRTManager present!!"); + return false; + } + + resetWorldBox(); + + mShapeLoaded = LoadShape(); + + setRenderTransform(mObjToWorld); + + addToScene(); + ColBox.set(getTransform(), (mObjBox.getExtents() * getScale() * COLBOX_SCALE)); + mObjSize = mWorldBox.getGreatestDiagonalLength(); + mObjScale = getScale(); + mTexTiles = mAbs(mTexTiles); + mSpeed.set(mSpeed1.x, mSpeed1.y, mSpeed2.x, mSpeed2.y); + mInvScale = (1.0f / getMax(getMax(mObjScale.x, mObjScale.y), mObjScale.z)); + + if (isClientObject()) + { + conn = GameConnection::getConnectionToServer(); + if (!conn) + { + Con::errorf("VolumetricFog::onAdd - No Serverconnection"); + return false; + } + + glowFX = static_cast(Sim::findObject("VolFogGlowPostFx")); + + mOldLightRayStrength = Con::getFloatVariable("$LightRayPostFX::brightScalar",1.0f); + + GuiCanvas* cv = dynamic_cast(Sim::findObject("Canvas")); + if (cv == NULL) + { + Con::errorf("VolumetricFog::onAdd - Canvas not found!!"); + return false; + } + mPlatformWindow = cv->getPlatformWindow(); + VolumetricFogRTManager::getVolumetricFogRTMResizeSignal().notify(this, &VolumetricFog::handleResize); + GuiCanvas::getCanvasSizeChangeSignal().notify(this, &VolumetricFog::handleCanvasResize); + + InitTexture(); + return setupRenderer(); + } + + VFRTM->IncFogObjects(); + + return true; +} + +void VolumetricFog::onRemove() +{ + if (isClientObject()) + { + if (isTicking()) + { + setProcessTick(false); + if (mGlowing != 0) + { + mGlowing = 0; + glowFX->disable(); + } + _leaveFog(static_cast(conn->getControlObject())); + } + VolumetricFogRTManager::getVolumetricFogRTMResizeSignal().remove(this, &VolumetricFog::handleResize); + GuiCanvas::getCanvasSizeChangeSignal().remove(this, &VolumetricFog::handleCanvasResize); + } + removeFromScene(); + VFRTM->DecFogObjects(); + Parent::onRemove(); +} +void VolumetricFog::handleCanvasResize(GuiCanvas* canvas) +{ + UpdateBuffers(0,true); +} + +void VolumetricFog::handleResize(VolumetricFogRTManager *RTM, bool resize) +{ + if (resize) + { + mResizing = true; + RTM->FogAnswered(); + } + else + mResizing = false; + + if (mIsTextured) + { + F32 width = (F32)mPlatformWindow->getClientExtent().x; + F32 height = (F32)mPlatformWindow->getClientExtent().y; + if (!mPlatformWindow->isFullscreen()) + height -= 20;//subtract caption bar from rendertarget size. + mTexScale.x = 2.0f - ((F32)mTexture.getWidth() / width); + mTexScale.y = 2.0f - ((F32)mTexture.getHeight() / height); + } + + UpdateBuffers(0,true); +} + +//----------------------------------------------------------------------------- +// Loadshape extracted from TSMesh and TSShapeInstance +//----------------------------------------------------------------------------- + +bool VolumetricFog::LoadShape() +{ + GFXPrimitiveType GFXdrawTypes[] = { GFXTriangleList, GFXTriangleStrip }; + if (!mShapeName || mShapeName[0] == '\0') + { + Con::errorf("VolumetricFog::LoadShape() - No shape name! Volumetric Fog will not be rendered!"); + return false; + } + + // Load shape, server side only reads bounds and radius + + Resource mShape; + mShape = ResourceManager::get().load(mShapeName); + if (bool(mShape) == false) + { + Con::errorf("VolumetricFog::LoadShape() - Unable to load shape: %s", mShapeName); + return false; + } + + mObjBox = mShape->bounds; + mRadius = mShape->radius; + resetWorldBox(); + + if (!isClientObject()) + return false; + + TSShapeInstance *mShapeInstance = new TSShapeInstance(mShape, false); + meshes mesh_detail; + + for (S32 i = 0; i < det_size.size(); i++) + { + if (det_size[i].indices != NULL) + delete(det_size[i].indices); + if (det_size[i].piArray != NULL) + delete(det_size[i].piArray); + if (det_size[i].verts != NULL) + delete(det_size[i].verts); + } + det_size.clear(); + + // browsing model for detail levels + + for (U32 i = 0; i < mShape->details.size(); i++) + { + const TSDetail *detail = &mShape->details[i]; + mesh_detail.det_size = detail->size; + mesh_detail.sub_shape = detail->subShapeNum; + mesh_detail.obj_det = detail->objectDetailNum; + mesh_detail.verts = NULL; + mesh_detail.piArray = NULL; + mesh_detail.indices = NULL; + if (detail->size >= 0.0f && detail->subShapeNum >= 0) + det_size.push_back(mesh_detail); + } + + for (U32 i = 0; i < det_size.size(); i++) + { + const S32 ss = det_size[i].sub_shape; + if (ss >= 0) + { + const S32 start = mShape->subShapeFirstObject[ss]; + const S32 end = start + mShape->subShapeNumObjects[ss]; + for (S32 j = start; j < end; j++) + { + // Loading shape, only the first mesh for each detail will be used! + TSShapeInstance::MeshObjectInstance *meshObj = &mShapeInstance->mMeshObjects[j]; + if (!meshObj) + continue; + TSMesh *mesh = meshObj->getMesh(det_size[i].obj_det); + if (mesh != NULL) + { + const U32 numNrms = mesh->mNumVerts; + GFXVertexPNTT *tmpVerts = NULL; + tmpVerts = new GFXVertexPNTT[numNrms]; + mIsVBDirty = true; + for (U32 k = 0; k < numNrms; k++) + { + Point3F norm = mesh->mVertexData[k].normal(); + Point3F vert = mesh->mVertexData[k].vert(); + Point2F uv = mesh->mVertexData[k].tvert(); + tmpVerts[k].point = vert; + tmpVerts[k].texCoord = uv; + tmpVerts[k].normal = norm; + } + det_size[i].verts = tmpVerts; + det_size[i].num_verts = numNrms; + + det_size[i].piArray = new Vector(); + GFXPrimitive pInfo; + + det_size[i].indices = new Vector(); + + for (U32 k = 0; k < mesh->indices.size(); k++) + det_size[i].indices->push_back(mesh->indices[k]); + + U32 primitivesSize = mesh->primitives.size(); + for (U32 k = 0; k < primitivesSize; k++) + { + const TSDrawPrimitive & draw = mesh->primitives[k]; + GFXPrimitiveType drawType = GFXdrawTypes[draw.matIndex >> 30]; + switch (drawType) + { + case GFXTriangleList: + pInfo.type = drawType; + pInfo.numPrimitives = draw.numElements / 3; + pInfo.startIndex = draw.start; + // Use the first index to determine which 16-bit address space we are operating in + pInfo.startVertex = mesh->indices[draw.start] & 0xFFFF0000; + pInfo.minIndex = pInfo.startVertex; + pInfo.numVertices = getMin((U32)0x10000, mesh->mNumVerts - pInfo.startVertex); + break; + case GFXTriangleStrip: + pInfo.type = drawType; + pInfo.numPrimitives = draw.numElements - 2; + pInfo.startIndex = draw.start; + // Use the first index to determine which 16-bit address space we are operating in + pInfo.startVertex = mesh->indices[draw.start] & 0xFFFF0000; + pInfo.minIndex = pInfo.startVertex; + pInfo.numVertices = getMin((U32)0x10000, mesh->mNumVerts - pInfo.startVertex); + break; + default: + Con::errorf("VolumetricFog::LoadShape Unknown drawtype!?!"); + return false; + break; + } + det_size[i].piArray->push_back(pInfo); + j = end; + } + } + else + { + Con::errorf("VolumetricFog::LoadShape Error loading mesh from shape!"); + delete mShapeInstance; + return false; + } + mIsVBDirty = true; + mIsPBDirty = true; + } + } + } + + mNumDetailLevels = det_size.size(); + mCurDetailLevel = 0; + UpdateBuffers(mCurDetailLevel); + delete mShapeInstance; + + return true; +} + +//----------------------------------------------------------------------------- +// UpdateBuffers called whenever detaillevel changes (LOD) +//----------------------------------------------------------------------------- + + +bool VolumetricFog::UpdateBuffers(U32 dl, bool force) +{ + if (mVB.isNull() || mIsVBDirty || dl != mCurDetailLevel || force) + { + mVB.set(GFX, det_size[dl].num_verts, GFXBufferTypeDynamic); + mIsVBDirty = false; + } + GFXVertexPNTT *vertPtr = mVB.lock(); + if (!vertPtr) + { + mVB.unlock(); + return false; + } + dMemcpy(vertPtr, det_size[dl].verts, sizeof (GFXVertexPNTT)* det_size[dl].num_verts); + mVB.unlock(); + + if (mIsPBDirty || mPB.isNull() || dl != mCurDetailLevel || force) + { + #ifdef TORQUE_DEBUG + mPB.set(GFX, det_size[dl].indices->size(), det_size[dl].piArray->size(), GFXBufferTypeDynamic, avar("%s() - VolFogPrimBuffer (line %d)", __FUNCTION__, __LINE__)); + #else + mPB.set(GFX, det_size[dl].indices->size(), det_size[dl].piArray->size(), GFXBufferTypeDynamic); + #endif + U16 *ibIndices = NULL; + GFXPrimitive *piInput = NULL; + mPB.lock(&ibIndices, &piInput); + dCopyArray(ibIndices, det_size[dl].indices->address(), det_size[dl].indices->size()); + dMemcpy(piInput, det_size[dl].piArray->address(), det_size[dl].piArray->size() * sizeof(GFXPrimitive)); + mPB.unlock(); + mIsPBDirty = false; + } + mCurDetailLevel = dl; + return true; +} + +U32 VolumetricFog::packUpdate(NetConnection *con, U32 mask, BitStream *stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + if (stream->writeFlag(mask & FogColorMask)) + stream->write(mFogColor); + if (stream->writeFlag(mask & FogDensityMask)) + stream->write(mFogDensity); + if (stream->writeFlag(mask & FogModulationMask)) + { + stream->write(mTextureName); + mTexTiles = mFabs(mTexTiles); + stream->write(mTexTiles); + stream->write(mStrength); + mathWrite(*stream, mSpeed); + } + if (stream->writeFlag(mask & FogPostFXMask)) + { + stream->writeFlag(mUseGlow); + stream->write(mGlowStrength); + stream->writeFlag(mModifLightRays); + stream->write(mLightRayMod); + } + if (stream->writeFlag(mask & VolumetricFogMask)) + { + stream->writeFlag(mIgnoreWater); + stream->writeFlag(mReflect); + stream->write(mFogReflStrength); + stream->writeFlag(mResizing); + stream->write(mMinDisplaySize); + stream->write(mFadeSize); + } + if (stream->writeFlag(mask & FogShapeMask)) + { + stream->writeString(mShapeName); + mathWrite(*stream, getTransform()); + mathWrite(*stream, getScale()); + if (!mShapeName || mShapeName[0] == '\0') + return retMask; + Resource mShape; + mShape = ResourceManager::get().load(mShapeName); + if (bool(mShape) == false) + return retMask; + mObjBox = mShape->bounds; + mRadius = mShape->radius; + resetWorldBox(); + mObjSize = mWorldBox.getGreatestDiagonalLength(); + mObjScale = getScale(); + mInvScale = (1.0f / getMax(getMax(mObjScale.x, mObjScale.y), mObjScale.z)); + } + return retMask; +} + +void VolumetricFog::unpackUpdate(NetConnection *con, BitStream *stream) +{ + Parent::unpackUpdate(con, stream); + MatrixF mat; + VectorF scale; + VectorF mOldScale = getScale(); + String oldTextureName = mTextureName; + StringTableEntry oldShape = mShapeName; + + if (stream->readFlag())// Fog color + stream->read(&mFogColor); + if (stream->readFlag())// Fog Density + { + stream->read(&mFogDensity); + if (isTicking()) + { + char buf[20]; + dSprintf(buf, sizeof(buf), "%3.7f", mFogDensity); + Con::setVariable("$VolumetricFog::density", buf); + } + } + if (stream->readFlag())// Fog Modulation + { + stream->read(&mTextureName); + stream->read(&mTexTiles); + mTexTiles = mFabs(mTexTiles); + stream->read(&mStrength); + mathRead(*stream, &mSpeed); + mSpeed1.set(mSpeed.x, mSpeed.y); + mSpeed2.set(mSpeed.z, mSpeed.w); + + if (isProperlyAdded()) + { + if (oldTextureName != mTextureName) + InitTexture(); + if (oldTextureName.isNotEmpty() && mTextureName.isEmpty()) + { + mIsTextured = false; + mTexture.free(); + } + } + } + if (stream->readFlag())//Fog PostFX + { + mUseGlow = stream->readFlag(); + stream->read(&mGlowStrength); + mModifLightRays = stream->readFlag(); + stream->read(&mLightRayMod); + if (isTicking()) + { + char buf[20]; + dSprintf(buf, sizeof(buf), "%3.7f", mGlowStrength); + Con::setVariable("$VolFogGlowPostFx::glowStrength", buf); + if (mUseGlow && !glowFX->isEnabled()) + glowFX->enable(); + if (!mUseGlow && glowFX->isEnabled()) + glowFX->disable(); + if (mModifLightRays) + { + char buf[20]; + dSprintf(buf, sizeof(buf), "%3.7f", mOldLightRayStrength * mLightRayMod); + Con::setVariable("$LightRayPostFX::brightScalar", buf); + } + if (!mModifLightRays) + { + char buf[20]; + dSprintf(buf, sizeof(buf), "%3.7f", mOldLightRayStrength); + Con::setVariable("$LightRayPostFX::brightScalar", buf); + } + } + } + if (stream->readFlag())//Volumetric Fog + { + mIgnoreWater = stream->readFlag(); + mReflect = stream->readFlag(); + stream->read(&mFogReflStrength); + mResizing = stream->readFlag(); + stream->read(&mMinDisplaySize); + stream->read(&mFadeSize); + } + if (stream->readFlag())//Fog shape + { + mShapeName = stream->readSTString(); + mathRead(*stream, &mat); + mathRead(*stream, &scale); + if (strcmp(oldShape, mShapeName) != 0) + { + mIsVBDirty = true; + mShapeLoaded = LoadShape(); + } + setScale(scale); + setTransform(mat); + ColBox.set(getTransform(), (mObjBox.getExtents() * getScale() * COLBOX_SCALE)); + mObjSize = mWorldBox.getGreatestDiagonalLength(); + mObjScale = getScale(); + mInvScale = (1.0f / getMax(getMax(mObjScale.x, mObjScale.y), mObjScale.z)); + } +} + +void VolumetricFog::processTick(const Move* move) +{ + Parent::processTick(move); + mCounter++; + if ( mGlowing==1 && mCurGlow < mGlowStrength ) + { + mCurGlow += (mGlowStrength / 10.0); + char buf[20]; + dSprintf(buf, sizeof(buf), "%3.7f", mCurGlow); + Con::setVariable("$VolFogGlowPostFx::glowStrength", buf); + } + else if ( mGlowing == 2 && mCurGlow > 0.0f ) + { + mCurGlow -= (mGlowStrength / 5.0f); + if (mCurGlow <= 0.0f) + { + glowFX->disable(); + mGlowing = 0; + setProcessTick(false); + return; + } + else + { + char buf[20]; + dSprintf(buf, sizeof(buf), "%3.7f", mCurGlow); + Con::setVariable("$VolFogGlowPostFx::glowStrength", buf); + } + } + if (mCounter == 3) + { + ShapeBase* control = static_cast(conn->getControlObject()); + MatrixF xfm; + control->getRenderEyeTransform(&xfm); + Point3F pos = xfm.getPosition(); + if (!ColBox.isContained(pos)) + _leaveFog(control); + mCounter = 0; + } +} + +void VolumetricFog::_enterFog(ShapeBase *control) +{ + if (mUseGlow) + { + if (glowFX) + { + mCurGlow = 0.0f; + Con::setVariable("$VolFogGlowPostFx::glowStrength", "0.0"); + glowFX->enable(); + mGlowing = 1; + } + } + if (mModifLightRays) + { + char buf[20]; + dSprintf(buf, sizeof(buf), "%3.7f", mOldLightRayStrength * mLightRayMod); + Con::setVariable("$LightRayPostFX::brightScalar", buf); + } + mCounter = 0; + char buf[20]; + dSprintf(buf, sizeof(buf), "%3.7f", mFogDensity); + Con::setVariable("$VolumetricFog::density", buf); + setProcessTick(true); + if (control) + onEnterFog_callback(control->getId()); +} + +void VolumetricFog::_leaveFog(ShapeBase *control) +{ + mCamInFog = false; + Con::setVariable("$VolumetricFog::density", "0.0"); + if (mModifLightRays) + { + char buf[20]; + dSprintf(buf, sizeof(buf), "%3.7f", mOldLightRayStrength); + Con::setVariable("$LightRayPostFX::brightScalar", buf); + } + if (mUseGlow) + { + if (glowFX && mGlowing != 2) + { + mCurGlow = mGlowStrength; + mGlowing = 2; + if (control) + onLeaveFog_callback(control->getId()); + } + } + else + { + setProcessTick(false); + if (control) + onLeaveFog_callback(control->getId()); + } +} + +//----------------------------------------------------------------------------- +// Setting up the renderers +//----------------------------------------------------------------------------- + +bool VolumetricFog::setupRenderer() +{ + // Search for the prepass rendertarget and shadermacros. + mPrepassTarget = NamedTexTarget::find("prepass"); + if (!mPrepassTarget.isValid()) + { + Con::errorf("VolumetricFog::setupRenderer - could not find PrepassTarget"); + return false; + } + + Vector macros; + if (mPrepassTarget) + mPrepassTarget->getShaderMacros(¯os); + + // Search the depth and frontbuffers which are created by the VolumetricFogRTManager + + mDepthBufferTarget = NamedTexTarget::find("volfogdepth"); + if (!mDepthBufferTarget.isValid()) + { + Con::errorf("VolumetricFog::setupRenderer - could not find depthbuffer"); + return false; + } + + mFrontBufferTarget = NamedTexTarget::find("volfogfront"); + if (!mFrontBufferTarget.isValid()) + { + Con::errorf("VolumetricFog::setupRenderer - could not find frontbuffer"); + return false; + } + + // Find and setup the prepass Shader + + ShaderData *shaderData; + mPrePassShader = Sim::findObject("VolumetricFogPrePassShader", shaderData) ? + shaderData->getShader() : NULL; + if (!mPrePassShader) + { + Con::errorf("VolumetricFog::setupRenderer - could not find VolumetricFogPrePassShader"); + return false; + } + + // Create ShaderConstBuffer and Handles + + mPPShaderConsts = mPrePassShader->allocConstBuffer(); + if (mPPShaderConsts.isNull()) + { + Con::errorf("VolumetricFog::setupRenderer - could not allocate ShaderConstants 1."); + return false; + } + + mPPModelViewProjSC = mPrePassShader->getShaderConstHandle("$modelView"); + + // Find and setup the VolumetricFog Shader + + shaderData = NULL; + mShader = Sim::findObject("VolumetricFogShader", shaderData) ? + shaderData->getShader(macros) : NULL; + if (!mShader) + { + Con::errorf("VolumetricFog::setupRenderer - could not find VolumetricFogShader"); + return false; + } + + // Create ShaderConstBuffer and Handles + + mShaderConsts = mShader->allocConstBuffer(); + if (mShaderConsts.isNull()) + { + Con::errorf("VolumetricFog::setupRenderer - could not allocate ShaderConstants 2."); + return false; + } + + mModelViewProjSC = mShader->getShaderConstHandle("$modelView"); + mFadeSizeSC = mShader->getShaderConstHandle("$fadesize"); + mFogColorSC = mShader->getShaderConstHandle("$fogColor"); + mFogDensitySC = mShader->getShaderConstHandle("$fogDensity"); + mPreBias = mShader->getShaderConstHandle("$preBias"); + mAccumTime = mShader->getShaderConstHandle("$accumTime"); + mIsTexturedSC = mShader->getShaderConstHandle("$textured"); + mTexTilesSC = mShader->getShaderConstHandle("$numtiles"); + mModStrengthSC = mShader->getShaderConstHandle("$modstrength"); + mModSpeedSC = mShader->getShaderConstHandle("$modspeed"); + mViewPointSC = mShader->getShaderConstHandle("$viewpoint"); + mTexScaleSC = mShader->getShaderConstHandle("$texscale"); + mAmbientColorSC = mShader->getShaderConstHandle("$ambientColor"); + + // Find and setup the reflection Shader + + shaderData = NULL; + mReflectionShader = Sim::findObject("VolumetricFogReflectionShader", shaderData) ? + shaderData->getShader() : NULL; + if (!mReflectionShader) + { + Con::errorf("VolumetricFog::setupRenderer - could not find VolumetricFogReflectionShader"); + return false; + } + + mReflShaderConsts = mReflectionShader->allocConstBuffer(); + if (mReflShaderConsts.isNull()) + { + Con::errorf("VolumetricFog::setupRenderer - could not allocate ShaderConstants for VolumetricFogReflectionShader."); + return false; + } + + mReflModelViewProjSC = mReflectionShader->getShaderConstHandle("$modelView"); + mReflFogColorSC = mReflectionShader->getShaderConstHandle("$fogColor"); + mReflFogDensitySC = mReflectionShader->getShaderConstHandle("$fogDensity"); + mReflFogStrengthSC = mReflectionShader->getShaderConstHandle("$reflStrength"); + + // Create the prepass StateBlock + + desc_preD.setCullMode(GFXCullCW); + desc_preD.setBlend(true); + desc_preD.setZReadWrite(false, false); + desc_preD.stencilEnable = false; + desc_preF.setCullMode(GFXCullCCW); + desc_preF.setBlend(true); + desc_preF.setZReadWrite(true, false); + desc_preF.stencilEnable = false; + + // Create the VolumetricFog StateBlock + + descD.setCullMode(GFXCullCW); + descD.setBlend(true); + descD.setZReadWrite(false, false);// desc.setZReadWrite(true, false); + + // prepassBuffer sampler + descD.samplersDefined = true; + descD.samplers[0].addressModeU = GFXAddressClamp; + descD.samplers[0].addressModeV = GFXAddressClamp; + descD.samplers[0].addressModeW = GFXAddressClamp; + descD.samplers[0].magFilter = GFXTextureFilterLinear; + descD.samplers[0].minFilter = GFXTextureFilterLinear; + descD.samplers[0].mipFilter = GFXTextureFilterLinear; + descD.samplers[0].textureColorOp = GFXTOPDisable; + + // DepthBuffer sampler + descD.samplers[1].addressModeU = GFXAddressClamp; + descD.samplers[1].addressModeV = GFXAddressClamp; + descD.samplers[1].addressModeW = GFXAddressClamp; + descD.samplers[1].magFilter = GFXTextureFilterLinear; + descD.samplers[1].minFilter = GFXTextureFilterLinear; + descD.samplers[1].mipFilter = GFXTextureFilterLinear; + descD.samplers[1].textureColorOp = GFXTOPModulate; + + // FrontBuffer sampler + descD.samplers[2].addressModeU = GFXAddressClamp; + descD.samplers[2].addressModeV = GFXAddressClamp; + descD.samplers[2].addressModeW = GFXAddressClamp; + descD.samplers[2].magFilter = GFXTextureFilterLinear; + descD.samplers[2].minFilter = GFXTextureFilterLinear; + descD.samplers[2].mipFilter = GFXTextureFilterLinear; + descD.samplers[2].textureColorOp = GFXTOPModulate; + + // animated density modifier map sampler + descD.samplers[3].addressModeU = GFXAddressWrap; + descD.samplers[3].addressModeV = GFXAddressWrap; + descD.samplers[3].addressModeW = GFXAddressWrap; + descD.samplers[3].magFilter = GFXTextureFilterLinear; + descD.samplers[3].minFilter = GFXTextureFilterLinear; + descD.samplers[3].mipFilter = GFXTextureFilterLinear; + descD.samplers[3].textureColorOp = GFXTOPModulate; + + dMemcpy(&descF, &descD, sizeof(GFXStateBlockDesc)); + descF.setCullMode(GFXCullCCW); + descF.setBlend(true); + descF.setZReadWrite(true, false); + + desc_refl.setCullMode(GFXCullCCW); + desc_refl.setBlend(true); + desc_refl.setZReadWrite(true, false); + + mStateblock_preD = GFX->createStateBlock(desc_preD); + mStateblock_preF = GFX->createStateBlock(desc_preF); + mStateblockD = GFX->createStateBlock(descD); + mStateblockF = GFX->createStateBlock(descF); + mStateblock_refl = GFX->createStateBlock(desc_refl); + + // Create Rendertarget + + z_buf = GFX->allocRenderToTextureTarget(); + if (z_buf == NULL) + { + Con::errorf("VolumetricFog::setupRenderer - Could not create Render Target"); + return false; + } + + return true; +} + +void VolumetricFog::prepRenderImage(SceneRenderState *state) +{ + if (!mShapeLoaded || mFogDensity <= 0.0f || mResizing) + return; + + if (!state->isDiffusePass()) + { + if (!state->isReflectPass()) + return; + } + + PROFILE_SCOPE(VolumetricFog_prepRenderImage); + + // Time critical therefore static_cast + ShapeBase* control = static_cast(conn->getControlObject()); + if (control->getWaterCoverage() >= 0.9f && !mIgnoreWater) + return; + + camPos = state->getCameraPosition(); + F32 dist = (camPos - getBoxCenter()).len(); + F32 scaleFactor = dist * mInvScale; + if (scaleFactor <= 0.0f) + { + if (mCurDetailLevel != 0) + UpdateBuffers(0); + } + const F32 pixelScale = state->getViewport().extent.y / 300.0f; + + mPixelSize = (mRadius / scaleFactor) * state->getWorldToScreenScale().y * pixelScale; + if (mPixelSize < mMinDisplaySize) + return; + if (mNumDetailLevels > 1) + { + if ((det_size[mCurDetailLevel].det_size > mPixelSize) && (mCurDetailLevel < mNumDetailLevels - 1)) + UpdateBuffers(mCurDetailLevel + 1); + else if (mCurDetailLevel > 0) + { + if (mPixelSize >= det_size[mCurDetailLevel - 1].det_size) + UpdateBuffers(mCurDetailLevel - 1); + } + } + + if (state->isReflectPass() && mReflect) + { + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind(this, &VolumetricFog::reflect_render); + ri->type = RenderPassManager::RIT_VolumetricFog; + ri->translucentSort = true; + ri->sortDistSq = getRenderWorldBox().getSqDistanceToPoint(camPos); + if (dist < 1.0f) + ri->defaultKey = 1; + else + ri->defaultKey = U32(dist); + state->getRenderPass()->addInst(ri); + return; + } + else if (state->isDiffusePass()) + { + viewDist = state->getFarPlane(); + mFOV = state->getCameraFrustum().getFov() / M_PI_F; + Point3F mEyeVec = state->getVectorEye() * viewDist; + + mViewPoint.x = ((mAtan2(mEyeVec.x, mEyeVec.y) / M_PI_F) + 1.0f) * mTexTiles; + mViewPoint.y = (0.5f - (mAsin(mEyeVec.z) / M_PI_F)) * mTexTiles; + + bool isInside = ColBox.isContained(camPos); + if (isInside && !mCamInFog) + { + mCamInFog = true; + _enterFog(control); + } + else if (!isInside && mCamInFog) + mCamInFog = false; + + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind(this, &VolumetricFog::render); + ri->type = RenderPassManager::RIT_VolumetricFog; + ri->translucentSort = true; + ri->sortDistSq = getRenderWorldBox().getSqDistanceToPoint(camPos); + if (dist < 1.0f) + ri->defaultKey = 1; + else + ri->defaultKey = U32(dist); + state->getRenderPass()->addInst(ri); + return; + } + return; +} + +void VolumetricFog::render(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat) +{ + if (overrideMat || !mShapeLoaded || !isClientObject() || mResizing) + return; + + PROFILE_SCOPE(VolumetricFog_Render); + + GFXTransformSaver saver; + GFX->setVertexBuffer(mVB); + GFX->setPrimitiveBuffer(mPB); + + MatrixF mat = getRenderTransform(); + mat.scale(mObjScale); + GFX->multWorld(mat); + + GFX->setShader(mPrePassShader); + GFX->setShaderConstBuffer(mPPShaderConsts); + GFX->setStateBlock(mStateblock_preD); + + // Set all the shader consts... + + MatrixF xform(GFX->getProjectionMatrix()); + xform *= GFX->getViewMatrix(); + xform *= GFX->getWorldMatrix(); + + mPPShaderConsts->setSafe(mPPModelViewProjSC, xform); + + LightInfo *lightinfo = LIGHTMGR->getSpecialLight(LightManager::slSunLightType); + const ColorF &sunlight = state->getAmbientLightColor(); + + Point3F ambientColor(sunlight.red, sunlight.green, sunlight.blue); + mShaderConsts->setSafe(mAmbientColorSC, ambientColor); + + GFXTextureObject *mDepthBuffer = mDepthBufferTarget ? mDepthBufferTarget->getTexture(0) : NULL; + GFXTextureObject *mFrontBuffer = mFrontBufferTarget ? mFrontBufferTarget->getTexture(0) : NULL; + + GFX->pushActiveRenderTarget(); + + //render backside to target mDepthBuffer + z_buf->attachTexture(GFXTextureTarget::DepthStencil, GFXTextureTarget::sDefaultDepthStencil); + z_buf->attachTexture(GFXTextureTarget::Color0, mDepthBuffer); + + GFX->setActiveRenderTarget(z_buf); + GFX->clear(GFXClearStencil | GFXClearTarget , ColorI(0,0,0,0), 1.0f, 0); + + GFX->drawPrimitive(0); + z_buf->resolve(); + + //render frontside to target mFrontBuffer + z_buf->attachTexture(GFXTextureTarget::DepthStencil, GFXTextureTarget::sDefaultDepthStencil); + z_buf->attachTexture(GFXTextureTarget::Color0, mFrontBuffer); + GFX->clear(GFXClearStencil | GFXClearTarget, ColorI(0, 0, 0, 0), 1.0f, 0); + + GFX->setStateBlock(mStateblock_preF); + + GFX->drawPrimitive(0); + z_buf->resolve(); + + GFX->popActiveRenderTarget(); + z_buf->attachTexture(GFXTextureTarget::Color0, NULL); + + //render Volumetric Fog + GFX->setShader(mShader); + GFX->setShaderConstBuffer(mShaderConsts); + + mShaderConsts->setSafe(mModelViewProjSC, xform); + if (mFadeSize > 0.0f) + mShaderConsts->setSafe(mFadeSizeSC, mClampF(mPixelSize / mFadeSize, 0.0f, 1.0f)); + else + mShaderConsts->setSafe(mFadeSizeSC, 1.0f); + mShaderConsts->setSafe(mFogColorSC, mFogColor); + mShaderConsts->setSafe(mFogDensitySC, mFogDensity); + mShaderConsts->setSafe(mPreBias, viewDist); + mShaderConsts->setSafe(mAccumTime, (F32)Sim::getCurrentTime() / 1000.0f); + mShaderConsts->setSafe(mModStrengthSC, mStrength); + mShaderConsts->setSafe(mModSpeedSC, mSpeed); + mShaderConsts->setSafe(mViewPointSC, mViewPoint); + mShaderConsts->setSafe(mTexScaleSC, mTexScale * mFOV); + mShaderConsts->setSafe(mTexTilesSC, mTexTiles); + + GFXTextureObject *prepasstex = mPrepassTarget ? mPrepassTarget->getTexture(0) : NULL; + + GFX->setTexture(0, prepasstex); + GFX->setTexture(1, mDepthBuffer); + GFX->setTexture(2, mFrontBuffer); + + if (mIsTextured && mStrength > 0.0f) + { + GFX->setTexture(3, mTexture); + mShaderConsts->setSafe(mIsTexturedSC, 1.0f); + } + else + mShaderConsts->setSafe(mIsTexturedSC, 0.0f); + + if (mCamInFog) + { + /*GFXLockedRect *rect=mDepthBuffer->lock(); + U32 pixoffset = 0;// 1572864 + (512 * 4); + U8 red = rect->bits[pixoffset]; + U8 green = rect->bits[pixoffset+1]; + U8 blue = rect->bits[pixoffset+2]; + U8 alpha = rect->bits[pixoffset+3]; + mDepthBuffer->unlock(); + S32 lval = ((alpha << 24) + (blue << 16) + (green << 8) + (red)); + F32 fval = ((F32)lval / S32_MAX); + Con::printf("Color %d %d %d %d %d %f", red, green, blue, alpha, lval, fval);*/ + GFX->setStateBlock(mStateblockD); + } + else + GFX->setStateBlock(mStateblockF); + + GFX->drawPrimitive(0); +} + +void VolumetricFog::reflect_render(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat) +{ + if (overrideMat || !mShapeLoaded || !isClientObject() || mResizing || (mFogReflStrength==0.0f)) + return; + + GFXTransformSaver saver; + GFX->setVertexBuffer(mVB); + GFX->setPrimitiveBuffer(mPB); + + MatrixF mat = getRenderTransform(); + mat.scale(mObjScale); + GFX->multWorld(mat); + + GFX->setShader(mReflectionShader); + GFX->setShaderConstBuffer(mReflShaderConsts); + GFX->setStateBlock(mStateblock_refl); + + // Set all the shader consts... + MatrixF xform(GFX->getProjectionMatrix()); + xform *= GFX->getViewMatrix(); + xform *= GFX->getWorldMatrix(); + + mReflShaderConsts->setSafe(mReflModelViewProjSC, xform); + mReflShaderConsts->setSafe(mReflFogColorSC, mFogColor); + mReflShaderConsts->setSafe(mReflFogDensitySC, mFogDensity); + mReflShaderConsts->setSafe(mReflFogStrengthSC, mFogReflStrength); + + GFX->drawPrimitive(0); +} + +//----------------------------------------------------------------------------- +// InitTexture is called whenever a modulation texture is added to the object +//----------------------------------------------------------------------------- + +void VolumetricFog::InitTexture() +{ + mIsTextured = false; + + if (mTextureName.isNotEmpty()) + mTexture.set(mTextureName, &GFXDefaultStaticDiffuseProfile, "VolumetricFogMod"); + + if (!mTexture.isNull()) + { + mIsTextured = true; + + F32 width = (F32)mPlatformWindow->getClientExtent().x; + F32 height = (F32)mPlatformWindow->getClientExtent().y; + + if (!mPlatformWindow->isFullscreen()) + height -= 20;//subtract caption bar from rendertarget size. + + mTexScale.x = 2.0f - ((F32)mTexture.getWidth() / width); + mTexScale.y = 2.0f - ((F32)mTexture.getHeight() / height); + } +} + +void VolumetricFog::setFogColor(ColorF color) +{ + mFogColor.set(255 * color.red,255 * color.green,255 * color.blue); + setMaskBits(FogColorMask); +} + +void VolumetricFog::setFogColor(ColorI color) +{ + mFogColor = color; + setMaskBits(FogColorMask); +} + +void VolumetricFog::setFogDensity(F32 density) +{ + if (density < 0.0f) + density = 0.0f; + mFogDensity = density; + setMaskBits(FogDensityMask); +} + +void VolumetricFog::setFogModulation(F32 strength,Point2F speed1,Point2F speed2) +{ + mStrength = strength; + mSpeed1 = speed1; + mSpeed2 = speed2; + mSpeed.set(speed1.x, speed1.y, speed2.x, speed2.y); + setMaskBits(FogModulationMask); +} + +void VolumetricFog::setFogGlow(bool on_off, F32 strength) +{ + mUseGlow = on_off; + mGlowStrength = strength; + setMaskBits(FogPostFXMask); +} + +void VolumetricFog::setFogLightray(bool on_off, F32 strength) +{ + mModifLightRays = on_off; + mLightRayMod = strength; + setMaskBits(FogPostFXMask); +} + +bool VolumetricFog::isInsideFog() +{ + return mCamInFog; +} + +DefineEngineMethod(VolumetricFog, SetFogColorF, void, (ColorF new_color), , +"@brief Changes the color of the fog\n\n." +"@params new_color the new fog color (rgb 0.0 - 1.0, a is ignored.") +{ + object->setFogColor(new_color); +} + +DefineEngineMethod(VolumetricFog, SetFogColor, void, (ColorI new_color), , +"@brief Changes the color of the fog\n\n." +"@params new_color the new fog color (rgb 0-255, a is ignored.") +{ + object->setFogColor(new_color); +} + +DefineEngineMethod(VolumetricFog, SetFogDensity, void, (F32 new_density), , +"@brief Changes the density of the fog\n\n." +"@params new_density the new fog density.") +{ + object->setFogDensity(new_density); +} + +DefineEngineMethod(VolumetricFog, SetFogModulation, void, (F32 new_strenght, Point2F new_speed1, Point2F new_speed2), , +"@brief Changes the modulation of the fog\n\n." +"@params new_strenght the new strength of the modulation.\n" +"@params new_speed1 the new speed (x y) of the modulation layer 1.\n" +"@params new_speed2 the new speed (x y) of the modulation layer 2.\n") +{ + object->setFogModulation(new_strenght, new_speed1, new_speed2); +} + +DefineEngineMethod(VolumetricFog, SetFogGlow, void, (bool on_off,F32 strength), , +"@brief Changes the glow postfx when inside the fog\n\n." +"@params on_off set to true to enable glow.\n" +"@params strength glow strength.\n") +{ + object->setFogGlow(on_off, strength); +} + +DefineEngineMethod(VolumetricFog, SetFogLightray, void, (bool on_off, F32 strength), , +"@brief Changes the lightrays postfx when inside the fog\n\n." +"@params on_off set to true to modification of the lightray postfx.\n" +"@params strength lightray strength.\n") +{ + object->setFogLightray(on_off, strength); +} + +DefineEngineMethod(VolumetricFog, isInsideFog, bool, (), , +"@brief returns true if control object is inside the fog\n\n.") +{ + return object->isInsideFog(); +} \ No newline at end of file diff --git a/Engine/source/environment/VolumetricFog.h b/Engine/source/environment/VolumetricFog.h new file mode 100644 index 000000000..ef2f39929 --- /dev/null +++ b/Engine/source/environment/VolumetricFog.h @@ -0,0 +1,243 @@ +//----------------------------------------------------------------------------- +// 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 _VolumetricFog_H_ +#define _VolumetricFog_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _MATTEXTURETARGET_H_ +#include "materials/matTextureTarget.h" +#endif +#ifndef _GFXSHADER_H_ +#include "gfx/gfxShader.h" +#endif +#ifndef _GFXTARGET_H_ +#include "gfx/gfxTarget.h" +#endif +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif +#ifndef _TSSHAPE_H_ +#include "ts/tsShape.h" +#endif +#ifndef _POST_EFFECT_H_ +#include "postFx/postEffect.h" +#endif + +#include "gui/core/guiCanvas.h" + +class VolumetricFogRTManager; + +class VolumetricFog : public SceneObject +{ + typedef SceneObject Parent; + + // Maskbits for updating + enum + { + VolumetricFogMask = Parent::NextFreeMask, + FogColorMask = Parent::NextFreeMask << 1, + FogDensityMask = Parent::NextFreeMask << 2, + FogModulationMask = Parent::NextFreeMask << 3, + FogPostFXMask = Parent::NextFreeMask << 4, + FogShapeMask = Parent::NextFreeMask << 5, + NextFreeMask = Parent::NextFreeMask << 6 + }; + +// Struct which holds the shape details + struct meshes + { + F32 det_size; + S32 sub_shape; + S32 obj_det; + U32 num_verts; + GFXVertexPNTT *verts; + Vector *piArray; + Vector *indices; + }; + + protected: + // Rendertargets; + GFXTextureTargetRef z_buf; + NamedTexTargetRef mPrepassTarget; + NamedTexTargetRef mDepthBufferTarget; + NamedTexTargetRef mFrontBufferTarget; + + // Fog Modulation texture + GFXTexHandle mTexture; + + // Shaders + GFXShaderRef mShader; + GFXShaderRef mPrePassShader; + GFXShaderRef mReflectionShader; + + // Stateblocks + GFXStateBlockDesc descD; + GFXStateBlockDesc descF; + GFXStateBlockDesc desc_preD; + GFXStateBlockDesc desc_preF; + GFXStateBlockDesc desc_refl; + + GFXStateBlockRef mStateblockD; + GFXStateBlockRef mStateblockF; + GFXStateBlockRef mStateblock_preD; + GFXStateBlockRef mStateblock_preF; + GFXStateBlockRef mStateblock_refl; + + // Shaderconstants + GFXShaderConstBufferRef mShaderConsts; + GFXShaderConstHandle *mModelViewProjSC; + GFXShaderConstHandle *mFadeSizeSC; + GFXShaderConstHandle *mFogColorSC; + GFXShaderConstHandle *mFogDensitySC; + GFXShaderConstHandle *mPreBias; + GFXShaderConstHandle *mAccumTime; + GFXShaderConstHandle *mIsTexturedSC; + GFXShaderConstHandle *mModSpeedSC; + GFXShaderConstHandle *mModStrengthSC; + GFXShaderConstHandle *mViewPointSC; + GFXShaderConstHandle *mTexScaleSC; + GFXShaderConstHandle *mTexTilesSC; + + GFXShaderConstBufferRef mPPShaderConsts; + GFXShaderConstHandle *mPPModelViewProjSC; + + GFXShaderConstHandle *mAmbientColorSC; + + GFXShaderConstBufferRef mReflShaderConsts; + GFXShaderConstHandle *mReflModelViewProjSC; + GFXShaderConstHandle *mReflFogColorSC; + GFXShaderConstHandle *mReflFogDensitySC; + GFXShaderConstHandle *mReflFogStrengthSC; + + // Vertex and Prim. Buffer + GFXVertexBufferHandle mVB; + GFXPrimitiveBufferHandle mPB; + + // Fog volume data; + StringTableEntry mShapeName; + ColorI mFogColor; + F32 mFogDensity; + bool mIgnoreWater; + bool mReflect; + Vector det_size; + bool mShapeLoaded; + F32 mPixelSize; + F32 mFadeSize; + U32 mCurDetailLevel; + U32 mNumDetailLevels; + F32 mObjSize; + F32 mRadius; + OrientedBox3F ColBox; + VectorF mObjScale; + F32 mMinDisplaySize; + F32 mInvScale; + + // Fog Modulation data + String mTextureName; + bool mIsTextured; + F32 mTexTiles; + F32 mStrength; + Point2F mSpeed1; + Point2F mSpeed2; + Point4F mSpeed; + Point2F mTexScale; + + // Fog Rendering data + Point3F camPos; + Point2F mViewPoint; + F32 mFOV; + F32 viewDist; + bool mIsVBDirty; + bool mIsPBDirty; + bool mCamInFog; + bool mResizing; + PlatformWindow *mPlatformWindow; + + // Reflections + F32 mFogReflStrength; + + // PostFX + PostEffect *glowFX; + bool mUseGlow; + F32 mGlowStrength; + U8 mGlowing; + F32 mCurGlow; + + bool mModifLightRays; + F32 mLightRayMod; + F32 mOldLightRayStrength; + + GameConnection* conn; + U32 mCounter; + + void ResizeRT(PlatformWindow *win, bool resize); + + protected: + // Protected methods + bool onAdd(); + void onRemove(); + void handleResize(VolumetricFogRTManager *RTM, bool resize); + void handleCanvasResize(GuiCanvas* canvas); + + bool LoadShape(); + bool setupRenderer(); + void InitTexture(); + bool UpdateBuffers(U32 dl,bool force=true); + + void processTick(const Move *move); + void _enterFog(ShapeBase *control); + void _leaveFog(ShapeBase *control); + + public: + // Public methods + VolumetricFog(); + ~VolumetricFog(); + + static void initPersistFields(); + virtual void inspectPostApply(); + + U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *conn, BitStream *stream); + + void prepRenderImage(SceneRenderState* state); + void render(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat); + void reflect_render(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat); + + // Methods for modifying & networking various fog elements + // Used in script + void setFogColor(ColorF color); + void setFogColor(ColorI color); + void setFogDensity(F32 density); + void setFogModulation(F32 strength, Point2F speed1, Point2F speed2); + void setFogGlow(bool on_off, F32 strength); + void setFogLightray(bool on_off, F32 strength); + bool isInsideFog(); + + DECLARE_CONOBJECT(VolumetricFog); + + DECLARE_CALLBACK(void, onEnterFog, (SimObjectId obj)); + DECLARE_CALLBACK(void, onLeaveFog, (SimObjectId obj)); +}; +#endif \ No newline at end of file diff --git a/Engine/source/environment/VolumetricFogRTManager.cpp b/Engine/source/environment/VolumetricFogRTManager.cpp new file mode 100644 index 000000000..2a927cc09 --- /dev/null +++ b/Engine/source/environment/VolumetricFogRTManager.cpp @@ -0,0 +1,299 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Volumetric Fog Rendertarget Manager +// +// Creates and maintains one set of rendertargets to be used by every +// VolumetricFog object in the scene. +// +// Will be loaded at startup end removed when ending game. +// +//----------------------------------------------------------------------------- + +#include "VolumetricFogRTManager.h" +#include "core/module.h" +#include "scene/sceneManager.h" +#include "windowManager/platformWindowMgr.h" +#include "console/engineAPI.h" +#include "gui/core/guiCanvas.h" + +MODULE_BEGIN(VolumetricFogRTManager) + +MODULE_INIT_AFTER(Scene) +MODULE_SHUTDOWN_BEFORE(Scene) + +MODULE_INIT +{ + gVolumetricFogRTManager = new VolumetricFogRTManager; + gClientSceneGraph->addObjectToScene(gVolumetricFogRTManager); +} + +MODULE_SHUTDOWN +{ + gClientSceneGraph->removeObjectFromScene(gVolumetricFogRTManager); + SAFE_DELETE(gVolumetricFogRTManager); +} + +MODULE_END; + +ConsoleDocClass( VolumetricFogRTManager, +"@brief Creates and maintains one set of rendertargets to be used by every\n" +"VolumetricFog object in the scene.\n\n" +"Will be loaded at startup end removed when ending game.\n\n" +"Methods:\n" +" get() returns the currently loaded VolumetricFogRTManager, also accessible\n" +" through VFRTM define.\n" +" Init() Initializes the rendertargets, called when a VolumetricFog object is\n" +" added to the scene.\n" +" isInitialed() returns true if Rendertargets are present, false if not, then\n" +" Init() should be called to create the rendertargets.\n" +" setQuality(U32 Quality) Normally a rendertarget has the same size as the view,\n" +" with this method you can scale down the size of it.\n" +" Be aware that scaling down will introduce renderartefacts.\n" +"@ingroup Atmosphere" +); + +VolumetricFogRTMResizeSignal VolumetricFogRTManager::smVolumetricFogRTMResizeSignal; + +VolumetricFogRTManager *gVolumetricFogRTManager = NULL; + +S32 VolumetricFogRTManager::mTargetScale = 1; + +IMPLEMENT_CONOBJECT(VolumetricFogRTManager); + +VolumetricFogRTManager::VolumetricFogRTManager() +{ + setGlobalBounds(); + mTypeMask |= EnvironmentObjectType; + mNetFlags.set(IsGhost); + mIsInitialized = false; + mNumFogObjects = 0; +} + +VolumetricFogRTManager::~VolumetricFogRTManager() +{ + if (mFrontTarget.isRegistered()) + mFrontTarget.unregister(); + + if (mDepthTarget.isRegistered()) + mDepthTarget.unregister(); + + if (mDepthBuffer.isValid()) + mDepthBuffer->kill(); + + if (mFrontBuffer.isValid()) + mFrontBuffer->kill(); +} + +void VolumetricFogRTManager::onSceneRemove() +{ + if (mIsInitialized) + mPlatformWindow->getScreenResChangeSignal().remove(this, &VolumetricFogRTManager::ResizeRT); +} + +void VolumetricFogRTManager::onRemove() +{ + removeFromScene(); + Parent::onRemove(); +} + +void VolumetricFogRTManager::consoleInit() +{ + Con::addVariable("$pref::VolumetricFog::Quality", TypeS32, &mTargetScale, + "The scale of the rendertargets.\n" + "@ingroup Rendering\n"); +} + +bool VolumetricFogRTManager::Init() +{ + if (mIsInitialized) + { + Con::errorf("VolumetricFogRTManager allready initialized!!"); + return true; + } + + GuiCanvas* cv = dynamic_cast(Sim::findObject("Canvas")); + if (cv == NULL) + { + Con::errorf("VolumetricFogRTManager::Init() - Canvas not found!!"); + return false; + } + + mPlatformWindow = cv->getPlatformWindow(); + mPlatformWindow->getScreenResChangeSignal().notify(this,&VolumetricFogRTManager::ResizeRT); + + if (mTargetScale < 1) + mTargetScale = 1; + + mWidth = mFloor(mPlatformWindow->getClientExtent().x / mTargetScale); + mHeight = mPlatformWindow->getClientExtent().y; + mFullScreen = mPlatformWindow->isFullscreen(); + if (!mFullScreen) + mHeight -= 20;//subtract caption bar from rendertarget size. + mHeight = mFloor(mHeight / mTargetScale); + + mDepthBuffer = GFXTexHandle(mWidth, mHeight, GFXFormatR32F, + &GFXDefaultRenderTargetProfile, avar("%s() - mDepthBuffer (line %d)", __FUNCTION__, __LINE__)); + if (!mDepthBuffer.isValid()) + { + Con::errorf("VolumetricFogRTManager Fatal Error: Unable to create Depthbuffer"); + return false; + } + if (!mDepthTarget.registerWithName("volfogdepth")) + { + Con::errorf("VolumetricFogRTManager Fatal Error : Unable to register Depthbuffer"); + return false; + } + mDepthTarget.setTexture(mDepthBuffer); + + mFrontBuffer = GFXTexHandle(mWidth, mHeight, GFXFormatR32F, + &GFXDefaultRenderTargetProfile, avar("%s() - mFrontBuffer (line %d)", __FUNCTION__, __LINE__)); + if (!mFrontBuffer.isValid()) + { + Con::errorf("VolumetricFogRTManager Fatal Error: Unable to create front buffer"); + return false; + } + if (!mFrontTarget.registerWithName("volfogfront")) + { + Con::errorf("VolumetricFogRTManager Fatal Error : Unable to register Frontbuffer"); + return false; + } + + mFrontTarget.setTexture(mFrontBuffer); + + Con::setVariable("$VolumetricFog::density", "0.0"); + + mIsInitialized = true; + + return true; +} + +U32 VolumetricFogRTManager::IncFogObjects() +{ + mNumFogObjects++; + return mNumFogObjects; +} + +U32 VolumetricFogRTManager::DecFogObjects() +{ + if (mNumFogObjects > 0) + mNumFogObjects--; + return mNumFogObjects; +} + +void VolumetricFogRTManager::ResizeRT(PlatformWindow* win,bool resize) +{ + mFogHasAnswered = 0; + smVolumetricFogRTMResizeSignal.trigger(this, true); +} + +void VolumetricFogRTManager::FogAnswered() +{ + mFogHasAnswered++; + if (mFogHasAnswered == mNumFogObjects) + { + if (Resize()) + smVolumetricFogRTMResizeSignal.trigger(this, false); + else + Con::errorf("VolumetricFogRTManager::FogAnswered - Error resizing rendertargets!"); + } +} + +bool VolumetricFogRTManager::Resize() +{ + if (mTargetScale < 1) + mTargetScale = 1; + mWidth = mFloor(mPlatformWindow->getClientExtent().x / mTargetScale); + mHeight = mPlatformWindow->getClientExtent().y; + + if (!mPlatformWindow->isFullscreen()) + mHeight -= 20;//subtract caption bar from rendertarget size. + mHeight = mFloor(mHeight / mTargetScale); + + if (mWidth < 16 || mHeight < 16) + return false; + + if (mFrontTarget.isRegistered()) + mFrontTarget.setTexture(NULL); + + if (mDepthTarget.isRegistered()) + mDepthTarget.setTexture(NULL); + + if (mDepthBuffer.isValid()) + mDepthBuffer->kill(); + + if (mFrontBuffer.isValid()) + mFrontBuffer->kill(); + + mFrontBuffer = GFXTexHandle(mWidth, mHeight, GFXFormatR32F, + &GFXDefaultRenderTargetProfile, avar("%s() - mFrontBuffer (line %d)", __FUNCTION__, __LINE__)); + if (!mFrontBuffer.isValid()) + { + Con::errorf("VolumetricFogRTManager::Resize() Fatal Error: Unable to create front buffer"); + return false; + } + mFrontTarget.setTexture(mFrontBuffer); + + mDepthBuffer = GFXTexHandle(mWidth, mHeight, GFXFormatR32F, + &GFXDefaultRenderTargetProfile, avar("%s() - mDepthBuffer (line %d)", __FUNCTION__, __LINE__)); + if (!mDepthBuffer.isValid()) + { + Con::errorf("VolumetricFogRTManager::Resize() Fatal Error: Unable to create Depthbuffer"); + return false; + } + mDepthTarget.setTexture(mDepthBuffer); + return true; +} + +S32 VolumetricFogRTManager::setQuality(U32 Quality) +{ + if (!mIsInitialized) + return (mTargetScale = Quality); + + if (Quality < 1) + Quality = 1; + + if (Quality == mTargetScale) + return mTargetScale; + + mTargetScale = Quality; + + mFogHasAnswered = 0; + smVolumetricFogRTMResizeSignal.trigger(this, true); + + return mTargetScale; +} + +VolumetricFogRTManager* VolumetricFogRTManager::get() +{ + return gVolumetricFogRTManager; +} + +DefineConsoleFunction(SetFogVolumeQuality, S32, (U32 new_quality), , +"@brief Resizes the rendertargets of the Volumetric Fog object.\n" +"@params new_quality new quality for the rendertargets 1 = full size, 2 = halfsize, 3 = 1/3, 4 = 1/4 ...") +{ + if (VFRTM == NULL) + return -1; + return VFRTM->setQuality(new_quality); +} \ No newline at end of file diff --git a/Engine/source/environment/VolumetricFogRTManager.h b/Engine/source/environment/VolumetricFogRTManager.h new file mode 100644 index 000000000..d69bed6bd --- /dev/null +++ b/Engine/source/environment/VolumetricFogRTManager.h @@ -0,0 +1,92 @@ +//----------------------------------------------------------------------------- +// 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 _VolumetricFogRTManager_H_ +#define _VolumetricFogRTManager_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _MATTEXTURETARGET_H_ +#include "materials/matTextureTarget.h" +#endif +#ifndef _GFXTARGET_H_ +#include "gfx/gfxTarget.h" +#endif +#ifndef _SIGNAL_H_ +#include "core/util/tSignal.h" +#endif + +class VolumetricFogRTManager; + +typedef Signal VolumetricFogRTMResizeSignal; + +#define VFRTM VolumetricFogRTManager::get() + +class VolumetricFogRTManager : public SceneObject +{ + public: + typedef SceneObject Parent; + + protected: + GFXTexHandle mDepthBuffer; + GFXTexHandle mFrontBuffer; + + NamedTexTarget mDepthTarget; + NamedTexTarget mFrontTarget; + + PlatformWindow* mPlatformWindow; + + static S32 mTargetScale; + bool mIsInitialized; + U32 mNumFogObjects; + U32 mFogHasAnswered; + U32 mWidth; + U32 mHeight; + bool mFullScreen; + + void onRemove(); + void onSceneRemove(); + void ResizeRT(PlatformWindow *win, bool resize); + + static VolumetricFogRTMResizeSignal smVolumetricFogRTMResizeSignal; + + public: + VolumetricFogRTManager(); + ~VolumetricFogRTManager(); + static VolumetricFogRTManager *get(); + bool Init(); + bool IsInitialized() { return mIsInitialized; } + static void consoleInit(); + static VolumetricFogRTMResizeSignal& getVolumetricFogRTMResizeSignal() { return smVolumetricFogRTMResizeSignal; } + void FogAnswered(); + S32 setQuality(U32 Quality); + bool Resize(); + U32 IncFogObjects(); + U32 DecFogObjects(); + + DECLARE_CONOBJECT(VolumetricFogRTManager); +}; + +extern VolumetricFogRTManager* gVolumetricFogRTManager; + +#endif \ No newline at end of file