@@ -0,0 +1,573 @@
+#include "console/engineAPI.h"
+#include "materials/shaderData.h"
+#include "gfx/gfxTextureManager.h"
+#include "gfx/gfxTransformSaver.h"
+#include "gfx/bitmap/cubemapSaver.h"
+
+namespace IBLUtilities
+{
+ void GenerateIrradianceMap(GFXTextureTargetRef renderTarget, GFXCubemapHandle cubemap, GFXCubemapHandle &cubemapOut)
+ {
+ GFXTransformSaver saver;
+ GFXStateBlockRef irrStateBlock;
+ ShaderData *irrShaderData;
+ GFXShaderRef irrShader = Sim::findObject("IrradianceShader", irrShaderData) ? irrShaderData->getShader() : NULL;
+ if (!irrShader)
+ Con::errorf("IBLUtilities::GenerateIrradianceMap() - could not find IrradianceShader");
+ return;
+ }
+ GFXShaderConstBufferRef irrConsts = irrShader->allocConstBuffer();
+ GFXShaderConstHandle* irrEnvMapSC = irrShader->getShaderConstHandle("$environmentMap");
+ GFXShaderConstHandle* irrFaceSC = irrShader->getShaderConstHandle("$face");
+ GFXStateBlockDesc desc;
+ desc.zEnable = false;
+ desc.samplersDefined = true;
+ desc.samplers[0].addressModeU = GFXAddressClamp;
+ desc.samplers[0].addressModeV = GFXAddressClamp;
+ desc.samplers[0].addressModeW = GFXAddressClamp;
+ desc.samplers[0].magFilter = GFXTextureFilterLinear;
+ desc.samplers[0].minFilter = GFXTextureFilterLinear;
+ desc.samplers[0].mipFilter = GFXTextureFilterLinear;
+ irrStateBlock = GFX->createStateBlock(desc);
+ GFX->pushActiveRenderTarget();
+ GFX->setShader(irrShader);
+ GFX->setShaderConstBuffer(irrConsts);
+ GFX->setStateBlock(irrStateBlock);
+ GFX->setVertexBuffer(NULL);
+ GFX->setCubeTexture(0, cubemap);
+ for (U32 i = 0; i < 6; i++)
+ renderTarget->attachTexture(GFXTextureTarget::Color0, cubemapOut, i);
+ irrConsts->setSafe(irrFaceSC, (S32)i);
+ GFX->setActiveRenderTarget(renderTarget);
+ GFX->clear(GFXClearTarget, LinearColorF::BLACK, 1.0f, 0);
+ GFX->drawPrimitive(GFXTriangleList, 0, 3);
+ renderTarget->resolve();
+ GFX->popActiveRenderTarget();
+ void GeneratePrefilterMap(GFXTextureTargetRef renderTarget, GFXCubemapHandle cubemap, U32 mipLevels, GFXCubemapHandle &cubemapOut)
+ ShaderData *prefilterShaderData;
+ GFXShaderRef prefilterShader = Sim::findObject("PrefiterCubemapShader", prefilterShaderData) ? prefilterShaderData->getShader() : NULL;
+ if (!prefilterShader)
+ Con::errorf("IBLUtilities::GeneratePrefilterMap() - could not find PrefiterCubemapShader");
+ GFXShaderConstBufferRef prefilterConsts = prefilterShader->allocConstBuffer();
+ GFXShaderConstHandle* prefilterEnvMapSC = prefilterShader->getShaderConstHandle("$environmentMap");
+ GFXShaderConstHandle* prefilterFaceSC = prefilterShader->getShaderConstHandle("$face");
+ GFXShaderConstHandle* prefilterRoughnessSC = prefilterShader->getShaderConstHandle("$roughness");
+ GFXShaderConstHandle* prefilterMipSizeSC = prefilterShader->getShaderConstHandle("$mipSize");
+ GFXShaderConstHandle* prefilterResolutionSC = prefilterShader->getShaderConstHandle("$resolution");
+ GFX->setShader(prefilterShader);
+ GFX->setShaderConstBuffer(prefilterConsts);
+ U32 prefilterSize = cubemapOut->getSize();
+ for (U32 face = 0; face < 6; face++)
+ prefilterConsts->setSafe(prefilterFaceSC, (S32)face);
+ prefilterConsts->setSafe(prefilterResolutionSC, renderTarget->getSize().x);
+ for (U32 mip = 0; mip < mipLevels; mip++)
+ S32 mipSize = prefilterSize >> mip;
+ F32 roughness = (float)mip / (float)(mipLevels - 1);
+ prefilterConsts->setSafe(prefilterRoughnessSC, roughness);
+ prefilterConsts->setSafe(prefilterMipSizeSC, mipSize);
+ U32 size = prefilterSize * mPow(0.5f, mip);
+ renderTarget->attachTexture(GFXTextureTarget::Color0, cubemapOut, face, mip);
+ GFX->setActiveRenderTarget(renderTarget, false);//we set the viewport ourselves
+ GFX->setViewport(RectI(0, 0, size, size));
+ void GenerateBRDFTexture(GFXTexHandle &textureOut)
+ ShaderData *brdfShaderData;
+ GFXShaderRef brdfShader = Sim::findObject("BRDFLookupShader", brdfShaderData) ? brdfShaderData->getShader() : NULL;
+ if (!brdfShader)
+ Con::errorf("IBLUtilities::GenerateBRDFTexture() - could not find BRDFLookupShader");
+ U32 textureSize = textureOut->getWidth();
+ GFXTextureTargetRef renderTarget = GFX->allocRenderToTextureTarget();
+ GFX->setShader(brdfShader);
+ renderTarget->attachTexture(GFXTextureTarget::Color0, textureOut);
+ GFX->setActiveRenderTarget(renderTarget);//potential bug here with the viewport not updating with the new size
+ GFX->setViewport(RectI(0, 0, textureSize, textureSize));//see above comment
+ GFX->clear(GFXClearTarget, LinearColorF::BLUE, 1.0f, 0);
+ void bakeReflection(String outputPath, S32 resolution)
+ //GFXDEBUGEVENT_SCOPE(ReflectionProbe_Bake, ColorI::WHITE);
+ /*PostEffect *preCapture = dynamic_cast<PostEffect*>(Sim::findObject("AL_PreCapture"));
+ PostEffect *deferredShading = dynamic_cast<PostEffect*>(Sim::findObject("AL_DeferredShading"));
+ if (preCapture)
+ preCapture->enable();
+ if (deferredShading)
+ deferredShading->disable();
+ //if (mReflectionModeType == StaticCubemap || mReflectionModeType == BakedCubemap || mReflectionModeType == SkyLight)
+ if (!mCubemap)
+ mCubemap = new CubemapData();
+ mCubemap->registerObject();
+ if (mReflectionModeType == DynamicCubemap && mDynamicCubemap.isNull())
+ //mCubemap->createMap();
+ mDynamicCubemap = GFX->createCubemap();
+ mDynamicCubemap->initDynamic(resolution, GFXFormatR8G8B8);
+ else if (mReflectionModeType != DynamicCubemap)
+ if (mReflectionPath.isEmpty() || !mPersistentId)
+ if (!mPersistentId)
+ mPersistentId = getOrCreatePersistentId();
+ mReflectionPath = outputPath.c_str();
+ mProbeUniqueID = std::to_string(mPersistentId->getUUID().getHash()).c_str();
+ bool validCubemap = true;
+ // Save the current transforms so we can restore
+ // it for child control rendering below.
+ //bool saveEditingMission = gEditingMission;
+ //gEditingMission = false;
+ //Set this to true to use the prior method where it goes through the SPT_Reflect path for the bake
+ bool probeRenderState = ReflectionProbe::smRenderReflectionProbes;
+ ReflectionProbe::smRenderReflectionProbes = false;
+ for (U32 i = 0; i < 6; ++i)
+ GFXTexHandle blendTex;
+ blendTex.set(resolution, resolution, GFXFormatR8G8B8A8, &GFXRenderTargetProfile, "");
+ GFXTextureTargetRef mBaseTarget = GFX->allocRenderToTextureTarget();
+ GFX->clearTextureStateImmediate(0);
+ if (mReflectionModeType == DynamicCubemap)
+ mBaseTarget->attachTexture(GFXTextureTarget::Color0, mDynamicCubemap, i);
+ else
+ mBaseTarget->attachTexture(GFXTextureTarget::Color0, blendTex);
+ // Standard view that will be overridden below.
+ VectorF vLookatPt(0.0f, 0.0f, 0.0f), vUpVec(0.0f, 0.0f, 0.0f), vRight(0.0f, 0.0f, 0.0f);
+ switch (i)
+ case 0: // D3DCUBEMAP_FACE_POSITIVE_X:
+ vLookatPt = VectorF(1.0f, 0.0f, 0.0f);
+ vUpVec = VectorF(0.0f, 1.0f, 0.0f);
+ break;
+ case 1: // D3DCUBEMAP_FACE_NEGATIVE_X:
+ vLookatPt = VectorF(-1.0f, 0.0f, 0.0f);
+ case 2: // D3DCUBEMAP_FACE_POSITIVE_Y:
+ vLookatPt = VectorF(0.0f, 1.0f, 0.0f);
+ vUpVec = VectorF(0.0f, 0.0f, -1.0f);
+ case 3: // D3DCUBEMAP_FACE_NEGATIVE_Y:
+ vLookatPt = VectorF(0.0f, -1.0f, 0.0f);
+ vUpVec = VectorF(0.0f, 0.0f, 1.0f);
+ case 4: // D3DCUBEMAP_FACE_POSITIVE_Z:
+ vLookatPt = VectorF(0.0f, 0.0f, 1.0f);
+ case 5: // D3DCUBEMAP_FACE_NEGATIVE_Z:
+ vLookatPt = VectorF(0.0f, 0.0f, -1.0f);
+ // create camera matrix
+ VectorF cross = mCross(vUpVec, vLookatPt);
+ cross.normalizeSafe();
+ MatrixF matView(true);
+ matView.setColumn(0, cross);
+ matView.setColumn(1, vLookatPt);
+ matView.setColumn(2, vUpVec);
+ matView.setPosition(getPosition());
+ matView.inverse();
+ // set projection to 90 degrees vertical and horizontal
+ F32 left, right, top, bottom;
+ F32 nearPlane = 0.01f;
+ F32 farDist = 1000.f;
+ MathUtils::makeFrustum(&left, &right, &top, &bottom, M_HALFPI_F, 1.0f, nearPlane);
+ Frustum frustum(false, left, right, top, bottom, nearPlane, farDist);
+ renderFrame(&mBaseTarget, matView, frustum, StaticObjectType | StaticShapeObjectType & EDITOR_RENDER_TYPEMASK, gCanvasClearColor);
+ mBaseTarget->resolve();
+ mCubemap->setCubeFaceTexture(i, blendTex);
+ if (mReflectionModeType != DynamicCubemap && validCubemap)
+ if (mCubemap->mCubemap)
+ mCubemap->updateFaces();
+ mCubemap->createMap();
+ char fileName[256];
+ dSprintf(fileName, 256, "%s%s.DDS", mReflectionPath.c_str(), mProbeUniqueID.c_str());
+ CubemapSaver::save(mCubemap->mCubemap, fileName);
+ if (!Platform::isFile(fileName))
+ validCubemap = false; //if we didn't save right, just
+ Con::errorf("Failed to properly save out the skylight baked cubemap!");
+ mDirty = false;
+ //calculateSHTerms();
+ ReflectionProbe::smRenderReflectionProbes = probeRenderState;
+ setMaskBits(-1);
+ preCapture->disable();
+ deferredShading->enable();*/
+ LinearColorF decodeSH(Point3F normal, const LinearColorF SHTerms[9], const F32 SHConstants[5])
+ float x = normal.x;
+ float y = normal.y;
+ float z = normal.z;
+ LinearColorF l00 = SHTerms[0];
+ LinearColorF l10 = SHTerms[1];
+ LinearColorF l11 = SHTerms[2];
+ LinearColorF l12 = SHTerms[3];
+ LinearColorF l20 = SHTerms[4];
+ LinearColorF l21 = SHTerms[5];
+ LinearColorF l22 = SHTerms[6];
+ LinearColorF l23 = SHTerms[7];
+ LinearColorF l24 = SHTerms[8];
+ LinearColorF result = (
+ l00 * SHConstants[0] +
+ l12 * SHConstants[1] * x +
+ l10 * SHConstants[1] * y +
+ l11 * SHConstants[1] * z +
+ l20 * SHConstants[2] * x*y +
+ l21 * SHConstants[2] * y*z +
+ l22 * SHConstants[3] * (3.0*z*z - 1.0) +
+ l23 * SHConstants[2] * x*z +
+ l24 * SHConstants[4] * (x*x - y * y)
+ );
+ return LinearColorF(mMax(result.red, 0), mMax(result.green, 0), mMax(result.blue, 0));
+ MatrixF getSideMatrix(U32 side)
+ switch (side)
+ MatrixF rotMat(true);
+ rotMat.setColumn(0, cross);
+ rotMat.setColumn(1, vLookatPt);
+ rotMat.setColumn(2, vUpVec);
+ //rotMat.inverse();
+ return rotMat;
+ F32 harmonics(U32 termId, Point3F normal)
+ F32 x = normal.x;
+ F32 y = normal.y;
+ F32 z = normal.z;
+ switch (termId)
+ case 0:
+ return 1.0;
+ case 1:
+ return y;
+ case 2:
+ return z;
+ case 3:
+ return x;
+ case 4:
+ return x * y;
+ case 5:
+ return y * z;
+ case 6:
+ return 3.0*z*z - 1.0;
+ case 7:
+ return x * z;
+ default:
+ return x * x - y * y;
+ LinearColorF sampleSide(GBitmap* cubeFaceBitmaps[6], const U32& cubemapResolution, const U32& termindex, const U32& sideIndex)
+ MatrixF sideRot = getSideMatrix(sideIndex);
+ LinearColorF result = LinearColorF::ZERO;
+ F32 divider = 0;
+ for (int y = 0; y<cubemapResolution; y++)
+ for (int x = 0; x<cubemapResolution; x++)
+ Point2F sidecoord = ((Point2F(x, y) + Point2F(0.5, 0.5)) / Point2F(cubemapResolution, cubemapResolution))*2.0 - Point2F(1.0, 1.0);
+ Point3F normal = Point3F(sidecoord.x, sidecoord.y, -1.0);
+ normal.normalize();
+ F32 minBrightness = Con::getFloatVariable("$pref::GI::Cubemap_Sample_MinBrightness", 0.001f);
+ LinearColorF texel = cubeFaceBitmaps[sideIndex]->sampleTexel(y, x);
+ texel = LinearColorF(mMax(texel.red, minBrightness), mMax(texel.green, minBrightness), mMax(texel.blue, minBrightness)) * Con::getFloatVariable("$pref::GI::Cubemap_Gain", 1.5);
+ Point3F dir;
+ sideRot.mulP(normal, &dir);
+ result += texel * harmonics(termindex, dir) * -normal.z;
+ divider += -normal.z;
+ result /= divider;
+ return result;
+ //
+ //SH Calculations
+ // From http://sunandblackcat.com/tipFullView.php?l=eng&topicid=32&topic=Spherical-Harmonics-From-Cube-Texture
+ // With shader decode logic from https://github.com/nicknikolov/cubemap-sh
+ void calculateSHTerms(GFXCubemapHandle cubemap, LinearColorF SHTerms[9], F32 SHConstants[5])
+ if (!cubemap)
+ const VectorF cubemapFaceNormals[6] =
+ // D3DCUBEMAP_FACE_POSITIVE_X:
+ VectorF(1.0f, 0.0f, 0.0f),
+ // D3DCUBEMAP_FACE_NEGATIVE_X:
+ VectorF(-1.0f, 0.0f, 0.0f),
+ // D3DCUBEMAP_FACE_POSITIVE_Y:
+ VectorF(0.0f, 1.0f, 0.0f),
+ // D3DCUBEMAP_FACE_NEGATIVE_Y:
+ VectorF(0.0f, -1.0f, 0.0f),
+ // D3DCUBEMAP_FACE_POSITIVE_Z:
+ VectorF(0.0f, 0.0f, 1.0f),
+ // D3DCUBEMAP_FACE_NEGATIVE_Z:
+ VectorF(0.0f, 0.0f, -1.0f),
+ };
+ U32 cubemapResolution = cubemap->getSize();
+ GBitmap* cubeFaceBitmaps[6];
+ cubeFaceBitmaps[i] = new GBitmap(cubemapResolution, cubemapResolution, false, GFXFormatR16G16B16A16F);
+ //If we fail to parse the cubemap for whatever reason, we really can't continue
+ if (!CubemapSaver::getBitmaps(cubemap, GFXFormatR8G8B8, cubeFaceBitmaps))
+ //Set up our constants
+ F32 L0 = Con::getFloatVariable("$pref::GI::SH_Term_L0", 1.0f);
+ F32 L1 = Con::getFloatVariable("$pref::GI::SH_Term_L1", 1.8f);
+ F32 L2 = Con::getFloatVariable("$pref::GI::SH_Term_L2", 0.83f);
+ F32 L2m2_L2m1_L21 = Con::getFloatVariable("$pref::GI::SH_Term_L2m2", 2.9f);
+ F32 L20 = Con::getFloatVariable("$pref::GI::SH_Term_L20", 0.58f);
+ F32 L22 = Con::getFloatVariable("$pref::GI::SH_Term_L22", 1.1f);
+ SHConstants[0] = L0;
+ SHConstants[1] = L1;
+ SHConstants[2] = L2 * L2m2_L2m1_L21;
+ SHConstants[3] = L2 * L20;
+ SHConstants[4] = L2 * L22;
+ for (U32 i = 0; i < 9; i++)
+ //Clear it, just to be sure
+ SHTerms[i] = LinearColorF(0.f, 0.f, 0.f);
+ //Now, encode for each side
+ SHTerms[i] = sampleSide(cubeFaceBitmaps, cubemapResolution, i, 0); //POS_X
+ SHTerms[i] += sampleSide(cubeFaceBitmaps, cubemapResolution, i, 1); //NEG_X
+ SHTerms[i] += sampleSide(cubeFaceBitmaps, cubemapResolution, i, 2); //POS_Y
+ SHTerms[i] += sampleSide(cubeFaceBitmaps, cubemapResolution, i, 3); //NEG_Y
+ SHTerms[i] += sampleSide(cubeFaceBitmaps, cubemapResolution, i, 4); //POS_Z
+ SHTerms[i] += sampleSide(cubeFaceBitmaps, cubemapResolution, i, 5); //NEG_Z
+ //Average
+ SHTerms[i] /= 6;
+ SAFE_DELETE(cubeFaceBitmaps[i]);
+ /*bool mExportSHTerms = false;
+ if (mExportSHTerms)
+ for (U32 f = 0; f < 6; f++)
+ dSprintf(fileName, 256, "%s%s_DecodedFaces_%d.png", mReflectionPath.c_str(),
+ mProbeUniqueID.c_str(), f);
+ LinearColorF color = decodeSH(cubemapFaceNormals[f]);
+ FileStream stream;
+ if (stream.open(fileName, Torque::FS::File::Write))
+ GBitmap bitmap(mCubemapResolution, mCubemapResolution, false, GFXFormatR8G8B8);
+ bitmap.fill(color.toColorI());
+ bitmap.writeBitmap("png", stream);
+ for (U32 f = 0; f < 9; f++)
+ dSprintf(fileName, 256, "%s%s_SHTerms_%d.png", mReflectionPath.c_str(),
+ LinearColorF color = mProbeInfo->SHTerms[f];
+ }*/
+ F32 areaElement(F32 x, F32 y)
+ return mAtan2(x * y, (F32)mSqrt(x * x + y * y + 1.0));
+ F32 texelSolidAngle(F32 aU, F32 aV, U32 width, U32 height)
+ // transform from [0..res - 1] to [- (1 - 1 / res) .. (1 - 1 / res)]
+ // ( 0.5 is for texel center addressing)
+ const F32 U = (2.0 * (aU + 0.5) / width) - 1.0;
+ const F32 V = (2.0 * (aV + 0.5) / height) - 1.0;
+ // shift from a demi texel, mean 1.0 / size with U and V in [-1..1]
+ const F32 invResolutionW = 1.0 / width;
+ const F32 invResolutionH = 1.0 / height;
+ // U and V are the -1..1 texture coordinate on the current face.
+ // get projected area for this texel
+ const F32 x0 = U - invResolutionW;
+ const F32 y0 = V - invResolutionH;
+ const F32 x1 = U + invResolutionW;
+ const F32 y1 = V + invResolutionH;
+ const F32 angle = areaElement(x0, y0) - areaElement(x0, y1) - areaElement(x1, y0) + areaElement(x1, y1);
+ return angle;
+};
@@ -0,0 +1,30 @@
+#pragma once
+ void GenerateIrradianceMap(GFXTextureTargetRef renderTarget, GFXCubemapHandle cubemap, GFXCubemapHandle &cubemapOut);
+ void GeneratePrefilterMap(GFXTextureTargetRef renderTarget, GFXCubemapHandle cubemap, U32 mipLevels, GFXCubemapHandle &cubemapOut);
+ void GenerateBRDFTexture(GFXTexHandle &textureOut);
+ void bakeReflection(String outputPath, S32 resolution);
+ LinearColorF decodeSH(Point3F normal, const LinearColorF SHTerms[9], const F32 SHConstants[5]);
+ MatrixF getSideMatrix(U32 side);
+ F32 harmonics(U32 termId, Point3F normal);
+ LinearColorF sampleSide(GBitmap* cubeFaceBitmaps[6], const U32& cubemapResolution, const U32& termindex, const U32& sideIndex);
+ void calculateSHTerms(GFXCubemapHandle cubemap, LinearColorF SHTerms[9], F32 SHConstants[5]);
+ F32 texelSolidAngle(F32 aU, F32 aV, U32 width, U32 height);
+ F32 areaElement(F32 x, F32 y);
@@ -0,0 +1,1285 @@
+//-----------------------------------------------------------------------------
+// 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 "T3D/lighting/reflectionProbe.h"
+#include "math/mathIO.h"
+#include "scene/sceneRenderState.h"
+#include "console/consoleTypes.h"
+#include "core/stream/bitStream.h"
+#include "materials/baseMatInstance.h"
+#include "gfx/gfxDrawUtil.h"
+#include "gfx/gfxDebugEvent.h"
+#include "math/mathUtils.h"
+#include "gfx/bitmap/gBitmap.h"
+#include "core/stream/fileStream.h"
+#include "core/fileObject.h"
+#include "core/resourceManager.h"
+#include "console/simPersistId.h"
+#include <string>
+#include "T3D/gameFunctions.h"
+#include "postFx/postEffect.h"
+#include "renderInstance/renderProbeMgr.h"
+#include "lighting/probeManager.h"
+#include "math/util/sphereMesh.h"
+#include "materials/materialManager.h"
+#include "math/util/matrixSet.h"
+#include "materials/materialFeatureTypes.h"
+#include "T3D/lighting/IBLUtilities.h"
+extern bool gEditingMission;
+extern ColorI gCanvasClearColor;
+bool ReflectionProbe::smRenderReflectionProbes = true;
+bool ReflectionProbe::smRenderPreviewProbes = true;
+IMPLEMENT_CO_NETOBJECT_V1(ReflectionProbe);
+ConsoleDocClass(ReflectionProbe,
+ "@brief An example scene object which renders a mesh.\n\n"
+ "This class implements a basic SceneObject that can exist in the world at a "
+ "3D position and render itself. There are several valid ways to render an "
+ "object in Torque. This class implements the preferred rendering method which "
+ "is to submit a MeshRenderInst along with a Material, vertex buffer, "
+ "primitive buffer, and transform and allow the RenderMeshMgr handle the "
+ "actual setup and rendering for you.\n\n"
+ "See the C++ code for implementation details.\n\n"
+ "@ingroup Examples\n");
+ImplementEnumType(ReflectProbeType,
+ "Type of mesh data available in a shape.\n"
+ "@ingroup gameObjects")
+{ ProbeInfo::Sphere, "Sphere", "Sphere shaped" },
+{ ProbeInfo::Box, "Box", "Box shape" }
+EndImplementEnumType;
+ImplementEnumType(IndrectLightingModeEnum,
+{ ReflectionProbe::NoIndirect, "No Lighting", "This probe does not provide any local indirect lighting data" },
+{ ReflectionProbe::AmbientColor, "Ambient Color", "Adds a flat color to act as the local indirect lighting" },
+{ ReflectionProbe::SphericalHarmonics, "Spherical Harmonics", "Creates spherical harmonics data based off the reflection data" },
+ EndImplementEnumType;
+ImplementEnumType(ReflectionModeEnum,
+{ ReflectionProbe::NoReflection, "No Reflections", "This probe does not provide any local reflection data"},
+{ ReflectionProbe::StaticCubemap, "Static Cubemap", "Uses a static CubemapData" },
+{ ReflectionProbe::BakedCubemap, "Baked Cubemap", "Uses a cubemap baked from the probe's current position" },
+//{ ReflectionProbe::DynamicCubemap, "Dynamic Cubemap", "Uses a cubemap baked from the probe's current position, updated at a set rate" },
+// Object setup and teardown
+ReflectionProbe::ReflectionProbe()
+ // Flag this object so that it will always
+ // be sent across the network to clients
+ mNetFlags.set(Ghostable | ScopeAlways);
+ mTypeMask = LightObjectType | MarkerObjectType;
+ mProbeShapeType = ProbeInfo::Sphere;
+ mIndrectLightingModeType = NoIndirect;
+ mAmbientColor = LinearColorF(1, 1, 1, 1);
+ mSphericalHarmonics = LinearColorF(0, 0, 0, 1);
+ mReflectionModeType = BakedCubemap;
+ mEnabled = true;
+ mBake = false;
+ mRadius = 10;
+ mUseCubemap = false;
+ mCubemap = NULL;
+ mReflectionPath = "";
+ mProbeUniqueID = "";
+ mEditorShapeInst = NULL;
+ mEditorShape = NULL;
+ mRefreshRateMS = 200;
+ mDynamicLastBakeMS = 0;
+ mMaxDrawDistance = 75;
+ mResourcesCreated = false;
+ mProbeInfo = new ProbeInfo();
+ mPrefilterSize = 64;
+ mPrefilterMipLevels = mLog2(F32(mPrefilterSize));
+}
+ReflectionProbe::~ReflectionProbe()
+ if (mEditorShapeInst)
+ SAFE_DELETE(mEditorShapeInst);
+ if (mReflectionModeType != StaticCubemap && mCubemap)
+ mCubemap->deleteObject();
+// Object Editing
+void ReflectionProbe::initPersistFields()
+ addGroup("Rendering");
+ addProtectedField("enabled", TypeBool, Offset(mEnabled, ReflectionProbe),
+ &_setEnabled, &defaultProtectedGetFn, "Regenerate Voxel Grid");
+ addField("ProbeShape", TypeReflectProbeType, Offset(mProbeShapeType, ReflectionProbe),
+ "The type of mesh data to use for collision queries.");
+ addField("radius", TypeF32, Offset(mRadius, ReflectionProbe), "The name of the material used to render the mesh.");
+ endGroup("Rendering");
+ /*addGroup("IndirectLighting");
+ addField("IndirectLightMode", TypeIndrectLightingModeEnum, Offset(mIndrectLightingModeType, ReflectionProbe),
+ addField("IndirectLight", TypeColorF, Offset(mAmbientColor, ReflectionProbe), "Path of file to save and load results.");
+ endGroup("IndirectLighting");*/
+ addGroup("Reflection");
+ addField("ReflectionMode", TypeReflectionModeEnum, Offset(mReflectionModeType, ReflectionProbe),
+ addField("reflectionPath", TypeImageFilename, Offset(mReflectionPath, ReflectionProbe),
+ addField("StaticCubemap", TypeCubemapName, Offset(mCubemapName, ReflectionProbe), "Cubemap used instead of reflection texture if fullReflect is off.");
+ addProtectedField("Bake", TypeBool, Offset(mBake, ReflectionProbe),
+ &_doBake, &defaultProtectedGetFn, "Regenerate Voxel Grid", AbstractClassRep::FieldFlags::FIELD_ComponentInspectors);
+ endGroup("Reflection");
+ Con::addVariable("$Light::renderReflectionProbes", TypeBool, &ReflectionProbe::smRenderReflectionProbes,
+ "Toggles rendering of light frustums when the light is selected in the editor.\n\n"
+ "@note Only works for shadow mapped lights.\n\n"
+ "@ingroup Lighting");
+ Con::addVariable("$Light::renderPreviewProbes", TypeBool, &ReflectionProbe::smRenderPreviewProbes,
+ /*addGroup("Internal");
+ addProtectedField("SHTerm", TypeRealString, NULL, &protectedSetSHTerms, &defaultProtectedGetFn,
+ "Do not modify, for internal use.", AbstractClassRep::FIELD_HideInInspectors);
+ addProtectedField("SHConsts", TypeRealString, NULL, &protectedSetSHConsts, &defaultProtectedGetFn,
+ endGroup("Internal");*/
+ // SceneObject already handles exposing the transform
+ Parent::initPersistFields();
+bool ReflectionProbe::protectedSetSHTerms(void *object, const char *index, const char *data)
+ ReflectionProbe *probe = static_cast< ReflectionProbe* >(object);
+ LinearColorF term;
+ U32 idx;
+ dSscanf(data, "%i %g %g %g", &idx, &term.red, &term.green, &term.blue);
+ probe->mProbeInfo->mSHTerms[idx] = term;
+ return false;
+bool ReflectionProbe::protectedSetSHConsts(void *object, const char *index, const char *data)
+ dSscanf(data, "%g %g %g %g %g", &probe->mProbeInfo->mSHConstants[0],
+ &probe->mProbeInfo->mSHConstants[1], &probe->mProbeInfo->mSHConstants[2], &probe->mProbeInfo->mSHConstants[3], &probe->mProbeInfo->mSHConstants[4]);
+void ReflectionProbe::writeFields(Stream &stream, U32 tabStop)
+ Parent::writeFields(stream, tabStop);
+ if (mIndrectLightingModeType != SphericalHarmonics)
+ // Now write all planes.
+ stream.write(2, "\r\n");
+ const LinearColorF shTerm = mProbeInfo->mSHTerms[i];
+ stream.writeTabs(tabStop);
+ char buffer[1024];
+ dMemset(buffer, 0, 1024);
+ dSprintf(buffer, 1024, "SHTerm = \"%i %g %g %g\";", i, shTerm.red, shTerm.green, shTerm.blue);
+ stream.writeLine((const U8*)buffer);
+ dSprintf(buffer, 1024, "SHConsts = \"%g %g %g %g %g\";", mProbeInfo->mSHConstants[0],
+ mProbeInfo->mSHConstants[1], mProbeInfo->mSHConstants[2], mProbeInfo->mSHConstants[3], mProbeInfo->mSHConstants[4]);
+bool ReflectionProbe::writeField(StringTableEntry fieldname, const char *value)
+ if (fieldname == StringTable->insert("SHTerm") || fieldname == StringTable->insert("SHConsts"))
+ return Parent::writeField(fieldname, value);
+void ReflectionProbe::inspectPostApply()
+ Parent::inspectPostApply();
+ mDirty = true;
+ // Flag the network mask to send the updates
+ // to the client object
+bool ReflectionProbe::_setEnabled(void *object, const char *index, const char *data)
+ ReflectionProbe* probe = reinterpret_cast< ReflectionProbe* >(object);
+ probe->mEnabled = dAtob(data);
+ probe->setMaskBits(-1);
+ return true;
+bool ReflectionProbe::_doBake(void *object, const char *index, const char *data)
+ if (probe->mDirty)
+ probe->bake(probe->mReflectionPath, 256);
+bool ReflectionProbe::onAdd()
+ if (!Parent::onAdd())
+ mObjBox.minExtents.set(-1, -1, -1);
+ mObjBox.maxExtents.set(1, 1, 1);
+ mObjScale.set(mRadius/2, mRadius/2, mRadius/2);
+ // Skip our transform... it just dirties mask bits.
+ Parent::setTransform(mObjToWorld);
+ resetWorldBox();
+ // Add this object to the scene
+ addToScene();
+ if (isServerObject())
+ // Refresh this object's material (if any)
+ if (isClientObject())
+ updateMaterial();
+void ReflectionProbe::onRemove()
+ // Remove this object from the scene
+ removeFromScene();
+ Parent::onRemove();
+void ReflectionProbe::setTransform(const MatrixF & mat)
+ // Let SceneObject handle all of the matrix manipulation
+ Parent::setTransform(mat);
+ // Dirty our network mask so that the new transform gets
+ // transmitted to the client object
+ setMaskBits(TransformMask);
+U32 ReflectionProbe::packUpdate(NetConnection *conn, U32 mask, BitStream *stream)
+ // Allow the Parent to get a crack at writing its info
+ U32 retMask = Parent::packUpdate(conn, mask, stream);
+ // Write our transform information
+ if (stream->writeFlag(mask & TransformMask))
+ mathWrite(*stream, getTransform());
+ mathWrite(*stream, getScale());
+ if (stream->writeFlag(mask & ShapeTypeMask))
+ stream->write((U32)mProbeShapeType);
+ if (stream->writeFlag(mask & UpdateMask))
+ stream->write(mAmbientColor);
+ stream->write(mRadius);
+ if (stream->writeFlag(mask & BakeInfoMask))
+ stream->write(mReflectionPath);
+ stream->write(mProbeUniqueID);
+ if (stream->writeFlag(mask & EnabledMask))
+ stream->writeFlag(mEnabled);
+ if (stream->writeFlag(mask & ModeMask))
+ stream->write((U32)mIndrectLightingModeType);
+ stream->write((U32)mReflectionModeType);
+ if (stream->writeFlag(mask & CubemapMask))
+ stream->writeFlag(mUseCubemap);
+ stream->write(mCubemapName);
+ return retMask;
+void ReflectionProbe::unpackUpdate(NetConnection *conn, BitStream *stream)
+ // Let the Parent read any info it sent
+ Parent::unpackUpdate(conn, stream);
+ if (stream->readFlag()) // TransformMask
+ mathRead(*stream, &mObjToWorld);
+ mathRead(*stream, &mObjScale);
+ setTransform(mObjToWorld);
+ if (stream->readFlag()) // ShapeTypeMask
+ U32 shapeType = ProbeInfo::Sphere;
+ stream->read(&shapeType);
+ mProbeShapeType = (ProbeInfo::ProbeShapeType)shapeType;
+ createGeometry();
+ if (stream->readFlag()) // UpdateMask
+ stream->read(&mAmbientColor);
+ stream->read(&mRadius);
+ if (stream->readFlag()) // BakeInfoMask
+ stream->read(&mReflectionPath);
+ stream->read(&mProbeUniqueID);
+ if (stream->readFlag()) // EnabledMask
+ mEnabled = stream->readFlag();
+ bool isMaterialDirty = false;
+ if (stream->readFlag()) // ModeMask
+ U32 indirectModeType = AmbientColor;
+ stream->read(&indirectModeType);
+ mIndrectLightingModeType = (IndrectLightingModeType)indirectModeType;
+ U32 reflectModeType = BakedCubemap;
+ stream->read(&reflectModeType);
+ mReflectionModeType = (ReflectionModeType)reflectModeType;
+ isMaterialDirty = true;
+ if (stream->readFlag()) // CubemapMask
+ mUseCubemap = stream->readFlag();
+ stream->read(&mCubemapName);
+ updateProbeParams();
+ if(isMaterialDirty)
+void ReflectionProbe::createGeometry()
+ // Clean up our previous shape
+ String shapeFile = "tools/resources/ReflectProbeSphere.dae";
+ // Attempt to get the resource from the ResourceManager
+ mEditorShape = ResourceManager::get().load(shapeFile);
+ if (mEditorShape)
+ mEditorShapeInst = new TSShapeInstance(mEditorShape, isClientObject());
+// Object Rendering
+void ReflectionProbe::updateProbeParams()
+ if (mProbeInfo == nullptr)
+ if (mIndrectLightingModeType == AmbientColor)
+ mProbeInfo->mAmbient = mAmbientColor;
+ mProbeInfo->mAmbient = LinearColorF(0, 0, 0, 0);
+ mProbeInfo->mProbeShapeType = mProbeShapeType;
+ mProbeInfo->setPosition(getPosition());
+ //Update the bounds
+ mObjScale.set(mRadius / 2, mRadius / 2, mRadius / 2);
+ mProbeInfo->mBounds = mWorldBox;
+ mProbeInfo->mRadius = mRadius;
+ mProbeInfo->mIsSkylight = false;
+void ReflectionProbe::updateMaterial()
+ if (mReflectionModeType != DynamicCubemap)
+ if ((mReflectionModeType == BakedCubemap) && !mProbeUniqueID.isEmpty())
+ Vector<FileName> fileNames;
+ if (Platform::isFile(fileName))
+ mCubemap->setCubemapFile(FileName(fileName));
+ validCubemap = false;
+ if (validCubemap)
+ mProbeInfo->mCubemap = &mCubemap->mCubemap;
+ else if (mReflectionModeType == StaticCubemap && !mCubemapName.isEmpty())
+ Sim::findObject(mCubemapName, mCubemap);
+ else if (mReflectionModeType == DynamicCubemap && !mDynamicCubemap.isNull())
+ mProbeInfo->mCubemap = &mDynamicCubemap;
+ generateTextures();
+ if (mPrefilterMap.isValid())
+ mProbeInfo->mCubemap = &mPrefilterMap;
+ mProbeInfo->mIrradianceCubemap = &mIrridianceMap;
+ mProbeInfo->mBRDFTexture = &mBrdfTexture;
+bool ReflectionProbe::createClientResources()
+ //irridiance resources
+ mIrridianceMap = GFX->createCubemap();
+ mIrridianceMap->initDynamic(128, GFXFormatR16G16B16A16F,1);
+ //prefilter resources - we share the irridiance stateblock
+ mPrefilterMap = GFX->createCubemap();
+ mPrefilterMap->initDynamic(mPrefilterSize, GFXFormatR16G16B16A16F, mPrefilterMipLevels);
+ //brdf lookup resources
+ //make the brdf lookup texture the same size as the prefilter texture
+ mBrdfTexture = TEXMGR->createTexture(mPrefilterSize, mPrefilterSize, GFXFormatR16G16B16A16F, &GFXRenderTargetProfile, 1, 0);
+ mResourcesCreated = true;
+void ReflectionProbe::generateTextures()
+ if (!mResourcesCreated)
+ if (!createClientResources())
+ Con::errorf("SkyLight::createIrridianceMap: Failed to create resources");
+ GFXTextureTargetRef renderTarget = GFX->allocRenderToTextureTarget(false);
+ //create irridiance cubemap
+ IBLUtilities::GenerateIrradianceMap(renderTarget, mCubemap->mCubemap, mIrridianceMap);
+ //create prefilter cubemap (radiance)
+ IBLUtilities::GeneratePrefilterMap(renderTarget, mCubemap->mCubemap, mPrefilterMipLevels, mPrefilterMap);
+ //create brdf lookup
+ IBLUtilities::GenerateBRDFTexture(mBrdfTexture);
+void ReflectionProbe::prepRenderImage(SceneRenderState *state)
+ if (!mEnabled || !ReflectionProbe::smRenderReflectionProbes)
+ Point3F distVec = getRenderPosition() - state->getCameraPosition();
+ F32 dist = distVec.len();
+ //Culling distance. Can be adjusted for performance options considerations via the scalar
+ if (dist > mMaxDrawDistance * Con::getFloatVariable("$pref::GI::ProbeDrawDistScale", 1.0))
+ if (mReflectionModeType == DynamicCubemap && mRefreshRateMS < (Platform::getRealMilliseconds() - mDynamicLastBakeMS))
+ bake("", 32);
+ mDynamicLastBakeMS = Platform::getRealMilliseconds();
+ //Submit our probe to actually do the probe action
+ // Get a handy pointer to our RenderPassmanager
+ //RenderPassManager *renderPass = state->getRenderPass();
+ //Update our score based on our radius, distance
+ mProbeInfo->mScore = mProbeInfo->mRadius/mMax(dist,1.0f);
+ Point3F vect = distVec;
+ vect.normalizeSafe();
+ mProbeInfo->mScore *= mMax(mAbs(mDot(vect, state->getCameraTransform().getForwardVector())),0.001f);
+ //Register
+ PROBEMGR->registerProbe(mProbeInfo, this);
+ if (ReflectionProbe::smRenderPreviewProbes && gEditingMission && mEditorShapeInst && mCubemap != nullptr)
+ // Calculate the distance of this object from the camera
+ Point3F cameraOffset;
+ getRenderTransform().getColumn(3, &cameraOffset);
+ cameraOffset -= state->getDiffuseCameraPosition();
+ F32 dist = cameraOffset.len();
+ if (dist < 0.01f)
+ dist = 0.01f;
+ // Set up the LOD for the shape
+ F32 invScale = (1.0f / getMax(getMax(mObjScale.x, mObjScale.y), mObjScale.z));
+ mEditorShapeInst->setDetailFromDistance(state, dist * invScale);
+ // Make sure we have a valid level of detail
+ if (mEditorShapeInst->getCurrentDetail() < 0)
+ BaseMatInstance* probePrevMat = mEditorShapeInst->getMaterialList()->getMaterialInst(0);
+ setPreviewMatParameters(state, probePrevMat);
+ // GFXTransformSaver is a handy helper class that restores
+ // the current GFX matrices to their original values when
+ // it goes out of scope at the end of the function
+ // Set up our TS render state
+ TSRenderState rdata;
+ rdata.setSceneState(state);
+ rdata.setFadeOverride(1.0f);
+ if(mReflectionModeType != DynamicCubemap)
+ rdata.setCubemap(mCubemap->mCubemap);
+ rdata.setCubemap(mDynamicCubemap);
+ // We might have some forward lit materials
+ // so pass down a query to gather lights.
+ LightQuery query;
+ query.init(getWorldSphere());
+ rdata.setLightQuery(&query);
+ // Set the world matrix to the objects render transform
+ MatrixF mat = getRenderTransform();
+ mat.scale(Point3F(1, 1, 1));
+ GFX->setWorldMatrix(mat);
+ // Animate the the shape
+ mEditorShapeInst->animate();
+ // Allow the shape to submit the RenderInst(s) for itself
+ mEditorShapeInst->render(rdata);
+ saver.restore();
+ // If the light is selected or light visualization
+ // is enabled then register the callback.
+ const bool isSelectedInEditor = (gEditingMission && isSelected());
+ if (isSelectedInEditor)
+ ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
+ ri->renderDelegate.bind(this, &ReflectionProbe::_onRenderViz);
+ ri->type = RenderPassManager::RIT_Editor;
+ state->getRenderPass()->addInst(ri);
+void ReflectionProbe::_onRenderViz(ObjectRenderInst *ri,
+ SceneRenderState *state,
+ BaseMatInstance *overrideMat)
+ if (!ReflectionProbe::smRenderReflectionProbes)
+ GFXDrawUtil *draw = GFX->getDrawUtil();
+ desc.setZReadWrite(true, false);
+ desc.setCullMode(GFXCullNone);
+ desc.setBlend(true);
+ // Base the sphere color on the light color.
+ ColorI color = ColorI::WHITE;
+ color.alpha = 50;
+ if (mProbeShapeType == ProbeInfo::Sphere)
+ draw->drawSphere(desc, mRadius, getPosition(), color);
+ Box3F cube(mRadius);
+ cube.setCenter(getPosition());
+ draw->drawCube(desc, cube, color);
+void ReflectionProbe::setPreviewMatParameters(SceneRenderState* renderState, BaseMatInstance* mat)
+ if (!mat->getFeatures().hasFeature(MFT_isDeferred))
+ //Set up the params
+ MaterialParameters *matParams = mat->getMaterialParameters();
+ //Get the deferred render target
+ NamedTexTarget* deferredTexTarget = NamedTexTarget::find("deferred");
+ GFXTextureObject *deferredTexObject = deferredTexTarget->getTexture();
+ if (!deferredTexObject)
+ GFX->setTexture(0, deferredTexObject);
+ //Set the cubemap
+ GFX->setCubeTexture(1, mCubemap->mCubemap);
+ //Set the invViewMat
+ MatrixSet &matrixSet = renderState->getRenderPass()->getMatrixSet();
+ const MatrixF &worldToCameraXfm = matrixSet.getWorldToCamera();
+ MaterialParameterHandle *invViewMat = mat->getMaterialParameterHandle("$invViewMat");
+ matParams->setSafe(invViewMat, worldToCameraXfm);
+LinearColorF ReflectionProbe::decodeSH(Point3F normal)
+ LinearColorF l00 = mProbeInfo->mSHTerms[0];
+ LinearColorF l10 = mProbeInfo->mSHTerms[1];
+ LinearColorF l11 = mProbeInfo->mSHTerms[2];
+ LinearColorF l12 = mProbeInfo->mSHTerms[3];
+ LinearColorF l20 = mProbeInfo->mSHTerms[4];
+ LinearColorF l21 = mProbeInfo->mSHTerms[5];
+ LinearColorF l22 = mProbeInfo->mSHTerms[6];
+ LinearColorF l23 = mProbeInfo->mSHTerms[7];
+ LinearColorF l24 = mProbeInfo->mSHTerms[8];
+ l00 * mProbeInfo->mSHConstants[0] +
+ l12 * mProbeInfo->mSHConstants[1] * x +
+ l10 * mProbeInfo->mSHConstants[1] * y +
+ l11 * mProbeInfo->mSHConstants[1] * z +
+ l20 * mProbeInfo->mSHConstants[2] * x*y +
+ l21 * mProbeInfo->mSHConstants[2] * y*z +
+ l22 * mProbeInfo->mSHConstants[3] * (3.0*z*z - 1.0) +
+ l23 * mProbeInfo->mSHConstants[2] * x*z +
+ l24 * mProbeInfo->mSHConstants[4] * (x*x - y*y)
+MatrixF ReflectionProbe::getSideMatrix(U32 side)
+F32 ReflectionProbe::harmonics(U32 termId, Point3F normal)
+ switch(termId)
+ return x*y;
+ return y*z;
+ return x*z;
+ return x*x - y*y;
+LinearColorF ReflectionProbe::sampleSide(U32 termindex, U32 sideIndex)
+ for (int y = 0; y<mCubemapResolution; y++)
+ for (int x = 0; x<mCubemapResolution; x++)
+ Point2F sidecoord = ((Point2F(x, y) + Point2F(0.5, 0.5)) / Point2F(mCubemapResolution, mCubemapResolution))*2.0 - Point2F(1.0, 1.0);
+ LinearColorF texel = mCubeFaceBitmaps[sideIndex]->sampleTexel(y, x);
+ result += texel * harmonics(termindex,dir) * -normal.z;
+//SH Calculations
+// From http://sunandblackcat.com/tipFullView.php?l=eng&topicid=32&topic=Spherical-Harmonics-From-Cube-Texture
+// With shader decode logic from https://github.com/nicknikolov/cubemap-sh
+void ReflectionProbe::calculateSHTerms()
+ if (!mCubemap || !mCubemap->mCubemap)
+ mCubemapResolution = mCubemap->mCubemap->getSize();
+ mCubeFaceBitmaps[i] = new GBitmap(mCubemapResolution, mCubemapResolution, false, GFXFormatR8G8B8);
+ if (!CubemapSaver::getBitmaps(mCubemap->mCubemap, GFXFormatR8G8B8, mCubeFaceBitmaps))
+ mProbeInfo->mSHConstants[0] = L0;
+ mProbeInfo->mSHConstants[1] = L1;
+ mProbeInfo->mSHConstants[2] = L2 * L2m2_L2m1_L21;
+ mProbeInfo->mSHConstants[3] = L2 * L20;
+ mProbeInfo->mSHConstants[4] = L2 * L22;
+ mProbeInfo->mSHTerms[i] = LinearColorF(0.f, 0.f, 0.f);
+ mProbeInfo->mSHTerms[i] = sampleSide(i, 0); //POS_X
+ mProbeInfo->mSHTerms[i] += sampleSide(i, 1); //NEG_X
+ mProbeInfo->mSHTerms[i] += sampleSide(i, 2); //POS_Y
+ mProbeInfo->mSHTerms[i] += sampleSide(i, 3); //NEG_Y
+ mProbeInfo->mSHTerms[i] += sampleSide(i, 4); //POS_Z
+ mProbeInfo->mSHTerms[i] += sampleSide(i, 5); //NEG_Z
+ mProbeInfo->mSHTerms[i] /= 6;
+ SAFE_DELETE(mCubeFaceBitmaps[i]);
+ bool mExportSHTerms = false;
+ LinearColorF color = mProbeInfo->mSHTerms[f];
+F32 ReflectionProbe::texelSolidAngle(F32 aU, F32 aV, U32 width, U32 height)
+F32 ReflectionProbe::areaElement(F32 x, F32 y)
+DefineEngineMethod(ReflectionProbe, postApply, void, (), ,
+ "A utility method for forcing a network update.\n")
+ object->inspectPostApply();
+void ReflectionProbe::bake(String outputPath, S32 resolution)
+ GFXDEBUGEVENT_SCOPE(ReflectionProbe_Bake, ColorI::WHITE);
+ PostEffect *preCapture = dynamic_cast<PostEffect*>(Sim::findObject("AL_PreCapture"));
+ /*if (mReflectionModeType != DynamicCubemap)
+ dSprintf(fileName, 256, "%s%s_%i.png", mReflectionPath.c_str(),
+ mProbeUniqueID.c_str(), i);
+ if (!stream.open(fileName, Torque::FS::File::Write))
+ Con::errorf("ReflectionProbe::bake(): Couldn't open cubemap face file fo writing " + String(fileName));
+ deferredShading->enable();
+ GBitmap bitmap(blendTex->getWidth(), blendTex->getHeight(), false, GFXFormatR8G8B8);
+ blendTex->copyToBmp(&bitmap);
+ if (Platform::isFile(fileName) && mCubemap)
+ mCubemap->setCubeFaceFile(i, FileName(fileName));
+ bitmap.deleteImage();
+DefineEngineMethod(ReflectionProbe, Bake, void, (String outputPath, S32 resolution), ("", 256),
+ "@brief returns true if control object is inside the fog\n\n.")
+ object->bake(outputPath, resolution);
@@ -0,0 +1,278 @@
+#ifndef REFLECTIONPROBE_H
+#define REFLECTIONPROBE_H
+#ifndef _SCENEOBJECT_H_
+#include "scene/sceneObject.h"
+#endif
+#ifndef _GFXVERTEXBUFFER_H_
+#include "gfx/gfxVertexBuffer.h"
+#ifndef _GFXPRIMITIVEBUFFER_H_
+#include "gfx/gfxPrimitiveBuffer.h"
+#ifndef _TSSHAPEINSTANCE_H_
+#include "ts/tsShapeInstance.h"
+#include "lighting/lightInfo.h"
+#ifndef _RENDERPASSMANAGER_H_
+#include "renderInstance/renderPassManager.h"
+#ifndef PROBEMANAGER_H
+class BaseMatInstance;
+// This class implements a basic SceneObject that can exist in the world at a
+// 3D position and render itself. There are several valid ways to render an
+// object in Torque. This class implements the preferred rendering method which
+// is to submit a MeshRenderInst along with a Material, vertex buffer,
+// primitive buffer, and transform and allow the RenderMeshMgr handle the
+// actual setup and rendering for you.
+class ReflectionProbe : public SceneObject
+ typedef SceneObject Parent;
+public:
+ enum IndrectLightingModeType
+ NoIndirect = 0,
+ AmbientColor = 1,
+ SphericalHarmonics = 2
+ enum ReflectionModeType
+ NoReflection = 0,
+ StaticCubemap = 1,
+ BakedCubemap = 2,
+ DynamicCubemap = 5,
+private:
+ // Networking masks
+ // We need to implement a mask specifically to handle
+ // updating our transform from the server object to its
+ // client-side "ghost". We also need to implement a
+ // maks for handling editor updates to our properties
+ // (like material).
+ enum MaskBits
+ TransformMask = Parent::NextFreeMask << 0,
+ UpdateMask = Parent::NextFreeMask << 1,
+ EnabledMask = Parent::NextFreeMask << 2,
+ CubemapMask = Parent::NextFreeMask << 3,
+ ModeMask = Parent::NextFreeMask << 4,
+ RadiusMask = Parent::NextFreeMask << 5,
+ ShapeTypeMask = Parent::NextFreeMask << 6,
+ BakeInfoMask = Parent::NextFreeMask << 7,
+ NextFreeMask = Parent::NextFreeMask << 8
+ bool mBake;
+ bool mEnabled;
+ bool mDirty;
+ Resource<TSShape> mEditorShape;
+ TSShapeInstance* mEditorShapeInst;
+ //--------------------------------------------------------------------------
+ // Rendering variables
+ ProbeInfo::ProbeShapeType mProbeShapeType;
+ ProbeInfo* mProbeInfo;
+ //Indirect Lighting Contribution stuff
+ IndrectLightingModeType mIndrectLightingModeType;
+ LinearColorF mAmbientColor;
+ LinearColorF mSphericalHarmonics;
+ //Reflection Contribution stuff
+ ReflectionModeType mReflectionModeType;
+ F32 mRadius;
+ String mCubemapName;
+ CubemapData *mCubemap;
+ GFXCubemapHandle mDynamicCubemap;
+ bool mUseCubemap;
+ GFXCubemapHandle mIrridianceMap;
+ //prefilter resources
+ GFXCubemapHandle mPrefilterMap;
+ U32 mPrefilterMipLevels;
+ U32 mPrefilterSize;
+ //brdflookup resources - shares the texture target with the prefilter
+ GFXTexHandle mBrdfTexture;
+ String mReflectionPath;
+ String mProbeUniqueID;
+ // Define our vertex format here so we don't have to
+ // change it in multiple spots later
+ typedef GFXVertexPNTTB VertexType;
+ // The GFX vertex and primitive buffers
+ GFXVertexBufferHandle< VertexType > mVertexBuffer;
+ GFXPrimitiveBufferHandle mPrimitiveBuffer;
+ U32 mSphereVertCount;
+ U32 mSpherePrimitiveCount;
+ //Debug rendering
+ static bool smRenderReflectionProbes;
+ static bool smRenderPreviewProbes;
+ U32 mDynamicLastBakeMS;
+ U32 mRefreshRateMS;
+ GBitmap* mCubeFaceBitmaps[6];
+ U32 mCubemapResolution;
+ F32 mMaxDrawDistance;
+ bool mResourcesCreated;
+ ReflectionProbe();
+ virtual ~ReflectionProbe();
+ // Declare this object as a ConsoleObject so that we can
+ // instantiate it into the world and network it
+ DECLARE_CONOBJECT(ReflectionProbe);
+ // Object Editing
+ // Since there is always a server and a client object in Torque and we
+ // actually edit the server object we need to implement some basic
+ // networking functions
+ // Set up any fields that we want to be editable (like position)
+ static void initPersistFields();
+ // Allows the object to update its editable settings
+ // from the server object to the client
+ virtual void inspectPostApply();
+ static bool _setEnabled(void *object, const char *index, const char *data);
+ static bool _doBake(void *object, const char *index, const char *data);
+ static bool protectedSetSHTerms(void *object, const char *index, const char *data);
+ static bool protectedSetSHConsts(void *object, const char *index, const char *data);
+ // Handle when we are added to the scene and removed from the scene
+ bool onAdd();
+ void onRemove();
+ virtual void writeFields(Stream &stream, U32 tabStop);
+ virtual bool writeField(StringTableEntry fieldname, const char *value);
+ // Override this so that we can dirty the network flag when it is called
+ void setTransform(const MatrixF &mat);
+ // This function handles sending the relevant data from the server
+ // object to the client object
+ U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream);
+ // This function handles receiving relevant data from the server
+ // object and applying it to the client object
+ void unpackUpdate(NetConnection *conn, BitStream *stream);
+ // Object Rendering
+ // Torque utilizes a "batch" rendering system. This means that it builds a
+ // list of objects that need to render (via RenderInst's) and then renders
+ // them all in one batch. This allows it to optimized on things like
+ // minimizing texture, state, and shader switching by grouping objects that
+ // use the same Materials.
+ // Create the geometry for rendering
+ void createGeometry();
+ // Get the Material instance
+ void updateMaterial();
+ void updateProbeParams();
+ bool createClientResources();
+ void generateTextures();
+ // This is the function that allows this object to submit itself for rendering
+ void prepRenderImage(SceneRenderState *state);
+ void _onRenderViz(ObjectRenderInst *ri,
+ BaseMatInstance *overrideMat);
+ void setPreviewMatParameters(SceneRenderState* renderState, BaseMatInstance* mat);
+ //Spherical Harmonics
+ void calculateSHTerms();
+ LinearColorF decodeSH(Point3F normal);
+ void calcDirectionVector(U32 face, U32 face_x, U32 face_y, F32& out_x, F32& out_y, F32& out_z) const;
+ F32 calcSolidAngle(U32 face, U32 x, U32 y) const;
+ LinearColorF sampleFace(U32 face, F32 s, F32 t);
+ LinearColorF readTexelClamped(U32 face, U32 x, U32 y);
+ void computeTexCoords(F32 x, F32 y, F32 z, U32& out_face, F32& out_s, F32& out_t);
+ LinearColorF readTexel(U32 face, U32 x, U32 y) const;
+ LinearColorF sampleSide(U32 termindex, U32 sideIndex);
+ //Baking
+ void bake(String outputPath, S32 resolution);
+typedef ProbeInfo::ProbeShapeType ReflectProbeType;
+DefineEnumType(ReflectProbeType);
+typedef ReflectionProbe::IndrectLightingModeType IndrectLightingModeEnum;
+DefineEnumType(IndrectLightingModeEnum);
+typedef ReflectionProbe::ReflectionModeType ReflectionModeEnum;
+DefineEnumType(ReflectionModeEnum);
+#endif // _ReflectionProbe_H_
@@ -0,0 +1,899 @@
+#include "T3D/lighting/Skylight.h"
+#include "gfx/bitmap/imageUtils.h"
+bool Skylight::smRenderSkylights = true;
+bool Skylight::smRenderPreviewProbes = true;
+IMPLEMENT_CO_NETOBJECT_V1(Skylight);
+ConsoleDocClass(Skylight,
+ImplementEnumType(SkylightReflectionModeEnum,
+{ Skylight::StaticCubemap, "Static Cubemap", "Uses a static CubemapData" },
+{ Skylight::BakedCubemap, "Baked Cubemap", "Uses a cubemap baked from the probe's current position" },
+Skylight::Skylight()
+ mReflectionModeType = StaticCubemap;
+ mIrridianceMap = NULL;
+ mPrefilterMap = NULL;
+ mBrdfTexture = NULL;
+ mPrefilterSize = 512;
+ mPrefilterMipLevels = 6;
+Skylight::~Skylight()
+void Skylight::initPersistFields()
+ addProtectedField("enabled", TypeBool, Offset(mEnabled, Skylight),
+ //addField("ReflectionMode", TypeSkylightReflectionModeEnum, Offset(mReflectionModeType, Skylight),
+ // "The type of mesh data to use for collision queries.");
+ //addField("reflectionPath", TypeImageFilename, Offset(mReflectionPath, Skylight),
+ addField("StaticCubemap", TypeCubemapName, Offset(mCubemapName, Skylight), "Cubemap used instead of reflection texture if fullReflect is off.");
+ //addProtectedField("Bake", TypeBool, Offset(mBake, Skylight),
+ // &_doBake, &defaultProtectedGetFn, "Regenerate Voxel Grid", AbstractClassRep::FieldFlags::FIELD_ComponentInspectors);
+ Con::addVariable("$Light::renderSkylights", TypeBool, &Skylight::smRenderSkylights,
+ Con::addVariable("$Light::renderPreviewProbes", TypeBool, &Skylight::smRenderPreviewProbes,
+void Skylight::inspectPostApply()
+bool Skylight::_setEnabled(void *object, const char *index, const char *data)
+ Skylight* probe = reinterpret_cast< Skylight* >(object);
+bool Skylight::_doBake(void *object, const char *index, const char *data)
+bool Skylight::onAdd()
+void Skylight::onRemove()
+void Skylight::setTransform(const MatrixF & mat)
+U32 Skylight::packUpdate(NetConnection *conn, U32 mask, BitStream *stream)
+ if (stream->writeFlag(mask & InitialUpdateMask))
+ //initial work, just in case?
+ /*if (stream->writeFlag(mask & BakeInfoMask))
+ /*if (stream->writeFlag(mask & ModeMask))
+void Skylight::unpackUpdate(NetConnection *conn, BitStream *stream)
+ if (stream->readFlag())
+ //some initial work?
+ /*if (stream->readFlag()) // BakeInfoMask
+ /*if (stream->readFlag()) // ModeMask
+ U32 reflectModeType = StaticCubemap;
+void Skylight::createGeometry()
+void Skylight::updateProbeParams()
+ mProbeInfo->mIntensity = 1;
+ mProbeInfo->mProbeShapeType = ProbeInfo::Sphere;
+ F32 visDist = gClientSceneGraph->getVisibleDistance();
+ Box3F skylightBounds = Box3F(visDist * 2);
+ skylightBounds.setCenter(Point3F::Zero);
+ mProbeInfo->setPosition(Point3F::Zero);
+ mProbeInfo->mBounds = skylightBounds;
+ setGlobalBounds();
+ mProbeInfo->mIsSkylight = true;
+ mProbeInfo->mScore = -1.0f; //sky comes first
+bool Skylight::createClientResources()
+ mIrridianceMap->initDynamic(128, GFXFormatR16G16B16A16F, 1);
+void Skylight::updateMaterial()
+ /*for (U32 i = 0; i < 6; ++i)
+ char faceFile[256];
+ dSprintf(faceFile, sizeof(faceFile), "%s%s_%i.png", mReflectionPath.c_str(),
+ if (Platform::isFile(faceFile))
+ fileNames.push_back(FileName(faceFile));
+ for(U32 i=0; i < 6; i++)
+ mCubemap->setCubeFaceFile(i, fileNames[i]);
+ //Now that the work is done, assign the relevent maps
+void Skylight::generateTextures()
+ //GFXTransformSaver saver;
+ //Write it out
+ dSprintf(fileName, 256, "levels/test/irradiance.DDS");
+ CubemapSaver::save(mIrridianceMap, fileName);
+ Con::errorf("Failed to properly save out the skylight baked irradiance!");
+ fileName[256];
+ dSprintf(fileName, 256, "levels/test/prefilter.DDS");
+ CubemapSaver::save(mPrefilterMap, fileName);
+ /*FileStream fs;
+ if (fs.open("levels/test/brdf.DDS", Torque::FS::File::Write))
+ // Read back the render target, dxt compress it, and write it to disk.
+ GBitmap brdfBmp(mBrdfTexture.getHeight(), mBrdfTexture.getWidth(), false, GFXFormatR8G8B8A8);
+ mBrdfTexture.copyToBmp(&brdfBmp);
+ brdfBmp.extrudeMipLevels();
+ DDSFile *brdfDDS = DDSFile::createDDSFileFromGBitmap(&brdfBmp);
+ ImageUtil::ddsCompress(brdfDDS, GFXFormatBC1);
+ // Write result to file stream
+ brdfDDS->write(fs);
+ delete brdfDDS;
+ fs.close();*/
+void Skylight::prepRenderImage(SceneRenderState *state)
+ if (!mEnabled || !Skylight::smRenderSkylights)
+ Point3F distVec = getPosition() - state->getCameraPosition();
+ //special hook-in for skylights
+ Point3F camPos = state->getCameraPosition();
+ mProbeInfo->mBounds.setCenter(camPos);
+ mProbeInfo->setPosition(camPos);
+ PROBEMGR->registerSkylight(mProbeInfo, this);
+ if (Skylight::smRenderPreviewProbes && gEditingMission && mEditorShapeInst && mCubemap != nullptr)
+void Skylight::setPreviewMatParameters(SceneRenderState* renderState, BaseMatInstance* mat)
+DefineEngineMethod(Skylight, postApply, void, (), ,
+void Skylight::bake(String outputPath, S32 resolution)
+ GFXDEBUGEVENT_SCOPE(Skylight_Bake, ColorI::WHITE);
+ if (mReflectionModeType == BakedCubemap)
+ bool probeRenderState = Skylight::smRenderSkylights;
+ Skylight::smRenderSkylights = false;
+ F32 nearPlane = 100.f;
+ F32 farDist = 10000.f;
+ renderFrame(&mBaseTarget, matView, frustum, StaticObjectType | StaticShapeObjectType & EDITOR_RENDER_TYPEMASK, ColorI::RED);
+ //remove the temp files
+ Platform::fileDelete(fileName);
+ Skylight::smRenderSkylights = probeRenderState;
+DefineEngineMethod(Skylight, Bake, void, (String outputPath, S32 resolution), ("", 256),
@@ -0,0 +1,212 @@
+#ifndef SKYLIGHT_H
+#define SKYLIGHT_H
+class Skylight : public SceneObject
+ BakedCubemap = 2
+ static bool smRenderSkylights;
+ Skylight();
+ virtual ~Skylight();
+ DECLARE_CONOBJECT(Skylight);
+typedef Skylight::IndrectLightingModeType SkylightIndrectLightingModeEnum;
+DefineEnumType(SkylightIndrectLightingModeEnum);
+typedef Skylight::ReflectionModeType SkylightReflectionModeEnum;
+DefineEnumType(SkylightReflectionModeEnum);
+#endif // _Skylight_H_
@@ -497,7 +497,6 @@ enum GFXTextureTransformFlags
// CodeReview: This number is used for the declaration of variables, but it
// should *not* be used for any run-time purposes [7/2/2007 Pat]
#define TEXTURE_STAGE_COUNT 32
-#define TEXTURE_STAGE_COUNT 16
enum GFXSamplerState
{
@@ -40,9 +40,10 @@
#include "gfx/gfxDebugEvent.h"
#include "math/util/matrixSet.h"
#include "console/consoleTypes.h"
-const RenderInstType AdvancedLightBinManager::RIT_LightInfo( "directLighting" );
-const String AdvancedLightBinManager::smBufferName( "directLighting" );
+const RenderInstType AdvancedLightBinManager::RIT_LightInfo( "specularLighting" );
+const String AdvancedLightBinManager::smBufferName( "specularLighting" );
ShadowFilterMode AdvancedLightBinManager::smShadowFilterMode = ShadowFilterMode_SoftShadowHighQuality;
bool AdvancedLightBinManager::smPSSMDebugRender = false;
@@ -180,6 +181,26 @@ bool AdvancedLightBinManager::setTargetSize(const Point2I &newTargetSize)
return ret;
}
+bool AdvancedLightBinManager::_updateTargets()
+ PROFILE_SCOPE(AdvancedLightBinManager_updateTargets);
+ bool ret = Parent::_updateTargets();
+ mDiffuseLightingTarget = NamedTexTarget::find("diffuseLighting");
+ if (mDiffuseLightingTarget.isValid())
+ mDiffuseLightingTex = mDiffuseLightingTarget->getTexture();
+ for (U32 i = 0; i < mTargetChainLength; i++)
+ mTargetChain[i]->attachTexture(GFXTextureTarget::Color1, mDiffuseLightingTex);
+ GFX->finalizeReset();
+ return ret;
void AdvancedLightBinManager::addLight( LightInfo *light )
// Get the light type.
@@ -90,6 +90,9 @@ public:
// registered buffer name
static const String smBufferName;
+ NamedTexTargetRef mDiffuseLightingTarget;
+ GFXTexHandle mDiffuseLightingTex;
/// The shadow filter mode to use on shadowed light materials.
static ShadowFilterMode smShadowFilterMode;
@@ -128,6 +131,7 @@ public:
bool MRTLightmapsDuringDeferred() const { return mMRTLightmapsDuringDeferred; }
void MRTLightmapsDuringDeferred(bool val);
+ bool _updateTargets();
typedef Signal<void(SceneRenderState *, AdvancedLightBinManager *)> RenderSignal;
static RenderSignal &getRenderSignal();
@@ -63,6 +63,7 @@ void AdvancedLightingFeatures::registerFeatures( const GFXFormat &deferredTarget
FEATUREMGR->registerFeature(MFT_PixSpecular, new DeferredPixelSpecularGLSL());
FEATUREMGR->registerFeature(MFT_MinnaertShading, new DeferredMinnaertGLSL());
FEATUREMGR->registerFeature(MFT_SubSurface, new DeferredSubSurfaceGLSL());
+ FEATUREMGR->registerFeature(MFT_ReflectionProbes, new ReflectionProbeFeatGLSL);
#endif
else
@@ -75,6 +76,7 @@ void AdvancedLightingFeatures::registerFeatures( const GFXFormat &deferredTarget
FEATUREMGR->registerFeature(MFT_PixSpecular, new DeferredPixelSpecularHLSL());
FEATUREMGR->registerFeature(MFT_MinnaertShading, new DeferredMinnaertHLSL());
FEATUREMGR->registerFeature(MFT_SubSurface, new DeferredSubSurfaceHLSL());
+ FEATUREMGR->registerFeature(MFT_ReflectionProbes, new ReflectionProbeFeatHLSL);
@@ -98,7 +98,7 @@ void DeferredRTLightingFeatGLSL::processPix( Vector<ShaderComponent*> &component
uvScene->setName( "uvScene" );
LangElement *uvSceneDecl = new DecOp( uvScene );
- String rtParamName = String::ToString( "rtParams%s", "directLightingBuffer" );
+ String rtParamName = String::ToString( "rtParams%s", "diffuseLightingBuffer" );
Var *rtParams = (Var*) LangElement::find( rtParamName );
if( !rtParams )
@@ -121,7 +121,7 @@ void DeferredRTLightingFeatGLSL::processPix( Vector<ShaderComponent*> &component
// create texture var
Var *lightInfoBuffer = new Var;
lightInfoBuffer->setType( "sampler2D" );
- lightInfoBuffer->setName( "directLightingBuffer" );
+ lightInfoBuffer->setName( "diffuseLightingBuffer" );
lightInfoBuffer->uniform = true;
lightInfoBuffer->sampler = true;
lightInfoBuffer->constNum = Var::getTexUnitNum(); // used as texture unit num here
@@ -207,7 +207,7 @@ void DeferredRTLightingFeatGLSL::setTexData( Material::StageData &stageDat,
mLastTexIndex = texIndex;
passData.mTexType[ texIndex ] = Material::TexTarget;
- passData.mSamplerNames[ texIndex ]= "directLightingBuffer";
+ passData.mSamplerNames[ texIndex ]= "diffuseLightingBuffer";
passData.mTexSlot[ texIndex++ ].texTarget = texTarget;
@@ -99,7 +99,7 @@ void DeferredRTLightingFeatHLSL::processPix( Vector<ShaderComponent*> &component
uvScene->setName("uvScene");
LangElement *uvSceneDecl = new DecOp(uvScene);
- String rtParamName = String::ToString("rtParams%s", "directLightingBuffer");
+ String rtParamName = String::ToString("rtParams%s", "diffuseLightingBuffer");
Var *rtParams = (Var*)LangElement::find(rtParamName);
if (!rtParams)
@@ -215,7 +215,7 @@ void DeferredRTLightingFeatHLSL::setTexData( Material::StageData &stageDat,
@@ -168,6 +168,7 @@ void BasicLightManager::activate( SceneManager *sceneManager )
FEATUREMGR->registerFeature( MFT_NormalMap, new BumpFeatGLSL );
FEATUREMGR->registerFeature( MFT_RTLighting, new RTLightingFeatGLSL );
FEATUREMGR->registerFeature( MFT_PixSpecular, new PixelSpecularGLSL );
@@ -178,6 +179,7 @@ void BasicLightManager::activate( SceneManager *sceneManager )
FEATUREMGR->registerFeature( MFT_NormalMap, new BumpFeatHLSL );
FEATUREMGR->registerFeature( MFT_RTLighting, new RTLightingFeatHLSL );
FEATUREMGR->registerFeature( MFT_PixSpecular, new PixelSpecularHLSL );
@@ -0,0 +1,809 @@
+#include "platform/platform.h"
+#include "console/console.h"
+#include "core/util/safeDelete.h"
+#include "console/sim.h"
+#include "console/simSet.h"
+#include "scene/sceneManager.h"
+#include "materials/sceneData.h"
+#include "lighting/lightingInterfaces.h"
+#include "T3D/gameBase/gameConnection.h"
+#include "gfx/gfxStringEnumTranslate.h"
+#include "renderInstance/renderDeferredMgr.h"
+#include "shaderGen/shaderGenVars.h"
+Signal<void(const char*,bool)> ProbeManager::smActivateSignal;
+ProbeManager *ProbeManager::smProbeManager = NULL;
+ProbeInfo::ProbeInfo()
+ : mTransform(true),
+ mAmbient(0.0f, 0.0f, 0.0f, 1.0f),
+ mPriority(1.0f),
+ mScore(0.0f),
+ mDebugRender(false),
+ mCubemap(NULL),
+ mIrradianceCubemap(NULL),
+ mBRDFTexture(NULL),
+ mRadius(1.0f),
+ mIntensity(1.0f)
+ for (U32 i = 0; i < 5; ++i)
+ mSHConstants[i] = 0;
+ProbeInfo::~ProbeInfo()
+ SAFE_DELETE(mCubemap);
+void ProbeInfo::set(const ProbeInfo *probeInfo)
+ mTransform = probeInfo->mTransform;
+ mAmbient = probeInfo->mAmbient;
+ mCubemap = probeInfo->mCubemap;
+ mIrradianceCubemap = probeInfo->mIrradianceCubemap;
+ mBRDFTexture = probeInfo->mBRDFTexture;
+ mRadius = probeInfo->mRadius;
+ mIntensity = probeInfo->mIntensity;
+ mProbeShapeType = probeInfo->mProbeShapeType;
+ numPrims = probeInfo->numPrims;
+ numVerts = probeInfo->numVerts;
+ numIndicesForPoly = probeInfo->numIndicesForPoly;
+ mBounds = probeInfo->mBounds;
+ mSHTerms[i] = probeInfo->mSHTerms[i];
+ for (U32 i = 0; i < 5; i++)
+ mSHConstants[i] = probeInfo->mSHConstants[i];
+void ProbeInfo::getWorldToLightProj(MatrixF *outMatrix) const
+ *outMatrix = getTransform();
+ outMatrix->inverse();
+void ProbeInfoList::registerProbe(ProbeInfo *light)
+ if (!light)
+ // just add the light, we'll try to scan for dupes later...
+ push_back(light);
+void ProbeInfoList::unregisterProbe(ProbeInfo *light)
+ // remove all of them...
+ ProbeInfoList &list = *this;
+ for (U32 i = 0; i<list.size(); i++)
+ if (list[i] != light)
+ continue;
+ // this moves last to i, which allows
+ // the search to continue forward...
+ list.erase_fast(i);
+ // want to check this location again...
+ i--;
+ProbeShaderConstants::ProbeShaderConstants()
+ : mInit(false),
+ mShader(NULL),
+ mProbeParamsSC(NULL),
+ mProbePositionSC(NULL),
+ mProbeRadiusSC(NULL),
+ mProbeBoxMinSC(NULL),
+ mProbeBoxMaxSC(NULL),
+ mProbeIsSphereSC(NULL),
+ mProbeLocalPosSC(NULL),
+ mProbeCubemapSC(NULL)
+ProbeShaderConstants::~ProbeShaderConstants()
+ if (mShader.isValid())
+ mShader->getReloadSignal().remove(this, &ProbeShaderConstants::_onShaderReload);
+ mShader = NULL;
+void ProbeShaderConstants::init(GFXShader* shader)
+ if (mShader.getPointer() != shader)
+ mShader = shader;
+ mShader->getReloadSignal().notify(this, &ProbeShaderConstants::_onShaderReload);
+ mProbeParamsSC = shader->getShaderConstHandle("$probeParams");
+ //Reflection Probes
+ mProbePositionSC = shader->getShaderConstHandle(ShaderGenVars::probePosition);
+ mProbeRadiusSC = shader->getShaderConstHandle(ShaderGenVars::probeRadius);
+ mProbeBoxMinSC = shader->getShaderConstHandle(ShaderGenVars::probeBoxMin);
+ mProbeBoxMaxSC = shader->getShaderConstHandle(ShaderGenVars::probeBoxMax);
+ mProbeIsSphereSC = shader->getShaderConstHandle(ShaderGenVars::probeIsSphere);
+ mProbeLocalPosSC = shader->getShaderConstHandle(ShaderGenVars::probeLocalPos);
+ mProbeCubemapSC = shader->getShaderConstHandle(ShaderGenVars::probeCubemap);
+ mInit = true;
+void ProbeShaderConstants::_onShaderReload()
+ init(mShader);
+ProbeManager::ProbeManager()
+ : mDefaultProbe( NULL ),
+ mSceneManager( NULL ),
+ mCullPos( Point3F::Zero )
+ dMemset( &mSpecialProbes, 0, sizeof(mSpecialProbes) );
+ mLastShader = NULL;
+ mLastConstants = NULL;
+ProbeManager::~ProbeManager()
+ProbeInfo* ProbeManager::createProbeInfo(ProbeInfo* probe /* = NULL */)
+ ProbeInfo *outProbe = (probe != NULL) ? probe : new ProbeInfo;
+ /*ProbeManagerMap &ProbeManagers = _getProbeManagers();
+ ProbeManagerMap::Iterator iter = ProbeManagers.begin();
+ for ( ; iter != ProbeManagers.end(); iter++ )
+ ProbeManager *lm = iter->value;
+ lm->_addLightInfoEx( outLight );
+ return outProbe;
+/*void ProbeManager::initLightFields()
+ ProbeManagerMap &ProbeManagers = _getProbeManagers();
+ lm->_initLightFields();
+}*/
+IMPLEMENT_GLOBAL_CALLBACK( onProbeManagerActivate, void, ( const char *name ), ( name ),
+ "A callback called by the engine when a light manager is activated.\n"
+ "@param name The name of the light manager being activated.\n"
+ "@ingroup Lighting\n" );
+void ProbeManager::activate( SceneManager *sceneManager )
+ AssertFatal( sceneManager, "ProbeManager::activate() - Got null scene manager!" );
+ //AssertFatal( mIsActive == false, "ProbeManager::activate() - Already activated!" );
+ AssertFatal(smProbeManager == NULL, "ProbeManager::activate() - A previous ProbeManager is still active!" );
+ mSceneManager = sceneManager;
+ smProbeManager = this;
+IMPLEMENT_GLOBAL_CALLBACK( onProbeManagerDeactivate, void, ( const char *name ), ( name ),
+ "A callback called by the engine when a light manager is deactivated.\n"
+ "@param name The name of the light manager being deactivated.\n"
+void ProbeManager::deactivate()
+ //AssertFatal( mIsActive == true, "ProbeManager::deactivate() - Already deactivated!" );
+ AssertFatal( smProbeManager == this, "ProbeManager::activate() - This isn't the active light manager!" );
+ //if( Sim::getRootGroup() ) // To protect against shutdown.
+ // onProbeManagerDeactivate_callback( getName() );
+ //mIsActive = false;
+ mSceneManager = NULL;
+ smProbeManager = NULL;
+ // Just in case... make sure we're all clear.
+ unregisterAllProbes();
+ProbeInfo* ProbeManager::getDefaultLight()
+ // The sun is always our default light when
+ // when its registered.
+ if ( mSpecialProbes[ ProbeManager::SkylightProbeType ] )
+ return mSpecialProbes[ ProbeManager::SkylightProbeType ];
+ // Else return a dummy special light.
+ //if ( !mDefaultLight )
+ // mDefaultLight = createLightInfo();
+ return NULL;
+ProbeInfo* ProbeManager::getSpecialProbe( ProbeManager::SpecialProbeTypesEnum type, bool useDefault )
+ //if ( mSpecialLights[type] )
+ // return mSpecialLights[type];
+ if ( useDefault )
+ return getDefaultLight();
+void ProbeManager::setSpecialProbe( ProbeManager::SpecialProbeTypesEnum type, ProbeInfo *probe )
+ if (probe && type == SkylightProbeType )
+ // The sun must be specially positioned and ranged
+ // so that it can be processed like a point light
+ // in the stock light shader used by Basic Lighting.
+ probe->setPosition( mCullPos - (probe->getDirection() * 10000.0f ) );
+ probe->mRadius = 2000000.0f;
+ mSpecialProbes[type] = probe;
+ registerProbe(probe, NULL );
+void ProbeManager::registerProbes( const Frustum *frustum, bool staticLighting )
+ PROFILE_SCOPE( ProbeManager_RegisterProbes );
+ // TODO: We need to work this out...
+ // 1. Why do we register and unregister lights on every
+ // render when they don't often change... shouldn't we
+ // just register once and keep them?
+ // 2. If we do culling of lights should this happen as part
+ // of registration or somewhere else?
+ // Grab the lights to process.
+ Vector<SceneObject*> activeLights;
+ const U32 lightMask = LightObjectType;
+ if ( staticLighting || !frustum )
+ // We're processing static lighting or want all the lights
+ // in the container registerd... so no culling.
+ getSceneManager()->getContainer()->findObjectList( lightMask, &activeLights );
+ // Cull the lights using the frustum.
+ getSceneManager()->getContainer()->findObjectList( *frustum, lightMask, &activeLights );
+ /* supress light culling filter until we can sort out why that's misbehaving with dynamic cube mapping
+ for (U32 i = 0; i < activeLights.size(); ++i)
+ if (!getSceneManager()->mRenderedObjectsList.contains(activeLights[i]))
+ activeLights.erase(i);
+ --i;
+ */
+ // Store the culling position for sun placement
+ // later... see setSpecialLight.
+ mCullPos = frustum->getPosition();
+ // HACK: Make sure the control object always gets
+ // processed as lights mounted to it don't change
+ // the shape bounds and can often get culled.
+ GameConnection *conn = GameConnection::getConnectionToServer();
+ if ( conn->getControlObject() )
+ GameBase *conObject = conn->getControlObject();
+ activeLights.push_back_unique( conObject );
+ // Let the lights register themselves.
+ /*for ( U32 i = 0; i < activeLights.size(); i++ )
+ ISceneLight *lightInterface = dynamic_cast<ISceneLight*>( activeLights[i] );
+ if ( lightInterface )
+ lightInterface->submitLights( this, staticLighting );
+void ProbeManager::registerSkylight(ProbeInfo *probe, SimObject *obj)
+ mSkylight = probe;
+ if (String("Advanced Lighting").equal(LIGHTMGR->getName(), String::NoCase))
+ SceneRenderState* state = mSceneManager->getCurrentRenderState();
+ RenderPassManager *renderPass = state->getRenderPass();
+ // Allocate an MeshRenderInst so that we can submit it to the RenderPassManager
+ ProbeRenderInst *probeInst = renderPass->allocInst<ProbeRenderInst>();
+ probeInst->set(probe);
+ probeInst->type = RenderPassManager::RIT_Probes;
+ // Submit our RenderInst to the RenderPassManager
+ state->getRenderPass()->addInst(probeInst);
+void ProbeManager::registerProbe(ProbeInfo *probe, SimObject *obj )
+ // AssertFatal( !mRegisteredProbes.contains(probe),
+ //"ProbeManager::registerGlobalLight - This light is already registered!" );
+ if (!mRegisteredProbes.contains(probe))
+ mRegisteredProbes.push_back(probe);
+void ProbeManager::unregisterProbe(ProbeInfo *probe )
+ mRegisteredProbes.unregisterProbe(probe);
+ // If this is the sun... clear the special light too.
+ if (probe == mSpecialProbes[SkylightProbeType] )
+ dMemset(mSpecialProbes, 0, sizeof(mSpecialProbes) );
+void ProbeManager::unregisterAllProbes()
+ //dMemset(mSpecialProbes, 0, sizeof(mSpecialProbes) );
+ mRegisteredProbes.clear();
+ mSkylight = nullptr;
+void ProbeManager::getAllUnsortedProbes( Vector<ProbeInfo*> *list ) const
+ list->merge( mRegisteredProbes );
+ProbeShaderConstants* ProbeManager::getProbeShaderConstants(GFXShaderConstBuffer* buffer)
+ if (!buffer)
+ PROFILE_SCOPE(ProbeManager_GetProbeShaderConstants);
+ GFXShader* shader = buffer->getShader();
+ // Check to see if this is the same shader, we'll get hit repeatedly by
+ // the same one due to the render bin loops.
+ if (mLastShader.getPointer() != shader)
+ ProbeConstantMap::Iterator iter = mConstantLookup.find(shader);
+ if (iter != mConstantLookup.end())
+ mLastConstants = iter->value;
+ ProbeShaderConstants* psc = new ProbeShaderConstants();
+ mConstantLookup[shader] = psc;
+ mLastConstants = psc;
+ // Set our new shader
+ mLastShader = shader;
+ mLastConstants = new ProbeShaderConstants();
+ // Make sure that our current lighting constants are initialized
+ if (!mLastConstants->mInit)
+ mLastConstants->init(shader);
+ return mLastConstants;
+void ProbeManager::_update4ProbeConsts( const SceneData &sgData,
+ MatrixSet &matSet,
+ GFXShaderConstHandle *probePositionSC,
+ GFXShaderConstHandle *probeRadiusSC,
+ GFXShaderConstHandle *probeBoxMinSC,
+ GFXShaderConstHandle *probeBoxMaxSC,
+ GFXShaderConstHandle *probeCubemapSC,
+ GFXShaderConstHandle *probeIsSphereSC,
+ GFXShaderConstHandle *probeLocalPosSC,
+ GFXShaderConstBuffer *shaderConsts )
+ PROFILE_SCOPE( ProbeManager_Update4ProbeConsts );
+ // Skip over gathering lights if we don't have to!
+ if (probePositionSC->isValid() ||
+ probeRadiusSC->isValid() ||
+ probeBoxMinSC->isValid() ||
+ probeBoxMaxSC->isValid() ||
+ probeCubemapSC->isValid() && (!mRegisteredProbes.empty() || mSkylight))
+ PROFILE_SCOPE(ProbeManager_Update4ProbeConsts_setProbes);
+ static AlignedArray<Point3F> probePositions(4, sizeof(Point3F));
+ static AlignedArray<F32> probeRadius(4, sizeof(F32));
+ static AlignedArray<Point3F> probeBoxMins(4, sizeof(Point3F));
+ static AlignedArray<Point3F> probeBoxMaxs(4, sizeof(Point3F));
+ static AlignedArray<Point3F> probeLocalPositions(4, sizeof(Point3F));
+ static AlignedArray<F32> probeIsSphere(4, sizeof(F32));
+ //static AlignedArray<CubemapData> probeCubemap(4, sizeof(CubemapData));
+ F32 range;
+ // Need to clear the buffers so that we don't leak
+ // lights from previous passes or have NaNs.
+ dMemset(probePositions.getBuffer(), 0, probePositions.getBufferSize());
+ dMemset(probeRadius.getBuffer(), 0, probeRadius.getBufferSize());
+ dMemset(probeBoxMins.getBuffer(), 0, probeBoxMins.getBufferSize());
+ dMemset(probeBoxMaxs.getBuffer(), 0, probeBoxMaxs.getBufferSize());
+ dMemset(probeLocalPositions.getBuffer(), 0, probeLocalPositions.getBufferSize());
+ dMemset(probeIsSphere.getBuffer(), 0, probeRadius.getBufferSize());
+ //dMemset(probeCubemap.getBuffer(), 0, probeCubemap.getBufferSize());
+ matSet.restoreSceneViewProjection();
+ const MatrixF &worldToCameraXfm = matSet.getWorldToCamera();
+ // Gather the data for the first 4 probes.
+ const ProbeInfo *probe;
+ for (U32 i = 0; i < 4; i++)
+ if (i >= mRegisteredProbes.size())
+ if (i == 0 && mSkylight)
+ //quickly try and see if we have a skylight, and set that to always be probe 0
+ probe = mSkylight;
+ probe = mRegisteredProbes[i];
+ if (!probe)
+ // The light positions and spot directions are
+ // in SoA order to make optimal use of the GPU.
+ const Point3F &probePos = probe->getPosition();
+ probePositions[i].x = probePos.x;
+ probePositions[i].y = probePos.y;
+ probePositions[i].z = probePos.z;
+ probeRadius[i] = probe->mRadius;
+ const Point3F &minExt = probe->mBounds.minExtents;
+ probeBoxMins[i].x = minExt.x;
+ probeBoxMins[i].y = minExt.y;
+ probeBoxMins[i].z = minExt.z;
+ const Point3F &maxExt = probe->mBounds.maxExtents;
+ probeBoxMaxs[i].x = maxExt.x;
+ probeBoxMaxs[i].y = maxExt.y;
+ probeBoxMaxs[i].z = maxExt.z;
+ probeIsSphere[i] = probe->mProbeShapeType == ProbeInfo::Sphere ? 1.0 : 0.0;
+ Point3F localProbePos;
+ worldToCameraXfm.mulP(probe->getPosition(), &localProbePos);
+ probeLocalPositions[i].x = localProbePos.x;
+ probeLocalPositions[i].y = localProbePos.y;
+ probeLocalPositions[i].z = localProbePos.z;
+ if (probe->mCubemap && !probe->mCubemap->isNull())
+ S32 samplerReg = probeCubemapSC->getSamplerRegister();
+ if(samplerReg != -1)
+ GFX->setCubeTexture(samplerReg + i, probe->mCubemap->getPointer());
+ shaderConsts->setSafe(probePositionSC, probePositions);
+ shaderConsts->setSafe(probeRadiusSC, probeRadius);
+ shaderConsts->setSafe(probeBoxMinSC, probeBoxMins);
+ shaderConsts->setSafe(probeBoxMaxSC, probeBoxMaxs);
+ shaderConsts->setSafe(probeLocalPosSC, probeLocalPositions);
+ shaderConsts->setSafe(probeIsSphereSC, probeIsSphere);
+ //shaderConsts->setSafe(lightSpotAngleSC, lightSpotAngle);
+ //shaderConsts->setSafe(lightSpotFalloffSC, lightSpotFalloff);
+ /*if (probe->mCubemap && !probe->mCubemap->isNull())
+ GFX->setCubeTexture(1, probe->mCubemap->getPointer());
+ if (probeCubemapSC->isValid())
+ for(U32 i=0; i < 4; ++i)
+ GFX->setCubeTexture(probeCubemapSC->getSamplerRegister() + i, NULL);
+void ProbeManager::setProbeInfo(ProcessedMaterial *pmat,
+ const Material *mat,
+ const SceneData &sgData,
+ const SceneRenderState *state,
+ U32 pass,
+ GFXShaderConstBuffer *shaderConsts)
+ // Skip this if we're rendering from the deferred bin.
+ if ( sgData.binType == SceneData::DeferredBin )
+ // if (mRegisteredProbes.empty())
+ // return;
+ PROFILE_SCOPE(ProbeManager_setProbeInfo);
+ ProbeShaderConstants *psc = getProbeShaderConstants(shaderConsts);
+ //ProbeInfo *probe;
+ //probe = mRegisteredProbes[0];
+ // NOTE: If you encounter a crash from this point forward
+ // while setting a shader constant its probably because the
+ // mConstantLookup has bad shaders/constants in it.
+ // This is a known crash bug that can occur if materials/shaders
+ // are reloaded and the light manager is not reset.
+ // We should look to fix this by clearing the table.
+ MatrixSet matSet = state->getRenderPass()->getMatrixSet();
+ // Update the forward shading light constants.
+ _update4ProbeConsts( sgData,
+ matSet,
+ psc->mProbePositionSC,
+ psc->mProbeRadiusSC,
+ psc->mProbeBoxMinSC,
+ psc->mProbeBoxMaxSC,
+ psc->mProbeCubemapSC,
+ psc->mProbeIsSphereSC,
+ psc->mProbeLocalPosSC,
+ shaderConsts );
+ // Static
+ /*if (lsm && light->getCastShadows())
+ if (psc->mWorldToLightProjSC->isValid())
+ shaderConsts->set(psc->mWorldToLightProjSC,
+ lsm->getWorldToLightProj(),
+ psc->mWorldToLightProjSC->getType());
+ if (psc->mViewToLightProjSC->isValid())
+ // TODO: Should probably cache these results and
+ // not do this mul here on every material that needs
+ // this transform.
+ shaderConsts->set(psc->mViewToLightProjSC,
+ lsm->getWorldToLightProj() * state->getCameraTransform(),
+ psc->mViewToLightProjSC->getType());
+ shaderConsts->setSafe(psc->mShadowMapSizeSC, 1.0f / (F32)lsm->getTexSize());
+ // Do this last so that overrides can properly override parameters previously set
+ lsm->setShaderParameters(shaderConsts, psc);
+ MatrixF proj;
+ light->getWorldToLightProj(&proj);
+ proj * state->getCameraTransform(),
+ // Dynamic
+ if (dynamicShadowMap)
+ if (psc->mDynamicWorldToLightProjSC->isValid())
+ shaderConsts->set(psc->mDynamicWorldToLightProjSC,
+ dynamicShadowMap->getWorldToLightProj(),
+ psc->mDynamicWorldToLightProjSC->getType());
+ if (psc->mDynamicViewToLightProjSC->isValid())
+ shaderConsts->set(psc->mDynamicViewToLightProjSC,
+ dynamicShadowMap->getWorldToLightProj() * state->getCameraTransform(),
+ psc->mDynamicViewToLightProjSC->getType());
+ shaderConsts->setSafe(psc->mShadowMapSizeSC, 1.0f / (F32)dynamicShadowMap->getTexSize());
+ dynamicShadowMap->setShaderParameters(shaderConsts, psc);
+/// Allows us to set textures during the Material::setTextureStage call, return true if we've done work.
+bool ProbeManager::setTextureStage(const SceneData &sgData,
+ const U32 currTexFlag,
+ const U32 textureSlot,
+ GFXShaderConstBuffer *shaderConsts,
+ ShaderConstHandles *handles)
+AvailableSLInterfaces* ProbeManager::getSceneLightingInterface()
+ //if ( !mAvailableSLInterfaces )
+ // mAvailableSLInterfaces = new AvailableSLInterfaces();
+/*bool ProbeManager::lightScene( const char* callback, const char* param )
+ BitSet32 flags = 0;
+ if ( param )
+ if ( !dStricmp( param, "forceAlways" ) )
+ flags.set( SceneLighting::ForceAlways );
+ else if ( !dStricmp(param, "forceWritable" ) )
+ flags.set( SceneLighting::ForceWritable );
+ else if ( !dStricmp(param, "loadOnly" ) )
+ flags.set( SceneLighting::LoadOnly );
+ // The SceneLighting object will delete itself
+ // once the lighting process is complete.
+ SceneLighting* sl = new SceneLighting( getSceneLightingInterface() );
+ return sl->lightScene( callback, flags );
+/*RenderDeferredMgr* ProbeManager::_findDeferredRenderBin()
+ RenderPassManager* rpm = getSceneManager()->getDefaultRenderPass();
+ for( U32 i = 0; i < rpm->getManagerCount(); i++ )
+ RenderBinManager *bin = rpm->getManager( i );
+ if( bin->getRenderInstType() == RenderDeferredMgr::RIT_Deferred )
+ return ( RenderDeferredMgr* ) bin;
+DefineEngineFunction( CreateProbeManager, bool, (),,
+ "Finds and activates the named light manager.\n"
+ "@return Returns true if the light manager is found and activated.\n"
+ "@ingroup Lighting\n" )
+ ProbeManager* probeManager = new ProbeManager();
+ if (probeManager != nullptr && gClientSceneGraph != nullptr)
+ probeManager->activate(gClientSceneGraph);
+DefineEngineFunction( resetProbeManager, void, (),,
+ "@brief Deactivates and then activates the currently active light manager."
+ "This causes most shaders to be regenerated and is often used when global "
+ "rendering changes have occured.\n"
+ ProbeManager *pm = PROBEMGR;
+ if ( !pm)
+ /*SceneManager *sm = lm->getSceneManager();
+ lm->deactivate();
+ lm->activate( sm );*/
@@ -0,0 +1,334 @@
+#define PROBEMANAGER_H
+#ifndef _TORQUE_STRING_H_
+#include "core/util/str.h"
+#ifndef _TSIGNAL_H_
+#include "core/util/tSignal.h"
+#ifndef _LIGHTINFO_H_
+#ifndef _LIGHTQUERY_H_
+#include "lighting/lightQuery.h"
+#ifndef _MATRIXSET_H_
+#ifndef _CUBEMAPDATA_H_
+#include "gfx/sim/cubemapData.h"
+class SimObject;
+class ProbeManager;
+class Material;
+class ProcessedMaterial;
+class SceneManager;
+struct SceneData;
+class Point3F;
+class AvailableSLInterfaces;
+class SceneObject;
+class GFXShaderConstBuffer;
+class GFXShaderConstHandle;
+class ShaderConstHandles;
+class SceneRenderState;
+class RenderDeferredMgr;
+class Frustum;
+struct ProbeInfo
+ LinearColorF mAmbient;
+ MatrixF mTransform;
+ F32 mIntensity;
+ Box3F mBounds;
+ GFXCubemapHandle *mCubemap;
+ GFXCubemapHandle *mIrradianceCubemap;
+ GFXTexHandle *mBRDFTexture;
+ /// The priority of this light used for
+ /// light and shadow scoring.
+ F32 mPriority;
+ /// A temporary which holds the score used
+ /// when prioritizing lights for rendering.
+ F32 mScore;
+ bool mIsSkylight;
+ /// Whether to render debugging visualizations
+ /// for this light.
+ bool mDebugRender;
+ //GFXPrimitiveBufferHandle primBuffer;
+ //GFXVertexBufferHandle<GFXVertexPC> vertBuffer;
+ U32 numPrims;
+ U32 numVerts;
+ Vector< U32 > numIndicesForPoly;
+ enum ProbeShapeType
+ Sphere = 0, ///< Sphere shaped
+ Box = 1, ///< Box-based shape
+ ProbeShapeType mProbeShapeType;
+ //Spherical Harmonics data
+ LinearColorF mSHTerms[9];
+ F32 mSHConstants[5];
+ ProbeInfo();
+ ~ProbeInfo();
+ // Copies data passed in from another probe
+ void set(const ProbeInfo *probe);
+ // Accessors
+ const MatrixF& getTransform() const { return mTransform; }
+ void setTransform(const MatrixF &xfm) { mTransform = xfm; }
+ Point3F getPosition() const { return mTransform.getPosition(); }
+ void setPosition(const Point3F &pos) { mTransform.setPosition(pos); }
+ VectorF getDirection() const { return mTransform.getForwardVector(); }
+ void setDirection(const VectorF &val);
+ const LinearColorF& getAmbient() const { return mAmbient; }
+ void setAmbient(const LinearColorF &val) { mAmbient = val; }
+ void setPriority(F32 priority) { mPriority = priority; }
+ F32 getPriority() const { return mPriority; }
+ void setScore(F32 score) { mScore = score; }
+ F32 getScore() const { return mScore; }
+ bool isDebugRenderingEnabled() const { return mDebugRender; }
+ void enableDebugRendering(bool value) { mDebugRender = value; }
+ // Builds the world to light view projection used for
+ // shadow texture and cookie lookups.
+ void getWorldToLightProj(MatrixF *outMatrix) const;
+ void clear();
+class ProbeInfoList : public Vector<ProbeInfo*>
+ void registerProbe(ProbeInfo *probe);
+ void unregisterProbe(ProbeInfo *probe);
+struct ProbeShaderConstants
+ bool mInit;
+ GFXShaderRef mShader;
+ GFXShaderConstHandle* mProbeParamsSC;
+ GFXShaderConstHandle *mProbePositionSC;
+ GFXShaderConstHandle *mProbeRadiusSC;
+ GFXShaderConstHandle *mProbeBoxMinSC;
+ GFXShaderConstHandle *mProbeBoxMaxSC;
+ GFXShaderConstHandle *mProbeIsSphereSC;
+ GFXShaderConstHandle *mProbeLocalPosSC;
+ GFXShaderConstHandle *mProbeCubemapSC;
+ ProbeShaderConstants();
+ ~ProbeShaderConstants();
+ void init(GFXShader* buffer);
+ void _onShaderReload();
+typedef Map<GFXShader*, ProbeShaderConstants*> ProbeConstantMap;
+class ProbeManager
+ enum SpecialProbeTypesEnum
+ SkylightProbeType,
+ SpecialProbeTypesCount
+ ProbeManager();
+ ~ProbeManager();
+ ///
+ static void initProbeFields();
+ static ProbeInfo* createProbeInfo(ProbeInfo* light = NULL);
+ /// The light manager activation signal.
+ static Signal<void(const char*,bool)> smActivateSignal;
+ /// Returns the active LM.
+ static inline ProbeManager* getProbeManager();
+ // Returns the scene manager passed at activation.
+ SceneManager* getSceneManager() { return mSceneManager; }
+ // Called when the lighting manager should become active
+ virtual void activate( SceneManager *sceneManager );
+ // Called when we don't want the light manager active (should clean up)
+ virtual void deactivate();
+ // Returns the active scene lighting interface for this light manager.
+ virtual AvailableSLInterfaces* getSceneLightingInterface();
+ // Returns a "default" light info that callers should not free. Used for instances where we don't actually care about
+ // the light (for example, setting default data for SceneData)
+ virtual ProbeInfo* getDefaultLight();
+ /// Returns the special light or the default light if useDefault is true.
+ /// @see getDefaultLight
+ virtual ProbeInfo* getSpecialProbe(SpecialProbeTypesEnum type,
+ bool useDefault = true );
+ /// Set a special light type.
+ virtual void setSpecialProbe(SpecialProbeTypesEnum type, ProbeInfo *light );
+ void registerSkylight(ProbeInfo *probe, SimObject *obj);
+ // registered before scene traversal...
+ virtual void registerProbe(ProbeInfo *light, SimObject *obj );
+ virtual void unregisterProbe(ProbeInfo *light );
+ virtual void registerProbes( const Frustum *frustum, bool staticlighting );
+ virtual void unregisterAllProbes();
+ /// Returns all unsorted and un-scored lights (both global and local).
+ void getAllUnsortedProbes( Vector<ProbeInfo*> *list ) const;
+ /// Sets shader constants / textures for light infos
+ virtual void setProbeInfo( ProcessedMaterial *pmat,
+ GFXShaderConstBuffer *shaderConsts );
+ /// Allows us to set textures during the Material::setTextureStage call, return true if we've done work.
+ virtual bool setTextureStage( const SceneData &sgData,
+ ShaderConstHandles *handles );
+protected:
+ /// The current active light manager.
+ static ProbeManager *smProbeManager;
+ /// Find the pre-pass render bin on the scene's default render pass.
+ RenderDeferredMgr* _findDeferredRenderBin();
+ ProbeShaderConstants* getProbeShaderConstants(GFXShaderConstBuffer* buffer);
+ /// This helper function sets the shader constansts
+ /// for the stock 4 light forward lighting code.
+ void _update4ProbeConsts( const SceneData &sgData,
+ /// A dummy default light used when no lights
+ /// happen to be registered with the manager.
+ ProbeInfo *mDefaultProbe;
+ /// The list of global registered lights which is
+ /// initialized before the scene is rendered.
+ ProbeInfoList mRegisteredProbes;
+ ProbeInfo* mSkylight;
+ /// The registered special light list.
+ ProbeInfo *mSpecialProbes[SpecialProbeTypesCount];
+ /// The root culling position used for
+ /// special sun light placement.
+ /// @see setSpecialLight
+ Point3F mCullPos;
+ //virtual void _initLightFields();
+ /// The scene graph the light manager is associated with.
+ SceneManager *mSceneManager;
+ ProbeConstantMap mConstantLookup;
+ GFXShaderRef mLastShader;
+ ProbeShaderConstants* mLastConstants;
+ProbeManager* ProbeManager::getProbeManager()
+ if (smProbeManager == nullptr)
+ if (gClientSceneGraph != nullptr)
+ delete probeManager;
+ return smProbeManager;
+/// Returns the current active light manager.
+#define PROBEMGR ProbeManager::getProbeManager()
+#endif // PROBEMANAGER_H
@@ -62,6 +62,8 @@ ImplementFeatureType( MFT_SubSurface, MFG_Lighting, 8.0f, true );
ImplementFeatureType( MFT_VertLit, MFG_Lighting, 9.0f, true );
ImplementFeatureType( MFT_MinnaertShading, MFG_Lighting, 10.0f, true );
+ImplementFeatureType(MFT_ReflectionProbes, MFG_Lighting, 11.0f, true);
ImplementFeatureType( MFT_GlowMask, MFG_PostLighting, 1.0f, true );
ImplementFeatureType( MFT_Visibility, MFG_PostLighting, 2.0f, true );
ImplementFeatureType( MFT_Fog, MFG_PostProcess, 3.0f, true );
@@ -129,6 +129,8 @@ DeclareFeatureType( MFT_InvertSmoothness );
DeclareFeatureType( MFT_SpecularMap );
DeclareFeatureType( MFT_GlossMap );
+DeclareFeatureType( MFT_ReflectionProbes );
/// This feature is only used to detect alpha transparency
/// and does not have any code associtated with it.
DeclareFeatureType( MFT_IsTranslucent );
@@ -40,6 +40,7 @@
#include "console/propertyParsing.h"
#include "gfx/util/screenspace.h"
#include "scene/reflectionManager.h"
ProcessedCustomMaterial::ProcessedCustomMaterial(Material &mat)
@@ -322,6 +323,10 @@ bool ProcessedCustomMaterial::setupPass( SceneRenderState *state, const SceneDat
if (lm)
lm->setLightInfo(this, NULL, sgData, state, pass, shaderConsts);
+ ProbeManager* pm = state ? PROBEMGR : NULL;
+ if (pm)
+ pm->setProbeInfo(this, NULL, sgData, state, pass, shaderConsts);
shaderConsts->setSafe(rpd->shaderHandles.mAccumTimeSC, MATMGR->getTotalTime());
return true;
@@ -41,6 +41,8 @@
// We need to include customMaterialDefinition for ShaderConstHandles::init
#include "materials/customMaterialDefinition.h"
@@ -343,10 +345,16 @@ void ProcessedShaderMaterial::_determineFeatures( U32 stageNum,
if ( mMaterial->mAlphaTest )
fd.features.addFeature( MFT_AlphaTest );
- if ( mMaterial->mEmissive[stageNum] )
- fd.features.addFeature( MFT_IsEmissive );
+ if (mMaterial->mEmissive[stageNum])
+ fd.features.addFeature(MFT_IsEmissive);
- fd.features.addFeature( MFT_RTLighting );
+ fd.features.addFeature(MFT_RTLighting);
+ if (mMaterial->isTranslucent())
+ fd.features.addFeature(MFT_ReflectionProbes);
if ( mMaterial->mAnimFlags[stageNum] )
fd.features.addFeature( MFT_TexAnim );
@@ -357,7 +365,7 @@ void ProcessedShaderMaterial::_determineFeatures( U32 stageNum,
// cubemaps only available on stage 0 for now - bramage
if ( stageNum < 1 && mMaterial->isTranslucent() &&
( ( mMaterial->mCubemapData && mMaterial->mCubemapData->mCubemap ) ||
- mMaterial->mDynamicCubemap ) )
+ mMaterial->mDynamicCubemap ) && !features.hasFeature(MFT_ReflectionProbes))
fd.features.addFeature( MFT_CubeMap );
@@ -367,6 +375,8 @@ void ProcessedShaderMaterial::_determineFeatures( U32 stageNum,
fd.features.addFeature(MFT_StaticCubemap);
fd.features.addFeature(MFT_CubeMap);
fd.features.addFeature(MFT_SkyBox);
+ fd.features.removeFeature(MFT_ReflectionProbes);
fd.features.addFeature( MFT_Visibility );
@@ -1296,13 +1306,15 @@ void ProcessedShaderMaterial::setSceneInfo(SceneRenderState * state, const Scene
shaderConsts->set(handles->mEyePosSC, eyepos);
- shaderConsts->setSafe(handles->mEyeMatSC, state->getCameraTransform());
+ shaderConsts->setSafe(handles->mEyeMatSC, state->getCameraTransform());
+ ShaderRenderPassData *rpd = _getRPD(pass);
+ for (U32 i = 0; i < rpd->featureShaderHandles.size(); i++)
+ rpd->featureShaderHandles[i]->setConsts(state, sgData, shaderConsts);
- ShaderRenderPassData *rpd = _getRPD( pass );
- for ( U32 i=0; i < rpd->featureShaderHandles.size(); i++ )
- rpd->featureShaderHandles[i]->setConsts( state, sgData, shaderConsts );
+ LIGHTMGR->setLightInfo(this, mMaterial, sgData, state, pass, shaderConsts);
- LIGHTMGR->setLightInfo( this, mMaterial, sgData, state, pass, shaderConsts );
+ PROBEMGR->setProbeInfo(this, mMaterial, sgData, state, pass, shaderConsts);
void ProcessedShaderMaterial::setBuffers( GFXVertexBufferHandleBase *vertBuffer, GFXPrimitiveBufferHandle *primBuffer )
@@ -86,8 +86,6 @@ public:
MaterialOverrideDelegate& getMatOverrideDelegate() { return mMatOverrideDelegate; }
-protected:
-
struct MainSortElem
RenderInst *inst;
@@ -95,6 +93,7 @@ protected:
U32 key2;
};
void setRenderPass( RenderPassManager *rpm );
/// Called from derived bins to add additional
@@ -56,7 +56,7 @@ const String RenderDeferredMgr::BufferName("deferred");
const RenderInstType RenderDeferredMgr::RIT_Deferred("Deferred");
const String RenderDeferredMgr::ColorBufferName("color");
const String RenderDeferredMgr::MatInfoBufferName("matinfo");
-const String RenderDeferredMgr::LightMapBufferName("indirectLighting");
+const String RenderDeferredMgr::LightMapBufferName("diffuseLighting");
IMPLEMENT_CONOBJECT(RenderDeferredMgr);
@@ -92,6 +92,7 @@ RenderDeferredMgr::RenderDeferredMgr( bool gatherDepth,
notifyType( RenderPassManager::RIT_Mesh );
notifyType( RenderPassManager::RIT_Terrain );
notifyType( RenderPassManager::RIT_Object );
+ notifyType( RenderPassManager::RIT_Probes );
// We want a full-resolution buffer
mTargetSizeType = RenderTexTargetBinManager::WindowSize;
@@ -186,10 +187,10 @@ bool RenderDeferredMgr::_updateTargets()
mTargetChain[i]->attachTexture(GFXTextureTarget::Color2, mMatInfoTarget.getTexture());
- if (mLightMapTex.getFormat() != mTargetFormat || mLightMapTex.getWidthHeight() != mTargetSize || GFX->recentlyReset())
+ if (mLightMapTex.getFormat() != GFXFormatR16G16B16A16F || mLightMapTex.getWidthHeight() != mTargetSize || GFX->recentlyReset())
mLightMapTarget.release();
- mLightMapTex.set(mTargetSize.x, mTargetSize.y, mTargetFormat,
+ mLightMapTex.set(mTargetSize.x, mTargetSize.y, GFXFormatR16G16B16A16F,
&GFXRenderTargetProfile, avar("%s() - (line %d)", __FUNCTION__, __LINE__),
1, GFXTextureManager::AA_MATCH_BACKBUFFER);
mLightMapTarget.setTexture(mLightMapTex);
@@ -238,6 +239,8 @@ void RenderDeferredMgr::addElement( RenderInst *inst )
const bool isTerrainInst = inst->type == RenderPassManager::RIT_Terrain;
+ const bool isProbeInst = inst->type == RenderPassManager::RIT_Probes;
// Get the material if its a mesh.
BaseMatInstance* matInst = NULL;
if ( isMeshInst || isDecalMeshInst )
@@ -262,6 +265,8 @@ void RenderDeferredMgr::addElement( RenderInst *inst )
elementList = &mElementList;
else if ( isTerrainInst )
elementList = &mTerrainElementList;
+ else if (isProbeInst)
+ elementList = &mProbeElementList;
elementList = &mObjectElementList;
@@ -294,6 +299,7 @@ void RenderDeferredMgr::sort()
void RenderDeferredMgr::clear()
Parent::clear();
+ mProbeElementList.clear();
mTerrainElementList.clear();
mObjectElementList.clear();
@@ -92,6 +92,9 @@ protected:
DeferredMatInstance *mDeferredMatInstance;
+ Vector< MainSortElem > mProbeElementList;
virtual void _registerFeatures();
virtual void _unregisterFeatures();
virtual bool _updateTargets();
@@ -61,6 +61,7 @@ const RenderInstType RenderPassManager::RIT_Custom("Custom");
const RenderInstType RenderPassManager::RIT_Particle("Particle");
const RenderInstType RenderPassManager::RIT_Occluder("Occluder");
const RenderInstType RenderPassManager::RIT_Editor("Editor");
+const RenderInstType RenderPassManager::RIT_Probes("Probes");
//*****************************************************************************
@@ -102,6 +103,11 @@ void OccluderRenderInst::clear()
dMemset( this, 0, sizeof(OccluderRenderInst) );
+void ProbeRenderInst::clear()
+ dMemset(this, 0, sizeof(ProbeRenderInst));
+ //mCubemap);
IMPLEMENT_CONOBJECT(RenderPassManager);
@@ -37,6 +37,18 @@
#ifndef _SCENEMANAGER_H_
#include "scene/sceneManager.h"
+#ifndef _SCENEMANAGER_H_
class SceneRenderState;
class ISceneObject;
@@ -48,6 +60,7 @@ class LightInfo;
struct RenderInst;
class MatrixSet;
class GFXPrimitiveBufferHandle;
+class CubemapData;
/// A RenderInstType hash value.
typedef U32 RenderInstTypeHash;
@@ -118,6 +131,7 @@ public:
static const RenderInstType RIT_Particle;
static const RenderInstType RIT_Occluder;
static const RenderInstType RIT_Editor;
+ static const RenderInstType RIT_Probes;
public:
@@ -463,4 +477,85 @@ struct OccluderRenderInst : public RenderInst
void clear();
+struct ProbeRenderInst : public RenderInst
+ GFXPrimitiveBufferHandle primBuffer;
+ GFXVertexBufferHandle<GFXVertexPC> vertBuffer;
+ ProbeRenderInst();
+ ~ProbeRenderInst();
+ // Copies data passed in from light
+ void set(const ProbeRenderInst *probeInfo);
+ void set(const ProbeInfo *probeInfo);
#endif // _RENDERPASSMANAGER_H_
@@ -0,0 +1,956 @@
+#include "renderProbeMgr.h"
+#include "materials/processedMaterial.h"
+#include "math/mPolyhedron.impl.h"
+IMPLEMENT_CONOBJECT(RenderProbeMgr);
+ConsoleDocClass( RenderProbeMgr,
+ "@brief A render bin which uses object callbacks for rendering.\n\n"
+ "This render bin gathers object render instances and calls its delegate "
+ "method to perform rendering. It is used infrequently for specialized "
+ "scene objects which perform custom rendering.\n\n"
+ "@ingroup RenderBin\n" );
+S32 QSORT_CALLBACK AscendingReflectProbeInfluence(const void* a, const void* b)
+ // Debug Profiling.
+ PROFILE_SCOPE(AdvancedLightBinManager_AscendingReflectProbeInfluence);
+ // Fetch asset definitions.
+ const ProbeRenderInst* pReflectProbeA = static_cast<ProbeRenderInst*>(((RenderBinManager::MainSortElem*)(a))->inst);
+ const ProbeRenderInst* pReflectProbeB = static_cast<ProbeRenderInst*>(((RenderBinManager::MainSortElem*)(b))->inst);
+ // Sort.
+ //First, immediate check on if either is a skylight. Skylight always gets the highest priority
+ //if (pReflectProbeA->mIsSkylight)
+ // return 1;
+ //else if (pReflectProbeB->mIsSkylight)
+ // return -1;
+ //No? then sort by score
+ if (pReflectProbeA->mScore > pReflectProbeB->mScore)
+ return 1;
+ else if (pReflectProbeA->mScore < pReflectProbeB->mScore)
+ return -1;
+ return 0;
+RenderProbeMgr::RenderProbeMgr()
+: RenderBinManager(RenderPassManager::RIT_Probes, 1.0f, 1.0f)
+ mReflectProbeMaterial = nullptr;
+ mSkylightMaterial = nullptr;
+RenderProbeMgr::RenderProbeMgr(RenderInstType riType, F32 renderOrder, F32 processAddOrder)
+ : RenderBinManager(riType, renderOrder, processAddOrder)
+void RenderProbeMgr::initPersistFields()
+void RenderProbeMgr::addElement(RenderInst *inst)
+ // If this instance is translucent handle it in RenderTranslucentMgr
+ if (inst->translucentSort)
+ //AssertFatal(inst->defaultKey != 0, "RenderMeshMgr::addElement() - Got null sort key... did you forget to set it?");
+ internalAddElement(inst);
+ ProbeRenderInst* probeInst = static_cast<ProbeRenderInst*>(inst);
+ if (probeInst->mIsSkylight)
+ addSkylightProbe(probeInst);
+ if (probeInst->mProbeShapeType == ProbeInfo::Sphere)
+ addSphereReflectionProbe(probeInst);
+ addConvexReflectionProbe(probeInst);
+//remove
+//Con::setIntVariable("lightMetrics::activeReflectionProbes", mReflectProbeBin.size());
+//Con::setIntVariable("lightMetrics::culledReflectProbes", 0/*mNumLightsCulled*/);
+GFXVertexBufferHandle<GFXVertexPC> RenderProbeMgr::getSphereMesh(U32 &outNumPrimitives, GFXPrimitiveBufferHandle &outPrimitives)
+ static SphereMesh sSphereMesh;
+ if (mSphereGeometry.isNull())
+ const SphereMesh::TriangleMesh * sphereMesh = sSphereMesh.getMesh(3);
+ S32 numPoly = sphereMesh->numPoly;
+ mSpherePrimitiveCount = 0;
+ mSphereGeometry.set(GFX, numPoly * 3, GFXBufferTypeStatic);
+ mSphereGeometry.lock();
+ S32 vertexIndex = 0;
+ for (S32 i = 0; i<numPoly; i++)
+ mSpherePrimitiveCount++;
+ mSphereGeometry[vertexIndex].point = sphereMesh->poly[i].pnt[0];
+ mSphereGeometry[vertexIndex].color = ColorI::WHITE;
+ vertexIndex++;
+ mSphereGeometry[vertexIndex].point = sphereMesh->poly[i].pnt[1];
+ mSphereGeometry[vertexIndex].point = sphereMesh->poly[i].pnt[2];
+ mSphereGeometry.unlock();
+ outNumPrimitives = mSpherePrimitiveCount;
+ outPrimitives = NULL; // For now
+ return mSphereGeometry;
+void RenderProbeMgr::addSkylightProbe(ProbeRenderInst *probeInfo)
+ probeInfo->vertBuffer = getSphereMesh(probeInfo->numPrims, probeInfo->primBuffer);
+ if (!mSkylightMaterial)
+ mSkylightMaterial = _getSkylightMaterial();
+void RenderProbeMgr::addSphereReflectionProbe(ProbeRenderInst *probeInfo)
+ if (!mReflectProbeMaterial)
+ mReflectProbeMaterial = _getReflectProbeMaterial();
+void RenderProbeMgr::addConvexReflectionProbe(ProbeRenderInst *probeInfo)
+ static const Point3F cubePoints[8] =
+ Point3F(1, -1, -1), Point3F(1, -1, 1), Point3F(1, 1, -1), Point3F(1, 1, 1),
+ Point3F(-1, -1, -1), Point3F(-1, 1, -1), Point3F(-1, -1, 1), Point3F(-1, 1, 1)
+ /*static const Point3F cubeNormals[6] =
+ Point3F(1, 0, 0), Point3F(-1, 0, 0), Point3F(0, 1, 0),
+ Point3F(0, -1, 0), Point3F(0, 0, 1), Point3F(0, 0, -1)
+ };*/
+ /*static const Point2F cubeTexCoords[4] =
+ Point2F(0, 0), Point2F(0, -1),
+ Point2F(1, 0), Point2F(1, -1)
+ static const U32 cubeFaces[36][3] =
+ { 3, 0, 3 },{ 0, 0, 0 },{ 1, 0, 1 },
+ { 2, 0, 2 },{ 0, 0, 0 },{ 3, 0, 3 },
+ { 7, 1, 1 },{ 4, 1, 2 },{ 5, 1, 0 },
+ { 6, 1, 3 },{ 4, 1, 2 },{ 7, 1, 1 },
+ { 3, 2, 1 },{ 5, 2, 2 },{ 2, 2, 0 },
+ { 7, 2, 3 },{ 5, 2, 2 },{ 3, 2, 1 },
+ { 1, 3, 3 },{ 4, 3, 0 },{ 6, 3, 1 },
+ { 0, 3, 2 },{ 4, 3, 0 },{ 1, 3, 3 },
+ { 3, 4, 3 },{ 6, 4, 0 },{ 7, 4, 1 },
+ { 1, 4, 2 },{ 6, 4, 0 },{ 3, 4, 3 },
+ { 2, 5, 1 },{ 4, 5, 2 },{ 0, 5, 0 },
+ { 5, 5, 3 },{ 4, 5, 2 },{ 2, 5, 1 }
+ // Fill the vertex buffer
+ GFXVertexPC *pVert = NULL;
+ probeInfo->numVerts = 36;
+ probeInfo->vertBuffer.set(GFX, 36, GFXBufferTypeStatic);
+ pVert = probeInfo->vertBuffer.lock();
+ Point3F halfSize = Point3F(probeInfo->mRadius, probeInfo->mRadius, probeInfo->mRadius);
+ for (U32 i = 0; i < 36; i++)
+ const U32& vdx = cubeFaces[i][0];
+ pVert[i].point = cubePoints[vdx] * halfSize;
+ probeInfo->vertBuffer.unlock();
+ // Fill the primitive buffer
+ U16 *pIdx = NULL;
+ probeInfo->primBuffer.set(GFX, 36, 12, GFXBufferTypeStatic);
+ probeInfo->primBuffer.lock(&pIdx);
+ for (U16 i = 0; i < 36; i++)
+ pIdx[i] = i;
+ probeInfo->primBuffer.unlock();
+ probeInfo->numPrims = 12;
+ // mReflectProbeBin.push_back(pEntry);
+void RenderProbeMgr::_setupPerFrameParameters(const SceneRenderState *state)
+ PROFILE_SCOPE(RenderProbeMgr_SetupPerFrameParameters);
+ const Frustum &frustum = state->getCameraFrustum();
+ MatrixF invCam(frustum.getTransform());
+ invCam.inverse();
+ const Point3F *wsFrustumPoints = frustum.getPoints();
+ const Point3F& cameraPos = frustum.getPosition();
+ // Perform a camera offset. We need to manually perform this offset on the sun (or vector) light's
+ // polygon, which is at the far plane.
+ Point3F cameraOffsetPos = cameraPos;
+ // Now build the quad for drawing full-screen vector light
+ // passes.... this is a volatile VB and updates every frame.
+ FarFrustumQuadVert verts[4];
+ verts[0].point.set(wsFrustumPoints[Frustum::FarTopLeft] - cameraPos);
+ invCam.mulP(wsFrustumPoints[Frustum::FarTopLeft], &verts[0].normal);
+ verts[0].texCoord.set(-1.0, 1.0);
+ verts[0].tangent.set(wsFrustumPoints[Frustum::FarTopLeft] - cameraOffsetPos);
+ verts[1].point.set(wsFrustumPoints[Frustum::FarTopRight] - cameraPos);
+ invCam.mulP(wsFrustumPoints[Frustum::FarTopRight], &verts[1].normal);
+ verts[1].texCoord.set(1.0, 1.0);
+ verts[1].tangent.set(wsFrustumPoints[Frustum::FarTopRight] - cameraOffsetPos);
+ verts[2].point.set(wsFrustumPoints[Frustum::FarBottomLeft] - cameraPos);
+ invCam.mulP(wsFrustumPoints[Frustum::FarBottomLeft], &verts[2].normal);
+ verts[2].texCoord.set(-1.0, -1.0);
+ verts[2].tangent.set(wsFrustumPoints[Frustum::FarBottomLeft] - cameraOffsetPos);
+ verts[3].point.set(wsFrustumPoints[Frustum::FarBottomRight] - cameraPos);
+ invCam.mulP(wsFrustumPoints[Frustum::FarBottomRight], &verts[3].normal);
+ verts[3].texCoord.set(1.0, -1.0);
+ verts[3].tangent.set(wsFrustumPoints[Frustum::FarBottomRight] - cameraOffsetPos);
+ mFarFrustumQuadVerts.set(GFX, 4);
+ dMemcpy(mFarFrustumQuadVerts.lock(), verts, sizeof(verts));
+ mFarFrustumQuadVerts.unlock();
+ PlaneF farPlane(wsFrustumPoints[Frustum::FarBottomLeft], wsFrustumPoints[Frustum::FarTopLeft], wsFrustumPoints[Frustum::FarTopRight]);
+ PlaneF vsFarPlane(verts[0].normal, verts[1].normal, verts[2].normal);
+ MatrixSet &matrixSet = getRenderPass()->getMatrixSet();
+ matrixSet.restoreSceneViewProjection();
+ MatrixF inverseViewMatrix = worldToCameraXfm;
+ //inverseViewMatrix.fullInverse();
+ //inverseViewMatrix.transpose();
+ //inverseViewMatrix = MatrixF::Identity;
+ // Parameters calculated, assign them to the materials
+ if (mSkylightMaterial != nullptr && mSkylightMaterial->matInstance != nullptr)
+ mSkylightMaterial->setViewParameters(frustum.getNearDist(),
+ frustum.getFarDist(),
+ frustum.getPosition(),
+ farPlane,
+ vsFarPlane, inverseViewMatrix);
+ if (mReflectProbeMaterial != nullptr && mReflectProbeMaterial->matInstance != nullptr)
+ mReflectProbeMaterial->setViewParameters(frustum.getNearDist(),
+// render objects
+void RenderProbeMgr::render( SceneRenderState *state )
+ PROFILE_SCOPE(RenderProbeMgr_render);
+ // Early out if nothing to draw.
+ if(!mElementList.size())
+ NamedTexTargetRef diffuseLightingTarget = NamedTexTarget::find("diffuseLighting");
+ if (diffuseLightingTarget.isNull())
+ NamedTexTargetRef specularLightingTarget = NamedTexTarget::find("specularLighting");
+ if (specularLightingTarget.isNull())
+ GFXTextureTargetRef probeLightingTargetRef = GFX->allocRenderToTextureTarget();
+ if (probeLightingTargetRef.isNull())
+ probeLightingTargetRef->attachTexture(GFXTextureTarget::Color0, diffuseLightingTarget->getTexture());
+ probeLightingTargetRef->attachTexture(GFXTextureTarget::Color1, specularLightingTarget->getTexture());
+ GFX->setActiveRenderTarget(probeLightingTargetRef);
+ GFX->setViewport(diffuseLightingTarget->getViewport());
+ //GFX->setViewport(specularLightingTarget->getViewport());
+ // Restore transforms
+ // Set up the SG Data
+ SceneData sgData;
+ sgData.init(state);
+ // Initialize and set the per-frame parameters after getting
+ // the vector light material as we use lazy creation.
+ _setupPerFrameParameters(state);
+ //Order the probes by size, biggest to smallest
+ dQsort(mElementList.address(), mElementList.size(), sizeof(const MainSortElem), AscendingReflectProbeInfluence);
+ //Specular
+ PROFILE_START(RenderProbeManager_ReflectProbeRender);
+ for (U32 i = 0; i<mElementList.size(); i++)
+ ProbeRenderInst *curEntry = static_cast<ProbeRenderInst*>(mElementList[i].inst);
+ if (curEntry->numPrims == 0)
+ if (curEntry->mIsSkylight && (!mSkylightMaterial || !mSkylightMaterial->matInstance))
+ if (!curEntry->mIsSkylight && (!mReflectProbeMaterial || !mReflectProbeMaterial->matInstance))
+ //Setup
+ MatrixF probeTrans = curEntry->getTransform();
+ if (!curEntry->mIsSkylight)
+ if (curEntry->mProbeShapeType == ProbeInfo::Sphere)
+ probeTrans.scale(curEntry->mRadius * 1.01f);
+ probeTrans.scale(10); //force it to be big enough to surround the camera
+ sgData.objTrans = &probeTrans;
+ if(curEntry->mIsSkylight)
+ mSkylightMaterial->setSkylightParameters(curEntry, state, worldToCameraXfm);
+ mReflectProbeMaterial->setProbeParameters(curEntry, state, worldToCameraXfm);
+ // Set geometry
+ GFX->setVertexBuffer(curEntry->vertBuffer);
+ GFX->setPrimitiveBuffer(curEntry->primBuffer);
+ if (curEntry->mIsSkylight)
+ while (mSkylightMaterial->matInstance->setupPass(state, sgData))
+ // Set transforms
+ matrixSet.setWorld(*sgData.objTrans);
+ mSkylightMaterial->matInstance->setTransforms(matrixSet, state);
+ mSkylightMaterial->matInstance->setSceneInfo(state, sgData);
+ GFX->drawPrimitive(GFXTriangleList, 0, curEntry->numPrims);
+ while (mReflectProbeMaterial->matInstance->setupPass(state, sgData))
+ mReflectProbeMaterial->matInstance->setTransforms(matrixSet, state);
+ mReflectProbeMaterial->matInstance->setSceneInfo(state, sgData);
+ probeLightingTargetRef->resolve();
+ PROBEMGR->unregisterAllProbes();
+ PROFILE_END();
+ GFX->setPrimitiveBuffer(NULL);
+ // Fire off a signal to let others know that light-bin rendering is ending now
+ //getRenderSignal().trigger(state, this);
+RenderProbeMgr::ReflectProbeMaterialInfo::ReflectProbeMaterialInfo(const String &matName,
+ const GFXVertexFormat *vertexFormat)
+ : matInstance(NULL),
+ zNearFarInvNearFar(NULL),
+ farPlane(NULL),
+ vsFarPlane(NULL),
+ negFarPlaneDotEye(NULL),
+ probeWSPos(NULL),
+ attenuation(NULL),
+ radius(NULL),
+ invViewMat(NULL)
+ Material *mat = MATMGR->getMaterialDefinitionByName(matName);
+ if (!mat)
+ matInstance = new ReflectProbeMatInstance(*mat);
+ const Vector<GFXShaderMacro> ¯os = Vector<GFXShaderMacro>();
+ for (U32 i = 0; i < macros.size(); i++)
+ matInstance->addShaderMacro(macros[i].name, macros[i].value);
+ matInstance->init(MATMGR->getDefaultFeatures(), vertexFormat);
+ attenuation = matInstance->getMaterialParameterHandle("$attenuation");
+ radius = matInstance->getMaterialParameterHandle("$radius");
+ probeLSPos = matInstance->getMaterialParameterHandle("$probeLSPos");
+ probeWSPos = matInstance->getMaterialParameterHandle("$probeWSPos");
+ farPlane = matInstance->getMaterialParameterHandle("$farPlane");
+ vsFarPlane = matInstance->getMaterialParameterHandle("$vsFarPlane");
+ negFarPlaneDotEye = matInstance->getMaterialParameterHandle("$negFarPlaneDotEye");
+ zNearFarInvNearFar = matInstance->getMaterialParameterHandle("$zNearFarInvNearFar");
+ invViewMat = matInstance->getMaterialParameterHandle("$invViewMat");
+ useCubemap = matInstance->getMaterialParameterHandle("$useCubemap");
+ cubemap = matInstance->getMaterialParameterHandle("$cubeMap");
+ eyePosWorld = matInstance->getMaterialParameterHandle("$eyePosWorld");
+ bbMin = matInstance->getMaterialParameterHandle("$bbMin");
+ bbMax = matInstance->getMaterialParameterHandle("$bbMax");
+ useSphereMode = matInstance->getMaterialParameterHandle("$useSphereMode");
+ for(U32 i=0; i < 9; i++)
+ shTerms[i] = matInstance->getMaterialParameterHandle(String::ToString("$SHTerms%d",i));
+ shConsts[i] = matInstance->getMaterialParameterHandle(String::ToString("$SHConsts%d", i));
+RenderProbeMgr::ReflectProbeMaterialInfo::~ReflectProbeMaterialInfo()
+ SAFE_DELETE(matInstance);
+void RenderProbeMgr::ReflectProbeMaterialInfo::setViewParameters(const F32 _zNear,
+ const F32 _zFar,
+ const Point3F &_eyePos,
+ const PlaneF &_farPlane,
+ const PlaneF &_vsFarPlane, const MatrixF &_inverseViewMatrix)
+ MaterialParameters *matParams = matInstance->getMaterialParameters();
+ matParams->setSafe(farPlane, *((const Point4F *)&_farPlane));
+ matParams->setSafe(vsFarPlane, *((const Point4F *)&_vsFarPlane));
+ if (negFarPlaneDotEye->isValid())
+ // -dot( farPlane, eyePos )
+ const F32 negFarPlaneDotEyeVal = -(mDot(*((const Point3F *)&_farPlane), _eyePos) + _farPlane.d);
+ matParams->set(negFarPlaneDotEye, negFarPlaneDotEyeVal);
+ matParams->setSafe(zNearFarInvNearFar, Point4F(_zNear, _zFar, 1.0f / _zNear, 1.0f / _zFar));
+ matParams->setSafe(invViewMat, _inverseViewMatrix);
+ Point4F frPlane = *((const Point4F *)&_farPlane);
+ Point4F vsFrPlane = *((const Point4F *)&_vsFarPlane);
+ Point4F nearFarInvNearFar = Point4F(_zNear, _zFar, 1.0f / _zNear, 1.0f / _zFar);
+void RenderProbeMgr::ReflectProbeMaterialInfo::setProbeParameters(const ProbeRenderInst *probeInfo, const SceneRenderState* renderState, const MatrixF &worldViewOnly)
+ matParams->setSafe(radius, probeInfo->mRadius);
+ Point3F probePos = probeInfo->getPosition();
+ //worldViewOnly.mulP(probeInfo->getPosition(), &probePos);
+ matParams->setSafe(probeWSPos, probePos);
+ worldViewOnly.mulP(probeInfo->getPosition(), &probePos);
+ matParams->setSafe(probeLSPos, probePos);
+ // Get the attenuation falloff ratio and normalize it.
+ Point3F attenRatio = Point3F(0.0f, 1.0f, 1.0f);
+ F32 total = attenRatio.x + attenRatio.y + attenRatio.z;
+ if (total > 0.0f)
+ attenRatio /= total;
+ F32 radius = probeInfo->mRadius;
+ Point2F attenParams((1.0f / radius) * attenRatio.y,
+ (1.0f / (radius * radius)) * attenRatio.z);
+ matParams->setSafe(attenuation, attenParams);
+ if (!deferredTexObject) return;
+ NamedTexTarget* matInfoTexTarget = NamedTexTarget::find("matinfo");
+ GFXTextureObject *matInfoTexObject = matInfoTexTarget->getTexture();
+ if (!matInfoTexObject) return;
+ GFX->setTexture(1, matInfoTexObject);
+ if (probeInfo->mCubemap && !probeInfo->mCubemap->isNull())
+ GFX->setCubeTexture(2, probeInfo->mCubemap->getPointer());
+ GFX->setCubeTexture(2, NULL);
+ if (probeInfo->mIrradianceCubemap && !probeInfo->mIrradianceCubemap->isNull())
+ GFX->setCubeTexture(3, probeInfo->mIrradianceCubemap->getPointer());
+ GFX->setCubeTexture(3, NULL);
+ if (probeInfo->mBRDFTexture && !probeInfo->mBRDFTexture->isNull())
+ GFX->setTexture(4, probeInfo->mBRDFTexture->getPointer());
+ GFX->setTexture(4, NULL);
+ matParams->setSafe(eyePosWorld, renderState->getCameraPosition());
+ matParams->setSafe(bbMin, probeInfo->mBounds.minExtents);
+ matParams->setSafe(bbMax, probeInfo->mBounds.maxExtents);
+ matParams->setSafe(useSphereMode, probeInfo->mProbeShapeType == ProbeInfo::Sphere ? 1.0f : 0.0f);
+ //SH Terms
+ //static AlignedArray<Point3F> shTermsArray(9, sizeof(Point3F));
+ //dMemset(shTermsArray.getBuffer(), 0, shTermsArray.getBufferSize());
+ matParams->setSafe(shTerms[i], probeInfo->mSHTerms[i]);
+ matParams->setSafe(shConsts[i], probeInfo->mSHConstants[i]);
+bool ReflectProbeMatInstance::init(const FeatureSet &features, const GFXVertexFormat *vertexFormat)
+ bool success = Parent::init(features, vertexFormat);
+ // If the initialization failed don't continue.
+ if (!success || !mProcessedMaterial || mProcessedMaterial->getNumPasses() == 0)
+bool ReflectProbeMatInstance::setupPass(SceneRenderState *state, const SceneData &sgData)
+ // Go no further if the material failed to initialize properly.
+ if (!mProcessedMaterial ||
+ mProcessedMaterial->getNumPasses() == 0)
+ bool bRetVal = Parent::setupPass(state, sgData);;
+ AssertFatal(mProcessedMaterial->getNumPasses() > 0, "No passes created! Ohnoes");
+ const RenderPassData *rpd = mProcessedMaterial->getPass(0);
+ AssertFatal(rpd, "No render pass data!");
+ AssertFatal(rpd->mRenderStates[0], "No render state 0!");
+ if (!mProjectionState)
+ desc.setZReadWrite(false);
+ desc.zWriteEnable = false;
+ desc.setBlend(true, GFXBlendOne, GFXBlendOne);
+ mProjectionState = GFX->createStateBlock(desc);
+ // Now override stateblock with our own
+ GFX->setStateBlock(mProjectionState);
+ return bRetVal;
+RenderProbeMgr::ReflectProbeMaterialInfo* RenderProbeMgr::_getReflectProbeMaterial()
+ PROFILE_SCOPE(AdvancedLightBinManager_getReflectProbeMaterial);
+ //ReflectProbeMaterialInfo *info = NULL;
+ // Now create the material info object.
+ mReflectProbeMaterial = new ReflectProbeMaterialInfo("ReflectionProbeMaterial",
+ getGFXVertexFormat<GFXVertexPC>());
+ return mReflectProbeMaterial;
+RenderProbeMgr::SkylightMaterialInfo::SkylightMaterialInfo(const String &matName,
+ matInstance = new SkylightMatInstance(*mat);
+ shTerms[i] = matInstance->getMaterialParameterHandle(String::ToString("$SHTerms%d", i));
+RenderProbeMgr::SkylightMaterialInfo::~SkylightMaterialInfo()
+void RenderProbeMgr::SkylightMaterialInfo::setViewParameters(const F32 _zNear,
+void RenderProbeMgr::SkylightMaterialInfo::setSkylightParameters(const ProbeRenderInst *probeInfo, const SceneRenderState* renderState, const MatrixF &worldViewOnly)
+bool SkylightMatInstance::init(const FeatureSet &features, const GFXVertexFormat *vertexFormat)
+bool SkylightMatInstance::setupPass(SceneRenderState *state, const SceneData &sgData)
+RenderProbeMgr::SkylightMaterialInfo* RenderProbeMgr::_getSkylightMaterial()
+ PROFILE_SCOPE(AdvancedLightBinManager_getSkylightMaterial);
+ mSkylightMaterial = new SkylightMaterialInfo("SklyightMaterial",
+ return mSkylightMaterial;
+ProbeRenderInst::ProbeRenderInst()
+ProbeRenderInst::~ProbeRenderInst()
+void ProbeRenderInst::set(const ProbeRenderInst *probeInfo)
+ mScore = probeInfo->mScore;
+ mIsSkylight = probeInfo->mIsSkylight;
+void ProbeRenderInst::set(const ProbeInfo *probeInfo)
+void ProbeRenderInst::getWorldToLightProj(MatrixF *outMatrix) const
@@ -0,0 +1,213 @@
+#ifndef RENDER_PROBE_MGR_H
+#define RENDER_PROBE_MGR_H
+#ifndef _RENDERBINMANAGER_H_
+#include "renderInstance/renderBinManager.h"
+#ifndef _MATINSTANCE_H_
+#include "materials/matInstance.h"
+#ifndef _MATTEXTURETARGET_H_
+#include "materials/matTextureTarget.h"
+class ReflectProbeMatInstance : public MatInstance
+ typedef MatInstance Parent;
+ MaterialParameterHandle *mProbeParamsSC;
+ bool mInternalPass;
+ GFXStateBlockRef mProjectionState;
+ ReflectProbeMatInstance(Material &mat) : Parent(mat), mProbeParamsSC(NULL), mInternalPass(false), mProjectionState(NULL){}
+ virtual bool init(const FeatureSet &features, const GFXVertexFormat *vertexFormat);
+ virtual bool setupPass(SceneRenderState *state, const SceneData &sgData);
+class SkylightMatInstance : public MatInstance
+ MaterialParameterHandle * mSkylightParamsSC;
+ SkylightMatInstance(Material &mat) : Parent(mat), mSkylightParamsSC(NULL), mInternalPass(false), mProjectionState(NULL) {}
+//**************************************************************************
+// RenderObjectMgr
+class RenderProbeMgr : public RenderBinManager
+ typedef RenderBinManager Parent;
+ typedef GFXVertexPNTT FarFrustumQuadVert;
+ struct ReflectProbeMaterialInfo
+ ReflectProbeMatInstance *matInstance;
+ // { zNear, zFar, 1/zNear, 1/zFar }
+ MaterialParameterHandle *zNearFarInvNearFar;
+ // Far frustum plane (World Space)
+ MaterialParameterHandle *farPlane;
+ // Far frustum plane (View Space)
+ MaterialParameterHandle *vsFarPlane;
+ MaterialParameterHandle *negFarPlaneDotEye;
+ // Inverse View matrix
+ MaterialParameterHandle *invViewMat;
+ // Light Parameters
+ MaterialParameterHandle *probeLSPos;
+ MaterialParameterHandle *probeWSPos;
+ MaterialParameterHandle *attenuation;
+ MaterialParameterHandle *radius;
+ MaterialParameterHandle *useCubemap;
+ MaterialParameterHandle *cubemap;
+ MaterialParameterHandle *eyePosWorld;
+ MaterialParameterHandle *bbMin;
+ MaterialParameterHandle *bbMax;
+ MaterialParameterHandle *useSphereMode;
+ MaterialParameterHandle *shTerms[9];
+ MaterialParameterHandle *shConsts[5];
+ ReflectProbeMaterialInfo(const String &matName, const GFXVertexFormat *vertexFormat);
+ virtual ~ReflectProbeMaterialInfo();
+ void setViewParameters(const F32 zNear,
+ const F32 zFar,
+ const Point3F &eyePos,
+ const PlaneF &farPlane,
+ const PlaneF &_vsFarPlane,
+ const MatrixF &_inverseViewMatrix);
+ void setProbeParameters(const ProbeRenderInst *probe, const SceneRenderState* renderState, const MatrixF &worldViewOnly);
+ struct SkylightMaterialInfo
+ SkylightMatInstance *matInstance;
+ SkylightMaterialInfo(const String &matName, const GFXVertexFormat *vertexFormat);
+ virtual ~SkylightMaterialInfo();
+ void setSkylightParameters(const ProbeRenderInst *probe, const SceneRenderState* renderState, const MatrixF &worldViewOnly);
+ GFXVertexBufferHandle<FarFrustumQuadVert> mFarFrustumQuadVerts;
+ GFXVertexBufferHandle<GFXVertexPC> getSphereMesh(U32 &outNumPrimitives, GFXPrimitiveBufferHandle &outPrimitives);
+ // Convex geometry for lights
+ GFXVertexBufferHandle<GFXVertexPC> mSphereGeometry;
+ GFXPrimitiveBufferHandle mSphereIndices;
+ RenderProbeMgr();
+ RenderProbeMgr(RenderInstType riType, F32 renderOrder, F32 processAddOrder);
+ // RenderBinMgr
+ void _setupPerFrameParameters(const SceneRenderState *state);
+ virtual void addElement(RenderInst *inst);
+ virtual void render(SceneRenderState * state);
+ // ConsoleObject
+ DECLARE_CONOBJECT(RenderProbeMgr);
+ ReflectProbeMaterialInfo* mReflectProbeMaterial;
+ ReflectProbeMaterialInfo* _getReflectProbeMaterial();
+ SkylightMaterialInfo* mSkylightMaterial;
+ SkylightMaterialInfo* _getSkylightMaterial();
+ // Add a reflection probe to the bin
+ void addSkylightProbe(ProbeRenderInst *probeInfo);
+ void addSphereReflectionProbe(ProbeRenderInst *probeInfo);
+ void addConvexReflectionProbe(ProbeRenderInst *probeInfo);
+#endif // RENDER_PROBE_MGR_H
@@ -670,4 +670,29 @@ public:
virtual String getName() { return "Hardware Skinning"; }
+/// Reflection Probes
+class ReflectionProbeFeatGLSL : public ShaderFeatureGLSL
+ virtual void processVert(Vector<ShaderComponent*> &componentList,
+ const MaterialFeatureData &fd) {}
+ virtual void processPix(Vector<ShaderComponent*> &componentList,
+ virtual Resources getResources(const MaterialFeatureData &fd) {
+ return Resources();
+ // Sets textures and texture flags for current pass
+ virtual void setTexData(Material::StageData &stageDat,
+ const MaterialFeatureData &fd,
+ RenderPassData &passData,
+ U32 &texIndex) {}
+ virtual String getName()
+ return "Reflection Probes";
#endif // _SHADERGEN_GLSL_SHADERFEATUREGLSL_H_
@@ -2933,3 +2933,225 @@ void HardwareSkinningFeatureHLSL::processVert( Vector<ShaderComponent*> &compo
output = meta;
+//****************************************************************************
+// ReflectionProbeFeatHLSL
+ReflectionProbeFeatHLSL::ReflectionProbeFeatHLSL()
+ : mDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/lighting.hlsl"))
+ addDependency(&mDep);
+void ReflectionProbeFeatHLSL::processPix(Vector<ShaderComponent*> &componentList,
+ const MaterialFeatureData &fd)
+ // Skip out on realtime lighting if we don't have a normal
+ // or we're doing some sort of baked lighting.
+ // TODO: We can totally detect for this in the material
+ // feature setup... we should move it out of here!
+ if (fd.features[MFT_LightMap] || fd.features[MFT_ToneMap] || fd.features[MFT_VertLit])
+ ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>(componentList[C_CONNECTOR]);
+ MultiLine *meta = new MultiLine;
+ // Look for a wsNormal or grab it from the connector.
+ Var *wsNormal = (Var*)LangElement::find("wsNormal");
+ if (!wsNormal)
+ wsNormal = connectComp->getElement(RT_TEXCOORD);
+ wsNormal->setName("wsNormal");
+ wsNormal->setStructName("IN");
+ wsNormal->setType("float3");
+ // If we loaded the normal its our responsibility
+ // to normalize it... the interpolators won't.
+ // Note we cast to half here to get partial precision
+ // optimized code which is an acceptable loss of
+ // precision for normals and performs much better
+ // on older Geforce cards.
+ meta->addStatement(new GenOp(" @ = normalize( half3( @ ) );\r\n", wsNormal, wsNormal));
+ // Now the wsPosition and wsView.
+ Var *wsPosition = getInWsPosition(componentList);
+ Var *wsView = getWsView(wsPosition, meta);
+ Var *metalness = (Var*)LangElement::find("metalness");
+ Var *smoothness = (Var*)LangElement::find("smoothness");
+ if (!fd.features[MFT_SpecularMap])
+ if (!metalness)
+ metalness = new Var("metalness", "float");
+ metalness->uniform = true;
+ metalness->constSortPos = cspPotentialPrimitive;
+ if (!smoothness)
+ smoothness = new Var("smoothness", "float");
+ smoothness->uniform = true;
+ smoothness->constSortPos = cspPotentialPrimitive;
+ Var *albedo = (Var*)LangElement::find(getOutputTargetVarName(ShaderFeature::DefaultTarget));
+ //Reflection Probe WIP
+ Var *inProbePos = new Var("inProbePos", "float3");
+ inProbePos->arraySize = 4;
+ inProbePos->uniform = true;
+ inProbePos->constSortPos = cspPotentialPrimitive;
+ Var *inProbeRadius = new Var("inProbeRadius", "float");
+ inProbeRadius->arraySize = 4;
+ inProbeRadius->uniform = true;
+ inProbeRadius->constSortPos = cspPotentialPrimitive;
+ Var *inProbeBoxMin = new Var("inProbeBoxMin", "float3");
+ inProbeBoxMin->arraySize = 4;
+ inProbeBoxMin->uniform = true;
+ inProbeBoxMin->constSortPos = cspPotentialPrimitive;
+ Var *inProbeBoxMax = new Var("inProbeBoxMax", "float3");
+ inProbeBoxMax->arraySize = 4;
+ inProbeBoxMax->uniform = true;
+ inProbeBoxMax->constSortPos = cspPotentialPrimitive;
+ Var *inProbeIsSphere = new Var("inProbeIsSphere", "float");
+ inProbeIsSphere->arraySize = 4;
+ inProbeIsSphere->uniform = true;
+ inProbeIsSphere->constSortPos = cspPotentialPrimitive;
+ Var *inProbeLocalPos = new Var("inProbeLocalPos", "float3");
+ inProbeLocalPos->arraySize = 4;
+ inProbeLocalPos->uniform = true;
+ inProbeLocalPos->constSortPos = cspPotentialPrimitive;
+ Var *inProbeCubemap = new Var("inProbeCubemap", "SamplerState");
+ //inProbeCubemap->arraySize = 4;
+ inProbeCubemap->uniform = true;
+ inProbeCubemap->sampler = true;
+ inProbeCubemap->constNum = Var::getTexUnitNum(); // used as texture unit num here
+ Var *inProbeCubemapTex = new Var("inProbeCubemapTex", "TextureCube");
+ //inProbeCubemapTex->arraySize = 4;
+ inProbeCubemapTex->uniform = true;
+ inProbeCubemapTex->texture = true;
+ inProbeCubemapTex->constNum = inProbeCubemap->constNum;
+ //Var *nDotL = new Var("nDotL", "float3");
+ //meta->addStatement(new GenOp(" @ = abs(dot(@,@);\r\n", new DecOp(nDotL), wsView, wsNormal));
+ Var *probeVec = new Var("probeVec", "float3");
+ meta->addStatement(new GenOp(" @ = @[0] - @;\r\n", new DecOp(probeVec), inProbePos, wsPosition));
+ Var *nDotL = new Var("nDotL", "float");
+ meta->addStatement(new GenOp(" @ = abs(dot(@, @));\r\n", new DecOp(nDotL), probeVec, wsNormal));
+ meta->addStatement(new GenOp(" \r\n"));
+ Var *reflectDir = new Var("reflectDir", "float3");
+ meta->addStatement(new GenOp(" @ = reflect(-float4(@,0),float4(@,@)).xyz;\r\n", new DecOp(reflectDir), wsView, wsNormal, nDotL));
+ Var *nrDir = new Var("nrDir", "float3");
+ meta->addStatement(new GenOp(" @ = normalize(@);\r\n", new DecOp(nrDir), reflectDir));
+ Var *rbmax = new Var("rbmax", "float3");
+ meta->addStatement(new GenOp(" @ = (@[0] - @) / @;\r\n", new DecOp(rbmax), inProbeBoxMax, wsPosition, nrDir));
+ Var *rbmin = new Var("rbmin", "float3");
+ meta->addStatement(new GenOp(" @ = (@[0] - @) / @;\r\n", new DecOp(rbmin), inProbeBoxMin, wsPosition, nrDir));
+ Var *rbMinMax = new Var("rbMinMax", "float3");
+ meta->addStatement(new GenOp(" @ = (@ > 0.0) ? @ : @;\r\n", new DecOp(rbMinMax), nrDir, rbmax, rbmin));
+ Var *fa = new Var("fa", "float3");
+ meta->addStatement(new GenOp(" @ = min(min(@.x,@.y),@.z);\r\n", new DecOp(fa), rbMinMax, rbMinMax, rbMinMax));
+ meta->addStatement(new GenOp("/* if (dot( @, @ ) < 0.0f)\r\n", probeVec, wsNormal));
+ meta->addStatement(new GenOp(" clip(@); */\r\n", fa));
+ Var *posOnBox = new Var("posOnBox", "float3");
+ meta->addStatement(new GenOp(" @ = @ + @ * @;\r\n", new DecOp(posOnBox), wsPosition, nrDir, fa));
+ meta->addStatement(new GenOp(" @ = @ - @[0];\r\n", reflectDir, posOnBox, inProbePos));
+ Var *probeColor = new Var("wipProbeColor", "float3");
+ Var *probeMip = new Var("probeMip", "float");
+ meta->addStatement(new GenOp(" @ = min((1.0 - @)*11.0 + 1.0, 8.0);\r\n", new DecOp(probeMip), smoothness));
+ meta->addStatement(new GenOp(" @ = @.SampleLevel(@, @, @).rgb;\r\n", new DecOp(probeColor), inProbeCubemapTex, inProbeCubemap, reflectDir, probeMip));
+ //meta->addStatement(new GenOp(" @ = @.rgb;\r\n", new DecOp(probeColor), inProbeTestColor));
+ Var *FRESNEL_BIAS = new Var("FRESNEL_BIAS", "float");
+ meta->addStatement(new GenOp(" @ = 0.1;\r\n", new DecOp(FRESNEL_BIAS)));
+ Var *FRESNEL_POWER = new Var("FRESNEL_POWER", "float");
+ meta->addStatement(new GenOp(" @ = 1;\r\n", new DecOp(FRESNEL_POWER)));
+ Var *angle = new Var("angle", "float");
+ meta->addStatement(new GenOp(" @ = saturate(dot(@, @));\r\n", new DecOp(angle), wsView, wsNormal));
+ meta->addStatement(new GenOp("\r\n"));
+ if (metalness)
+ Var *dColor = new Var("difColor", "float3");
+ Var *reflectColor = new Var("reflctColor", "float3");
+ meta->addStatement(new GenOp(" @ = @.rgb - (@.rgb * @);\r\n", new DecOp(dColor), albedo, albedo, metalness));
+ meta->addStatement(new GenOp(" @ = @; //@.rgb*(@).rgb*@;\r\n", new DecOp(reflectColor), probeColor, albedo, probeColor, metalness));
+ meta->addStatement(new GenOp(" @.rgb = simpleFresnel(@, @, @, @, @, @);\r\n", albedo, dColor, reflectColor, metalness, angle, FRESNEL_BIAS, FRESNEL_POWER));
+ //else if (lerpVal)
+ // meta->addStatement(new GenOp(" @ *= float4(@.rgb*@.a, @.a);\r\n", targ, texCube, lerpVal, targ));
+ meta->addStatement(new GenOp(" @.rgb = simpleFresnel(@.rgb, @, 0, @, @, @));\r\n", albedo, albedo, probeColor, angle, FRESNEL_BIAS, FRESNEL_POWER));
+ output = meta;
+ShaderFeature::Resources ReflectionProbeFeatHLSL::getResources(const MaterialFeatureData &fd)
+ Resources res;
+ //res.numTex = 4;
+ //res.numTexReg = 4;
+ res.numTex = 4;
+ res.numTexReg = 4;
+ return res;
+void ReflectionProbeFeatHLSL::setTexData(Material::StageData &stageDat,
+ const MaterialFeatureData &stageFeatures,
+ U32 &texIndex)
+ if (stageFeatures.features[MFT_ReflectionProbes])
+ // assuming here that it is a scenegraph cubemap
+ passData.mSamplerNames[texIndex] = "inProbeCubemap";
+ passData.mTexType[texIndex++] = Material::SGCube;
@@ -672,4 +672,30 @@ public:
+class ReflectionProbeFeatHLSL : public ShaderFeatureHLSL
+ ShaderIncludeDependency mDep;
+ ReflectionProbeFeatHLSL();
+ const MaterialFeatureData &fd);
+ virtual Resources getResources(const MaterialFeatureData &fd);
+ U32 &texIndex);
#endif // _SHADERGEN_HLSL_SHADERFEATUREHLSL_H_
@@ -64,6 +64,7 @@ void _initShaderGenHLSL( ShaderGen *shaderGen )
FEATUREMGR->registerFeature( MFT_DetailMap, new DetailFeatHLSL );
FEATUREMGR->registerFeature( MFT_StaticCubemap, new NamedFeatureHLSL( "Static Cubemap" ) );
FEATUREMGR->registerFeature( MFT_CubeMap, new ReflectCubeFeatHLSL );
+ FEATUREMGR->registerFeature( MFT_ReflectionProbes, new ReflectionProbeFeatHLSL);
FEATUREMGR->registerFeature( MFT_InvertSmoothness, new NamedFeatureHLSL( "Roughest = 1.0" ) );
FEATUREMGR->registerFeature( MFT_IsTranslucent, new NamedFeatureHLSL( "Translucent" ) );
@@ -68,6 +68,15 @@ const String ShaderGenVars::specularColor("$specularColor");
const String ShaderGenVars::smoothness("$smoothness");
const String ShaderGenVars::metalness("$metalness");
+//Reflection Probes
+const String ShaderGenVars::probePosition("$inProbePos");
+const String ShaderGenVars::probeRadius("$inProbeRadius");
+const String ShaderGenVars::probeBoxMin("$inProbeBoxMin");
+const String ShaderGenVars::probeBoxMax("$inProbeBoxMax");
+const String ShaderGenVars::probeLocalPos("$inProbeLocalPos");
+const String ShaderGenVars::probeIsSphere("$inProbeIsSphere");
+const String ShaderGenVars::probeCubemap("$inProbeCubemap");
// These are ignored by the D3D layers.
const String ShaderGenVars::fogMap("$fogMap");
const String ShaderGenVars::dlightMap("$dlightMap");
@@ -80,6 +80,15 @@ struct ShaderGenVars
const static String specularColor;
const static String smoothness;
const static String metalness;
+ const static String probePosition;
+ const static String probeRadius;
+ const static String probeBoxMin;
+ const static String probeBoxMax;
+ const static String probeLocalPos;
+ const static String probeIsSphere;
+ const static String probeCubemap;
// Textures
const static String fogMap;
@@ -49,6 +49,7 @@ namespace
FEATUREMGR->registerFeature( MFT_TerrainLightMap, new TerrainLightMapFeatGLSL );
FEATUREMGR->registerFeature( MFT_TerrainSideProject, new NamedFeatureGLSL( "Terrain Side Projection" ) );
FEATUREMGR->registerFeature( MFT_TerrainAdditive, new TerrainAdditiveFeatGLSL );
+ FEATUREMGR->registerFeature( MFT_TerrainCompositeMap, new TerrainCompositeMapFeatGLSL );
FEATUREMGR->registerFeature( MFT_DeferredTerrainBlankInfoMap, new TerrainBlankInfoMapFeatGLSL );
@@ -141,6 +142,24 @@ Var* TerrainFeatGLSL::_getNormalMapTex()
return normalMap;
+Var* TerrainFeatGLSL::_getCompositeMapTex()
+ String name(String::ToString("compositeMap%d", getProcessIndex()));
+ Var *compositeMap = (Var*)LangElement::find(name);
+ if (!compositeMap)
+ compositeMap = new Var;
+ compositeMap->setType("sampler2D");
+ compositeMap->setName(name);
+ compositeMap->uniform = true;
+ compositeMap->sampler = true;
+ compositeMap->constNum = Var::getTexUnitNum();
+ return compositeMap;
Var* TerrainFeatGLSL::_getDetailIdStrengthParallax()
String name( String::ToString( "detailIdStrengthParallax%d", getProcessIndex() ) );
@@ -203,7 +222,7 @@ void TerrainBaseMapFeatGLSL::processVert( Vector<ShaderComponent*> &componentLis
// So instead i fixed this by flipping the base and detail
// coord y scale to compensate when rendering.
//
- meta->addStatement( new GenOp( " @ = @.xyz * float3( @, @, -@ );\r\n",
+ meta->addStatement( new GenOp( " @ = @.xyz * vec3( @, @, -@ );\r\n",
new DecOp( inTex ), inPos, oneOverTerrainSize, oneOverTerrainSize, oneOverTerrainSize ) );
@@ -223,7 +242,7 @@ void TerrainBaseMapFeatGLSL::processVert( Vector<ShaderComponent*> &componentLis
Var *inNormal = (Var*)LangElement::find( "normal" );
meta->addStatement(
- new GenOp( " @.z = pow( abs( dot( normalize( float3( @.x, @.y, 0 ) ), float3( 0, 1, 0 ) ) ), 10.0 );\r\n",
+ new GenOp( " @.z = pow( abs( dot( normalize( vec3( @.x, @.y, 0 ) ), vec3( 0, 1, 0 ) ) ), 10.0 );\r\n",
outTex, inNormal, inNormal ) );
@@ -239,8 +258,8 @@ void TerrainBaseMapFeatGLSL::processVert( Vector<ShaderComponent*> &componentLis
Var *inTangentZ = getVertTexCoord( "tcTangentZ" );
Var *inTanget = new Var( "T", "vec3" );
Var *squareSize = _getUniformVar( "squareSize", "float", cspPass );
- meta->addStatement( new GenOp( " @ = normalize( float3( @, 0, @ ) );\r\n",
- new DecOp( inTanget ), squareSize, inTangentZ ) );
+ meta->addStatement( new GenOp( " @ = normalize( vec3( @, 0, @ ) );\r\n",
+ new DecOp( inTanget ), squareSize, inTangentZ ) );
void TerrainBaseMapFeatGLSL::processPix( Vector<ShaderComponent*> &componentList,
@@ -334,7 +353,7 @@ void TerrainDetailMapFeatGLSL::processVert( Vector<ShaderComponent*> &component
outNegViewTS->setName( "outNegViewTS" );
outNegViewTS->setStructName( "OUT" );
outNegViewTS->setType( "vec3" );
- meta->addStatement( new GenOp( " @ = tMul( @, float3( @ - @.xyz ) );\r\n",
+ meta->addStatement( new GenOp( " @ = tMul( @, vec3( @ - @.xyz ) );\r\n",
outNegViewTS, objToTangentSpace, eyePos, inPos ) );
@@ -490,7 +509,7 @@ void TerrainDetailMapFeatGLSL::processPix( Vector<ShaderComponent*> &component
// create color var
outColor = new Var;
- outColor->setType("float4");
+ outColor->setType("vec4");
outColor->setName("col");
outColor->setStructName("OUT");
meta->addStatement(new GenOp(" @;\r\n", outColor));
@@ -500,7 +519,7 @@ void TerrainDetailMapFeatGLSL::processPix( Vector<ShaderComponent*> &component
if (!detailColor)
detailColor = new Var;
- detailColor->setType("float4");
+ detailColor->setType("vec4");
detailColor->setName("detailColor");
meta->addStatement(new GenOp(" @;\r\n", new DecOp(detailColor)));
@@ -1106,6 +1125,176 @@ void TerrainAdditiveFeatGLSL::processPix( Vector<ShaderComponent*> &componentLis
//standard matInfo map contains data of the form .r = bitflags, .g = (will contain AO),
//.b = specular strength, a= spec power.
+void TerrainCompositeMapFeatGLSL::processVert(Vector<ShaderComponent*> &componentList,
+ const S32 detailIndex = getProcessIndex();
+ // Grab incoming texture coords... the base map feature
+ // made sure this was created.
+ Var *inTex = (Var*)LangElement::find("texCoord");
+ AssertFatal(inTex, "The texture coord is missing!");
+ // Grab the input position.
+ Var *inPos = (Var*)LangElement::find("inPosition");
+ if (!inPos)
+ inPos = (Var*)LangElement::find("position");
+ // Get the object space eye position.
+ Var *eyePos = _getUniformVar("eyePos", "vec3", cspPotentialPrimitive);
+ // If we have parallax mapping then make sure we've sent
+ // the negative view vector to the pixel shader.
+ if (fd.features.hasFeature(MFT_TerrainParallaxMap) &&
+ !LangElement::find("outNegViewTS"))
+ // Get the object to tangent transform which
+ // will consume 3 output registers.
+ Var *objToTangentSpace = getOutObjToTangentSpace(componentList, meta, fd);
+ // Now use a single output register to send the negative
+ // view vector in tangent space to the pixel shader.
+ Var *outNegViewTS = connectComp->getElement(RT_TEXCOORD);
+ outNegViewTS->setName("outNegViewTS");
+ outNegViewTS->setStructName("OUT");
+ outNegViewTS->setType("vec3");
+ meta->addStatement(new GenOp(" @ = @ * vec3( @ - @.xyz );\r\n",
+ outNegViewTS, objToTangentSpace, eyePos, inPos));
+ // Get the distance from the eye to this vertex.
+ Var *dist = (Var*)LangElement::find("dist");
+ if (!dist)
+ dist = new Var;
+ dist->setType("float");
+ dist->setName("dist");
+ meta->addStatement(new GenOp(" @ = distance( @.xyz, @ );\r\n",
+ new DecOp(dist), inPos, eyePos));
+ // grab connector texcoord register
+ Var *outTex = (Var*)LangElement::find(String::ToString("detCoord%d", detailIndex));
+ if (outTex == NULL)
+ outTex = connectComp->getElement(RT_TEXCOORD);
+ outTex->setName(String::ToString("detCoord%d", detailIndex));
+ outTex->setStructName("OUT");
+ outTex->setType("vec4");
+ // Get the detail scale and fade info.
+ Var *detScaleAndFade = (Var*)LangElement::find(String::ToString("detailScaleAndFade%d", detailIndex));
+ if (detScaleAndFade == NULL)
+ detScaleAndFade->setType("vec4");
+ detScaleAndFade->setName(String::ToString("detailScaleAndFade%d", detailIndex));
+ detScaleAndFade->uniform = true;
+ detScaleAndFade->constSortPos = cspPotentialPrimitive;
+ // Setup the detail coord.
+ // NOTE: You see here we scale the texture coord by 'xyx'
+ // to generate the detail coord. This y is here because
+ // its scale is flipped to correct for the non negative y
+ // in texCoord.
+ // See TerrainBaseMapFeatGLSL::processVert().
+ meta->addStatement(new GenOp(" @.xyz = @ * @.xyx;\r\n", outTex, inTex, detScaleAndFade));
+ // And sneak the detail fade thru the w detailCoord.
+ meta->addStatement(new GenOp(" @.w = clamp( ( @.z - @ ) * @.w, 0.0, 1.0 );\r\n",
+ outTex, detScaleAndFade, dist, detScaleAndFade));
+U32 TerrainCompositeMapFeatGLSL::getOutputTargets(const MaterialFeatureData &fd) const
+ return fd.features[MFT_isDeferred] ? ShaderFeature::RenderTarget2 : ShaderFeature::RenderTarget1;
+void TerrainCompositeMapFeatGLSL::processPix(Vector<ShaderComponent*> &componentList,
+ /// Get the texture coord.
+ Var *inDet = _getInDetailCoord(componentList);
+ Var *inTex = getVertTexCoord("texCoord");
+ const S32 compositeIndex = getProcessIndex();
+ Var *compositeMap = _getCompositeMapTex();
+ // Sample the normal map.
+ // We take two normal samples and lerp between them for
+ // side projection layers... else a single sample.
+ LangElement *texOp;
+ if (fd.features.hasFeature(MFT_TerrainSideProject, compositeIndex))
+ texOp = new GenOp("lerp( tex2D( @, @.yz ), tex2D( @, @.xz ), @.z )",
+ compositeMap, inDet, compositeMap, inDet, inTex);
+ texOp = new GenOp("tex2D(@, @.xy)", compositeMap, inDet);
+ // search for material var
+ Var *material;
+ OutputTarget targ = RenderTarget1;
+ if (fd.features[MFT_isDeferred])
+ targ = RenderTarget2;
+ material = (Var*)LangElement::find(getOutputTargetVarName(targ));
+ MultiLine * meta = new MultiLine;
+ if (!material)
+ // create color var
+ material = new Var;
+ material->setType("fragout");
+ material->setName(getOutputTargetVarName(targ));
+ material->setStructName("OUT");
+ Var *detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", compositeIndex));
+ AssertFatal(detailBlend, "The detail blend is missing!");
+ String matinfoName(String::ToString("matinfoCol%d", compositeIndex));
+ Var *matinfoCol = new Var(matinfoName, "vec3");
+ Var *priorComp = (Var*)LangElement::find(String::ToString("matinfoCol%d", compositeIndex - 1));
+ if (priorComp)
+ meta->addStatement(new GenOp(" @ = @.grb*@;\r\n", new DecOp(matinfoCol), texOp, detailBlend));
+ meta->addStatement(new GenOp(" @.gba += @;\r\n", material, matinfoCol));
+ meta->addStatement(new GenOp(" @ = lerp(vec3(1,0,0),@.grb,@);\r\n", new DecOp(matinfoCol), texOp, detailBlend));
+ meta->addStatement(new GenOp(" @ = vec4(0.0,@);\r\n", material, matinfoCol));
+ShaderFeature::Resources TerrainCompositeMapFeatGLSL::getResources(const MaterialFeatureData &fd)
+ res.numTex = 1;
+ res.numTexReg += 1;
//here, it's merely a cutout for now, so that lightmapping (target3) doesn't get mangled.
//we'll most likely revisit that later. possibly several ways...
@@ -1136,7 +1325,7 @@ void TerrainBlankInfoMapFeatGLSL::processPix(Vector<ShaderComponent*> &component
material->setStructName("OUT");
- meta->addStatement(new GenOp(" @ = float4(0.0,0.0,0.0,0.0001);\r\n", material));
+ meta->addStatement(new GenOp(" @ = vec4(0.0,1.0,0.0,0.0001);\r\n", material));
@@ -45,7 +45,9 @@ public:
Var* _getInMacroCoord(Vector<ShaderComponent*> &componentList );
Var* _getNormalMapTex();
+ Var* _getCompositeMapTex();
static Var* _getUniformVar( const char *name, const char *type, ConstantSortPosition csp );
Var* _getDetailIdStrengthParallax();
@@ -160,6 +162,22 @@ public:
virtual String getName() { return "Terrain Additive"; }
+class TerrainCompositeMapFeatGLSL : public TerrainFeatGLSL
+ virtual U32 getOutputTargets(const MaterialFeatureData &fd) const;
+ virtual String getName() { return "Composite Matinfo map"; }
class TerrainBlankInfoMapFeatGLSL : public ShaderFeatureGLSL
FEATUREMGR->registerFeature( MFT_TerrainLightMap, new TerrainLightMapFeatHLSL );
FEATUREMGR->registerFeature( MFT_TerrainSideProject, new NamedFeatureHLSL( "Terrain Side Projection" ) );
FEATUREMGR->registerFeature( MFT_TerrainAdditive, new TerrainAdditiveFeatHLSL );
+ FEATUREMGR->registerFeature( MFT_TerrainCompositeMap, new TerrainCompositeMapFeatHLSL );
FEATUREMGR->registerFeature( MFT_DeferredTerrainBlankInfoMap, new TerrainBlankInfoMapFeatHLSL );
@@ -140,6 +141,24 @@ Var* TerrainFeatHLSL::_getNormalMapTex()
+Var* TerrainFeatHLSL::_getCompositeMapTex()
+ compositeMap->setType("SamplerState");
Var* TerrainFeatHLSL::_getDetailIdStrengthParallax()
@@ -1116,6 +1135,183 @@ void TerrainAdditiveFeatHLSL::processPix( Vector<ShaderComponent*> &componentLis
+void TerrainCompositeMapFeatHLSL::processVert(Vector<ShaderComponent*> &componentList,
+ Var *eyePos = _getUniformVar("eyePos", "float3", cspPotentialPrimitive);
+ outNegViewTS->setType("float3");
+ meta->addStatement(new GenOp(" @ = mul( @, float3( @ - @.xyz ) );\r\n",
+ outTex->setType("float4");
+ detScaleAndFade->setType("float4");
+ // See TerrainBaseMapFeatHLSL::processVert().
+U32 TerrainCompositeMapFeatHLSL::getOutputTargets(const MaterialFeatureData &fd) const
+void TerrainCompositeMapFeatHLSL::processPix(Vector<ShaderComponent*> &componentList,
+ String name(String::ToString("compositeMapTex%d", getProcessIndex()));
+ Var *compositeMapTex = (Var*)LangElement::find(name);
+ if (!compositeMapTex)
+ compositeMapTex = new Var;
+ compositeMapTex->setName(String::ToString("compositeMapTex%d", getProcessIndex()));
+ compositeMapTex->setType("Texture2D");
+ compositeMapTex->uniform = true;
+ compositeMapTex->texture = true;
+ compositeMapTex->constNum = compositeMap->constNum;
+ texOp = new GenOp("lerp( @.Sample( @, @.yz ), @.Sample( @, @.xz ), @.z )",
+ compositeMapTex, compositeMap, inDet, compositeMapTex, compositeMap, inDet, inTex);
+ texOp = new GenOp("@.Sample(@, @.xy)", compositeMapTex, compositeMap, inDet);
+ Var *matinfoCol = new Var(matinfoName, "float3");
+ meta->addStatement(new GenOp(" @ = lerp(float3(1,0,0),@.grb,@);\r\n", new DecOp(matinfoCol), texOp, detailBlend));
+ meta->addStatement(new GenOp(" @ = float4(0.0,@);\r\n", material, matinfoCol));
+ShaderFeature::Resources TerrainCompositeMapFeatHLSL::getResources(const MaterialFeatureData &fd)
@@ -46,6 +46,7 @@ public:
@@ -161,6 +162,22 @@ public:
+class TerrainCompositeMapFeatHLSL : public TerrainFeatHLSL
class TerrainBlankInfoMapFeatHLSL : public TerrainFeatHLSL
@@ -59,7 +59,8 @@ Vector<String> _initSamplerNames()
samplerNames.push_back(avar("$normalMap%d",i));
samplerNames.push_back(avar("$detailMap%d",i));
- samplerNames.push_back(avar("$macroMap%d",i));
+ samplerNames.push_back(avar("$macroMap%d", i));
+ samplerNames.push_back(avar("$compositeMap%d", i));
return samplerNames;
@@ -149,6 +150,19 @@ void TerrainCellMaterial::_updateDefaultAnisotropy()
desc.samplers[sampler].minFilter = GFXTextureFilterLinear;
+ if (matInfo->compositeTexConst->isValid())
+ const S32 sampler = matInfo->compositeTexConst->getSamplerRegister();
+ if (maxAnisotropy > 1)
+ desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic;
+ desc.samplers[sampler].maxAnisotropy = maxAnisotropy;
+ desc.samplers[sampler].minFilter = GFXTextureFilterLinear;
} // for ( U32 m=0; m < pass.materials.size(); m++ )
// Set the updated stateblock.
@@ -424,6 +438,14 @@ bool TerrainCellMaterial::_createPass( Vector<MaterialInfo*> *materials,
features.addFeature(MFT_isDeferred, featureIndex);
features.addFeature( MFT_TerrainDetailMap, featureIndex );
+ if (!(mat->getCompositeMap().isEmpty()))
+ if (deferredMat)
+ features.addFeature(MFT_isDeferred, featureIndex);
+ features.addFeature(MFT_TerrainCompositeMap, featureIndex);
+ features.removeFeature(MFT_DeferredTerrainBlankInfoMap);
pass->materials.push_back( (*materials)[i] );
normalMaps.increment();
@@ -496,7 +518,7 @@ bool TerrainCellMaterial::_createPass( Vector<MaterialInfo*> *materials,
// isn't fooled into thinking there is a real bug. That is until
// we get down to a single material. If a single material case
// fails it means it cannot generate any passes at all!
- const bool logErrors = matCount == 1;
+ const bool logErrors = true;// matCount == 1;
GFXShader::setLogging( logErrors, true );
pass->shader = SHADERGEN->getShader( featureData, getGFXVertexFormat<TerrVertex>(), NULL, mSamplerNames );
@@ -613,6 +635,27 @@ bool TerrainCellMaterial::_createPass( Vector<MaterialInfo*> *materials,
&GFXStaticTextureProfile, "TerrainCellMaterial::_createPass() - DetailMap" );
+ matInfo->compositeTexConst = pass->shader->getShaderConstHandle(avar("$compositeMap%d", i));
+ matInfo->compositeTex.set(matInfo->mat->getCompositeMap(),
+ &GFXStaticTextureProfile, "TerrainCellMaterial::_createPass() - CompositeMap");
+ Con::errorf("sampler=%i", sampler);
+ desc.samplers[sampler] = GFXSamplerStateDesc::getWrapLinear();
+ desc.samplers[sampler].magFilter = GFXTextureFilterLinear;
+ desc.samplers[sampler].mipFilter = GFXTextureFilterLinear;
matInfo->macroInfoVConst = pass->shader->getShaderConstHandle( avar( "$macroScaleAndFade%d", i ) );
matInfo->macroInfoPConst = pass->shader->getShaderConstHandle( avar( "$macroIdStrengthParallax%d", i ) );
@@ -810,6 +853,8 @@ bool TerrainCellMaterial::setupPass( const SceneRenderState *state,
GFX->setTexture( matInfo->macroTexConst->getSamplerRegister(), matInfo->macroTex );
if ( matInfo->normalTexConst->isValid() )
GFX->setTexture( matInfo->normalTexConst->getSamplerRegister(), matInfo->normalTex );
+ if ( matInfo->compositeTexConst->isValid() )
+ GFX->setTexture( matInfo->compositeTexConst->getSamplerRegister(), matInfo->compositeTex );
pass.consts->setSafe( pass.layerSizeConst, (F32)mTerrain->mLayerTex.getWidth() );
@@ -837,7 +882,7 @@ bool TerrainCellMaterial::setupPass( const SceneRenderState *state,
pass.lightParamsConst->isValid() )
if ( !mLightInfoTarget )
- mLightInfoTarget = NamedTexTarget::find( "directLighting" );
+ mLightInfoTarget = NamedTexTarget::find( "diffuseLighting" );
GFXTextureObject *texObject = mLightInfoTarget->getTexture();
@@ -77,6 +77,9 @@ protected:
GFXShaderConstHandle *normalTexConst;
GFXTexHandle normalTex;
+ GFXShaderConstHandle *compositeTexConst;
+ GFXTexHandle compositeTex;
GFXShaderConstHandle *detailInfoVConst;
GFXShaderConstHandle *detailInfoPConst;
@@ -36,3 +36,4 @@ ImplementFeatureType( MFT_TerrainSideProject, MFG_Texture, 106.0f, false );
ImplementFeatureType( MFT_TerrainAdditive, MFG_PostProcess, 999.0f, false );
//Deferred Shading
ImplementFeatureType( MFT_DeferredTerrainBlankInfoMap, MFG_Texture, 104.1f, false);
+ImplementFeatureType( MFT_TerrainCompositeMap, MFG_Texture, 104.2f, false);
@@ -36,6 +36,7 @@ DeclareFeatureType( MFT_TerrainLightMap );
DeclareFeatureType( MFT_TerrainSideProject );
DeclareFeatureType( MFT_TerrainAdditive );
+DeclareFeatureType( MFT_TerrainCompositeMap );
DeclareFeatureType( MFT_DeferredTerrainBlankInfoMap );
@@ -97,6 +97,7 @@ void TerrainMaterial::initPersistFields()
addField( "parallaxScale", TypeF32, Offset( mParallaxScale, TerrainMaterial ), "Used to scale the height from the normal map to give some self "
"occlusion effect (aka parallax) to the terrain material" );
+ addField("compositeMap", TypeStringFilename, Offset(mCompositeMap, TerrainMaterial), "Composite map for the material");
Parent::initPersistFields();
// Gotta call this at least once or it won't get created!
@@ -49,6 +49,9 @@ protected:
///
FileName mDetailMap;
+ FileName mCompositeMap;
/// The size of the detail map in meters used
/// to generate the texture coordinates for the
/// detail and normal maps.
@@ -103,6 +106,8 @@ public:
const String& getMacroMap() const { return mMacroMap; }
+ const String& getCompositeMap() const { return mCompositeMap; }
F32 getDetailSize() const { return mDetailSize; }
F32 getDetailStrength() const { return mDetailStrength; }
@@ -28,8 +28,10 @@ singleton Material(Cheetah_Main)
specularPower[0] = "10";
translucentBlendOp = "None";
normalMap[0] = "art/shapes/Cheetah/Cheetah_N";
- specularMap[0] = "art/shapes/Cheetah/Cheetah_S";
castDynamicShadows = true;
+ diffuseColor[3] = "1 1 1 1";
+ specular0 = "0.9 0.9 0.9 1";
+ specularPower0 = "10";
singleton Material(Cheetah_TailLights)
@@ -24,19 +24,18 @@ singleton Material(Mat_Soldier_Main)
mapTo = "base_Soldier_Main";
- diffuseMap[0] = "Soldier_Dif.dds";
- normalMap[0] = "Soldier_N.dds";
- specularMap[0] = "Soldier_Spec.dds";
- diffuseColor[0] = "1 1 1 1";
- specular[0] = "0.9 0.9 0.9 1";
- specularPower[0] = 10;
- doubleSided = false;
- translucent = false;
- showFootprints = "0";
+ diffuseMap[0] = "art/shapes/actors/Soldier/Soldier_Dif.dds";
+ normalMap[0] = "art/shapes/actors/Soldier/Soldier_N.dds";
+ specularMap[0] = "art/shapes/actors/Soldier/Soldier_c";
+ FlipRB[0] = "1";
materialTag0 = "Player";
+ isSRGb[0] = "1";
+ roughness0 = "1";
+ FlipRB0 = "1";
+ pixelSpecular0 = "0";
singleton Material(Mat_Soldier_Dazzle)
@@ -110,7 +109,8 @@ singleton Material(Mat_Orange_Soldier_Main : Mat_Soldier_Main)
singleton Material(Mat_Red_Soldier_Main : Mat_Soldier_Main)
mapTo = "Red_Soldier_Main";
- diffuseMap[0] = "Soldier_Red_Dif.dds";
+ diffuseMap[0] = "art/shapes/actors/Soldier/Soldier_Red_Dif.dds";
singleton Material(Mat_Teal_Soldier_Main : Mat_Soldier_Main)
@@ -25,9 +25,6 @@ singleton Material(cube_GridMaterial)
mapTo = "GridMaterial";
- diffuseMap[0] = "grid";
- normalMap[0] = "";
- specularMap[0] = "";
diffuseColor[0] = "1 1 1 1";
specular[0] = "0.9 0.9 0.9 1";
@@ -38,6 +35,12 @@ singleton Material(cube_GridMaterial)
doubleSided = false;
translucent = false;
+ materialTag0 = "Miscellaneous";
+ smoothness[0] = "1";
+ metalness[0] = "1";
+ specularPower0 = "0.415939";
//--- cube.dae MATERIALS END ---
@@ -24,22 +24,33 @@ new Material(Structure_wall)
mapTo = "building01walls";
diffuseMap[0] = "art/shapes/station/building01walls";
+ normalMap[0] = "art/shapes/station/building01walls_n.dds";
+ specularMap[0] = "art/shapes/station/building01walls_c.dds";
+ effectColor[0] = "InvisibleBlack";
//emissive[0] = true;
-new Material(Structure_grid)
+new Material(Structure_plate)
- mapTo = "grid";
- diffuseMap[0] = "art/shapes/station/grid";
+ mapTo = "plate";
+ diffuseMap[0] = "art/shapes/station/plate";
+ normalMap[0] = "art/shapes/station/plate_n.dds";
+ specularMap[0] = "art/shapes/station/plate_c.dds";
-new Material(Structure_plate)
+new Material(Structure_grid)
- mapTo = "plate";
- diffuseMap[0] = "art/shapes/station/plate";
+ mapTo = "grid";
+ diffuseMap[0] = "art/shapes/station/grid";
+ normalMap[0] = "art/shapes/station/grid_n.dds";
+ specularMap[0] = "art/shapes/station/grid_c.dds";
@@ -151,6 +151,7 @@ new TerrainMaterial()
detailBrightness = "1";
Enabled = "1";
diffuseSize = "400";
+ compositeMap = "art/terrains/Example/snowtop";
// ----------------------------------------------------------------------------
@@ -51,8 +51,12 @@ singleton Material( Grid512_ForestGreenLines_Mat )
singleton Material( Grid512_Green_Mat )
mapTo = "Grid512_Green_Mat";
- diffuseMap[0] = "512_green";
+ diffuseMap[0] = "core/art/grids/512_green";
materialTag0 = "TestMaterial";
+ translucent = "0";
+ translucentBlendOp = "Add";
singleton Material( Grid512_Grey_Mat )
@@ -65,8 +69,10 @@ singleton Material( Grid512_Grey_Mat )
singleton Material( Grid512_GreyBase_Mat )
mapTo = "Grid512_GreyBase_Mat";
- diffuseMap[0] = "512_grey_base";
+ diffuseMap[0] = "core/art/grids/512_grey_base";
singleton Material( Grid512_Orange_Mat )
@@ -47,23 +47,56 @@ new ShaderData( AL_DeferredShader )
OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/deferredShadingP.glsl";
samplerNames[0] = "colorBufferTex";
- samplerNames[1] = "directLightingBuffer";
+ samplerNames[1] = "diffuseLightingBuffer";
samplerNames[2] = "matInfoTex";
- samplerNames[3] = "indirectLightingBuffer";
+ samplerNames[3] = "specularLightingBuffer";
samplerNames[4] = "deferredTex";
pixVersion = 2.0;
+new ShaderData( AL_ProbeShader )
+ DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl";
+ DXPixelShaderFile = "shaders/common/lighting/advanced/probeShadingP.hlsl";
+ OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl";
+ OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/probeShadingP.glsl";
+ samplerNames[0] = "colorBufferTex";
+ samplerNames[2] = "matInfoTex";
+ samplerNames[4] = "deferredTex";
+ pixVersion = 2.0;
+singleton PostEffect( AL_PreCapture )
+ renderTime = "PFXBeforeBin";
+ renderBin = "ProbeBin";
+ shader = AL_ProbeShader;
+ stateBlock = AL_DeferredShadingState;
+ texture[0] = "#color";
+ texture[1] = "#diffuseLighting";
+ texture[2] = "#matinfo";
+ texture[3] = "#specularLighting";
+ texture[4] = "#deferred";
+ target = "$backBuffer";
+ renderPriority = 10000;
+ allowReflectPass = true;
singleton PostEffect( AL_DeferredShading )
renderTime = "PFXAfterBin";
- renderBin = "SkyBin";
shader = AL_DeferredShader;
stateBlock = AL_DeferredShadingState;
texture[0] = "#color";
- texture[1] = "#directLighting";
texture[2] = "#matinfo";
- texture[3] = "#indirectLighting";
texture[4] = "#deferred";
target = "$backBuffer";
@@ -192,7 +225,7 @@ new ShaderData( AL_LightMapShader )
OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl";
OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/dbgLightMapVisualizeP.glsl";
- samplerNames[0] = "indirectLightingBuffer";
+ samplerNames[0] = "specularLightingBuffer";
@@ -200,7 +233,7 @@ singleton PostEffect( AL_LightMapVisualize )
shader = AL_LightMapShader;
stateBlock = AL_DefaultVisualizeState;
- texture[0] = "#indirectLighting";
+ texture[0] = "#specularLighting";
renderPriority = 9999;
@@ -149,7 +149,7 @@ new ShaderData( AL_LightColorVisualizeShader )
OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/dbgLightColorVisualizeP.glsl";
- samplerNames[0] = "directLightingBuffer";
+ samplerNames[0] = "diffuseLightingBuffer";
@@ -158,7 +158,7 @@ singleton PostEffect( AL_LightColorVisualize )
shader = AL_LightColorVisualizeShader;
- texture[0] = "#directLighting";
+ texture[0] = "#diffuseLighting";
@@ -184,16 +184,16 @@ new ShaderData( AL_LightSpecularVisualizeShader )
OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/dbgLightSpecularVisualizeP.glsl";
singleton PostEffect( AL_LightSpecularVisualize )
- shader = AL_LightSpecularVisualizeShader;
+ shader = AL_LightColorVisualizeShader;
@@ -86,11 +86,11 @@ new CustomMaterial( AL_VectorLightMaterial )
sampler["shadowMap"] = "$dynamiclight";
sampler["dynamicShadowMap"] = "$dynamicShadowMap";
sampler["ssaoMask"] = "#ssaoMask";
- sampler["lightBuffer"] = "#indirectLighting";
+ sampler["lightBuffer"] = "#specularLighting";
sampler["colorBuffer"] = "#color";
sampler["matInfoBuffer"] = "#matinfo";
- target = "directLighting";
+ target = "diffuseLighting";
pixVersion = 3.0;
@@ -163,11 +163,11 @@ new CustomMaterial( AL_PointLightMaterial )
sampler["cookieMap"] = "$dynamiclightmask";
@@ -202,11 +202,11 @@ new CustomMaterial( AL_SpotLightMaterial )
@@ -269,7 +269,124 @@ new CustomMaterial( AL_ParticlePointLightMaterial )
stateBlock = AL_ConvexLightState;
sampler["deferredBuffer"] = "#deferred";
+//Reflection probe Specular
+new ShaderData( ReflectionProbeShader )
+ DXVertexShaderFile = "shaders/common/lighting/advanced/convexGeometryV.hlsl";
+ DXPixelShaderFile = "shaders/common/lighting/advanced/reflectionProbeP.hlsl";
+ OGLVertexShaderFile = "shaders/common/lighting/advanced/gl/convexGeometryV.glsl";
+ OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/reflectionProbeP.glsl";
+ samplerNames[0] = "$deferredBuffer";
+ samplerNames[1] = "$matInfoBuffer";
+ pixVersion = 3.0;
+// Convex-geometry light states
+new GFXStateBlockData( AL_ProbeState )
+ blendDefined = true;
+ blendEnable = true;
+ blendSrc = GFXBlendOne;
+ blendDest = GFXBlendOne;
+ blendOp = GFXBlendOpAdd;
+ zDefined = true;
+ zEnable = true;
+ zWriteEnable = false;
+ zFunc = GFXCmpGreaterEqual;
+ samplersDefined = true;
+ samplerStates[0] = SamplerClampPoint; // G-buffer
+ mSamplerNames[0] = "deferredBuffer";
+ samplerStates[1] = SamplerClampLinear; // Shadow Map (Do not use linear, these are perspective projections)
+ mSamplerNames[1] = "matInfoBuffer";
+ cullDefined = true;
+ cullMode = GFXCullCW;
+ stencilDefined = true;
+ stencilEnable = true;
+ stencilFailOp = GFXStencilOpKeep;
+ stencilZFailOp = GFXStencilOpKeep;
+ stencilPassOp = GFXStencilOpKeep;
+ stencilFunc = GFXCmpLess;
+ stencilRef = 0;
+new CustomMaterial( ReflectionProbeMaterial )
+ shader = ReflectionProbeShader;
+ stateBlock = AL_ProbeState;
+ sampler["deferredBuffer"] = "#deferred";
+ sampler["matInfoBuffer"] = "#matinfo";
+//Skylight
+new ShaderData( IrradianceShader )
+ DXVertexShaderFile = "shaders/common/lighting/advanced/cubemapV.hlsl";
+ DXPixelShaderFile = "shaders/common/lighting/advanced/irradianceP.hlsl";
+ OGLVertexShaderFile = "shaders/common/lighting/advanced/gl/cubemapV.glsl";
+ OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/irradianceP.glsl";
+new ShaderData( PrefiterCubemapShader )
+ DXPixelShaderFile = "shaders/common/lighting/advanced/prefilterP.hlsl";
+ OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/prefilterP.glsl";
+new ShaderData( BRDFLookupShader )
+ DXPixelShaderFile = "shaders/common/lighting/advanced/brdfLookupP.hlsl";
+ OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/brdfLookupP.glsl";
+new ShaderData( SklyightShader )
+ DXPixelShaderFile = "shaders/common/lighting/advanced/skylightP.hlsl";
+ OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/skylightP.glsl";
+new CustomMaterial( SklyightMaterial )
+ shader = SklyightShader;
@@ -64,7 +64,7 @@ singleton CustomMaterial( BL_ProjectedShadowMaterial )
function onActivateBasicLM()
// If HDR is enabled... enable the special format token.
- if ( $platform !$= "macos" && HDRPostFx.isEnabled )
+ if ( HDRPostFx.isEnabled )
AL_FormatToken.enable();
// Create render pass for projected shadow.
@@ -51,7 +51,8 @@ function initRenderManager()
// meshes... but that causes issues in reflections.
DiffuseRenderPassManager.addManager( new RenderObjectMgr(SkyBin) { bintype = "Sky"; renderOrder = 0.1; processAddOrder = 0.1; } );
- //DiffuseRenderPassManager.addManager( new RenderVistaMgr() { bintype = "Vista"; renderOrder = 0.15; processAddOrder = 0.15; } );
+ DiffuseRenderPassManager.addManager( new RenderProbeMgr(ProbeBin) { bintype = "Probes"; renderOrder = 0.15; processAddOrder = 0.15; } );
+ //DiffuseRenderPassManager.addManager( new RenderVistaMgr() { bintype = "Vista"; renderOrder = 0.15; processAddOrder = 0.15; } );
DiffuseRenderPassManager.addManager( new RenderObjectMgr(BeginBin) { bintype = "Begin"; renderOrder = 0.2; processAddOrder = 0.2; } );
// Normal mesh rendering.
@@ -28,7 +28,7 @@ new SimGroup(MissionGroup) {
levelName = "A Probe Test";
new SkyBox(theSky) {
- Material = "DesertSkyMat";
+ Material = "sky_day_hdr";
drawBottom = "0";
fogBandHeight = "0";
position = "0 0 0";
@@ -37,48 +37,16 @@ new SimGroup(MissionGroup) {
canSave = "1";
canSaveDynamicFields = "1";
- new Sun(theSun) {
- azimuth = "290";
- elevation = "50";
- color = "1 1 1 1";
- ambient = "0.473532 0.473532 0.473532 1";
- brightness = "1";
- castShadows = "1";
- staticRefreshFreq = "250";
- dynamicRefreshFreq = "8";
- coronaEnabled = "1";
- coronaScale = "0.5";
- coronaTint = "1 1 1 1";
- coronaUseLightColor = "1";
- flareScale = "1";
- attenuationRatio = "0 1 1";
- shadowType = "PSSM";
- texSize = "1024";
- overDarkFactor = "3000 1500 750 250";
- shadowDistance = "100";
- shadowSoftness = "0.25";
- numSplits = "4";
- logWeight = "0.9";
- fadeStartDistance = "0";
- lastSplitTerrainOnly = "0";
- representedInLightmap = "0";
- shadowDarkenColor = "0 0 0 -1";
- includeLightmappedGeometryInShadow = "0";
+ new Skylight() {
+ enabled = "1";
+ StaticCubemap = "sky_day_hdr_cubemap";
rotation = "1 0 0 0";
scale = "1 1 1";
- bias = "0.1";
- Blur = "1";
- enabled = "1";
- height = "1024";
- lightBleedFactor = "0.8";
- minVariance = "0";
- pointShadowType = "PointShadowType_Paraboloid";
- shadowBox = "-100 -100 -100 100 100 100";
- splitFadeDistances = "1 1 1 1";
- width = "3072";
+ persistentId = "e4c73467-4089-11e8-b478-cd227cd60b8b";
+ reflectionPath = "levels/AProbeTest/probes/";
new SimGroup(PlayerDropPoints) {
@@ -117,13 +85,46 @@ new SimGroup(MissionGroup) {
+ new Sun() {
+ azimuth = "200";
+ elevation = "20";
+ color = "0.8 0.8 0.8 1";
+ ambient = "0.2 0.2 0.2 1";
+ brightness = "1";
+ castShadows = "1";
+ staticRefreshFreq = "250";
+ dynamicRefreshFreq = "8";
+ coronaEnabled = "1";
+ coronaMaterial = "Corona_Mat";
+ coronaScale = "0.5";
+ coronaTint = "1 1 1 1";
+ coronaUseLightColor = "1";
+ flareType = "SunFlareExample";
+ flareScale = "1";
+ attenuationRatio = "0 1 1";
+ shadowType = "PSSM";
+ texSize = "512";
+ overDarkFactor = "2000 1000 500 100";
+ shadowDistance = "400";
+ shadowSoftness = "0.15";
+ numSplits = "4";
+ logWeight = "0.91";
+ fadeStartDistance = "0";
+ lastSplitTerrainOnly = "0";
+ representedInLightmap = "0";
+ shadowDarkenColor = "0 0 0 -1";
+ includeLightmappedGeometryInShadow = "0";
+ position = "35.7492 65.5507 -15.5377";
+ rotation = "1 0 0 0";
+ scale = "1 1 1";
+ canSave = "1";
+ canSaveDynamicFields = "1";
+ direction = "1 1 -1";
new ReflectionProbe() {
+ enabled = "0";
ProbeShape = "Sphere";
radius = "10";
- Intensity = "1";
- IndirectLightMode = "Ambient Color";
- IndirectLight = "1 1 1 1";
ReflectionMode = "Baked Cubemap";
reflectionPath = "levels/probeTest/probes/";
Bake = "0";
@@ -134,6 +135,9 @@ new SimGroup(MissionGroup) {
persistentId = "8072e1be-2846-11e7-9f56-abd46b190c60";
GroundColor = "0.8 0.7 0.5 1";
+ IndirectLight = "1 1 1 1";
+ IndirectLightMode = "Spherical Harmonics";
+ Intensity = "1";
SkyColor = "0.5 0.5 1 1";
new ConvexShape() {
@@ -243,7 +247,7 @@ new SimGroup(MissionGroup) {
new PointLight() {
- isEnabled = "1";
+ isEnabled = "0";
color = "0.887923 1 0.008023 1";
brightness = "1";
castShadows = "0";
@@ -272,5 +276,24 @@ new SimGroup(MissionGroup) {
+ new ReflectionProbe() {
+ ProbeShape = "Box";
+ radius = "10";
+ ReflectionMode = "Baked Cubemap";
+ reflectionPath = "levels/probeTest/probes/";
+ Bake = "0";
+ position = "15.4549 0.559989 4.53387";
+ scale = "5 5 5";
+ persistentId = "4ec88e62-4e8f-11e8-ae68-993c6bb8eb5b";
+ GroundColor = "0.8 0.7 0.5 1";
+ SkyColor = "0.5 0.5 1 1";
//--- OBJECT WRITE END ---
@@ -1,82 +1,1539 @@
//--- OBJECT WRITE BEGIN ---
new SimGroup(MissionGroup) {
- Enabled = "1";
new LevelInfo(theLevelInfo) {
+ nearClip = "0.1";
visibleDistance = "1000";
+ visibleGhostDistance = "0";
+ decalBias = "0.0015";
fogColor = "0.6 0.6 0.7 1";
fogDensity = "0";
fogDensityOffset = "700";
fogAtmosphereHeight = "0";
canvasClearColor = "0 0 0 255";
- canSaveDynamicFields = "1";
- levelName = "Empty Room";
- desc0 = "An empty room ready to be populated with Torque objects.";
- };
- new SkyBox(theSky) {
+ ambientLightBlendPhase = "1";
+ ambientLightBlendCurve = "0 0 -1 -1";
+ LevelEnvMap = "BlueSkyCubemap";
+ soundAmbience = "AudioAmbienceDefault";
+ soundDistanceModel = "Linear";
- Position = "0 0 0";
- rotation = "1 0 0 0";
- scale = "1 1 1";
- Material = "BlackSkyMat";
- drawBottom = "0";
- fogBandHeight = "0";
+ desc0 = "An empty room ready to be populated with Torque objects.";
+ levelName = "Empty Room";
- azimuth = "230.396";
- elevation = "45";
- color = "0.968628 0.901961 0.901961 1";
- ambient = "0.078431 0.113725 0.156863 1";
+ new ScatterSky() {
+ skyBrightness = "25";
+ sunSize = "1";
+ colorizeAmount = "0";
+ colorize = "0 0 0 1";
+ rayleighScattering = "0.0035";
+ sunScale = "1 1 1 1";
+ ambientScale = "1 1 1 1";
+ fogScale = "1 1 1 1";
+ exposure = "1";
+ zOffset = "0";
+ azimuth = "0";
+ elevation = "90";
+ moonAzimuth = "0";
+ moonElevation = "45";
castShadows = "1";
+ staticRefreshFreq = "8";
+ nightColor = "0.0196078 0.0117647 0.109804 1";
+ nightFogColor = "0.0196078 0.0117647 0.109804 1";
+ moonEnabled = "1";
+ moonMat = "Moon_Glow_Mat";
+ moonScale = "0.2";
+ moonLightColor = "0.192157 0.192157 0.192157 1";
+ useNightCubemap = "1";
+ nightCubemap = "NightCubemap";
attenuationRatio = "0 1 1";
shadowType = "PSSM";
texSize = "1024";
- overDarkFactor = "3000 2000 1000 250";
- shadowDistance = "400";
+ shadowDistance = "200";
numSplits = "4";
- logWeight = "0.96";
- fadeStartDistance = "325";
lastSplitTerrainOnly = "0";
+ position = "171.065 195.399 -1.10657";
+ mieScattering = "0.0045";
new SpawnSphere() {
- Position = "0 0 2";
- dataBlock = "SpawnSphereMarker";
- radius = "5";
- spawnClass = "";
- spawnDatablock = "";
autoSpawn = "0";
+ spawnTransform = "0";
+ radius = "5";
sphereWeight = "1";
indoorWeight = "1";
outdoorWeight = "1";
+ isAIControlled = "0";
+ dataBlock = "SpawnSphereMarker";
+ position = "0 0 2";
homingCount = "0";
lockCount = "0";
- new GroundPlane() {
+ new SimGroup(CameraBookmarks) {
+ new CameraBookmark() {
+ dataBlock = "CameraBookmarkMarker";
+ position = "12.4869 4.10897 4.58326";
+ rotation = "-0.0713349 -0.106471 -0.991754 112.795";
+ internalName = "CryEngine_Sponza_Compare";
+ position = "-17.8058 0.291567 6.37798";
+ rotation = "0.214026 0.125288 -0.96876 62.2862";
+ internalName = "mirrorTest";
+ position = "15.8629 -8.35756 4.69165";
+ rotation = "0.0151395 -0.00460125 0.999875 33.8145";
+ internalName = "NewCamera_0";
+ new GroundPlane() {
squareSize = "128";
scaleU = "12";
scaleV = "12";
- Material = "BlankWhite";
+ Material = "Grid512_Grey_Mat";
+ position = "0 0 0";
+ new TSStatic() {
+ shapeName = "art/shapes/sponza/sponza.dae";
+ playAmbient = "1";
+ meshCulling = "0";
+ originSort = "0";
+ CollisionType = "Collision Mesh";
+ DecalType = "Visible Mesh";
+ allowPlayerStep = "0";
+ alphaFadeEnable = "0";
+ alphaFadeStart = "100";
+ alphaFadeEnd = "150";
+ alphaFadeInverse = "0";
+ renderNormals = "0";
+ forceDetail = "-1";
+ ignoreZodiacs = "0";
+ useGradientRange = "0";
+ gradientRange = "0 180";
+ invertGradientRange = "0";
+ position = "-8.91623 -0.551134 0.392056";
+ bricks = "bricks_mat";
+ flagpole = "flagpole_mat";
+ shapeName = "art/shapes/sponza/sponza_extra_curtains.dae";
+ DecalType = "Collision Mesh";
+ hidden = "1";
+ new SimGroup(Probes) {
+ ProbeShape = "Sphere";
+ IndirectLightMode = "Ambient Color";
+ IndirectLight = "0.618686 0.45908 0.147998 0.5";
+ reflectionPath = "levels/Empty Room/probes/";
+ position = "-11.4607 0.712664 16.8264";
+ persistentId = "c04383f9-2147-11e7-9298-9a7d9c6feabe";
+ ProbeMode = "Baked Cubemap";
+ IndirectLight = "0.036 0.032 0.006 0.505";
+ StaticCubemap = "DesertSkyCubemap";
+ position = "19.0392 11.1366 16.5406";
+ persistentId = "6ab2beca-20a0-11e7-9c9c-8cefe314de25";
+ position = "8.19861 11.1366 16.5406";
+ persistentId = "6ab8d961-20a0-11e7-9c9c-8cefe314de25";
+ position = "-0.368001 11.1366 16.5406";
+ persistentId = "6abfde5c-20a0-11e7-9c9c-8cefe314de25";
+ position = "-10.2042 11.1366 16.5406";
+ persistentId = "6ac869fc-20a0-11e7-9c9c-8cefe314de25";
+ position = "-19.4857 11.1366 16.5406";
+ persistentId = "6acef9c4-20a0-11e7-9c9c-8cefe314de25";
+ position = "-29.2419 11.1366 16.5406";
+ persistentId = "6ad5145b-20a0-11e7-9c9c-8cefe314de25";
+ position = "-38.8665 11.1366 16.5406";
+ persistentId = "6adb7d13-20a0-11e7-9c9c-8cefe314de25";
+ position = "18.8673 -10.4486 16.5257";
+ persistentId = "6ae25afd-20a0-11e7-9c9c-8cefe314de25";
+ position = "8.02669 -10.4486 16.5257";
+ persistentId = "6aea4a5b-20a0-11e7-9c9c-8cefe314de25";
+ position = "-0.53992 -10.4486 16.5257";
+ persistentId = "6af0da23-20a0-11e7-9c9c-8cefe314de25";
+ position = "-10.3761 -10.4486 16.5257";
+ persistentId = "6af769ec-20a0-11e7-9c9c-8cefe314de25";
+ position = "-19.6576 -10.4486 16.5257";
+ persistentId = "6afdf9b4-20a0-11e7-9c9c-8cefe314de25";
+ position = "-29.4138 -10.4486 16.5257";
+ persistentId = "6b05c202-20a0-11e7-9c9c-8cefe314de25";
+ position = "-39.0384 -10.4486 16.5257";
+ persistentId = "6b0dd870-20a0-11e7-9c9c-8cefe314de25";
+ IndirectLight = "1 0.00455975 0.00455975 1";
+ position = "19.0392 11.1366 5.01901";
+ persistentId = "25d6efbe-20a0-11e7-9c9c-8cefe314de25";
+ SkyColor = "1 0.00455975 0.00455975 1";
+ IndirectLight = "0.201096 0.00775103 0.00775103 1";
+ position = "8.19861 11.1366 5.01901";
+ persistentId = "25de1bc8-20a0-11e7-9c9c-8cefe314de25";
+ IndirectLight = "0.0356144 0.0315514 0.00604059 1";
+ position = "-0.368001 11.1366 5.01901";
+ persistentId = "25e4365f-20a0-11e7-9c9c-8cefe314de25";
+ position = "-10.2042 11.1366 5.01901";
+ persistentId = "25eaed38-20a0-11e7-9c9c-8cefe314de25";
+ IndirectLight = "0.036 0.032 0.006 0.532";
+ position = "-19.4857 11.1366 5.01901";
+ persistentId = "25f17d01-20a0-11e7-9c9c-8cefe314de25";
+ position = "-29.2419 11.1366 5.01901";
+ persistentId = "25fa7dd3-20a0-11e7-9c9c-8cefe314de25";
+ position = "-38.8665 11.1366 5.01901";
+ persistentId = "26021f0f-20a0-11e7-9c9c-8cefe314de25";
+ position = "18.8673 -10.4486 5.00406";
+ persistentId = "ac3fe3f0-1ff5-11e7-b852-d5bab2d8fbcc";
+ position = "8.02669 -10.4486 5.00406";
+ persistentId = "f41bf404-209f-11e7-9c9c-8cefe314de25";
+ IndirectLight = "0.0461488 0.113921 0.0915184 1";
+ position = "-0.53992 -10.4486 5.00406";
+ persistentId = "fa446dc9-209f-11e7-9c9c-8cefe314de25";
+ IndirectLight = "0.499505 1 0.687031 1";
+ position = "-10.3761 -10.4486 5.00406";
+ persistentId = "fe03f4d6-209f-11e7-9c9c-8cefe314de25";
+ position = "-19.6576 -10.4486 5.00406";
+ persistentId = "02bbe946-20a0-11e7-9c9c-8cefe314de25";
+ position = "-29.4138 -10.4486 5.00406";
+ persistentId = "072e6f42-20a0-11e7-9c9c-8cefe314de25";
+ IndirectLight = "0.119264 0.334458 0.554227 1";
+ position = "8.19861 0.0539651 26.7401";
+ persistentId = "afa9693c-20a0-11e7-9c9c-8cefe314de25";
+ position = "-0.368001 0.0539651 26.7401";
+ persistentId = "afa1c7ff-20a0-11e7-9c9c-8cefe314de25";
+ position = "-10.2042 0.0539651 26.7401";
+ persistentId = "af993c5f-20a0-11e7-9c9c-8cefe314de25";
+ position = "-19.4857 0.0539651 26.7401";
+ persistentId = "af919b23-20a0-11e7-9c9c-8cefe314de25";
+ position = "-29.2419 0.0539651 26.7401";
+ persistentId = "af8a4807-20a0-11e7-9c9c-8cefe314de25";
+ position = "19.0392 0.0539651 16.5406";
+ persistentId = "a1f5851f-20a0-11e7-9c9c-8cefe314de25";
+ position = "8.19861 0.0539651 16.5406";
+ persistentId = "a1eef556-20a0-11e7-9c9c-8cefe314de25";
+ position = "-0.368001 0.0539651 16.5406";
+ persistentId = "a1e8658e-20a0-11e7-9c9c-8cefe314de25";
+ IndirectLight = "1 0.1 0.005 1";
+ position = "-9.37523 0.0539651 16.5406";
+ persistentId = "a1e13983-20a0-11e7-9c9c-8cefe314de25";
+ position = "-19.4857 0.0539651 16.5406";
+ persistentId = "a1d8ade3-20a0-11e7-9c9c-8cefe314de25";
+ position = "-29.2419 0.0539651 16.5406";
+ persistentId = "a1d133b7-20a0-11e7-9c9c-8cefe314de25";
+ position = "-38.8665 0.0539651 16.5406";
+ persistentId = "a1cacaff-20a0-11e7-9c9c-8cefe314de25";
+ IndirectLight = "0.89 0.39 0.148 0.949";
+ position = "19.0392 0.0539651 5.01901";
+ persistentId = "a1c43b36-20a0-11e7-9c9c-8cefe314de25";
+ IndirectLight = "0.618686 0.45908 0.147998 1";
+ position = "8.19861 0.0539651 5.01901";
+ persistentId = "a1bdf98f-20a0-11e7-9c9c-8cefe314de25";
+ position = "-0.368001 0.0539651 5.01901";
+ persistentId = "a1b71ba5-20a0-11e7-9c9c-8cefe314de25";
+ position = "-10.2042 0.0539651 5.01901";
+ persistentId = "a1afef9a-20a0-11e7-9c9c-8cefe314de25";
+ position = "-19.4857 0.0539651 5.01901";
+ persistentId = "a1a9fc14-20a0-11e7-9c9c-8cefe314de25";
+ position = "-29.2419 0.0539651 5.01901";
+ persistentId = "a1a3e17d-20a0-11e7-9c9c-8cefe314de25";
+ position = "-38.8665 0.0539651 5.01901";
+ persistentId = "a19dedf6-20a0-11e7-9c9c-8cefe314de25";
+ position = "-39.0384 -10.4486 5.00406";
+ persistentId = "0b06aecb-20a0-11e7-9c9c-8cefe314de25";
+ new ReflectionProbe(Skylight) {
+ radius = "200";
+ Intensity = "0.1";
+ position = "0 0 50";
+ scale = "100 100 100";
+ persistentId = "0039b2a6-20c2-11e7-b558-91c869a431e1";
+ shapeName = "art/shapes/textures/pbrGrid.dae";
+ position = "-26.8371 -4.49793 0.284616";
+ shapeName = "art/shapes/textures/PBRTEST.dae";
+ position = "23.1539 0.397566 1.22924";
+ new PointLight() {
+ radius = "15";
+ isEnabled = "1";
+ color = "1 0.275833 0.00455975 1";
+ castShadows = "0";
+ priority = "1";
+ animate = "1";
+ animationPeriod = "1";
+ animationPhase = "1";
+ shadowType = "DualParaboloidSinglePass";
+ numSplits = "1";
+ position = "-38.6032 -10.4171 3.87317";
+ position = "-38.8136 10.7957 3.87317";
+ position = "19.1024 10.5955 3.87317";
+ position = "19.2761 -10.4968 3.87317";
+ color = "0.0802193 0.804559 0.7593 1";
+ position = "-9.85153 -12.8646 7.06711";
+ shapeName = "art/shapes/cube/cube.dae";
+ position = "-19.6187 2.45554 4.946";
+ new Prefab() {
+ fileName = "art/prefabs/vaseFlame.prefab";
+ position = "3.24204 4.91314 3.64629";
+ position = "3.27432 -4.14598 3.64629";
+ position = "-24.3409 -4.14598 3.64629";
+ position = "-24.378 4.92861 3.5101";
+ position = "24.1149 10.5531 0.125195";
+ scale = "1 10 10";
+ position = "12.7735 12.0238 0.464151";
@@ -16,7 +16,6 @@ new SimGroup(MissionGroup) {
ambientLightBlendPhase = "1";
ambientLightBlendCurve = "0 0 -1 -1";
- LevelEnvMap = "DesertSkyCubemap";
soundAmbience = "AudioAmbienceDefault";
soundDistanceModel = "Linear";
@@ -33,6 +32,7 @@ new SimGroup(MissionGroup) {
baseTexFormat = "DDS";
lightMapSize = "256";
screenError = "16";
position = "-1024 -1024 179.978";
@@ -67,8 +67,8 @@ new SimGroup(MissionGroup) {
playAmbient = "1";
meshCulling = "0";
originSort = "0";
- collisionType = "Collision Mesh";
- decalType = "Collision Mesh";
allowPlayerStep = "1";
alphaFadeEnable = "0";
alphaFadeStart = "100";
@@ -76,6 +76,10 @@ new SimGroup(MissionGroup) {
alphaFadeInverse = "0";
renderNormals = "0";
forceDetail = "-1";
position = "9.42739 27.7428 241.767";
@@ -133,8 +137,8 @@ new SimGroup(MissionGroup) {
@@ -142,6 +146,10 @@ new SimGroup(MissionGroup) {
position = "17.8995 16.1596 241.136";
@@ -158,27 +166,18 @@ new SimGroup(MissionGroup) {
surface = "0 0 0 1 1.29457 -0.42643 0.5";
surface = "0 1 0 0 1.29457 -0.42643 -0.5";
- surface = "0.707107 0 0 0.707107 1.29457 5.75621 0";
- surface = "0 0.707107 -0.707107 0 1.29457 -6.60907 -4.54747e-013";
- surface = "0.5 0.5 -0.5 0.5 -3.35148 -0.42643 -1.28542e-008";
- surface = "0.5 -0.5 0.5 0.5 5.94063 -0.42643 -1.28542e-008";
- new EnvVolume() {
- AreaEnvMap = "MipCubemap";
- cubeReflectorDesc = "DefaultCubeDesc";
- position = "8.21068 19.3464 241.855";
- scale = "10 10 10";
- canSave = "1";
+ surface = "0.707107 0 0 0.707106 1.29457 5.75621 0";
+ surface = "0 0.707107 -0.707107 0 1.29457 -6.60907 -4.54747e-13";
+ surface = "0.5 0.5 -0.5 0.5 -3.35148 -0.42643 -1.28542e-08";
+ surface = "0.5 -0.5 0.5 0.5 5.94063 -0.42643 -1.28542e-08";
new TSStatic() {
shapeName = "art/shapes/textures/PBRTEST2.dae";
@@ -186,6 +185,10 @@ new SimGroup(MissionGroup) {
position = "5.60579 18.8689 241.462";
@@ -198,8 +201,8 @@ new SimGroup(MissionGroup) {
cubeReflectorDesc = "DefaultCubeDesc";
@@ -207,6 +210,10 @@ new SimGroup(MissionGroup) {
position = "22.1653 4.41543 241.364";
@@ -218,8 +225,8 @@ new SimGroup(MissionGroup) {
@@ -227,6 +234,10 @@ new SimGroup(MissionGroup) {
position = "15.72 5.9186 241.191";
@@ -246,8 +257,8 @@ new SimGroup(MissionGroup) {
allowPlayerStep = "0";
@@ -255,6 +266,10 @@ new SimGroup(MissionGroup) {
position = "12.8895 -2.18239 238.765";
@@ -266,8 +281,8 @@ new SimGroup(MissionGroup) {
@@ -275,6 +290,10 @@ new SimGroup(MissionGroup) {
position = "10.406 10.0939 240.551";
@@ -300,9 +319,6 @@ new SimGroup(MissionGroup) {
enabled = "1";
reflectionPath = "levels/empty terrain/";
@@ -312,14 +328,14 @@ new SimGroup(MissionGroup) {
persistentId = "fc28816f-65a6-11e7-b9a1-b6f5317ad553";
@@ -329,6 +345,20 @@ new SimGroup(MissionGroup) {
persistentId = "10bb5f41-65a7-11e7-b9a1-b6f5317ad553";
+ StaticCubemap = "HdrSkyCubemap";
+ position = "16.4688 10.5898 241.399";
+ persistentId = "de646cc8-4f49-11e8-8a25-ec48d38e6b49";
+ reflectionPath = "levels/Empty Terrain/probes/";
@@ -1,6 +1,6 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
-//
+// Portions Copyright Zefiros
// 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
@@ -38,12 +38,140 @@ uniform vec4 inLightColor[4];
uniform vec4 ambient;
#define ambientCameraFactor 0.3
-uniform float specularPower;
-uniform vec4 specularColor;
+uniform float smoothness;
+uniform float metalness;
+uniform vec4 albedo;
#endif // !TORQUE_SHADERGEN
+vec3 F_schlick( in vec3 f0, in vec3 f90, in float u )
+ // F( v, h ) = F0 + ( 1.0 - F0 ) * pow( 1.0f - HdotV, 5.0f )
+ // where
+ // F0 = BaseColor * nDotL
+ // Dielectric materials always have a range of 0.02 < F0 < 0.05 , use a stock value of 0.04 ( roughly plastics )
+ return f0 + ( f90 - f0 ) * pow( 1.0f - u , 5.0f );
+float Fr_DisneyDiffuse ( float NdotV , float NdotL , float LdotH , float linearRoughness )
+ float energyBias = mix (0 , 0.5 , linearRoughness );
+ float energyFactor = mix (1.0 , 1.0 / 1.51 , linearRoughness );
+ float fd90 = energyBias + 2.0 * LdotH * LdotH * linearRoughness ;
+ vec3 f0 = vec3 ( 1.0f , 1.0f , 1.0f );
+ float lightScatter = F_schlick( f0 , vec3(fd90), NdotL ).r;
+ float viewScatter = F_schlick(f0 , vec3(fd90), NdotV ).r;
+ return lightScatter * viewScatter * energyFactor ;
+float SmithGGX( float NdotL, float NdotV, float alpha )
+ // G( L, V, h ) = G( L ) G( V )
+ // nDotL
+ // G( L ) = _________________________
+ // nDotL ( 1 - k ) + k
+ // NdotV
+ // G( V ) = _________________________
+ // NdotV ( 1 - k ) + k
+ // pow( ( Roughness + 1 ), 2)
+ // , Where k = __________________________ ( unreal 4 )
+ // 8
+ float alphaSqr = alpha * alpha;
+ //float GGX_V = NdotL * sqrt ( ( - NdotV * alphaSqr + NdotV ) * NdotV + alphaSqr );
+ //float GGX_L = NdotV * sqrt ( ( - NdotL * alphaSqr + NdotL ) * NdotL + alphaSqr );
+ float GGX_V = NdotL + sqrt ( ( - NdotV * alphaSqr + NdotV ) * NdotV + alphaSqr );
+ float GGX_L = NdotV + sqrt ( ( - NdotL * alphaSqr + NdotL ) * NdotL + alphaSqr );
+ return 1.0/( GGX_V + GGX_L );
+ //return 0.5f / ( GGX_V + GGX_L );
+float D_GGX( float NdotH , float alpha )
+ // or GGX ( disney / unreal 4 )
+ // alpha = pow( roughness, 2 );
+ // pow( alpha, 2 )
+ // D( h ) = ________________________________________________________________
+ // PI pow( pow( NdotH , 2 ) ( pow( alpha, 2 ) - 1 ) + 1 ), 2 )
+ float alphaSqr = alpha*alpha;
+ float f = ( NdotH * alphaSqr - NdotH ) * NdotH + 1;
+ return alphaSqr / ( M_PI_F * (f * f) );
+vec4 EvalBDRF( vec3 baseColor, vec3 lightColor, vec3 toLight, vec3 position, vec3 normal, float roughness, float metallic )
+ // Microfacet Specular Cook-Torrance
+ // D( h ) F( v, h ) G( l, v, h )
+ // f( l, v ) = ____________________________
+ // 4 ( dot( n, l ) dot( n, v )
+ vec3 L = normalize( toLight );
+ vec3 V = normalize( -position );
+ vec3 H = normalize( L + V );
+ vec3 N = normal;
+ float NdotV = abs( dot( N, V ) ) + 1e-5f;
+ float NdotH = saturate( dot( N, H ) );
+ float NdotL = saturate( dot( N, L ) );
+ float LdotH = saturate( dot( L, H ) );
+ float VdotH = saturate( dot( V, H ) );
+ if ( NdotL == 0 )
+ return vec4( 0.0f, 0.0f, 0.0f, 0.0f );
+ float alpha = roughness;
+ float visLinAlpha = alpha * alpha;
+ vec3 f0 = baseColor;
+ float metal = metallic;
+ vec3 F_conductor= F_schlick( f0, vec3( 1.0, 1.0, 1.0 ), VdotH );
+ vec3 F_dielec = F_schlick( vec3( 0.04, 0.04, 0.04 ), vec3( 1.0, 1.0, 1.0 ), VdotH );
+ float Vis = SmithGGX( NdotL, NdotV, visLinAlpha );
+ float D = D_GGX( NdotH, visLinAlpha );
+ vec3 Fr_dielec = D * F_dielec * Vis;
+ vec3 Fr_conductor = D * F_conductor * Vis;
+ vec3 Fd = vec3(Fr_DisneyDiffuse( NdotV , NdotL , LdotH , visLinAlpha ) / M_PI_F);
+ vec3 specular = ( 1.0f - metal ) * Fr_dielec + metal * Fr_conductor;
+ vec3 diffuse = ( 1.0f - metal ) * Fd * f0;
+ vec3 ret = ( diffuse + specular + lightColor) * vec3(NdotL);
+ float FR = saturate(length(specular));
+ return vec4(ret,FR);
void compute4Lights( vec3 wsView,
vec3 wsPosition,
vec3 wsNormal,
@@ -57,8 +185,9 @@ void compute4Lights( vec3 wsView,
vec4 inLightSpotDir[3],
vec4 inLightSpotAngle,
vec4 inLightSpotFalloff,
- float specularPower,
- vec4 specularColor,
+ float smoothness,
+ float metalness,
+ vec4 albedo,
#endif // TORQUE_SHADERGEN
@@ -81,9 +210,6 @@ void compute4Lights( vec3 wsView,
for ( i = 0; i < 3; i++ )
lightVectors[i] = wsPosition[i] - inLightPos[i];
- vec4 squareDists = vec4(0);
- for ( i = 0; i < 3; i++ )
- squareDists += lightVectors[i] * lightVectors[i];
// Accumulate the dot product between the light
// vector and the normal.
@@ -99,40 +225,12 @@ void compute4Lights( vec3 wsView,
vec4 nDotL = vec4(0);
nDotL += lightVectors[i] * -wsNormal[i];
- vec4 rDotL = vec4(0);
- #ifndef TORQUE_BL_NOSPECULAR
- // We're using the Phong specular reflection model
- // here where traditionally Torque has used Blinn-Phong
- // which has proven to be more accurate to real materials.
- //
- // We do so because its cheaper as do not need to
- // calculate the half angle for all 4 lights.
- // Advanced Lighting still uses Blinn-Phong, but the
- // specular reconstruction it does looks fairly similar
- // to this.
- vec3 R = reflect( wsView, -wsNormal );
- rDotL += lightVectors[i] * R[i];
- #endif
- // Normalize the dots.
- // Notice we're using the half type here to get a
- // much faster sqrt via the rsq_pp instruction at
- // the loss of some precision.
- // Unless we have some extremely large point lights
- // i don't believe the precision loss will matter.
+ vec4 squareDists = vec4(0);
+ for ( i = 0; i < 3; i++ )
+ squareDists += lightVectors[i] * lightVectors[i];
half4 correction = half4(inversesqrt( squareDists ));
nDotL = saturate( nDotL * correction );
- rDotL = clamp( rDotL * correction, 0.00001, 1.0 );
// First calculate a simple point light linear
// attenuation factor.
@@ -157,93 +255,18 @@ void compute4Lights( vec3 wsView,
- // Finally apply the shadow masking on the attenuation.
- atten *= shadowMask;
// Get the final light intensity.
vec4 intensity = nDotL * atten;
- // Combine the light colors for output.
- outDiffuse = vec4(0);
- for ( i = 0; i < 4; i++ )
- outDiffuse += intensity[i] * inLightColor[i];
- // Output the specular power.
- vec4 specularIntensity = pow( rDotL, vec4(specularPower) ) * atten;
- // Apply the per-light specular attenuation.
- vec4 specular = vec4(0,0,0,1);
+ // Combine the light colors for output.
+ vec4 lightColor = vec4(0);
for ( i = 0; i < 4; i++ )
- specular += vec4( inLightColor[i].rgb * inLightColor[i].a * specularIntensity[i], 1 );
- // Add the final specular intensity values together
- // using a single dot product operation then get the
- // final specular lighting color.
- outSpecular = specularColor * specular;
-}
-// This value is used in AL as a constant power to raise specular values
-// to, before storing them into the light info buffer. The per-material
-// specular value is then computer by using the integer identity of
-// exponentiation:
-// (a^m)^n = a^(m*n)
-// or
-// (specular^constSpecular)^(matSpecular/constSpecular) = specular^(matSpecular*constSpecular)
-#define AL_ConstantSpecularPower 12.0f
-/// The specular calculation used in Advanced Lighting.
-///
-/// @param toLight Normalized vector representing direction from the pixel
-/// being lit, to the light source, in world space.
-/// @param normal Normalized surface normal.
-/// @param toEye The normalized vector representing direction from the pixel
-/// being lit to the camera.
-float AL_CalcSpecular( vec3 toLight, vec3 normal, vec3 toEye )
-{
- // (R.V)^c
- float specVal = dot( normalize( -reflect( toLight, normal ) ), toEye );
- // Return the specular factor.
- return pow( max( specVal, 0.00001f ), AL_ConstantSpecularPower );
-/// The output for Deferred Lighting
-vec4 AL_DeferredOutput(
- vec3 lightColor,
- vec3 diffuseColor,
- vec4 matInfo,
- vec4 ambient,
- float specular,
- float shadowAttenuation)
- vec3 specularColor = vec3(specular);
- bool metalness = getFlag(matInfo.r, 3);
- if ( metalness )
- {
- specularColor = 0.04 * (1 - specular) + diffuseColor * specular;
- }
- //specular = color * map * spec^gloss
- float specularOut = (specularColor * matInfo.b * min(pow(max(specular,1.0f), max((matInfo.a / AL_ConstantSpecularPower),1.0f)),matInfo.a)).r;
+ lightColor += intensity[i] * inLightColor[i];
- lightColor *= vec3(shadowAttenuation);
- lightColor += ambient.rgb;
- return vec4(lightColor.rgb, specularOut);
+ vec3 toLight = vec3(0);
+ toLight += lightVectors[i].rgb;
+ outDiffuse = vec4(albedo.rgb*(1.0-metalness),albedo.a);
+ outSpecular = EvalBDRF( vec3( 1.0, 1.0, 1.0 ), lightColor.rgb, toLight, wsPosition, wsNormal, smoothness, metalness );
@@ -336,4 +336,20 @@ vec3 toGamma(vec3 tex)
#endif //
+vec3 PBRFresnel(vec3 albedo, vec3 indirect, float metalness, float fresnel)
+ vec3 diffuseColor = albedo - (albedo * metalness);
+ vec3 reflectColor = mix(indirect*albedo, indirect, fresnel);
+ return diffuseColor + reflectColor;
+vec3 simpleFresnel(vec3 diffuseColor, vec3 reflectColor, float metalness, float angle, float bias, float power)
+ float fresnelTerm = bias + (1.0 - bias) * pow(abs(1.0 - max(angle, 0)), power);
+ fresnelTerm *= metalness;
+ return mix(diffuseColor, reflectColor, fresnelTerm);
#endif // _TORQUE_GLSL_
@@ -70,7 +70,7 @@ float Fr_DisneyDiffuse ( float NdotV , float NdotL , float LdotH , float linearR
float lightScatter = F_schlick( f0 , fd90 , NdotL ).r;
float viewScatter = F_schlick(f0 , fd90 , NdotV ).r;
- return lightScatter * viewScatter * energyFactor ;
+ return lightScatter * viewScatter * energyFactor;
float SmithGGX( float NdotL, float NdotV, float alpha )
@@ -268,4 +268,41 @@ void compute4Lights( float3 wsView,
outDiffuse = float4(albedo.rgb*(1.0-metalness),albedo.a);
outSpecular = EvalBDRF( float3( 1.0, 1.0, 1.0 ), lightColor.rgb, toLight, wsPosition, wsNormal, smoothness, metalness );
+float G1V(float dotNV, float k)
+ return 1.0f/(dotNV*(1.0f-k)+k);
+float3 directSpecular(float3 N, float3 V, float3 L, float roughness, float F0)
+ float alpha = roughness*roughness;
+ //TODO don't need to calculate all this again timmy!!!!!!
+ float3 H = normalize(V + L);
+ float dotNL = clamp(dot(N,L), 0.0, 1.0);
+ float dotNV = clamp(dot(N,V), 0.0, 1.0);
+ float dotNH = clamp(dot(N,H), 0.0, 1.0);
+ float dotHV = clamp(dot(H,V), 0.0, 1.0);
+ float dotLH = clamp(dot(L,H), 0.0, 1.0);
+ float F, D, vis;
+ // D
+ float pi = 3.14159f;
+ float denom = dotNH * dotNH *(alphaSqr-1.0) + 1.0f;
+ D = alphaSqr/(pi * denom * denom);
+ // F
+ float dotLH5 = pow(1.0f-dotLH,5);
+ F = F0 + (1.0-F0)*(dotLH5);
+ // V
+ float k = alpha/2.0f;
+ vis = G1V(dotNL,k)*G1V(dotNV,k);
+ float specular = dotNL * D * F * vis;
+ return float3(specular,specular,specular);
@@ -0,0 +1,138 @@
+#include "../../torque.hlsl"
+struct ConnectData
+ float4 hpos : TORQUE_POSITION;
+ float2 uv : TEXCOORD;
+// ----------------------------------------------------------------------------
+// http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
+// efficient VanDerCorpus calculation.
+float RadicalInverse_VdC(uint bits)
+ bits = (bits << 16u) | (bits >> 16u);
+ bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
+ bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
+ bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
+ bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
+ return float(bits) * 2.3283064365386963e-10; // / 0x100000000
+float2 Hammersley(uint i, uint N)
+ return float2(float(i)/float(N), RadicalInverse_VdC(i));
+float3 ImportanceSampleGGX(float2 Xi, float3 N, float roughness)
+ float a = roughness*roughness;
+ float phi = 2.0 * M_PI_F * Xi.x;
+ float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y));
+ float sinTheta = sqrt(1.0 - cosTheta*cosTheta);
+ // from spherical coordinates to cartesian coordinates - halfway vector
+ float3 H;
+ H.x = cos(phi) * sinTheta;
+ H.y = sin(phi) * sinTheta;
+ H.z = cosTheta;
+ // from tangent-space H vector to world-space sample vector
+ float3 up = abs(N.z) < 0.999 ? float3(0.0, 0.0, 1.0) : float3(1.0, 0.0, 0.0);
+ float3 tangent = normalize(cross(up, N));
+ float3 bitangent = cross(N, tangent);
+ float3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
+ return normalize(sampleVec);
+float GeometrySchlickGGX(float NdotV, float roughness)
+ // note that we use a different k for IBL
+ float a = roughness;
+ float k = (a * a) / 2.0;
+ float nom = NdotV;
+ float denom = NdotV * (1.0 - k) + k;
+ return nom / denom;
+float GeometrySmith(float3 N, float3 V, float3 L, float roughness)
+ float NdotV = max(dot(N, V), 0.0);
+ float NdotL = max(dot(N, L), 0.0);
+ float ggx2 = GeometrySchlickGGX(NdotV, roughness);
+ float ggx1 = GeometrySchlickGGX(NdotL, roughness);
+ return ggx1 * ggx2;
+float2 IntegrateBRDF(float NdotV, float roughness)
+ float3 V;
+ V.x = sqrt(1.0 - NdotV*NdotV);
+ V.y = 0.0;
+ V.z = NdotV;
+ float A = 0.0;
+ float B = 0.0;
+ float3 N = float3(0.0, 0.0, 1.0);
+ const uint SAMPLE_COUNT = 1024u;
+ for(uint i = 0u; i < SAMPLE_COUNT; ++i)
+ // generates a sample vector that's biased towards the
+ // preferred alignment direction (importance sampling).
+ float2 Xi = Hammersley(i, SAMPLE_COUNT);
+ float3 H = ImportanceSampleGGX(Xi, N, roughness);
+ float3 L = normalize(2.0 * dot(V, H) * H - V);
+ float NdotL = max(L.z, 0.0);
+ float NdotH = max(H.z, 0.0);
+ float VdotH = max(dot(V, H), 0.0);
+ if(NdotL > 0.0)
+ float G = GeometrySmith(N, V, L, roughness);
+ float G_Vis = (G * VdotH) / (NdotH * NdotV);
+ float Fc = pow(1.0 - VdotH, 5.0);
+ A += (1.0 - Fc) * G_Vis;
+ B += Fc * G_Vis;
+ A /= float(SAMPLE_COUNT);
+ B /= float(SAMPLE_COUNT);
+ return float2(A, B);
+float4 main(ConnectData IN) : TORQUE_TARGET0
+ return float4(IntegrateBRDF(IN.uv.x, IN.uv.y).rg,0,1);
+ //return float2(1,1);
@@ -0,0 +1,37 @@
+#include "../../shaderModel.hlsl"
+ConnectData main( uint vertexID : SV_VertexID )
+ ConnectData result;
+ result.uv = float2((vertexID << 1) & 2, vertexID & 2);
+ result.hpos = float4(result.uv * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);
@@ -23,11 +23,11 @@
#include "shadergen:/autogenConditioners.h"
#include "../../postfx/postFx.hlsl"
-TORQUE_UNIFORM_SAMPLER2D(indirectLightingBuffer,0);
+TORQUE_UNIFORM_SAMPLER2D(specularLightingBuffer,0);
float4 main( PFXVertToPix IN ) : TORQUE_TARGET0
- float4 directlighting = float4(TORQUE_TEX2D( indirectLightingBuffer, IN.uv0 ).rgb,1.0);
- return directlighting;
+ float4 diffuseLighting = float4(TORQUE_TEX2D( specularLightingBuffer, IN.uv0 ).rgb,1.0);
+ return diffuseLighting;
@@ -25,9 +25,9 @@
#include "shaders/common/torque.hlsl"
TORQUE_UNIFORM_SAMPLER2D(colorBufferTex,0);
-TORQUE_UNIFORM_SAMPLER2D(directLightingBuffer,1);
+TORQUE_UNIFORM_SAMPLER2D(diffuseLightingBuffer,1);
TORQUE_UNIFORM_SAMPLER2D(matInfoTex,2);
-TORQUE_UNIFORM_SAMPLER2D(indirectLightingBuffer,3);
+TORQUE_UNIFORM_SAMPLER2D(specularLightingBuffer,3);
TORQUE_UNIFORM_SAMPLER2D(deferredTex,4);
@@ -37,25 +37,27 @@ float4 main( PFXVertToPix IN ) : TORQUE_TARGET0
if (depth>0.9999)
return float4(0,0,0,0);
- float3 colorBuffer = TORQUE_TEX2D( colorBufferTex, IN.uv0 ).rgb; //albedo
+ float3 albedo = TORQUE_TEX2D( colorBufferTex, IN.uv0 ).rgb; //albedo
float4 matInfo = TORQUE_TEX2D(matInfoTex, IN.uv0); //flags|smoothness|ao|metallic
bool emissive = getFlag(matInfo.r, 0);
if (emissive)
- return float4(colorBuffer, 1.0);
+ return float4(albedo, 1.0);
- float4 directLighting = TORQUE_TEX2D( directLightingBuffer, IN.uv0 ); //shadowmap*specular
- float3 indirectLighting = TORQUE_TEX2D( indirectLightingBuffer, IN.uv0 ).rgb; //environment mapping*lightmaps
+ float4 diffuse = TORQUE_TEX2D( diffuseLightingBuffer, IN.uv0 ); //shadowmap*specular
+ float4 specular = TORQUE_TEX2D( specularLightingBuffer, IN.uv0 ); //environment mapping*lightmaps
float metalness = matInfo.a;
- float frez = directLighting.a;
- float3 diffuseColor = colorBuffer - (colorBuffer * metalness);
- float3 reflectColor = indirectLighting*colorBuffer;
- colorBuffer = diffuseColor+lerp(reflectColor,indirectLighting,frez);
- colorBuffer *= max(directLighting.rgb,float3(0,0,0));
+ float3 diffuseColor = albedo - (albedo * metalness);
+ float3 specularColor = lerp(float3(0.04,0.04,0.04), albedo, metalness);
+ float3 light = (diffuseColor * diffuse.rgb) + (specularColor * specular.rgb);
+ //albedo = diffuseColor+lerp(reflectColor,indiffuseLighting,frez);
+ //albedo *= max(diffuseLighting.rgb,float3(0,0,0));
- return hdrEncode( float4(colorBuffer.rgb, 1.0) );
+ return float4(light.rgb, 1.0);
@@ -0,0 +1,31 @@
+#include "../../../gl/hlslCompat.glsl"
+out vec2 uv;
+void main()
+ uv = vec2((gl_VertexID << 1) & 2, gl_VertexID & 2);
+ gl_Position = vec4(uv * vec2(2.0f, -2.0f) + vec2(-1.0f, 1.0f), 0.0f, 1.0f);
+ correctSSP(gl_Position);
@@ -24,12 +24,12 @@
in vec2 uv0;
-uniform sampler2D directLightingBuffer;
+uniform sampler2D diffuseLightingBuffer;
out vec4 OUT_col;
void main()
- vec4 directLighting = vec4(texture( directLightingBuffer, uv0 ).rgb,1.0);
- OUT_col = directLighting;
+ vec4 diffuseLighting = vec4(texture( diffuseLightingBuffer, uv0 ).rgb,1.0);
+ OUT_col = diffuseLighting;
@@ -23,6 +23,7 @@
out vec4 OUT_col1;
out vec4 OUT_col2;
+out vec4 OUT_col3;
// Main
@@ -33,8 +34,11 @@ void main()
OUT_col = vec4(1.0, 1.0, 1.0, 1.0);
// Clear Color Buffer.
- OUT_col1 = vec4(0.0, 0.0, 0.0, 1.0);
+ OUT_col1 = vec4(0.0, 0.0, 0.0, 0.0001);
// Clear Material Info Buffer.
- OUT_col2 = vec4(0.0, 0.0, 0.0, 1.0);
+ OUT_col2 = vec4(0.0, 0.0, 0.0, 0.0);
+ // Clear Light Info Buffer.
+ OUT_col3 = vec4(0.0, 0.0, 0.0, 0.0);
@@ -26,8 +26,9 @@
#include "../../../gl/torque.glsl"
uniform sampler2D colorBufferTex;
-uniform sampler2D lightDeferredTex;
uniform sampler2D matInfoTex;
+uniform sampler2D specularLightingBuffer;
uniform sampler2D deferredTex;
@@ -40,20 +41,27 @@ void main()
OUT_col = vec4(0.0);
return;
- vec4 lightBuffer = texture( lightDeferredTex, uv0 );
- vec4 colorBuffer = texture( colorBufferTex, uv0 );
- vec4 matInfo = texture( matInfoTex, uv0 );
- float specular = clamp(lightBuffer.a,0.0,1.0);
- // Diffuse Color Altered by Metalness
+ vec3 colorBuffer = texture( colorBufferTex, uv0 ).rgb; //albedo
+ vec4 matInfo = texture( matInfoTex, uv0 ); //flags|smoothness|ao|metallic
+ bool emissive = getFlag(matInfo.r, 0);
+ if (emissive)
- colorBuffer *= (1.0 - colorBuffer.a);
+ OUT_col = float4(colorBuffer, 1.0);
- colorBuffer += vec4(specular, specular, specular, 1.0);
- colorBuffer *= vec4(lightBuffer.rgb, 1.0);
- OUT_col = hdrEncode( vec4(colorBuffer.rgb, 1.0) );
+ vec4 diffuseLighting = texture( diffuseLightingBuffer, uv0 ); //shadowmap*specular
+ vec3 specularLighting = texture( specularLightingBuffer, uv0 ).rgb; //environment mapping*lightmaps
+ float metalness = matInfo.a;
+ float frez = diffuseLighting.a;
+ vec3 diffuseColor = colorBuffer - (colorBuffer * metalness);
+ vec3 reflectColor = specularLighting*colorBuffer;
+ colorBuffer = diffuseColor+lerp(reflectColor,specularLighting,frez);
+ colorBuffer *= max(diffuseLighting.rgb,vec3(0,0,0));
+ OUT_col = hdrEncode(vec4(colorBuffer,1.0));
@@ -0,0 +1,60 @@
+#include "../../../gl/torque.glsl"
+in vec2 uv;
+uniform int face;
+uniform samplerCube environmentMap;
+out vec4 outColor;
+ vec3 N = getCubeDir(face, uv);
+ vec3 irradiance = vec3(0.0);
+ // tangent space calculation from origin point
+ vec3 up = vec3(0.0, 0.0, 1.0);
+ vec3 right = cross(up, N);
+ up = cross(N, right);
+ float sampleDelta = 0.025;
+ int nrSamples = 0;
+ for(float phi = 0.0; phi < M_2PI_F; phi += sampleDelta)
+ for(float theta = 0.0; theta < M_HALFPI_F; theta += sampleDelta)
+ // spherical to cartesian (in tangent space)
+ vec3 tangentSample = vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));
+ // tangent space to world
+ vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * N;
+ irradiance += texture(environmentMap, sampleVec).rgb * cos(theta) * sin(theta);
+ nrSamples++;
+ irradiance = M_PI_F * irradiance * (1.0 / float(nrSamples));
+ outColor = vec4(irradiance, 1.0);
@@ -138,8 +138,9 @@ void main()
vec3 ssPos = ssPos.xyz / ssPos.w;
vec2 uvScene = getUVFromSSPos( ssPos, rtParams0 );
- // Emissive.
+ // Matinfo flags
vec4 matInfo = texture( matInfoBuffer, uvScene );
+ //early out if emissive
bool emissive = getFlag( matInfo.r, 0 );
if ( emissive )
@@ -245,12 +246,17 @@ void main()
// cause the hardware occlusion query to disable the shadow.
// Specular term
- float specular = AL_CalcSpecular( lightVec,
- normal,
- normalize( -eyeRay ) ) * lightBrightness * atten * shadowed;
+ float specular = 0;
+ vec4 real_specular = EvalBDRF( colorSample.rgb,
+ lightcol,
+ lightVec,
+ viewSpacePos,
+ normal,
+ 1.05-matInfo.b*0.9, //slightly compress roughness to allow for non-baked lighting
+ matInfo.a );
+ vec3 lightColorOut = real_specular.rgb * lightBrightness * shadowed* atten;
float Sat_NL_Att = saturate( nDotL * atten * shadowed ) * lightBrightness;
- vec3 lightColorOut = lightMapParams.rgb * lightcol;
vec4 addToResult = vec4(0.0);
// TODO: This needs to be removed when lightmapping is disabled
@@ -269,5 +275,5 @@ void main()
addToResult = ( 1.0 - shadowed ) * abs(lightMapParams);
- OUT_col = AL_DeferredOutput(lightColorOut+subsurface*(1.0-Sat_NL_Att), colorSample.rgb, matInfo, addToResult, specular, Sat_NL_Att);
+ OUT_col = vec4((lightColorOut*Sat_NL_Att+subsurface*(1.0-Sat_NL_Att)+addToResult.rgb),real_specular.a);
@@ -0,0 +1,59 @@
+#include "shadergen:/autogenConditioners.h"
+#include "../../../postFx/gl/postFX.glsl"
+uniform sampler2D colorBufferTex;
+uniform sampler2D matInfoTex;
+uniform sampler2D deferredTex;
+out vec4 OUT_col;
+ float depth = deferredUncondition( deferredTex, uv0 ).w;
+ if (depth>0.9999)
+ OUT_col = vec4(0.0);
@@ -0,0 +1,229 @@
+#include "farFrustumQuad.glsl"
+#include "lightingUtils.glsl"
+#include "../../../gl/lighting.glsl"
+#line 8
+in vec4 pos;
+in vec4 wsEyeDir;
+in vec4 ssPos;
+in vec4 vsEyeDir;
+uniform sampler2D deferredBuffer;
+uniform sampler2D matInfoBuffer;
+uniform samplerCube cubeMap;
+uniform vec4 rtParams0;
+uniform vec3 probeWSPos;
+uniform vec3 probeLSPos;
+uniform vec4 vsFarPlane;
+uniform float lightRange;
+uniform vec2 lightAttenuation;
+uniform mat4 invViewMat;
+uniform vec3 eyePosWorld;
+uniform vec3 bbMin;
+uniform vec3 bbMax;
+uniform float Intensity;
+//SHTerms
+uniform vec4 SHTerms0;
+uniform vec4 SHTerms1;
+uniform vec4 SHTerms2;
+uniform vec4 SHTerms3;
+uniform vec4 SHTerms4;
+uniform vec4 SHTerms5;
+uniform vec4 SHTerms6;
+uniform vec4 SHTerms7;
+uniform vec4 SHTerms8;
+uniform float SHConsts0;
+uniform float SHConsts1;
+uniform float SHConsts2;
+uniform float SHConsts3;
+uniform float SHConsts4;
+uniform float useSphereMode;
+vec4 decodeSH(vec3 normal)
+ vec3 l00 = SHTerms0.rgb;
+ vec3 l10 = SHTerms1.rgb;
+ vec3 l11 = SHTerms2.rgb;
+ vec3 l12 = SHTerms3.rgb;
+ vec3 l20 = SHTerms4.rgb;
+ vec3 l21 = SHTerms5.rgb;
+ vec3 l22 = SHTerms6.rgb;
+ vec3 l23 = SHTerms7.rgb;
+ vec3 l24 = SHTerms8.rgb;
+ vec3 result = (
+ l00 * SHConsts0 +
+ l12 * SHConsts1 * x +
+ l10 * SHConsts1 * y +
+ l11 * SHConsts1 * z +
+ l20 * SHConsts2 * x*y +
+ l21 * SHConsts2 * y*z +
+ l22 * SHConsts3 * (3.0*z*z - 1.0) +
+ l23 * SHConsts2 * x*z +
+ l24 * SHConsts4 * (x*x - y*y)
+ return vec4(result,1);
+ // Compute scene UV
+ vec3 ssPos = ssPos.xyz / ssPos.w;
+ //vec4 hardCodedRTParams0 = vec4(0,0.0277777780,1,0.972222209);
+ vec2 uvScene = getUVFromSSPos( ssPos, rtParams0 );
+ vec4 matInfo = texture( matInfoBuffer, uvScene );
+ // Sample/unpack the normal/z data
+ vec4 deferredSample = deferredUncondition( deferredBuffer, uvScene );
+ vec3 normal = deferredSample.rgb;
+ float depth = deferredSample.a;
+ OUT_col = vec4(0,0,0,0);
+ // Need world-space normal.
+ vec3 wsNormal = tMul(vec4(normal, 1), invViewMat).rgb;
+ vec4 color = vec4(1, 1, 1, 1);
+ vec4 ref = vec4(0,0,0,0);
+ float alpha = 0;
+ vec3 eyeRay = getDistanceVectorToPlane( -vsFarPlane.w, vsEyeDir.xyz, vsFarPlane );
+ vec3 viewSpacePos = eyeRay * depth;
+ vec3 wsEyeRay = tMul(vec4(eyeRay, 1), invViewMat).rgb;
+ // Use eye ray to get ws pos
+ vec3 worldPos = vec3(eyePosWorld + wsEyeRay * depth);
+ float smoothness = min((1.0 - matInfo.b)*11.0 + 1.0, 8.0);//bump up to 8 for finalization
+ if(useSphereMode>0.0)
+ // Eye ray - Eye -> Pixel
+ // Build light vec, get length, clip pixel if needed
+ vec3 lightVec = probeLSPos - viewSpacePos;
+ float lenLightV = length( lightVec );
+ clip( lightRange - lenLightV );
+ // Get the attenuated falloff.
+ float atten = attenuate( vec4(1,1,1,1), lightAttenuation, lenLightV );
+ clip( atten - 1e-6 );
+ // Normalize lightVec
+ lightVec /= lenLightV;
+ // If we can do dynamic branching then avoid wasting
+ // fillrate on pixels that are backfacing to the light.
+ float nDotL = abs(dot( lightVec, normal ));
+ float Sat_NL_Att = saturate( nDotL * atten );
+ vec3 reflectionVec = reflect(wsEyeDir, vec4(wsNormal,nDotL)).xyz;
+ vec3 nrdir = normalize(reflectionVec);
+ vec3 rbmax = (bbMax - worldPos.xyz) / nrdir;
+ vec3 rbmin = (bbMin - worldPos.xyz) / nrdir;
+ vec3 rbminmax = rbmin;
+ if (nrdir.x > 0.0)
+ rbminmax.x = rbmax.x;
+ if (nrdir.y > 0.0)
+ rbminmax.y = rbmax.y;
+ if (nrdir.z > 0.0)
+ rbminmax.z = rbmax.z;
+ float fa = min(min(rbminmax.x,rbminmax.y),rbminmax.z);
+ if (dot( lightVec, normal )<0.0f)
+ clip(fa);
+ vec3 posOnBox = worldPos.xyz + nrdir * fa;
+ reflectionVec = posOnBox - probeWSPos;
+ //reflectionVec = tMul(probeWSPos,reflectionVec);
+ ref = vec4(reflectionVec, smoothness);
+ alpha = Sat_NL_Att;
+ float lenLightV = length(lightVec);
+ //clip(lightRange - lenLightV);
+ float nDotL = abs(dot(lightVec, normal));
+ vec3 reflectionVec = reflect(wsEyeDir, vec4(wsNormal, nDotL)).xyz;
+ float fa = min(min(rbminmax.x, rbminmax.y), rbminmax.z);
+ if (dot(lightVec, normal)<0.0f)
+ //Try to clip anything that falls outside our box as well
+ //TODO: Make it support rotated boxes as well
+ if(worldPos.x > bbMax.x || worldPos.y > bbMax.y || worldPos.z > bbMax.z ||
+ worldPos.x < bbMin.x || worldPos.y < bbMin.y || worldPos.z < bbMin.z)
+ clip(-1);
+ alpha = 1;
+ color = textureLod(cubeMap, vec3(ref.xyz), float(ref.w));
+ vec4 specularColor = (color);
+ vec4 indirectColor = (decodeSH(wsNormal));
+ color.rgb = lerp(indirectColor.rgb * 1.5, specularColor.rgb * 1.5, matInfo.b);
+ OUT_col = vec4(color.rgb, alpha);
@@ -0,0 +1,32 @@
+// This is the shader input
+struct Vert
+ float4 position : POSITION;
+ float2 uv0 : TEXCOORD0;
+ float3 wsEyeRay : TEXCOORD1;
+// This is the shader output data.
+struct Conn
+// Render Target Paramaters
+float4 rtParams0;
+Conn main(Vert IN,
+ uniform float4x4 modelView : register(C0))
+ Conn OUT;
+ OUT.position = IN.position;
+ OUT.uv0 = viewportCoordToRenderTarget( IN.uv0, rtParams0 );
+ OUT.wsEyeRay = IN.wsEyeRay;
+ return OUT;
@@ -80,12 +80,13 @@ void main()
vec3 ssPos = IN_ssPos.xyz / IN_ssPos.w;
- OUT_col = vec4(0.0, 0.0, 0.0, 0.0);
+ OUT_col = vec4(0.0, 0.0, 0.0, 0.0);
@@ -182,12 +183,19 @@ void main()
- float specular = AL_CalcSpecular( -lightToPxlVec,
+ vec3 lightVec = lightPosition - viewSpacePos;
@@ -206,5 +214,5 @@ void main()
@@ -193,12 +193,13 @@ vec4 AL_VectorLightShadowCast( sampler2D _sourceshadowMap,
float4 matInfo = texture( matInfoBuffer, uv0 );
- OUT_col = vec4(1.0, 1.0, 1.0, 0.0);
@@ -289,29 +290,20 @@ void main()
#endif // !NO_SHADOW
- // Specular term
- float specular = AL_CalcSpecular( -lightDirection,
- normalize(-vsEyeRay) ) * lightBrightness * shadowed;
+ // Specular term
+ vec3 viewSpacePos = vsEyeRay * depth;
+ lightColor.rgb,
+ normalize( -lightDirection ),
+ 1.0-matInfo.b,
+ vec3 lightColorOut = real_specular.rgb * lightBrightness * shadowed;
float Sat_NL_Att = saturate( dotNL * shadowed ) * lightBrightness;
- vec3 lightColorOut = lightMapParams.rgb * lightColor.rgb;
- vec4 addToResult = (lightAmbient * (1 - ambientCameraFactor)) + ( lightAmbient * ambientCameraFactor * saturate(dot(normalize(-vsEyeRay), normal)) );
- // TODO: This needs to be removed when lightmapping is disabled
- // as its extra work per-pixel on dynamic lit scenes.
- // Special lightmapping pass.
- if ( lightMapParams.a < 0.0 )
- // This disables shadows on the backsides of objects.
- shadowed = dotNL < 0.0f ? 1.0f : shadowed;
- Sat_NL_Att = 1.0f;
- lightColorOut = vec3(shadowed);
- specular *= lightBrightness;
- addToResult = ( 1.0 - shadowed ) * abs(lightMapParams);
+ float Sat_NdotV = saturate(dot(normalize(-vsEyeRay), normal));
+ vec4 addToResult = ( lightAmbient * (1 - ambientCameraFactor)) + ( lightAmbient * ambientCameraFactor * Sat_NdotV );
// Sample the AO texture.
#ifdef USE_SSAO_MASK
@@ -323,5 +315,5 @@ void main()
lightColorOut = debugColor;
+ OUT_col = vec4(matInfo.g*(lightColorOut*Sat_NL_Att+subsurface*(1.0-Sat_NL_Att)+addToResult.rgb),real_specular.a);
@@ -0,0 +1,63 @@
+TORQUE_UNIFORM_SAMPLERCUBE(environmentMap, 0);
+ float3 N = getCubeDir(face,IN.uv);
+ float3 irradiance = 0;
+ float3 up = float3(0.0, 0.0, 1.0);
+ float3 right = cross(up, N);
+ float3 tangentSample = float3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));
+ float3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * N;
+ irradiance += TORQUE_TEXCUBE(environmentMap, sampleVec).rgb * cos(theta) * sin(theta);
+ return float4(irradiance, 1.0);
@@ -136,48 +136,58 @@ uniform float2 lightAttenuation;
uniform float3x3 viewToLightProj;
uniform float3x3 dynamicViewToLightProj;
-float4 main( ConvexConnectP IN ) : TORQUE_TARGET0
+struct PS_OUTPUT
+ float4 diffuse: TORQUE_TARGET0;
+ float4 spec: TORQUE_TARGET1;
+PS_OUTPUT main(ConvexConnectP IN)
+ PS_OUTPUT Output = (PS_OUTPUT)0;
// Compute scene UV
float3 ssPos = IN.ssPos.xyz / IN.ssPos.w;
- float2 uvScene = getUVFromSSPos( ssPos, rtParams0 );
+ float2 uvScene = getUVFromSSPos(ssPos, rtParams0);
// Matinfo flags
- float4 matInfo = TORQUE_TEX2D( matInfoBuffer, uvScene );
+ float4 matInfo = TORQUE_TEX2D(matInfoBuffer, uvScene);
//early out if emissive
- return float4(0.0, 0.0, 0.0, 0.0);
+ //return float4(0.0, 0.0, 0.0, 0.0);
+ return Output;
- float4 colorSample = TORQUE_TEX2D( colorBuffer, uvScene );
- float3 subsurface = float3(0.0,0.0,0.0);
- if (getFlag( matInfo.r, 1 ))
+ float4 colorSample = TORQUE_TEX2D(colorBuffer, uvScene);
+ float3 subsurface = float3(0.0, 0.0, 0.0);
+ if (getFlag(matInfo.r, 1))
subsurface = colorSample.rgb;
- if (colorSample.r>colorSample.g)
+ if (colorSample.r > colorSample.g)
subsurface = float3(0.772549, 0.337255, 0.262745);
subsurface = float3(0.337255, 0.772549, 0.262745);
// Sample/unpack the normal/z data
- float4 deferredSample = TORQUE_DEFERRED_UNCONDITION( deferredBuffer, uvScene );
+ float4 deferredSample = TORQUE_DEFERRED_UNCONDITION(deferredBuffer, uvScene);
float3 normal = deferredSample.rgb;
float depth = deferredSample.a;
// Eye ray - Eye -> Pixel
- float3 eyeRay = getDistanceVectorToPlane( -vsFarPlane.w, IN.vsEyeDir.xyz, vsFarPlane );
+ float3 eyeRay = getDistanceVectorToPlane(-vsFarPlane.w, IN.vsEyeDir.xyz, vsFarPlane);
float3 viewSpacePos = eyeRay * depth;
// Build light vec, get length, clip pixel if needed
float3 lightVec = lightPosition - viewSpacePos;
- float lenLightV = length( lightVec );
- clip( lightRange - lenLightV );
+ clip(lightRange - lenLightV);
// Get the attenuated falloff.
- float atten = attenuate( lightColor, lightAttenuation, lenLightV );
- clip( atten - 1e-6 );
+ float atten = attenuate(lightColor, lightAttenuation, lenLightV);
+ clip(atten - 1e-6);
// Normalize lightVec
lightVec /= lenLightV;
@@ -187,97 +197,93 @@ float4 main( ConvexConnectP IN ) : TORQUE_TARGET0
float nDotL = dot( lightVec, normal );
//DB_CLIP( nDotL < 0 );
- #ifdef NO_SHADOW
- float shadowed = 1.0;
- #else
- // Get a linear depth from the light source.
- float distToLight = lenLightV / lightRange;
- #ifdef SHADOW_CUBE
- // TODO: We need to fix shadow cube to handle soft shadows!
- float occ = TORQUE_TEXCUBE( shadowMap, mul( viewToLightProj, -lightVec ) ).r;
- float shadowed = saturate( exp( lightParams.y * ( occ - distToLight ) ) );
- // Static
- float2 shadowCoord = decodeShadowCoord( mul( viewToLightProj, -lightVec ) ).xy;
- float static_shadowed = softShadow_filter( TORQUE_SAMPLER2D_MAKEARG(shadowMap),
- ssPos.xy,
- shadowCoord,
- shadowSoftness,
- distToLight,
- nDotL,
- lightParams.y );
- // Dynamic
- float2 dynamicShadowCoord = decodeShadowCoord( mul( dynamicViewToLightProj, -lightVec ) ).xy;
- float dynamic_shadowed = softShadow_filter( TORQUE_SAMPLER2D_MAKEARG(dynamicShadowMap),
- dynamicShadowCoord,
- float shadowed = min(static_shadowed, dynamic_shadowed);
+#ifdef NO_SHADOW
+ float shadowed = 1.0;
+#else
+ // Get a linear depth from the light source.
+ float distToLight = lenLightV / lightRange;
+#ifdef SHADOW_CUBE
+ // TODO: We need to fix shadow cube to handle soft shadows!
+ float occ = TORQUE_TEXCUBE(shadowMap, mul(viewToLightProj, -lightVec)).r;
+ float shadowed = saturate(exp(lightParams.y * (occ - distToLight)));
+ float2 shadowCoord = decodeShadowCoord(mul(viewToLightProj, -lightVec)).xy;
+ float static_shadowed = softShadow_filter(TORQUE_SAMPLER2D_MAKEARG(shadowMap),
+ ssPos.xy,
+ shadowCoord,
+ shadowSoftness,
+ distToLight,
+ nDotL,
+ lightParams.y);
+ float2 dynamicShadowCoord = decodeShadowCoord(mul(dynamicViewToLightProj, -lightVec)).xy;
+ float dynamic_shadowed = softShadow_filter(TORQUE_SAMPLER2D_MAKEARG(dynamicShadowMap),
+ dynamicShadowCoord,
+ float shadowed = min(static_shadowed, dynamic_shadowed);
+#endif // !NO_SHADOW
- #endif // !NO_SHADOW
float3 lightcol = lightColor.rgb;
- #ifdef USE_COOKIE_TEX
+#ifdef USE_COOKIE_TEX
- // Lookup the cookie sample.
- float4 cookie = TORQUE_TEXCUBE( cookieMap, mul( viewToLightProj, -lightVec ) );
+ // Lookup the cookie sample.
+ float4 cookie = TORQUE_TEXCUBE(cookieMap, mul(viewToLightProj, -lightVec));
- // Multiply the light with the cookie tex.
- lightcol *= cookie.rgb;
+ // Multiply the light with the cookie tex.
+ lightcol *= cookie.rgb;
- // Use a maximum channel luminance to attenuate
- // the lighting else we get specular in the dark
- // regions of the cookie texture.
- atten *= max( cookie.r, max( cookie.g, cookie.b ) );
+ // Use a maximum channel luminance to attenuate
+ // the lighting else we get specular in the dark
+ // regions of the cookie texture.
+ atten *= max(cookie.r, max(cookie.g, cookie.b));
// NOTE: Do not clip on fully shadowed pixels as it would
- float specular = 0;
+ float3 l = lightVec;// normalize(-lightDirection);
+ float3 v = eyeRay;// normalize(eyePosWorld - worldPos.xyz);
+ float3 h = normalize(v + l);
+ float dotNLa = clamp(dot(normal, l), 0.0, 1.0);
+ float dotNVa = clamp(dot(normal, v), 0.0, 1.0);
+ float dotNHa = clamp(dot(normal, h), 0.0, 1.0);
+ float dotHVa = clamp(dot(normal, v), 0.0, 1.0);
+ float dotLHa = clamp(dot(l, h), 0.0, 1.0);
+ float roughness = matInfo.g;
+ float metalness = matInfo.b;
+ //diffuse
+ float disDiff = Fr_DisneyDiffuse(dotNVa, dotNLa, dotLHa, roughness);
+ float3 diffuse = float3(disDiff, disDiff, disDiff) / M_PI_F;// alternative: (lightColor * dotNL) / Pi;
+ //specular
+ float3 specular = directSpecular(normal, v, l, roughness, 1.0) * lightColor.rgb;
- float4 real_specular = EvalBDRF( float3( 1.0, 1.0, 1.0 ),
- lightcol,
- lightVec,
- viewSpacePos,
- 1.0-matInfo.b,
- matInfo.a );
- float3 lightColorOut = real_specular.rgb * lightBrightness * shadowed* atten;
- //lightColorOut /= colorSample.rgb;
- float Sat_NL_Att = saturate( nDotL * atten * shadowed ) * lightBrightness;
- float4 addToResult = 0.0;
- shadowed = nDotL < 0.0f ? 1.0f : shadowed;
- shadowed = lerp( 1.0f, shadowed, atten );
- lightColorOut = shadowed;
- return float4((lightColorOut*Sat_NL_Att+subsurface*(1.0-Sat_NL_Att)+addToResult.rgb),real_specular.a);
+ if (nDotL<0) shadowed = 0;
+ float Sat_NL_Att = saturate( nDotL * shadowed ) * lightBrightness;
+ //output
+ Output.diffuse = float4(diffuse * lightBrightness*shadowed, Sat_NL_Att);
+ Output.spec = float4(specular * lightBrightness*shadowed, Sat_NL_Att);
@@ -0,0 +1,130 @@
+ float4 hpos : SV_Position;
+uniform float roughness;
+uniform int mipSize;
+uniform int resolution;
+ return float2(float(i) / float(N), RadicalInverse_VdC(i));
+float DistributionGGX(float3 N, float3 H, float roughness)
+ float a = roughness * roughness;
+ float a2 = a * a;
+ float NdotH = max(dot(N, H), 0.0);
+ float NdotH2 = NdotH * NdotH;
+ float nom = a2;
+ float denom = (NdotH2 * (a2 - 1.0) + 1.0);
+ denom = M_PI_F * denom * denom;
+float3 ImportanceSampleGGX(float2 Xi, float3 N)
+ float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
+ // from spherical coordinates to cartesian coordinates
+ // from tangent-space vector to world-space sample vector
+float3 prefilterEnvMap(float3 R)
+ int sampleCount = resolution*2;
+ float3 N = R;
+ float3 V = R;
+ float totalWeight = 0.0;
+ float3 prefilteredColor = float3(0.0, 0.0, 0.0);
+ for (int i = 0; i < sampleCount; ++i)
+ float2 Xi = Hammersley(i, sampleCount);
+ float3 H = ImportanceSampleGGX(Xi, N);
+ if (NdotL > 0.0)
+ // sample from the environment's mip level based on roughness/pdf
+ float D = DistributionGGX(N, H, roughness);
+ float HdotV = max(dot(H, V), 0.0);
+ float pdf = D * NdotH / (4.0 * HdotV) + 0.0001;
+ float saTexel = 4.0 * M_PI_F / (6.0 * sampleCount * sampleCount);
+ float saSample = 1.0 / (float(sampleCount) * pdf + 0.0001);
+ float mipLevel = roughness == 0.0 ? 0.0 : 0.5 * log2(saSample / saTexel);
+ prefilteredColor += TORQUE_TEXCUBELOD(environmentMap, float4(L, mipLevel)).rgb * NdotL;
+ totalWeight += NdotL;
+ return (prefilteredColor / totalWeight);
+ float3 N = getCubeDir(face, IN.uv);
+ return float4(prefilterEnvMap(N), 1.0);
@@ -0,0 +1,52 @@
+#include "../../shaderModelAutoGen.hlsl"
+#include "../../postfx/postFx.hlsl"
+#include "shaders/common/torque.hlsl"
+TORQUE_UNIFORM_SAMPLER2D(colorBufferTex,0);
+TORQUE_UNIFORM_SAMPLER2D(matInfoTex,2);
+TORQUE_UNIFORM_SAMPLER2D(deferredTex,4);
+float4 main( PFXVertToPix IN) : TORQUE_TARGET0
+ float depth = TORQUE_DEFERRED_UNCONDITION( deferredTex, IN.uv0 ).w;
+ return float4(0,0,0,0);
+ float3 colorBuffer = TORQUE_TEX2D( colorBufferTex, IN.uv0 ).rgb; //albedo
+ float4 matInfo = TORQUE_TEX2D(matInfoTex, IN.uv0); //flags|smoothness|ao|metallic
+ return float4(colorBuffer, 1.0);
+ float4 diffuseLighting = TORQUE_TEX2D( diffuseLightingBuffer, IN.uv0 ); //shadowmap*specular
+ colorBuffer *= diffuseLighting.rgb;
+ return hdrEncode( float4(colorBuffer.rgb, 1.0) );
@@ -0,0 +1,217 @@
+#include "farFrustumQuad.hlsl"
+#include "lightingUtils.hlsl"
+#include "../../lighting.hlsl"
+struct ConvexConnectP
+ float4 pos : TORQUE_POSITION;
+ float4 wsEyeDir : TEXCOORD0;
+ float4 ssPos : TEXCOORD1;
+ float4 vsEyeDir : TEXCOORD2;
+TORQUE_UNIFORM_SAMPLER2D(deferredBuffer, 0);
+TORQUE_UNIFORM_SAMPLER2D(matInfoBuffer, 1);
+TORQUE_UNIFORM_SAMPLERCUBE(cubeMap, 2);
+TORQUE_UNIFORM_SAMPLERCUBE(irradianceCubemap, 3);
+TORQUE_UNIFORM_SAMPLER2D(BRDFTexture, 4);
+uniform float4 rtParams0;
+uniform float3 probeWSPos;
+uniform float3 probeLSPos;
+uniform float4 vsFarPlane;
+uniform float radius;
+uniform float2 attenuation;
+uniform float4x4 invViewMat;
+uniform float3 eyePosWorld;
+uniform float3 bbMin;
+uniform float3 bbMax;
+float3 iblSpecular(float3 v, float3 n, float roughness)
+ float3 R = reflect(v, n);
+ const float MAX_REFLECTION_LOD = 6.0;
+ float3 prefilteredColor = TORQUE_TEXCUBELOD(cubeMap, float4(R, roughness * MAX_REFLECTION_LOD)).rgb;
+ float2 envBRDF = TORQUE_TEX2D(BRDFTexture, float2(max(dot(n, v), 0.0), roughness)).rg;
+ //return prefilteredColor * (envBRDF.x + envBRDF.y);
+ return prefilteredColor;
+// Box Projected IBL Lighting
+// Based on: http://www.gamedev.net/topic/568829-box-projected-cubemap-environment-mapping/
+float3 boxProject(float3 wsPosition, float3 reflectDir, float3 boxWSPos, float3 boxMin, float3 boxMax)
+ float3 nrdir = normalize(reflectDir);
+ float3 rbmax = (boxMax - wsPosition) / nrdir;
+ float3 rbmin = (boxMin - wsPosition) / nrdir;
+ float3 rbminmax;
+ rbminmax.x = (nrdir.x > 0.0) ? rbmax.x : rbmin.x;
+ rbminmax.y = (nrdir.y > 0.0) ? rbmax.y : rbmin.y;
+ rbminmax.z = (nrdir.z > 0.0) ? rbmax.z : rbmin.z;
+ float3 posonbox = wsPosition + nrdir * fa;
+ return posonbox - boxWSPos;
+float3 iblBoxDiffuse(float3 normal,
+ float3 wsPos,
+ TORQUE_SAMPLERCUBE(irradianceCube),
+ float3 boxPos,
+ float3 boxMin,
+ float3 boxMax)
+ // Irradiance (Diffuse)
+ float3 cubeN = normalize(normal);
+ float3 irradiance = TORQUE_TEXCUBE(irradianceCube, cubeN).xyz;
+ return irradiance;
+float3 iblBoxSpecular(float3 normal,
+ float roughness,
+ float3 viewDir,
+ TORQUE_SAMPLER2D(brdfTexture),
+ TORQUE_SAMPLERCUBE(radianceCube),
+ float3 v = viewDir;
+ float3 n = normalize(normal);
+ float ndotv = clamp(dot(n, v), 0.0, 1.0);
+ // BRDF
+ float2 brdf = TORQUE_TEX2D(brdfTexture, float2(roughness, ndotv)).xy;
+ // Radiance (Specular)
+ float lod = roughness * 6.0;
+ float3 r = 2.0 * ndotv * n - v; // reflect(v, n);
+ float3 cubeR = normalize(r);
+ cubeR = boxProject(wsPos, cubeR, boxPos, boxMin, boxMax);
+ float3 radiance = TORQUE_TEXCUBELOD(radianceCube, float4(cubeR, lod)).xyz * (brdf.x + brdf.y);
+ return radiance;
+PS_OUTPUT main( ConvexConnectP IN )
+ float3 ssPos = IN.ssPos.xyz / IN.ssPos.w;
+ //float4 hardCodedRTParams0 = float4(0,0.0277777780,1,0.972222209);
+ float2 uvScene = getUVFromSSPos( ssPos, rtParams0 );
+ float4 matInfo = TORQUE_TEX2D( matInfoBuffer, uvScene );
+ float4 deferredSample = TORQUE_DEFERRED_UNCONDITION( deferredBuffer, uvScene );
+ float3 normal = deferredSample.rgb;
+ float3 wsNormal = mul(float4(normal, 1), invViewMat).rgb;
+ float4 color = float4(1, 1, 1, 1);
+ float4 ref = float4(0,0,0,0);
+ float alpha = 1;
+ float3 eyeRay = getDistanceVectorToPlane( -vsFarPlane.w, IN.vsEyeDir.xyz, vsFarPlane );
+ float3 viewSpacePos = eyeRay * depth;
+ float3 wsEyeRay = mul(float4(eyeRay, 1), invViewMat).rgb;
+ float3 worldPos = float3(eyePosWorld + wsEyeRay * depth);
+ if(useSphereMode)
+ float3 lightVec = probeLSPos - viewSpacePos;
+ clip( radius - lenLightV );
+ float atten = attenuate( float4(1,1,1,1), attenuation, lenLightV );
+ lightVec = normalize(lightVec);
+ float3 reflectionVec = reflect(IN.wsEyeDir, float4(wsNormal,nDotL)).xyz;
+ float3 nrdir = normalize(reflectionVec);
+ float3 rbmax = (bbMax - worldPos.xyz) / nrdir;
+ float3 rbmin = (bbMin - worldPos.xyz) / nrdir;
+ float3 rbminmax = (nrdir > 0.0) ? rbmax : rbmin;
+ float3 posOnBox = worldPos.xyz + nrdir * fa;
+ reflectionVec = mul(probeWSPos,reflectionVec);
+ ref = float4(reflectionVec, smoothness);
+ float roughness = 1 - matInfo.b;
+ float3 irradiance = TORQUE_TEXCUBELOD(irradianceCubemap, ref).rgb;
+ float3 specular = TORQUE_TEXCUBELOD(cubeMap, ref).rgb;// iblSpecular(wsEyeRay, wsNormal, roughness);
+ Output.diffuse = float4(irradiance.rgb, alpha);
+ Output.spec = float4(specular.rgb, alpha);
+ float blendVal = 1.0;
+ float3 pixDir = normalize(eyePosWorld.xyz - worldPos.xyz);
+ Output.diffuse = float4(iblBoxDiffuse(wsNormal, worldPos, TORQUE_SAMPLERCUBE_MAKEARG(irradianceCubemap), probeWSPos, bbMin, bbMax), blendVal);
+ Output.spec = float4(iblBoxSpecular(wsNormal, worldPos, 1.0 - matInfo.b, pixDir, TORQUE_SAMPLER2D_MAKEARG(BRDFTexture), TORQUE_SAMPLERCUBE_MAKEARG(cubeMap), probeWSPos, bbMin, bbMax), blendVal);
@@ -0,0 +1,145 @@
+/*uniform float4 SHTerms0;
+uniform float4 SHTerms1;
+uniform float4 SHTerms2;
+uniform float4 SHTerms3;
+uniform float4 SHTerms4;
+uniform float4 SHTerms5;
+uniform float4 SHTerms6;
+uniform float4 SHTerms7;
+uniform float4 SHTerms8;
+float4 decodeSH(float3 normal)
+ float3 l00 = SHTerms0.rgb;
+ float3 l10 = SHTerms1.rgb;
+ float3 l11 = SHTerms2.rgb;
+ float3 l12 = SHTerms3.rgb;
+ float3 l20 = SHTerms4.rgb;
+ float3 l21 = SHTerms5.rgb;
+ float3 l22 = SHTerms6.rgb;
+ float3 l23 = SHTerms7.rgb;
+ float3 l24 = SHTerms8.rgb;
+ float3 result = (
+ return float4(result,1);
+ const float MAX_REFLECTION_LOD = 4.0;
+ return prefilteredColor * (envBRDF.x + envBRDF.y);
+ //return prefilteredColor;
+ float3 reflectionVec = reflect(IN.wsEyeDir, float4(wsNormal,1)).xyz;
+ float3 v = normalize(eyePosWorld - worldPos);
+ float3 irradiance = TORQUE_TEXCUBE(irradianceCubemap, wsNormal).rgb;
+ float3 specular = iblSpecular(wsEyeRay, wsNormal, roughness);
+ Output.diffuse = float4(irradiance.rgb, 1);
+ Output.spec = float4(specular.rgb, 1);
@@ -192,23 +192,24 @@ float4 AL_VectorLightShadowCast( TORQUE_SAMPLER2D(sourceShadowMap),
dot( finalMask, overDarkPSSM ) ) );
-float4 main( FarFrustumQuadConnectP IN ) : TORQUE_TARGET0
+ float4 spec: TORQUE_TARGET0;
+ float4 diffuse: TORQUE_TARGET1;
+PS_OUTPUT main(FarFrustumQuadConnectP IN)
- float4 matInfo = TORQUE_TEX2D( matInfoBuffer, IN.uv0 );
- //early out if emissive
- bool emissive = getFlag(matInfo.r, 0);
- if (emissive)
- float4 colorSample = TORQUE_TEX2D( colorBuffer, IN.uv0 );
+ float4 matInfo = TORQUE_TEX2D(matInfoBuffer, IN.uv0);
+ float4 colorSample = TORQUE_TEX2D(colorBuffer, IN.uv0);
@@ -224,96 +225,97 @@ float4 main( FarFrustumQuadConnectP IN ) : TORQUE_TARGET0
// Get the light attenuation.
float dotNL = dot(-lightDirection, normal);
- #ifdef PSSM_DEBUG_RENDER
- float3 debugColor = float3(0,0,0);
+#ifdef PSSM_DEBUG_RENDER
+ float3 debugColor = float3(0, 0, 0);
- // Fully unshadowed.
- debugColor = float3(1.0,1.0,1.0);
+ // Fully unshadowed.
- float4 static_shadowed_colors = AL_VectorLightShadowCast( TORQUE_SAMPLER2D_MAKEARG(shadowMap),
- IN.uv0.xy,
- worldToLightProj,
- worldPos,
- scaleX, scaleY,
- offsetX, offsetY,
- farPlaneScalePSSM,
- atlasXOffset, atlasYOffset,
- atlasScale,
- dotNL,
- overDarkPSSM);
- float4 dynamic_shadowed_colors = AL_VectorLightShadowCast( TORQUE_SAMPLER2D_MAKEARG(dynamicShadowMap),
- dynamicWorldToLightProj,
- dynamicScaleX, dynamicScaleY,
- dynamicOffsetX, dynamicOffsetY,
- dynamicFarPlaneScalePSSM,
- float static_shadowed = static_shadowed_colors.a;
- float dynamic_shadowed = dynamic_shadowed_colors.a;
- debugColor = static_shadowed_colors.rgb*0.5+dynamic_shadowed_colors.rgb*0.5;
- // Fade out the shadow at the end of the range.
- float4 zDist = (zNearFarInvNearFar.x + zNearFarInvNearFar.y * depth);
- float fadeOutAmt = ( zDist.x - fadeStartLength.x ) * fadeStartLength.y;
+ debugColor = float3(1.0, 1.0, 1.0);
- static_shadowed = lerp( static_shadowed, 1.0, saturate( fadeOutAmt ) );
- dynamic_shadowed = lerp( dynamic_shadowed, 1.0, saturate( fadeOutAmt ) );
+ float4 static_shadowed_colors = AL_VectorLightShadowCast(TORQUE_SAMPLER2D_MAKEARG(shadowMap),
+ IN.uv0.xy,
+ worldToLightProj,
+ worldPos,
+ scaleX, scaleY,
+ offsetX, offsetY,
+ farPlaneScalePSSM,
+ atlasXOffset, atlasYOffset,
+ atlasScale,
+ dotNL,
+ overDarkPSSM);
+ float4 dynamic_shadowed_colors = AL_VectorLightShadowCast(TORQUE_SAMPLER2D_MAKEARG(dynamicShadowMap),
+ dynamicWorldToLightProj,
+ dynamicScaleX, dynamicScaleY,
+ dynamicOffsetX, dynamicOffsetY,
+ dynamicFarPlaneScalePSSM,
+ float static_shadowed = static_shadowed_colors.a;
+ float dynamic_shadowed = dynamic_shadowed_colors.a;
+ debugColor = static_shadowed_colors.rgb*0.5 + dynamic_shadowed_colors.rgb*0.5;
- // temp for debugging. uncomment one or the other.
- //float shadowed = static_shadowed;
- //float shadowed = dynamic_shadowed;
+ // Fade out the shadow at the end of the range.
+ float4 zDist = (zNearFarInvNearFar.x + zNearFarInvNearFar.y * depth);
+ float fadeOutAmt = (zDist.x - fadeStartLength.x) * fadeStartLength.y;
- if ( fadeOutAmt > 1.0 )
- debugColor = 1.0;
+ static_shadowed = lerp(static_shadowed, 1.0, saturate(fadeOutAmt));
+ dynamic_shadowed = lerp(dynamic_shadowed, 1.0, saturate(fadeOutAmt));
- float3 viewSpacePos = IN.vsEyeRay * depth;
- lightColor.rgb,
- normalize( -lightDirection ),
- float3 lightColorOut = real_specular.rgb * lightBrightness * shadowed;
- float Sat_NL_Att = saturate( dotNL * shadowed ) * lightBrightness;
- float Sat_NdotV = saturate(dot(normalize(-IN.vsEyeRay), normal));
- float4 addToResult = ( lightAmbient * (1 - ambientCameraFactor)) + ( lightAmbient * ambientCameraFactor * Sat_NdotV );
- // Sample the AO texture.
- #ifdef USE_SSAO_MASK
- float ao = 1.0 - TORQUE_TEX2D( ssaoMask, viewportCoordToRenderTarget( IN.uv0.xy, rtParams3 ) ).r;
- addToResult *= ao;
- lightColorOut = debugColor;
+ // temp for debugging. uncomment one or the other.
+ //float shadowed = static_shadowed;
+ //float shadowed = dynamic_shadowed;
+ if (fadeOutAmt > 1.0)
+ debugColor = 1.0;
+ float3 l = normalize(-lightDirection);
+ float3 v = normalize(eyePosWorld - worldPos.xyz);
+ //float dotNL = clamp(dot(normal,l), 0.0, 1.0);
- return float4(matInfo.g*(lightColorOut*Sat_NL_Att+subsurface*(1.0-Sat_NL_Att)+addToResult.rgb),real_specular.a);
+ float finalShadowed = shadowed;
+//output
+ Output.diffuse = float4(diffuse * (lightBrightness*shadowed), dotNLa);
+ Output.spec = float4(specular * (lightBrightness*shadowed), dotNLa);
@@ -55,15 +55,16 @@ float3 Uncharted2Tonemap(const float3 x)
return ((x*(A*x + C*B) + D*E) / (x*(A*x + B) + D*F)) - E / F;
-float3 tonemap(float3 c)
+float3 tonemap(float3 color)
const float W = 11.2;
float ExposureBias = 2.0f;
- float ExposureAdjust = 1.5f;
- c *= ExposureAdjust;
- float3 curr = Uncharted2Tonemap(ExposureBias*c);
- float3 whiteScale = 1.0f / Uncharted2Tonemap(W);
- return curr*whiteScale;
+ //float ExposureAdjust = 1.5f;
+ //c *= ExposureAdjust;
+ color = Uncharted2Tonemap(ExposureBias*color);
+ color = color * (1.0f / Uncharted2Tonemap(W));
+ return color;
@@ -100,5 +101,7 @@ float4 main( PFXVertToPix IN ) : TORQUE_TARGET0
sample.g = TORQUE_TEX1D( colorCorrectionTex, sample.g ).g;
sample.b = TORQUE_TEX1D( colorCorrectionTex, sample.b ).b;
+ sample = float4(tonemap(sample.rgb),1);
return sample;
@@ -48,6 +48,29 @@ uniform float Contrast;
+// uncharted 2 tonemapper see: http://filmicgames.com/archives/75
+vec3 Uncharted2Tonemap(vec3 x)
+ const float A = 0.15;
+ const float B = 0.50;
+ const float C = 0.10;
+ const float D = 0.20;
+ const float E = 0.02;
+ const float F = 0.30;
+ return ((x*(A*x + C*B) + D*E) / (x*(A*x + B) + D*F)) - E / F;
+vec3 tonemap(vec3 c)
+ const float W = 11.2;
+ float ExposureBias = 2.0f;
+ float ExposureAdjust = 1.5f;
+ c *= ExposureAdjust;
+ vec3 curr = Uncharted2Tonemap(ExposureBias*c);
+ vec3 whiteScale = 1.0f / Uncharted2Tonemap(vec3(W,W,W));
+ return curr*whiteScale;
vec4 _sample = hdrDecode( texture( sceneTex, IN_uv0 ) );
@@ -76,17 +99,6 @@ void main()
// Add the bloom effect.
_sample += g_fBloomScale * bloom;
- // Map the high range of color values into a range appropriate for
- // display, taking into account the user's adaptation level,
- // white point, and selected value for for middle gray.
- if ( g_fEnableToneMapping > 0.0f )
- float Lp = (g_fMiddleGray / (adaptedLum + 0.0001)) * hdrLuminance( _sample.rgb );
- //float toneScalar = ( Lp * ( 1.0 + ( Lp / ( g_fWhiteCutoff ) ) ) ) / ( 1.0 + Lp );
- float toneScalar = Lp;
- _sample.rgb = mix( _sample.rgb, _sample.rgb * toneScalar, g_fEnableToneMapping );
// Apply the color correction.
_sample.r = texture( colorCorrectionTex, _sample.r ).r;
@@ -310,4 +310,38 @@ float3 simpleFresnel(float3 diffuseColor, float3 reflectColor, float metalness,
return lerp(diffuseColor, reflectColor, fresnelTerm);
+//hlsl version of the glsl funcion mod - note hlsl fmod is different
+#define mod(x,y) (x-y*floor(x/y))
+//get direction for a cube face
+float3 getCubeDir(int face, float2 uv)
+ float2 debiased = uv * 2.0f - 1.0f;
+ float3 dir = 0;
+ switch (face)
+ case 0: dir = float3(1, -debiased.y, -debiased.x);
+ case 1: dir = float3(-1, -debiased.y, debiased.x);
+ case 2: dir = float3(debiased.x, 1, debiased.y);
+ case 3: dir = float3(debiased.x, -1, -debiased.y);
+ case 4: dir = float3(debiased.x, -debiased.y, 1);
+ case 5: dir = float3(-debiased.x, -debiased.y, -1);
+ return normalize(dir);
#endif // _TORQUE_HLSL_
@@ -120,7 +120,6 @@ void main()
// Modulate baseColor by the ambientColor.
vec4 waterBaseColor = baseColor * vec4( ambientColor.rgb, 1 );
- waterBaseColor = waterBaseColor;
// Get the bumpNorm...
vec3 bumpNorm = ( texture( bumpMap, IN_rippleTexCoord01.xy ).rgb * 2.0 - 1.0 ) * rippleMagnitude.x;
@@ -324,7 +324,6 @@ void main()
// Calculate the water "base" color based on depth.
vec4 waterBaseColor = baseColor * texture( depthGradMap, saturate( delta / depthGradMax ) );
waterBaseColor *= vec4( ambientColor.rgb, 1 );
@@ -0,0 +1 @@
+/procedural/
@@ -0,0 +1,8 @@
+singleton Material(ReflectProbePreviewMat)
+ mapTo = "ReflectProbePreviewMat";
+ diffuseColor[0] = "1 1 1 1";
+ translucentBlendOp = "None";
@@ -0,0 +1,49 @@
+#include "shaders/common/shaderModelAutoGen.hlsl"
+#include "shaders/common/lighting/advanced/farFrustumQuad.hlsl"
+#include "shaders/common/lighting/advanced/lightingUtils.hlsl"
+#include "shaders/common/lighting.hlsl"
+TORQUE_UNIFORM_SAMPLERCUBE(cubeMap, 1);
+float4 main( ConvexConnectP IN ) : TORQUE_TARGET0
+ //float3 eyeRay = IN.vsEyeDir.xyz;
+ float3 reflectionVec = reflect(IN.wsEyeDir, float4(normalize(wsNormal),1)).rgb;
+ float4 color = TORQUE_TEXCUBE(cubeMap, reflectionVec);
+ //simple visibility testing
+ //float4 color = float4(1,0,0,1);
+ return float4(color.rgb, 1);
@@ -0,0 +1,58 @@
+#include "shaders/common/hlslStructs.hlsl"
+#include "shaders/common/shaderModel.hlsl"
+struct VertData
+ float3 pos : POSITION;
+ float tangentW : TEXCOORD3;
+ float3 normal : NORMAL;
+ float3 T : TANGENT;
+ float2 texCoord : TEXCOORD0;
+struct ConvexConnectV
+uniform float4x4 modelview;
+uniform float4x4 objTrans;
+uniform float4x4 worldViewOnly;
+ConvexConnectV main( VertData IN )
+ ConvexConnectV OUT;
+ OUT.hpos = mul( modelview, float4(IN.pos,1.0) );
+ OUT.wsEyeDir = mul(objTrans, float4(IN.pos, 1.0)) - float4(eyePosWorld, 0.0);
+ OUT.vsEyeDir = mul(worldViewOnly, float4(IN.pos, 1.0));
+ OUT.ssPos = OUT.hpos;
@@ -1,637 +1,239 @@
-%guiContent = new GuiControl(TerrainMaterialDlg, EditorGuiGroup) {
- canSaveDynamicFields = "0";
- isContainer = "1";
- Profile = "ToolsGuiDefaultProfile";
- HorizSizing = "right";
- VertSizing = "bottom";
+%guiContent = new GuiControl(TerrainMaterialDlg,EditorGuiGroup) {
position = "0 0";
- Extent = "800 768";
- MinExtent = "8 2";
- Visible = "1";
- tooltipprofile = "ToolsGuiToolTipProfile";
+ extent = "1024 768";
+ minExtent = "8 2";
+ horizSizing = "right";
+ vertSizing = "bottom";
+ profile = "ToolsGuiDefaultProfile";
+ visible = "1";
+ active = "1";
+ tooltipProfile = "ToolsGuiToolTipProfile";
hovertime = "1000";
+ isContainer = "1";
+ activeMat = "17411";
+ matIndex = "0";
+ onApplyCallback = "EPainter_TerrainMaterialUpdateCallback";
new GuiWindowCtrl() {
- Profile = "ToolsGuiWindowProfile";
- HorizSizing = "center";
- VertSizing = "center";
- position = "221 151";
- Extent = "394 432";
- MinExtent = "358 432";
- hovertime = "1000";
- Docking = "None";
- Margin = "4 4 4 4";
- Padding = "0 0 0 0";
- AnchorTop = "0";
- AnchorBottom = "0";
- AnchorLeft = "0";
- AnchorRight = "0";
+ text = "Terrain Materials Editor";
resizeWidth = "1";
resizeHeight = "1";
canMove = "1";
canClose = "1";
canMinimize = "0";
canMaximize = "0";
- minSize = "50 50";
+ canCollapse = "0";
closeCommand = "TerrainMaterialDlg.dialogCancel();";
- EdgeSnap = "0";
- text = "Terrain Materials Editor";
- new GuiContainer(){ //Node Properties
+ edgeSnap = "0";
+ docking = "None";
+ margin = "4 4 4 4";
+ padding = "0 0 0 0";
+ anchorTop = "0";
+ anchorBottom = "0";
+ anchorLeft = "0";
+ anchorRight = "0";
+ position = "315 168";
+ extent = "394 432";
+ minExtent = "358 432";
+ horizSizing = "center";
+ vertSizing = "center";
+ profile = "ToolsGuiWindowProfile";
+ hovertime = "1000";
+ canSaveDynamicFields = "0";
+ new GuiContainer() {
+ margin = "0 0 0 0";
+ anchorTop = "1";
+ anchorLeft = "1";
+ position = "6 25";
+ extent = "189 64";
+ horizSizing = "width";
+ profile = "inspectorStyleRolloutDarkProfile";
+ tooltipProfile = "GuiToolTipProfile";
isContainer = "1";
- Profile = "inspectorStyleRolloutDarkProfile";
- HorizSizing = "width";
- Position = "6 25";
- Extent = "189 64";
- new GuiTextCtrl(){
- Position = "5 0";
- Extent = "91 18";
+ new GuiTextCtrl() {
text = "Terrain Materials";
+ maxLength = "1024";
+ position = "5 0";
+ extent = "91 18";
new GuiBitmapButtonCtrl() {
- isContainer = "0";
- Profile = "ToolsGuiButtonProfile";
- HorizSizing = "left";
- VertSizing = "top";
- position = "160 2";
- Extent = "15 15";
- Command = "TerrainMaterialDlg.newMat();";
+ bitmap = "tools/gui/images/new";
+ bitmapMode = "Stretched";
+ autoFitExtents = "0";
+ useModifiers = "0";
+ useStates = "1";
+ masked = "0";
groupNum = "-1";
buttonType = "PushButton";
useMouseEvents = "0";
- bitmap = "tools/gui/images/new";
- new GuiBitmapButtonCtrl() {
+ position = "160 2";
+ extent = "15 15";
+ horizSizing = "left";
+ vertSizing = "top";
+ profile = "ToolsGuiButtonProfile";
+ command = "TerrainMaterialDlg.newMat();";
isContainer = "0";
- position = "173 2";
- Command = "TerrainMaterialDlg.deleteMat();";
+ new GuiBitmapButtonCtrl() {
+ bitmap = "tools/gui/images/delete";
- bitmap = "tools/gui/images/delete";
+ position = "173 2";
+ command = "TerrainMaterialDlg.deleteMat();";
+ isContainer = "0";
new GuiContainer() {
- internalName = "matSettingsParent";
- Profile = "inspectorStyleRolloutProfile";
- VertSizing = "height";
position = "202 26";
- Extent = "185 363";
+ extent = "185 363";
+ vertSizing = "height";
+ profile = "inspectorStyleRolloutProfile";
- Margin = "0 0 0 0";
- AnchorTop = "1";
- AnchorLeft = "1";
+ internalName = "matSettingsParent";
- new GuiBitmapCtrl() {
- position = "1 0";
- Extent = "183 2";
- bitmap = "core/art/gui/images/separator-v";
- wrap = "0";
new GuiTextCtrl() {
- Profile = "ToolsGuiTextProfile";
- position = "8 22";
- Extent = "44 17";
- tooltipprofile = "ToolsGuiDefaultProfile";
text = "Name";
maxLength = "1024";
- new GuiTextEditCtrl() {
- internalName = "matNameCtrl";
+ position = "8 22";
+ extent = "44 17";
+ profile = "ToolsGuiTextProfile";
+ tooltipProfile = "ToolsGuiDefaultProfile";
- Profile = "ToolsGuiTextEditProfile";
- position = "39 21";
- Extent = "143 18";
- maxLength = "1024";
+ new GuiTextEditCtrl() {
historySize = "0";
- password = "0";
tabComplete = "0";
sinkAllKeyEvents = "0";
+ password = "0";
passwordMask = "*";
- altCommand = "TerrainMaterialDlg.setMaterialName( $ThisControl.getText() );";
- new GuiTextCtrl() {
- Profile = "ToolsGuiInspectorTitleTextProfile";
- position = "8 0";
- Extent = "117 16";
- text = "Material Properties";
+ text = "sand";
- new GuiContainer() {
- position = "6 43";
- Extent = "185 75";
+ position = "39 21";
+ extent = "143 18";
+ profile = "ToolsGuiTextEditProfile";
+ altCommand = "TerrainMaterialDlg.setMaterialName( $ThisControl.getText() );";
- new GuiCheckBoxCtrl() {
- internalName = "sideProjectionCtrl";
- Profile = "ToolsGuiCheckBoxProfile";
- position = "55 54";
- Extent = "119 16";
- text = " Use Side Projection";
- groupNum = "-1";
- buttonType = "ToggleButton";
- useMouseEvents = "0";
- useInactiveState = "0";
- internalName = "baseTexCtrl";
- position = "1 1";
- Extent = "47 47";
- bitmap = "tools/materialEditor/gui/unknownImage";
- Extent = "48 48";
- Command = "TerrainMaterialDlg.changeBase();";
- ToolTip = "Change the Active Diffuse Map for this layer";
- buttonType = "PushButton";
- bitmap = "tools/materialEditor/gui/cubemapBtnBorder";
- Profile = "EditorTextProfile";
- position = "56 -3";
- Extent = "39 18";
- text = "Diffuse";
- position = "56 16";
- Extent = "116 17";
- text = "None";
- new GuiButtonCtrl() {
- position = "116 0";
- Extent = "40 16";
- text = "Edit";
- position = "159 0";
- Extent = "16 16";
- Command = "TerrainMaterialDlg-->baseTexCtrl.setBitmap(\"tools/materialEditor/gui/unknownImage\");";
- position = "132 35";
- Extent = "39 16";
- text = "Size";
- internalName = "baseSizeCtrl";
- position = "94 34";
- Extent = "34 18";
- historySize = "0";
- tabComplete = "0";
- sinkAllKeyEvents = "0";
- passwordMask = "*";
- position = "6 116";
- Extent = "175 2";
+ internalName = "matNameCtrl";
- bitmap = "tools/gui/images/separator-v";
canSaveDynamicFields = "0";
- position = "6 295";
- Extent = "185 50";
- internalName = "normTexCtrl";
- text = "Normal";
- Command = "TerrainMaterialDlg.changeNormal();";
- ToolTip = "Change the active Normal Map for this layer.";
- position = "56 15";
- Command = "TerrainMaterialDlg-->normTexCtrl.setBitmap(\"tools/materialEditor/gui/unknownImage\");";
- position = "92 34";
- Extent = "77 16";
- text = "Parallax Scale";
- internalName = "parallaxScaleCtrl";
- position = "55 33";
- text = "0.00";
- position = "6 288";
- extent = "175 2";
+ text = "Material Properties";
+ position = "8 0";
+ extent = "117 16";
minExtent = "8 2";
- horizSizing = "width";
vertSizing = "bottom";
- profile = "ToolsGuiDefaultProfile";
+ profile = "ToolsGuiInspectorTitleTextProfile";
visible = "1";
active = "1";
tooltipProfile = "ToolsGuiToolTipProfile";
@@ -640,19 +242,27 @@
+ new GuiScrollCtrl() {
+ willFirstRespond = "1";
+ hScrollBar = "dynamic";
+ vScrollBar = "dynamic";
+ lockHorizScroll = "1";
+ lockVertScroll = "0";
+ constantThumbHeight = "0";
+ childMargin = "0 0";
+ mouseWheelScrollSpeed = "-1";
margin = "0 0 0 0";
padding = "0 0 0 0";
anchorTop = "1";
anchorBottom = "0";
anchorLeft = "1";
anchorRight = "0";
- position = "6 122";
- extent = "185 72";
+ position = "0 45";
+ extent = "189 329";
horizSizing = "width";
- vertSizing = "bottom";
+ profile = "ToolsGuiScrollProfile";
@@ -661,730 +271,1473 @@
- extent = "47 47";
- minExtent = "8 2";
- horizSizing = "right";
- visible = "1";
- active = "1";
- tooltipProfile = "ToolsGuiToolTipProfile";
- internalName = "macroTexCtrl";
- bitmapMode = "Stretched";
- autoFitExtents = "0";
- useModifiers = "0";
- useStates = "1";
- extent = "48 48";
- command = "TerrainMaterialDlg.changeMacro();";
- tooltipProfile = "ToolsGuiDefaultProfile";
- tooltip = "Change the active Macro Map for this layer.";
- text = "Macro";
- extent = "34 18";
+ position = "1 1";
+ extent = "174 73";
- profile = "EditorTextProfile";
+ new GuiCheckBoxCtrl() {
+ text = " Use Side Projection";
+ groupNum = "-1";
+ buttonType = "ToggleButton";
+ useMouseEvents = "0";
+ position = "55 54";
+ extent = "119 16";
+ profile = "ToolsGuiCheckBoxProfile";
+ internalName = "sideProjectionCtrl";
+ new GuiBitmapCtrl() {
+ bitmap = "art/terrains/Example/sand";
+ color = "255 255 255 255";
+ wrap = "0";
+ extent = "47 47";
+ internalName = "baseTexCtrl";
+ bitmap = "tools/materialEditor/gui/cubemapBtnBorder";
+ buttonType = "PushButton";
+ extent = "48 48";
+ command = "TerrainMaterialDlg.changeBase();";
+ tooltip = "Change the Active Diffuse Map for this layer";
+ text = "Diffuse";
+ position = "56 -3";
+ extent = "39 18";
+ profile = "EditorTextProfile";
+ text = "None";
+ position = "56 16";
+ extent = "79 17";
+ new GuiButtonCtrl() {
+ text = "Edit";
+ position = "110 0";
+ extent = "40 16";
+ position = "153 0";
+ extent = "16 16";
+ command = "TerrainMaterialDlg-->baseTexCtrl.setBitmap(\"tools/materialEditor/gui/unknownImage\");";
+ text = "Size";
+ position = "132 35";
+ extent = "39 16";
+ historySize = "0";
+ tabComplete = "0";
+ sinkAllKeyEvents = "0";
+ passwordMask = "*";
+ text = "200";
+ position = "94 34";
+ extent = "34 18";
+ internalName = "baseSizeCtrl";
+ bitmap = "tools/gui/images/separator-v";
+ position = "1 71";
+ extent = "148 2";
- position = "56 17";
- extent = "116 17";
+ position = "4 78";
+ extent = "174 76";
- profile = "ToolsGuiTextProfile";
- extent = "40 16";
- horizSizing = "left";
- profile = "ToolsGuiButtonProfile";
- extent = "16 16";
profile = "ToolsGuiDefaultProfile";
- command = "TerrainMaterialDlg-->macroTexCtrl.setBitmap(\"tools/materialEditor/gui/unknownImage\");";
- margin = "0 0 0 0";
- padding = "0 0 0 0";
- anchorTop = "0";
- anchorBottom = "0";
- anchorLeft = "0";
- anchorRight = "0";
- position = "132 33";
- extent = "39 16";
- position = "94 32";
- profile = "ToolsGuiTextEditProfile";
- internalName = "macroSizeCtrl";
- text = "Strength";
- position = "39 54";
- extent = "46 16";
+ bitmap = "art/terrains/Example/sand_d";
+ internalName = "detailTexCtrl";
+ command = "TerrainMaterialDlg.changeDetail();";
+ tooltip = "Change the active Detail Map for this layer.";
+ text = "Detail";
+ extent = "30 18";
+ position = "111 0";
+ position = "154 0";
+ command = "TerrainMaterialDlg-->detailTexCtrl.setBitmap(\"tools/materialEditor/gui/unknownImage\");";
+ position = "132 33";
+ text = "10";
+ position = "94 32";
+ internalName = "detSizeCtrl";
+ text = "Strength";
+ position = "39 54";
+ extent = "46 16";
+ text = "1";
+ position = "1 53";
+ internalName = "detStrengthCtrl";
+ text = "Distance";
+ position = "132 54";
+ extent = "45 16";
+ text = "100";
+ position = "94 53";
+ internalName = "detDistanceCtrl";
+ bitmap = "core/art/gui/images/separator-v";
+ position = "1 73";
- position = "1 53";
+ position = "6 158";
+ extent = "174 54";
- internalName = "macroStrengthCtrl";
+ bitmap = "tools/materialEditor/gui/unknownImage";
+ internalName = "normTexCtrl";
+ text = "Normal";
+ command = "TerrainMaterialDlg.changeNormal();";
+ tooltip = "Change the active Normal Map for this layer.";
+ position = "56 15";
+ command = "TerrainMaterialDlg-->normTexCtrl.setBitmap(\"tools/materialEditor/gui/unknownImage\");";
+ text = "Parallax Scale";
+ position = "92 34";
+ extent = "77 16";
+ text = "0";
+ position = "55 33";
+ internalName = "parallaxScaleCtrl";
+ position = "-10 53";
- text = "Distance";
- position = "132 54";
- extent = "45 16";
+ position = "6 216";
+ extent = "174 53";
+ internalName = "compositeTexCtrl";
+ command = "TerrainMaterialDlg.changecomposite();";
+ tooltip = "Change the active composite Map for this layer.";
+ text = "Composite";
+ extent = "61 18";
+ position = "56 17";
+ command = "TerrainMaterialDlg-->compositeTexCtrl.setBitmap(\"tools/materialEditor/gui/unknownImage\");";
+ position = "4 51";
- position = "94 53";
+ position = "6 272";
+ extent = "174 71";
- internalName = "macroDistanceCtrl";
- position = "6 200";
- position = "6 206";
- Extent = "185 72";
- internalName = "detailTexCtrl";
- Command = "TerrainMaterialDlg.changeDetail();";
- ToolTip = "Change the active Detail Map for this layer.";
- Extent = "30 18";
- text = "Detail";
- Command = "TerrainMaterialDlg-->detailTexCtrl.setBitmap(\"tools/materialEditor/gui/unknownImage\");";
- internalName = "detSizeCtrl";
- Extent = "46 16";
- internalName = "detStrengthCtrl";
- Extent = "45 16";
- internalName = "detDistanceCtrl";
+ internalName = "macroTexCtrl";
+ command = "TerrainMaterialDlg.changeMacro();";
+ tooltip = "Change the active Macro Map for this layer.";
+ text = "Macro";
+ extent = "111 17";
+ command = "TerrainMaterialDlg-->macroTexCtrl.setBitmap(\"tools/materialEditor/gui/unknownImage\");";
+ internalName = "macroSizeCtrl";
+ text = "0.7";
+ internalName = "macroStrengthCtrl";
+ text = "500";
+ internalName = "macroDistanceCtrl";
new GuiControl() {
position = "6 42";
- Extent = "189 373";
+ extent = "189 373";
new GuiScrollCtrl() {
- Profile = "ToolsGuiScrollProfile";
- position = "0 0";
- Extent = "189 374";
willFirstRespond = "1";
hScrollBar = "dynamic";
vScrollBar = "dynamic";
- lockHorizScroll = "false";
- lockVertScroll = "false";
+ lockHorizScroll = "0";
constantThumbHeight = "0";
childMargin = "0 0";
mouseWheelScrollSpeed = "-1";
+ position = "0 -1";
+ extent = "189 374";
new GuiTreeViewCtrl() {
- internalName = "matLibTree";
- class = "TerrainMaterialTreeCtrl";
- className = "TerrainMaterialTreeCtrl";
- Profile = "ToolsGuiTreeViewProfile";
- Extent = "125 84";
tabSize = "16";
textOffset = "2";
fullRowSelect = "0";
itemHeight = "21";
destroyTreeOnSleep = "1";
- MouseDragging = "0";
- MultipleSelections = "0";
- DeleteObjectAllowed = "0";
- DragToItemAllowed = "0";
- ClearAllOnSingleSelection = "1";
+ mouseDragging = "0";
+ multipleSelections = "0";
+ deleteObjectAllowed = "0";
+ dragToItemAllowed = "0";
+ clearAllOnSingleSelection = "1";
showRoot = "0";
- internalNamesOnly = "1";
- objectNamesOnly = "0";
+ useInspectorTooltips = "0";
+ tooltipOnWidthOnly = "0";
+ showObjectIds = "0";
+ showClassNames = "0";
+ showObjectNames = "0";
+ showInternalNames = "1";
+ showClassNameForUnnamedObjects = "0";
+ compareToObjectID = "1";
+ canRenameObjects = "1";
+ renameInternal = "0";
+ extent = "109 147";
+ profile = "ToolsGuiTreeViewProfile";
+ internalName = "matLibTree";
+ class = "TerrainMaterialTreeCtrl";
new GuiButtonCtrl() {
- position = "202 394";
- Extent = "98 22";
- Command = "TerrainMaterialDlg.dialogApply();";
text = "Apply&Select";
+ position = "202 394";
+ extent = "98 22";
+ command = "TerrainMaterialDlg.dialogApply();";
- position = "307 394";
- Extent = "80 22";
- Command = "TerrainMaterialDlg.dialogCancel();";
text = "Cancel";
+ position = "307 394";
+ extent = "80 22";
+ command = "TerrainMaterialDlg.dialogCancel();";
- new GuiBitmapCtrl() { // inactive overlay
- internalName = "inactiveOverlay";
- position = "199 23";
- Extent = "190 267";
- isContainer = true;
- Visible = false;
bitmap = "tools/gui/images/inactive-overlay";
- internalName = "inactiveOverlayDlg";
- Profile = "ToolsGuiTextCenterProfile";
- position = "0 104";
- Extent = "190 64";
+ position = "199 23";
+ extent = "190 267";
+ visible = "0";
+ internalName = "inactiveOverlay";
text = "Inactive";
+ position = "0 104";
+ extent = "190 64";
+ profile = "ToolsGuiTextCenterProfile";
+ internalName = "inactiveOverlayDlg";
@@ -877,6 +877,24 @@ function ObjectBuilderGui::buildParticleSimulation(%this)
%this.process();
+function ObjectBuilderGui::buildReflectionProbe(%this)
+ %this.objectClassName = "ReflectionProbe";
+ %this.process();
+ %defaultPath = filePath($Server::MissionFile) @ "/" @ fileBase($Server::MissionFile) @ "/probes/";
+ %this.addField("reflectionPath", "TypeFilepath", "reflectionPath", %defaultPath);
+function ObjectBuilderGui::buildSkylight(%this)
+ %this.objectClassName = "Skylight";
//------------------------------------------------------------------------------
// Mission
@@ -0,0 +1,192 @@
+//--- OBJECT WRITE BEGIN ---
+%guiContent = new GuiControl(ProbeBakeDlg) {
+ position = "0 0";
+ profile = "GuiDefaultProfile";
+ new GuiWindowCtrl() {
+ text = "Update Environment Probes";
+ resizeWidth = "0";
+ resizeHeight = "0";
+ canMove = "1";
+ canClose = "1";
+ canMinimize = "0";
+ canMaximize = "0";
+ closeCommand = "Canvas.popDialog(ProbeBakeDlg);";
+ position = "392 314";
+ extent = "270 164";
+ profile = "GuiWindowProfile";
+ text = "Probe Resolution";
+ position = "11 32";
+ extent = "91 13";
+ profile = "GuiTextProfile";
+ new GuiPopUpMenuCtrl(ProbeBakeDlg_ProbeResList) {
+ maxPopupHeight = "200";
+ sbUsesNAColor = "0";
+ reverseTextList = "0";
+ bitmapBounds = "16 16";
+ text = "64";
+ position = "103 29";
+ extent = "157 19";
+ profile = "GuiPopUpMenuProfile";
+ text = "Number of bake iterations";
+ position = "11 56";
+ extent = "129 13";
+ new GuiTextEditCtrl(ProbeBakeDlg_NumIterTxt) {
+ position = "150 53";
+ extent = "108 18";
+ profile = "GuiTextEditProfile";
+ new GuiButtonCtrl(ProbeBakeDlg_RunBake) {
+ text = "Update Probes";
+ position = "68 120";
+ extent = "140 30";
+ profile = "GuiButtonProfile";
+ new GuiProgressCtrl(ProbeBakeDlg_Progress) {
+ position = "8 80";
+ extent = "251 25";
+ profile = "GuiProgressProfile";
+//--- OBJECT WRITE END ---
@@ -43,6 +43,7 @@ function initializeWorldEditor()
exec("./gui/AddFMODProjectDlg.ed.gui");
exec("./gui/SelectObjectsWindow.ed.gui");
exec("./gui/ProceduralTerrainPainterGui.gui" );
+ exec("./gui/probeBakeDlg.gui" );
// Load Scripts.
exec("./scripts/menus.ed.cs");
@@ -61,6 +62,7 @@ function initializeWorldEditor()
exec("./scripts/ManageSFXParametersWindow.ed.cs");
exec("./scripts/AddFMODProjectDlg.ed.cs");
exec("./scripts/SelectObjectsWindow.ed.cs");
+ exec("./scripts/probeBake.ed.cs");
// Load Custom Editors
loadDirectory(expandFilename("./scripts/editors"));
@@ -115,10 +117,11 @@ function initializeWorldEditor()
EVisibility.addOption( "Debug Render: Light Frustums", "$Light::renderLightFrustums", "" );
EVisibility.addOption( "Debug Render: Bounding Boxes", "$Scene::renderBoundingBoxes", "" );
EVisibility.addOption( "Debug Render: Physics World", "$PhysicsWorld::render", "togglePhysicsDebugViz" );
+ EVisibility.addOption( "Debug Render: Reflection Probes", "$Light::renderReflectionProbes", "" );
+ EVisibility.addOption( "Debug Render: Probe Previews", "$Light::renderPreviewProbes", "" );
EVisibility.addOption( "AL: Disable Shadows", "$Shadows::disable", "" );
- EVisibility.addOption( "AL: Light Color Viz", "$AL_LightColorVisualizeVar", "toggleLightColorViz" );
- EVisibility.addOption( "AL: Environment Light", "$AL_LightMapShaderVar", "toggleLightMapViz" );
- EVisibility.addOption( "AL: Light Specular Viz", "$AL_LightSpecularVisualizeVar", "toggleLightSpecularViz" );
+ EVisibility.addOption( "AL: Diffuse Lighting Viz", "$AL_LightColorVisualizeVar", "toggleLightColorViz" );
+ EVisibility.addOption( "AL: Specular Lighting Viz", "$AL_LightSpecularVisualizeVar", "toggleLightSpecularViz" );
EVisibility.addOption( "AL: Normals Viz", "$AL_NormalsVisualizeVar", "toggleNormalsViz" );
EVisibility.addOption( "AL: Depth Viz", "$AL_DepthVisualizeVar", "toggleDepthViz" );
EVisibility.addOption( "AL: Color Buffer", "$AL_ColorBufferShaderVar", "toggleColorBufferViz" );
@@ -55,6 +55,10 @@ function EWCreatorWindow::init( %this )
%this.registerMissionObject( "PointLight", "Point Light" );
%this.registerMissionObject( "SpotLight", "Spot Light" );
+ %this.registerMissionObject( "ReflectionProbe", "Reflection Probe" );
+ %this.registerMissionObject( "Skylight", "Skylight" );
%this.registerMissionObject( "GroundCover", "Ground Cover" );
%this.registerMissionObject( "TerrainBlock", "Terrain Block" );
%this.registerMissionObject( "GroundPlane", "Ground Plane" );
@@ -294,6 +294,28 @@ function TerrainMaterialDlg::changeNormal( %this )
+function TerrainMaterialDlg::changecomposite( %this )
+ %ctrl = %this-->compositeTexCtrl;
+ %file = %ctrl.bitmap;
+ if( getSubStr( %file, 0 , 6 ) $= "tools/" )
+ %file = "";
+ %file = TerrainMaterialDlg._selectTextureFileDialog( %file );
+ if( %file $= "" )
+ if( %ctrl.bitmap !$= "" )
+ %file = "tools/materialEditor/gui/unknownImage";
+ %file = makeRelativePath( %file, getMainDotCsDir() );
+ %ctrl.setBitmap( %file );
function TerrainMaterialDlg::newMat( %this )
// Create a unique material name.
@@ -394,7 +416,12 @@ function TerrainMaterialDlg::setActiveMaterial( %this, %mat )
%this-->normTexCtrl.setBitmap( "tools/materialEditor/gui/unknownImage" );
}else{
%this-->normTexCtrl.setBitmap( %mat.normalMap );
+ if (%mat.compositeMap $= ""){
+ %this-->compositeTexCtrl.setBitmap( "tools/materialEditor/gui/unknownImage" );
+ }else{
+ %this-->compositeTexCtrl.setBitmap( %mat.compositeMap );
%this-->detSizeCtrl.setText( %mat.detailSize );
%this-->baseSizeCtrl.setText( %mat.diffuseSize );
%this-->detStrengthCtrl.setText( %mat.detailStrength );
@@ -448,6 +475,11 @@ function TerrainMaterialDlg::saveDirtyMaterial( %this, %mat )
%newMacro = %this-->macroTexCtrl.bitmap;
+ if (%this-->compositeTexCtrl.bitmap $= "tools/materialEditor/gui/unknownImage"){
+ %newComposite = "";
+ %newComposite = %this-->compositeTexCtrl.bitmap;
%detailSize = %this-->detSizeCtrl.getText();
%diffuseSize = %this-->baseSizeCtrl.getText();
%detailStrength = %this-->detStrengthCtrl.getText();
@@ -466,6 +498,7 @@ function TerrainMaterialDlg::saveDirtyMaterial( %this, %mat )
%mat.diffuseMap $= %newDiffuse &&
%mat.normalMap $= %newNormal &&
%mat.detailMap $= %newDetail &&
+ %mat.compositeMap $= %newComposite &&
%mat.macroMap $= %newMacro &&
%mat.detailSize == %detailSize &&
%mat.diffuseSize == %diffuseSize &&
@@ -497,7 +530,8 @@ function TerrainMaterialDlg::saveDirtyMaterial( %this, %mat )
%mat.diffuseMap = %newDiffuse;
- %mat.normalMap = %newNormal;
+ %mat.normalMap = %newNormal;
+ %mat.compositeMap = %newComposite;
%mat.detailMap = %newDetail;
%mat.macroMap = %newMacro;
%mat.detailSize = %detailSize;
@@ -544,6 +578,7 @@ function TerrainMaterialDlg::snapshotMaterials( %this )
diffuseMap = %mat.diffuseMap;
normalMap = %mat.normalMap;
detailMap = %mat.detailMap;
+ compositeMap = %mat.compositeMap;
macroMap = %mat.macroMap;
detailSize = %mat.detailSize;
diffuseSize = %mat.diffuseSize;
@@ -578,6 +613,7 @@ function TerrainMaterialDlg::restoreMaterials( %this )
%mat.diffuseMap = %obj.diffuseMap;
%mat.normalMap = %obj.normalMap;
%mat.detailMap = %obj.detailMap;
+ %mat.compositeMap = %obj.compositeMap;
%mat.macroMap = %obj.macroMap;
%mat.detailSize = %obj.detailSize;
%mat.diffuseSize = %obj.diffuseSize;
@@ -61,3 +61,21 @@ function EditorLightingMenu::onMenuSelect( %this )
//%selSize = EWorldEditor.getSelectionSize();
%this.enableItem( 1, true /*%selSize == 1*/ );
+function updateReflectionProbes()
+ /*%probeIds = parseMissionGroupForIds("ReflectionProbe", "");
+ %probeCount = getWordCount(%probeIds);
+ for(%i=0; %i < %probeCount; %i++)
+ %probe = getWord(%probeIds, %i);
+ %path = filePath($Server::MissionFile) @ "/" @ fileBase($Server::MissionFile) @ "/probes/";
+ %probe.bake(%path, 64);
+ EWorldEditor.isDirty = true;*/
+ Canvas.pushDialog(ProbeBakeDlg);
@@ -263,6 +263,8 @@ function EditorGui::buildMenus(%this)
item[0] = "Full Relight" TAB "Alt L" TAB "Editor.lightScene(\"\", forceAlways);";
item[1] = "Toggle ShadowViz" TAB "" TAB "toggleShadowViz();";
item[2] = "-";
+ item[3] = "Update Reflection Probes" TAB "" TAB "updateReflectionProbes();";
+ item[4] = "-";
// NOTE: The light managers will be inserted as the
// last menu items in EditorLightingMenu::onAdd().
@@ -0,0 +1,48 @@
+function ProbeBakeDlg::onWake(%this)
+ //set up
+ ProbeBakeDlg_ProbeResList.add( "32" );
+ ProbeBakeDlg_ProbeResList.add( "64" );
+ ProbeBakeDlg_ProbeResList.add( "128" );
+ ProbeBakeDlg_ProbeResList.add( "256" );
+ ProbeBakeDlg_ProbeResList.add( "512" );
+ ProbeBakeDlg_ProbeResList.add( "1024" );
+ ProbeBakeDlg_ProbeResList.add( "2048" );
+ ProbeBakeDlg_ProbeResList.setSelected( 1, false );
+ ProbeBakeDlg_NumIterTxt.setText("1");
+function ProbeBakeDlg_RunBake::onClick(%this)
+ %probeIds = parseMissionGroupForIds("ReflectionProbe", "");
+ %skylightIds = parseMissionGroupForIds("Skylight", "");
+ %probeIds = rtrim(ltrim(%probeIds SPC %skylightIds));
+ %numIter = ProbeBakeDlg_NumIterTxt.getText();
+ %resolution = ProbeBakeDlg_ProbeResList.getText();
+ %progressStep = 100 / (%numIter * %probeCount);
+ %currentProgressValue = 0;
+ ProbeBakeDlg_Progress.setValue(%currentProgressValue);
+ for(%iter=0; %iter < %numIter; %iter++)
+ %probe.bake(%path, %resolution);
+ %currentProgressValue += %progressStep;
+ Canvas.repaint();
+ EWorldEditor.isDirty = true;