diff --git a/Engine/source/gfx/D3D11/gfxD3D11Device.cpp b/Engine/source/gfx/D3D11/gfxD3D11Device.cpp index 1bbd68e6d..01fd8056f 100644 --- a/Engine/source/gfx/D3D11/gfxD3D11Device.cpp +++ b/Engine/source/gfx/D3D11/gfxD3D11Device.cpp @@ -40,6 +40,8 @@ #include "shaderGen/shaderGen.h" #include //d3dperf +#include "gfxD3D11TextureArray.h" + #ifdef TORQUE_DEBUG #include "d3d11sdklayers.h" #endif @@ -48,6 +50,8 @@ #pragma comment(lib, "d3d9.lib") //d3dperf #pragma comment(lib, "d3d11.lib") +class GFXD3D11TextureArray; + class GFXPCD3D11RegisterDevice { public: @@ -1736,6 +1740,13 @@ GFXCubemapArray * GFXD3D11Device::createCubemapArray() return cubeArray; } +GFXTextureArray * GFXD3D11Device::createTextureArray() +{ + GFXD3D11TextureArray* textureArray = new GFXD3D11TextureArray(); + textureArray->registerResourceWithDevice(this); + return textureArray; +} + // Debug events //------------------------------------------------------------------------------ void GFXD3D11Device::enterDebugEvent(ColorI color, const char *name) diff --git a/Engine/source/gfx/D3D11/gfxD3D11Device.h b/Engine/source/gfx/D3D11/gfxD3D11Device.h index 75525ce95..b60e5d2a6 100644 --- a/Engine/source/gfx/D3D11/gfxD3D11Device.h +++ b/Engine/source/gfx/D3D11/gfxD3D11Device.h @@ -231,6 +231,7 @@ public: virtual GFXCubemap *createCubemap(); virtual GFXCubemapArray *createCubemapArray(); + virtual GFXTextureArray* createTextureArray(); virtual F32 getPixelShaderVersion() const { return mPixVersion; } virtual void setPixelShaderVersion( F32 version ){ mPixVersion = version;} diff --git a/Engine/source/gfx/D3D11/gfxD3D11Shader.cpp b/Engine/source/gfx/D3D11/gfxD3D11Shader.cpp index 1dcf8ca10..17827349d 100644 --- a/Engine/source/gfx/D3D11/gfxD3D11Shader.cpp +++ b/Engine/source/gfx/D3D11/gfxD3D11Shader.cpp @@ -1385,7 +1385,8 @@ void GFXD3D11Shader::_buildSamplerShaderConstantHandles( Vector + +#include "gfxD3D11Device.h" +#include "gfxD3D11EnumTranslate.h" +#include "core/util/tVector.h" +#include "gfx/util/screenspace.h" +#include "shaderGen/shaderFeature.h" + + +void GFXD3D11TextureArray::init() +{ + mTextureArrayDesc.Width = mWidth; + mTextureArrayDesc.Height = mHeight; + mTextureArrayDesc.MipLevels = mMipLevels; + mTextureArrayDesc.ArraySize = mArraySize; + mTextureArrayDesc.Format = GFXD3D11TextureFormat[mFormat]; + mTextureArrayDesc.SampleDesc.Count = 1; + mTextureArrayDesc.SampleDesc.Quality = 0; + mTextureArrayDesc.Usage = D3D11_USAGE_DEFAULT; + mTextureArrayDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + mTextureArrayDesc.CPUAccessFlags = 0; + mTextureArrayDesc.MiscFlags = 0; + + HRESULT hr = D3D11DEVICE->CreateTexture2D(&mTextureArrayDesc, NULL, &mTextureArray); + AssertFatal(SUCCEEDED(hr), "GFXD3D11TextureArray::init failed to create texture array!"); + + //--------------------------------------------------------------------------------------- + // Create a resource view to the texture array. + //--------------------------------------------------------------------------------------- + createResourceView(mTextureArrayDesc.Format, mTextureArrayDesc.MipLevels, mTextureArrayDesc.BindFlags); + //--------------------------------------------------------------------------------------- +} + +void GFXD3D11TextureArray::_setTexture(const GFXTexHandle& texture, U32 slot) +{ + GFXD3D11TextureObject *texObj = dynamic_cast(texture.getPointer()); + ID3D11Texture2D* tex2d = texObj->get2DTex(); + D3D11_TEXTURE2D_DESC desc; + tex2d->GetDesc(&desc); + // for each mipmap level... + for (UINT j = 0; j < desc.MipLevels; ++j) + { + const U32 srcSubResource = D3D11CalcSubresource(j, 0, desc.MipLevels); + const U32 dstSubResource = D3D11CalcSubresource(j, slot, mTextureArrayDesc.MipLevels); + D3D11DEVICECONTEXT->CopySubresourceRegion(mTextureArray, dstSubResource, 0, 0, 0, tex2d, srcSubResource, NULL); + } +} + +void GFXD3D11TextureArray::setToTexUnit(U32 tuNum) +{ + D3D11DEVICECONTEXT->PSSetShaderResources(tuNum, 1, &mSRView); +} + +void GFXD3D11TextureArray::createResourceView(DXGI_FORMAT format, U32 numMipLevels, U32 usageFlags) +{ + HRESULT hr; + if (usageFlags & D3D11_BIND_SHADER_RESOURCE) + { + D3D11_SHADER_RESOURCE_VIEW_DESC desc; + + if (usageFlags & D3D11_BIND_DEPTH_STENCIL) + desc.Format = DXGI_FORMAT_R24_UNORM_X8_TYPELESS; // reads the depth + else + desc.Format = format; + + desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; + desc.Texture2DArray.MostDetailedMip = 0; + desc.Texture2DArray.MipLevels = numMipLevels; + desc.Texture2DArray.FirstArraySlice = 0; + desc.Texture2DArray.ArraySize = mArraySize; + + hr = D3D11DEVICE->CreateShaderResourceView(mTextureArray, &desc, &mSRView); + AssertFatal(SUCCEEDED(hr), "GFXD3D11TextureArray::CreateShaderResourceView failed to create view!"); + } + + if (usageFlags & D3D11_BIND_RENDER_TARGET) + { + D3D11_RENDER_TARGET_VIEW_DESC desc; + desc.Format = format; + desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; + desc.Texture2DArray.MipSlice = 0; + desc.Texture2DArray.FirstArraySlice = 0; + desc.Texture2DArray.ArraySize = mArraySize; + hr = D3D11DEVICE->CreateRenderTargetView(mTextureArray, &desc, &mRTView); + AssertFatal(SUCCEEDED(hr), "GFXD3D11TextureArray::CreateRenderTargetView failed to create view!"); + } + + if (usageFlags & D3D11_BIND_DEPTH_STENCIL) + { + D3D11_DEPTH_STENCIL_VIEW_DESC desc; + desc.Format = format; + desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY; + desc.Texture2DArray.MipSlice = 0; + desc.Texture2DArray.FirstArraySlice = 0; + desc.Texture2DArray.ArraySize = mArraySize; + desc.Flags = 0; + hr = D3D11DEVICE->CreateDepthStencilView(mTextureArray, &desc, &mDSView); + AssertFatal(SUCCEEDED(hr), "GFXD3D11TextureArray::CreateDepthStencilView failed to create view!"); + } +} + + +void GFXD3D11TextureArray::Release() +{ + SAFE_RELEASE(mSRView) + SAFE_RELEASE(mRTView) + SAFE_RELEASE(mDSView) + SAFE_RELEASE(mTextureArray) + + GFXTextureArray::Release(); +} + +ID3D11ShaderResourceView* GFXD3D11TextureArray::getSRView() +{ + return mSRView; +} +ID3D11RenderTargetView* GFXD3D11TextureArray::getRTView() +{ + return mRTView; +} +ID3D11DepthStencilView* GFXD3D11TextureArray::getDSView() +{ + return mDSView; +} + +ID3D11ShaderResourceView** GFXD3D11TextureArray::getSRViewPtr() +{ + return &mSRView; +} +ID3D11RenderTargetView** GFXD3D11TextureArray::getRTViewPtr() +{ + return &mRTView; +} + +ID3D11DepthStencilView** GFXD3D11TextureArray::getDSViewPtr() +{ + return &mDSView; +} + +void GFXD3D11TextureArray::zombify() +{ + // Unsupported +} + +void GFXD3D11TextureArray::resurrect() +{ + // Unsupported +} diff --git a/Engine/source/gfx/D3D11/gfxD3D11TextureArray.h b/Engine/source/gfx/D3D11/gfxD3D11TextureArray.h new file mode 100644 index 000000000..be7851d75 --- /dev/null +++ b/Engine/source/gfx/D3D11/gfxD3D11TextureArray.h @@ -0,0 +1,56 @@ +#ifndef _GFXD3D11TEXTUREARRAY_H_ +#define _GFXD3D11TEXTUREARRAY_H_ + +#include + + +#include "gfx/gfxTextureArray.h" +#include "gfx/gfxTextureManager.h" +#include "core/util/safeRelease.h" +#include "gfxD3D11TextureManager.h" + +class GFXD3D11TextureArray : public GFXTextureArray +{ +public: + GFXD3D11TextureArray() + : mSRView( NULL ), + mRTView( NULL ), + mDSView( NULL ), + mTextureArray( NULL ) + { + } + + ~GFXD3D11TextureArray() { Release(); }; + + void init(); + void setToTexUnit(U32 tuNum) override; + + void createResourceView(DXGI_FORMAT format, U32 numMipLevels, U32 usageFlags); + + // GFXResource interface + void zombify() override; + void resurrect() override; + void Release() override; + + + ID3D11ShaderResourceView* getSRView(); + ID3D11RenderTargetView* getRTView(); + ID3D11DepthStencilView* getDSView(); + + ID3D11ShaderResourceView** getSRViewPtr(); + ID3D11RenderTargetView** getRTViewPtr(); + ID3D11DepthStencilView** getDSViewPtr(); + +protected: + void _setTexture(const GFXTexHandle& texture, U32 slot) override; + +private: + ID3D11ShaderResourceView* mSRView; // for shader resource input + ID3D11RenderTargetView* mRTView; // for render targets + ID3D11DepthStencilView* mDSView; //render target view for depth stencil + ID3D11Texture2D* mTextureArray; + D3D11_TEXTURE2D_DESC mTextureArrayDesc; +}; + + +#endif diff --git a/Engine/source/gfx/D3D11/gfxD3D11TextureObject.cpp b/Engine/source/gfx/D3D11/gfxD3D11TextureObject.cpp index ea26e0042..1a06fac5e 100644 --- a/Engine/source/gfx/D3D11/gfxD3D11TextureObject.cpp +++ b/Engine/source/gfx/D3D11/gfxD3D11TextureObject.cpp @@ -210,8 +210,8 @@ bool GFXD3D11TextureObject::copyToBmp(GBitmap* bmp) // check format limitations // at the moment we only support RGBA for the source (other 4 byte formats should // be easy to add though) - AssertFatal(mFormat == GFXFormatR16G16B16A16F || mFormat == GFXFormatR8G8B8A8 || mFormat == GFXFormatR8G8B8A8_LINEAR_FORCE || mFormat == GFXFormatR8G8B8A8_SRGB, "copyToBmp: invalid format"); - if (mFormat != GFXFormatR16G16B16A16F && mFormat != GFXFormatR8G8B8A8 && mFormat != GFXFormatR8G8B8A8_LINEAR_FORCE && mFormat != GFXFormatR8G8B8A8_SRGB) + AssertFatal(mFormat == GFXFormatR16G16B16A16F || mFormat == GFXFormatR8G8B8A8 || mFormat == GFXFormatR8G8B8A8_LINEAR_FORCE || mFormat == GFXFormatR8G8B8A8_SRGB || mFormat == GFXFormatR8G8B8, "copyToBmp: invalid format"); + if (mFormat != GFXFormatR16G16B16A16F && mFormat != GFXFormatR8G8B8A8 && mFormat != GFXFormatR8G8B8A8_LINEAR_FORCE && mFormat != GFXFormatR8G8B8A8_SRGB && mFormat != GFXFormatR8G8B8) return false; PROFILE_START(GFXD3D11TextureObject_copyToBmp); @@ -248,6 +248,7 @@ bool GFXD3D11TextureObject::copyToBmp(GBitmap* bmp) desc.BindFlags = 0; desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; desc.Usage = D3D11_USAGE_STAGING; + desc.MiscFlags = 0; ID3D11Texture2D* pStagingTexture = NULL; HRESULT hr = D3D11DEVICE->CreateTexture2D(&desc, NULL, &pStagingTexture); diff --git a/Engine/source/gfx/Null/gfxNullDevice.cpp b/Engine/source/gfx/Null/gfxNullDevice.cpp index 8767d4eee..92e688a48 100644 --- a/Engine/source/gfx/Null/gfxNullDevice.cpp +++ b/Engine/source/gfx/Null/gfxNullDevice.cpp @@ -178,6 +178,19 @@ public: virtual void resurrect() {} }; +class GFXNullTextureArray : public GFXTextureArray +{ +public: + void zombify() override {} + void resurrect() override {} + void Release() override {} + void setToTexUnit(U32 tuNum) override { } + void init() override { } + +protected: + void _setTexture(const GFXTexHandle& texture, U32 slot) override { } +}; + class GFXNullVertexBuffer : public GFXVertexBuffer { unsigned char* tempBuf; @@ -317,6 +330,11 @@ GFXCubemapArray* GFXNullDevice::createCubemapArray() return new GFXNullCubemapArray(); }; +GFXTextureArray* GFXNullDevice::createTextureArray() +{ + return new GFXNullTextureArray(); +}; + void GFXNullDevice::enumerateAdapters( Vector &adapterList ) { // Add the NULL renderer diff --git a/Engine/source/gfx/Null/gfxNullDevice.h b/Engine/source/gfx/Null/gfxNullDevice.h index a2ebe6c23..e50c3583b 100644 --- a/Engine/source/gfx/Null/gfxNullDevice.h +++ b/Engine/source/gfx/Null/gfxNullDevice.h @@ -130,6 +130,7 @@ protected: public: virtual GFXCubemap * createCubemap(); virtual GFXCubemapArray *createCubemapArray(); + virtual GFXTextureArray *createTextureArray(); virtual F32 getFillConventionOffset() const { return 0.0f; }; diff --git a/Engine/source/gfx/bitmap/gBitmap.cpp b/Engine/source/gfx/bitmap/gBitmap.cpp index 380070346..b317107f2 100644 --- a/Engine/source/gfx/bitmap/gBitmap.cpp +++ b/Engine/source/gfx/bitmap/gBitmap.cpp @@ -319,7 +319,7 @@ void GBitmap::allocateBitmap(const U32 in_width, const U32 in_height, const bool U32 currWidth = in_width; U32 currHeight = in_height; - do + while (currWidth != 1 || currHeight != 1) { mMipLevelOffsets[mNumMipLevels] = mMipLevelOffsets[mNumMipLevels - 1] + (currWidth * currHeight * mBytesPerPixel); @@ -330,7 +330,7 @@ void GBitmap::allocateBitmap(const U32 in_width, const U32 in_height, const bool mNumMipLevels++; allocPixels += currWidth * currHeight * mBytesPerPixel; - } while (currWidth != 1 || currHeight != 1); + } U32 expectedMips = mFloor(mLog2(mMax(in_width, in_height))) + 1; AssertFatal(mNumMipLevels == expectedMips, "GBitmap::allocateBitmap: mipmap count wrong"); @@ -727,7 +727,7 @@ bool GBitmap::checkForTransparency() } //------------------------------------------------------------------------------ -LinearColorF GBitmap::sampleTexel(F32 u, F32 v) const +LinearColorF GBitmap::sampleTexel(F32 u, F32 v, bool retAlpha) const { LinearColorF col(0.5f, 0.5f, 0.5f); // normally sampling wraps all the way around at 1.0, @@ -751,6 +751,13 @@ LinearColorF GBitmap::sampleTexel(F32 u, F32 v) const col.red = F32(buffer[lexelindex + 0]) / 255.0f; col.green = F32(buffer[lexelindex + 1]) / 255.0f; col.blue = F32(buffer[lexelindex + 2]) / 255.0f; + if (retAlpha) + { + if (getHasTransparency()) + col.alpha = F32(buffer[lexelindex + 3]) / 255.0f; + else + col.alpha = 1.0f; + } } return col; @@ -1352,7 +1359,7 @@ U32 GBitmap::getSurfaceSize(const U32 mipLevel) const } DefineEngineFunction( getBitmapInfo, String, ( const char *filename ),, - "Returns image info in the following format: width TAB height TAB bytesPerPixel. " + "Returns image info in the following format: width TAB height TAB bytesPerPixel TAB format. " "It will return an empty string if the file is not found.\n" "@ingroup Rendering\n" ) { @@ -1360,7 +1367,8 @@ DefineEngineFunction( getBitmapInfo, String, ( const char *filename ),, if ( !image ) return String::EmptyString; - return String::ToString( "%d\t%d\t%d", image->getWidth(), + return String::ToString( "%d\t%d\t%d\t%d", image->getWidth(), image->getHeight(), - image->getBytesPerPixel() ); + image->getBytesPerPixel(), + image->getFormat()); } diff --git a/Engine/source/gfx/bitmap/gBitmap.h b/Engine/source/gfx/bitmap/gBitmap.h index 16cb24985..00960c047 100644 --- a/Engine/source/gfx/bitmap/gBitmap.h +++ b/Engine/source/gfx/bitmap/gBitmap.h @@ -205,7 +205,7 @@ public: /// the bitmap bits and to check for alpha values less than 255 bool checkForTransparency(); - LinearColorF sampleTexel(F32 u, F32 v) const; + LinearColorF sampleTexel(F32 u, F32 v, bool retAlpha = false) const; bool getColor(const U32 x, const U32 y, ColorI& rColor) const; bool setColor(const U32 x, const U32 y, const ColorI& rColor); U8 getChanelValueAt(U32 x, U32 y, U32 chan); diff --git a/Engine/source/gfx/gfxDevice.cpp b/Engine/source/gfx/gfxDevice.cpp index 19f3efb0a..5dfc4dab2 100644 --- a/Engine/source/gfx/gfxDevice.cpp +++ b/Engine/source/gfx/gfxDevice.cpp @@ -132,6 +132,8 @@ GFXDevice::GFXDevice() mCurrentCubemap[i] = NULL; mNewCubemap[i] = NULL; mCurrentCubemapArray[i] = NULL; + mNewTextureArray[i] = NULL; + mCurrentTextureArray[i] = NULL; mNewCubemapArray[i] = NULL; mTexType[i] = GFXTDT_Normal; @@ -266,6 +268,8 @@ GFXDevice::~GFXDevice() mNewCubemap[i] = NULL; mCurrentCubemapArray[i] = NULL; mNewCubemapArray[i] = NULL; + mCurrentTextureArray[i] = NULL; + mNewTextureArray[i] = NULL; } mCurrentRT = NULL; @@ -403,14 +407,23 @@ void GFXDevice::updateStates(bool forceSetAll /*=false*/) } break; case GFXTDT_CubeArray: - { - mCurrentCubemapArray[i] = mNewCubemapArray[i]; - if (mCurrentCubemapArray[i]) - mCurrentCubemapArray[i]->setToTexUnit(i); - else - setTextureInternal(i, NULL); - } - break; + { + mCurrentCubemapArray[i] = mNewCubemapArray[i]; + if (mCurrentCubemapArray[i]) + mCurrentCubemapArray[i]->setToTexUnit(i); + else + setTextureInternal(i, NULL); + } + break; + case GFXTDT_TextureArray: + { + mCurrentTextureArray[i] = mNewTextureArray[i]; + if (mCurrentTextureArray[i]) + mCurrentTextureArray[i]->setToTexUnit(i); + else + setTextureInternal(i, NULL); + } + break; default: AssertFatal(false, "Unknown texture type!"); break; @@ -557,6 +570,15 @@ void GFXDevice::updateStates(bool forceSetAll /*=false*/) mCurrentCubemapArray[i]->setToTexUnit(i); else setTextureInternal(i, NULL); + } + break; + case GFXTDT_TextureArray: + { + mCurrentTextureArray[i] = mNewTextureArray[i]; + if (mCurrentTextureArray[i]) + mCurrentTextureArray[i]->setToTexUnit(i); + else + setTextureInternal(i, NULL); } break; default: @@ -801,6 +823,8 @@ void GFXDevice::setTexture( U32 stage, GFXTextureObject *texture ) mCurrentCubemap[stage] = NULL; mNewCubemapArray[stage] = NULL; mCurrentCubemapArray[stage] = NULL; + mNewTextureArray[stage] = NULL; + mCurrentTextureArray[stage] = NULL; } //----------------------------------------------------------------------------- @@ -827,6 +851,8 @@ void GFXDevice::setCubeTexture( U32 stage, GFXCubemap *cubemap ) mCurrentTexture[stage] = NULL; mNewCubemapArray[stage] = NULL; mCurrentCubemapArray[stage] = NULL; + mNewTextureArray[stage] = NULL; + mCurrentTextureArray[stage] = NULL; } //----------------------------------------------------------------------------- @@ -853,6 +879,36 @@ void GFXDevice::setCubeArrayTexture(U32 stage, GFXCubemapArray *cubemapArray) mCurrentTexture[stage] = NULL; mNewCubemap[stage] = NULL; mCurrentCubemap[stage] = NULL; + mNewTextureArray[stage] = NULL; + mCurrentTextureArray[stage] = NULL; +} + +//----------------------------------------------------------------------------- +// Set texture array +//----------------------------------------------------------------------------- +void GFXDevice::setTextureArray(U32 stage, GFXTextureArray *textureArray) +{ + AssertFatal(stage < getNumSamplers(), avar("GFXDevice::setTextureArray - out of range stage! %i>%i", stage, getNumSamplers())); + + if (mTexType[stage] == GFXTDT_TextureArray && + ((mTextureDirty[stage] && mNewTextureArray[stage].getPointer() == textureArray) || + (!mTextureDirty[stage] && mCurrentTextureArray[stage].getPointer() == textureArray))) + return; + + mStateDirty = true; + mTexturesDirty = true; + mTextureDirty[stage] = true; + + mNewTextureArray[stage] = textureArray; + mTexType[stage] = GFXTDT_TextureArray; + + // Clear out textures + mNewTexture[stage] = NULL; + mCurrentTexture[stage] = NULL; + mNewCubemap[stage] = NULL; + mCurrentCubemap[stage] = NULL; + mNewCubemapArray[stage] = NULL; + mCurrentCubemapArray[stage] = NULL; } //------------------------------------------------------------------------------ diff --git a/Engine/source/gfx/gfxDevice.h b/Engine/source/gfx/gfxDevice.h index 7b80aef3d..4caecd55b 100644 --- a/Engine/source/gfx/gfxDevice.h +++ b/Engine/source/gfx/gfxDevice.h @@ -57,6 +57,7 @@ #ifndef _PLATFORM_PLATFORMTIMER_H_ #include "platform/platformTimer.h" #endif +#include "gfxTextureArray.h" class FontRenderBatcher; class GFont; @@ -498,7 +499,8 @@ protected: { GFXTDT_Normal, GFXTDT_Cube, - GFXTDT_CubeArray + GFXTDT_CubeArray, + GFXTDT_TextureArray }; GFXTexHandle mCurrentTexture[TEXTURE_STAGE_COUNT]; @@ -507,6 +509,8 @@ protected: GFXCubemapHandle mNewCubemap[TEXTURE_STAGE_COUNT]; GFXCubemapArrayHandle mCurrentCubemapArray[TEXTURE_STAGE_COUNT]; GFXCubemapArrayHandle mNewCubemapArray[TEXTURE_STAGE_COUNT]; + GFXTextureArrayHandle mCurrentTextureArray[TEXTURE_STAGE_COUNT]; + GFXTextureArrayHandle mNewTextureArray[TEXTURE_STAGE_COUNT]; TexDirtyType mTexType[TEXTURE_STAGE_COUNT]; bool mTextureDirty[TEXTURE_STAGE_COUNT]; @@ -757,6 +761,7 @@ protected: public: virtual GFXCubemap * createCubemap() = 0; virtual GFXCubemapArray *createCubemapArray() = 0; + virtual GFXTextureArray *createTextureArray() = 0; inline GFXTextureManager *getTextureManager() { @@ -952,6 +957,7 @@ public: void setTexture(U32 stage, GFXTextureObject *texture); void setCubeTexture( U32 stage, GFXCubemap *cubemap ); void setCubeArrayTexture( U32 stage, GFXCubemapArray *cubemapArray); + void setTextureArray( U32 stage, GFXTextureArray *textureArray); inline GFXTextureObject* getCurrentTexture( U32 stage ) { return mCurrentTexture[stage]; } /// @} diff --git a/Engine/source/gfx/gfxEnums.h b/Engine/source/gfx/gfxEnums.h index f5c233990..748afacd4 100644 --- a/Engine/source/gfx/gfxEnums.h +++ b/Engine/source/gfx/gfxEnums.h @@ -599,7 +599,8 @@ enum GFXShaderConstType // Samplers GFXSCT_Sampler, GFXSCT_SamplerCube, - GFXSCT_SamplerCubeArray + GFXSCT_SamplerCubeArray, + GFXSCT_SamplerTextureArray }; diff --git a/Engine/source/gfx/gfxTextureArray.cpp b/Engine/source/gfx/gfxTextureArray.cpp new file mode 100644 index 000000000..c648386c1 --- /dev/null +++ b/Engine/source/gfx/gfxTextureArray.cpp @@ -0,0 +1,181 @@ +#include "gfxTextureArray.h" + + +#include "gfxDevice.h" +#include "gfxTextureManager.h" +#include "bitmap/imageUtils.h" +#include "console/console.h" + +GFXTextureArray::GFXTextureArray() + : mFormat(GFXFormat_COUNT), + mIsCompressed(false), + mWidth(0), + mHeight(0), + mArraySize(0), + mMipLevels(0) +{ +} + +void GFXTextureArray::set(U32 width, U32 height, U32 size, GFXFormat format, U32 mipLevels) +{ + if (mipLevels == 0 && width == height && isPow2(width)) + { + mipLevels = mLog2(static_cast(width)) + 1; + } + if ( + mWidth == width && + mHeight == height && + mArraySize == size && + mFormat == format && + mMipLevels == mipLevels + ) + { + return; + } + + Release(); + + mWidth = width; + mHeight = height; + mArraySize = size; + mFormat = format; + mIsCompressed = ImageUtil::isCompressedFormat(mFormat); + mMipLevels = getMax(mipLevels, static_cast(1)); + + mTextures.setSize(size); + + init(); +} + +bool GFXTextureArray::fromTextureArray(const Vector& textureArray, U32 capacity) +{ + bool success = true; + + // Not initialized, infer it from the given array of textures + if (mArraySize == 0) + { + bool found = false; + for (const GFXTexHandle& texObj : textureArray) + { + if (texObj.isValid()) + { + if (!found) + { + found = true; + mFormat = texObj.getFormat(); + mWidth = texObj.getWidth(); + mHeight = texObj.getHeight(); + mMipLevels = texObj->getMipLevels(); + } + + if (mFormat != texObj.getFormat() || mWidth != texObj.getWidth() || mHeight != texObj.getHeight()) + { + AssertWarn(true, "GFXTextureArray::fromTextureArray there was a mismatch in texture formats, defaulting to uncompressed format"); + Con::warnf("GFXTextureArray::fromTextureArray there was a mismatch in texture formats, defaulting to uncompressed format"); + success = false; + mFormat = GFXFormatR8G8B8A8; + } + } + } + + // One might think this should return false in this case, but the return value is mostly to highlight internal errors not input errors. + if (!found) return true; + + + //--------------------------------------------------------------------------------------- + // Create the texture array. Each element in the texture + // array has the same format/dimensions. + //--------------------------------------------------------------------------------------- + U32 size = capacity; + if (size == 0) + { + size = textureArray.size(); + } + set(mWidth, mHeight, size, mFormat, mMipLevels); + } + //--------------------------------------------------------------------------------------- + + + //--------------------------------------------------------------------------------------- + // Copy individual texture elements into texture array. + //--------------------------------------------------------------------------------------- + // for each texture element... + for (U32 i = 0; i < textureArray.size(); ++i) + { + if (textureArray[i].isValid()) + { + setTexture(textureArray[i], i); + } + } + //--------------------------------------------------------------------------------------- + + return success; +} + +void GFXTextureArray::setTexture(const GFXTexHandle& texture, U32 slot) +{ + GFXTexHandle handle = texture; + if (texture->getPath().isNotEmpty()) + { + if (texture.getHeight() != mHeight || texture.getWidth() != mWidth || texture.getFormat() != mFormat || texture->getMipLevels() < mMipLevels) + { + if (texture.getHeight() != mHeight || texture.getWidth() != mWidth) + { + AssertWarn(true, "GFXTextureArray::setTexture all textures should be the same size"); + Con::warnf("GFXTextureArray::setTexture all textures should be the same size"); + } + else if (texture->getMipLevels() < mMipLevels) + { + AssertWarn(true, "GFXTextureArray::setTexture all textures should have at least the same number of mips"); + Con::warnf("GFXTextureArray::setTexture all textures should have at least the same number of mips"); + } + else + { + AssertWarn(true, "GFXTextureArray::setTexture all textures should have the same format"); + Con::warnf("GFXTextureArray::setTexture all textures should have the same format"); + } + + GBitmap* inBitmap = TEXMGR->loadUncompressedTexture(texture->getPath(), &GFXTexturePersistentProfile, mWidth, mHeight); + if (!inBitmap->setFormat(mFormat)) + { + AssertFatal(true, "GFXTextureArray::setTexture all textures must be convertible to GFXFormat " + mFormat); + Con::errorf("GFXTextureArray::setTexture all textures must be convertible to GFXFormat" + mFormat); + handle = NULL; + delete inBitmap; + } + else + { + handle = TEXMGR->createTexture(inBitmap, "", &GFXStaticTextureProfile, true); + } + } + } + if (!handle.isValid()) + { + return; + } + + if (handle.getHeight() != mHeight || handle.getWidth() != mWidth || handle.getFormat() != mFormat || handle->getMipLevels() < mMipLevels) + { + AssertFatal(true, "GFXTextureArray::setTexture all textures must have the same size and format"); + Con::errorf("GFXTextureArray::setTexture all textures must have the same size and format"); + return; + } + + mTextures[slot] = handle; + + _setTexture(handle, slot); +} + +void GFXTextureArray::Release() +{ + for (GFXTexHandle& mTexture : mTextures) + { + mTexture = NULL; + } +} + +const String GFXTextureArray::describeSelf() const +{ + // We've got nothing + return String(); +} diff --git a/Engine/source/gfx/gfxTextureArray.h b/Engine/source/gfx/gfxTextureArray.h new file mode 100644 index 000000000..7f8b144a1 --- /dev/null +++ b/Engine/source/gfx/gfxTextureArray.h @@ -0,0 +1,88 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXTEXTUREARRAY_H_ +#define _GFXTEXTUREARRAY_H_ + +#ifndef _REFBASE_H_ +#include "core/util/refBase.h" +#endif +#ifndef _GFXRESOURCE_H_ +#include "gfx/gfxResource.h" +#endif +#ifndef _GFXENUMS_H_ +#include "gfxEnums.h" +#endif +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfxTextureHandle.h" +#endif +#include "core/util/tVector.h" + + +class GFXTextureProfile; +class GFXTextureObject; + +class GFXTextureArray : public StrongRefBase, public GFXResource +{ +public: + GFXTextureArray(); + + virtual void init() = 0; + virtual void set(U32 width, U32 height, U32 size, GFXFormat format, U32 mipLevels = 0); + virtual bool fromTextureArray(const Vector &textureArray, U32 capacity = 0); + virtual void setTexture(const GFXTexHandle &texture, U32 slot); + virtual void setToTexUnit(U32 tuNum) = 0; + + + // GFXResource interface + virtual void zombify() = 0; + virtual void resurrect() = 0; + virtual void Release(); + + virtual const String describeSelf() const; + + GFXFormat mFormat; + bool mIsCompressed; + U32 mWidth; + U32 mHeight; + U32 mArraySize; + U32 mMipLevels; + + Vector mTextures; + +protected: + virtual void _setTexture(const GFXTexHandle& texture, U32 slot) = 0; +}; + + +/// A reference counted handle to a texture array resource. +class GFXTextureArrayHandle : public StrongRefPtr +{ +public: + GFXTextureArrayHandle() {} + GFXTextureArrayHandle(GFXTextureArray* textureArray) { StrongRefPtr::set(textureArray); } + + /// Releases the texture handle. + void free() { StrongObjectRef::set(NULL); } +}; + +#endif // _GFXTEXTUREARRAY_H_ diff --git a/Engine/source/gfx/gfxTextureManager.cpp b/Engine/source/gfx/gfxTextureManager.cpp index 6bd027028..8b5a2c9e4 100644 --- a/Engine/source/gfx/gfxTextureManager.cpp +++ b/Engine/source/gfx/gfxTextureManager.cpp @@ -894,6 +894,45 @@ Torque::Path GFXTextureManager::validatePath(const Torque::Path &path) return correctPath; } +GBitmap *GFXTextureManager::loadUncompressedTexture(const Torque::Path &path, GFXTextureProfile *profile, U32 width, U32 height, bool genMips) +{ + GBitmap* inBitmap = loadUncompressedTexture(path, &GFXTexturePersistentProfile); + + if (inBitmap == NULL) + { + Con::warnf("GFXTextureManager::loadUncompressedTexture unable to load texture: %s", path.getFullPath()); + return NULL; + } + + // Set the format so we don't have to handle which channels are where. + if (!inBitmap->setFormat(GFXFormatR8G8B8A8)) + { + Con::warnf("GFXTextureManager::loadUncompressedTexture unable to handle texture format: %s", path.getFullPath()); + return NULL; + } + + GBitmap* outBmp = new GBitmap(width, height, true, GFXFormatR8G8B8A8); + + U8* oBits = (U8*)outBmp->getWritableBits(); + for (S32 y = 0; y < width; y++) + { + for (S32 x = 0; x < height; x++) + { + ColorI texelColor = inBitmap->sampleTexel(x / F32(width), y / F32(height), true).toColorI(true); + + oBits[(y * width + x) * 4] = texelColor.red; + oBits[(y * width + x) * 4 + 1] = texelColor.green; + oBits[(y * width + x) * 4 + 2] = texelColor.blue; + oBits[(y * width + x) * 4 + 3] = texelColor.alpha; + } + } + + if (genMips) + outBmp->extrudeMipLevels(); + + return outBmp; +} + GBitmap *GFXTextureManager::loadUncompressedTexture(const Torque::Path &path, GFXTextureProfile *profile) { PROFILE_SCOPE(GFXTextureManager_loadUncompressedTexture); diff --git a/Engine/source/gfx/gfxTextureManager.h b/Engine/source/gfx/gfxTextureManager.h index be1640351..5e4d7a070 100644 --- a/Engine/source/gfx/gfxTextureManager.h +++ b/Engine/source/gfx/gfxTextureManager.h @@ -41,6 +41,7 @@ #ifndef _TSIGNAL_H_ #include "core/util/tSignal.h" #endif +#include "gfxTextureHandle.h" namespace Torque @@ -131,6 +132,7 @@ public: S32 antialiasLevel); Torque::Path validatePath(const Torque::Path &path); + GBitmap *loadUncompressedTexture(const Torque::Path& path, GFXTextureProfile* profile, U32 width, U32 height, bool genMips = false); GBitmap *loadUncompressedTexture(const Torque::Path &path, GFXTextureProfile *profile); virtual GFXTextureObject *createCompositeTexture(const Torque::Path &pathR, const Torque::Path &pathG, const Torque::Path &pathB, const Torque::Path &pathA, U32 inputKey[4], GFXTextureProfile *profile); diff --git a/Engine/source/gfx/gl/gfxGLDevice.cpp b/Engine/source/gfx/gl/gfxGLDevice.cpp index c90080b3f..d0e9db09f 100644 --- a/Engine/source/gfx/gl/gfxGLDevice.cpp +++ b/Engine/source/gfx/gl/gfxGLDevice.cpp @@ -22,6 +22,8 @@ #include "platform/platform.h" #include "gfx/gl/gfxGLDevice.h" + +#include "gfxGLTextureArray.h" #include "platform/platformGL.h" #include "gfx/gfxCubemap.h" @@ -457,6 +459,13 @@ GFXCubemapArray *GFXGLDevice::createCubemapArray() return cubeArray; } +GFXTextureArray* GFXGLDevice::createTextureArray() +{ + GFXGLTextureArray* textureArray = new GFXGLTextureArray(); + textureArray->registerResourceWithDevice(this); + return textureArray; +} + void GFXGLDevice::endSceneInternal() { // nothing to do for opengl @@ -766,6 +775,22 @@ void GFXGLDevice::setCubemapArrayInternal(U32 textureUnit, const GFXGLCubemapArr } } +void GFXGLDevice::setTextureArrayInternal(U32 textureUnit, const GFXGLTextureArray* texture) +{ + if (texture) + { + mActiveTextureType[textureUnit] = GL_TEXTURE_2D_ARRAY; + texture->bind(textureUnit); + } + else if (mActiveTextureType[textureUnit] != GL_ZERO) + { + glActiveTexture(GL_TEXTURE0 + textureUnit); + glBindTexture(mActiveTextureType[textureUnit], 0); + getOpenglCache()->setCacheBindedTex(textureUnit, mActiveTextureType[textureUnit], 0); + mActiveTextureType[textureUnit] = GL_ZERO; + } +} + void GFXGLDevice::setMatrix( GFXMatrixType mtype, const MatrixF &mat ) { // ONLY NEEDED ON FFP diff --git a/Engine/source/gfx/gl/gfxGLDevice.h b/Engine/source/gfx/gl/gfxGLDevice.h index 3e127367e..0282a103f 100644 --- a/Engine/source/gfx/gl/gfxGLDevice.h +++ b/Engine/source/gfx/gl/gfxGLDevice.h @@ -35,6 +35,7 @@ #include "gfx/gfxResource.h" #include "gfx/gl/gfxGLStateBlock.h" +class GFXGLTextureArray; class GFXGLVertexBuffer; class GFXGLPrimitiveBuffer; class GFXGLTextureTarget; @@ -83,6 +84,7 @@ public: virtual GFXCubemap * createCubemap(); virtual GFXCubemapArray *createCubemapArray(); + virtual GFXTextureArray *createTextureArray(); virtual F32 getFillConventionOffset() const { return 0.0f; } @@ -173,6 +175,7 @@ protected: virtual void setTextureInternal(U32 textureUnit, const GFXTextureObject*texture); virtual void setCubemapInternal(U32 textureUnit, const GFXGLCubemap* texture); virtual void setCubemapArrayInternal(U32 textureUnit, const GFXGLCubemapArray* texture); + virtual void setTextureArrayInternal(U32 textureUnit, const GFXGLTextureArray* texture); virtual void setLightInternal(U32 lightStage, const GFXLightInfo light, bool lightEnable); virtual void setLightMaterialInternal(const GFXLightMaterial mat); @@ -210,6 +213,7 @@ private: friend class GFXGLTextureObject; friend class GFXGLCubemap; friend class GFXGLCubemapArray; + friend class GFXGLTextureArray; friend class GFXGLWindowTarget; friend class GFXGLPrimitiveBuffer; friend class GFXGLVertexBuffer; diff --git a/Engine/source/gfx/gl/gfxGLShader.cpp b/Engine/source/gfx/gl/gfxGLShader.cpp index 5d9d4d380..a92d4bc54 100644 --- a/Engine/source/gfx/gl/gfxGLShader.cpp +++ b/Engine/source/gfx/gl/gfxGLShader.cpp @@ -82,6 +82,7 @@ static U32 shaderConstTypeSize(GFXShaderConstType type) case GFXSCT_Sampler: case GFXSCT_SamplerCube: case GFXSCT_SamplerCubeArray: + case GFXSCT_SamplerTextureArray: return 4; case GFXSCT_Float2: case GFXSCT_Int2: @@ -630,6 +631,9 @@ void GFXGLShader::initConstantDescs() case GL_SAMPLER_CUBE_MAP_ARRAY_ARB: desc.constType = GFXSCT_SamplerCubeArray; break; + case GL_SAMPLER_2D_ARRAY: + desc.constType = GFXSCT_SamplerTextureArray; + break; default: AssertFatal(false, "GFXGLShader::initConstantDescs - unrecognized uniform type"); // If we don't recognize the constant don't add its description. @@ -661,7 +665,10 @@ void GFXGLShader::initHandles() HandleMap::Iterator handle = mHandles.find(desc.name); S32 sampler = -1; - if(desc.constType == GFXSCT_Sampler || desc.constType == GFXSCT_SamplerCube || desc.constType == GFXSCT_SamplerCubeArray) + if(desc.constType == GFXSCT_Sampler || + desc.constType == GFXSCT_SamplerCube || + desc.constType == GFXSCT_SamplerCubeArray || + desc.constType == GFXSCT_SamplerTextureArray) { S32 idx = mSamplerNamesOrdered.find_next(desc.name); AssertFatal(idx != -1, ""); @@ -704,7 +711,11 @@ void GFXGLShader::initHandles() for (HandleMap::Iterator iter = mHandles.begin(); iter != mHandles.end(); ++iter) { GFXGLShaderConstHandle* handle = iter->value; - if(handle->isValid() && (handle->getType() == GFXSCT_Sampler || handle->getType() == GFXSCT_SamplerCube || handle->getType() == GFXSCT_SamplerCubeArray)) + if(handle->isValid() && + (handle->getType() == GFXSCT_Sampler || + handle->getType() == GFXSCT_SamplerCube || + handle->getType() == GFXSCT_SamplerCubeArray || + handle->getType() == GFXSCT_SamplerTextureArray)) { // Set sampler number on our program. glUniform1i(handle->mLocation, handle->mSamplerNum); @@ -837,6 +848,7 @@ void GFXGLShader::setConstantsFromBuffer(GFXGLShaderConstBuffer* buffer) case GFXSCT_Sampler: case GFXSCT_SamplerCube: case GFXSCT_SamplerCubeArray: + case GFXSCT_SamplerTextureArray: glUniform1iv(handle->mLocation, handle->mDesc.arraySize, (GLint*)(mConstBuffer + handle->mOffset)); break; case GFXSCT_Int2: diff --git a/Engine/source/gfx/gl/gfxGLStateCache.h b/Engine/source/gfx/gl/gfxGLStateCache.h index 24b3179e4..c35a2423f 100644 --- a/Engine/source/gfx/gl/gfxGLStateCache.h +++ b/Engine/source/gfx/gl/gfxGLStateCache.h @@ -20,11 +20,11 @@ public: class TextureUnit { public: - TextureUnit() : mTexture1D(0), mTexture2D(0), mTexture3D(0), mTextureCube(0), mTextureCubeArray(0) + TextureUnit() : mTexture1D(0), mTexture2D(0), mTexture3D(0), mTextureCube(0), mTextureCubeArray(0), mTextureArray(0) { } - GLuint mTexture1D, mTexture2D, mTexture3D, mTextureCube, mTextureCubeArray; + GLuint mTexture1D, mTexture2D, mTexture3D, mTextureCube, mTextureCubeArray, mTextureArray; }; /// after glBindTexture @@ -48,6 +48,9 @@ public: case GL_TEXTURE_CUBE_MAP_ARRAY: mTextureUnits[mActiveTexture].mTextureCubeArray = handle; break; + case GL_TEXTURE_2D_ARRAY: + mTextureUnits[mActiveTexture].mTextureArray = handle; + break; default: AssertFatal(0, avar("GFXGLStateCache::setCacheBindedTex - binding (%x) not supported.", biding) ); return; @@ -74,6 +77,9 @@ public: case GL_TEXTURE_CUBE_MAP_ARRAY: mTextureUnits[mActiveTexture].mTextureCubeArray = handle; break; + case GL_TEXTURE_2D_ARRAY: + mTextureUnits[mActiveTexture].mTextureArray = handle; + break; case GL_FRAMEBUFFER: mBindedFBO_W = mBindedFBO_R = handle; break; @@ -109,6 +115,8 @@ public: return mTextureUnits[mActiveTexture].mTextureCube; case GL_TEXTURE_CUBE_MAP_ARRAY: return mTextureUnits[mActiveTexture].mTextureCubeArray; + case GL_TEXTURE_2D_ARRAY: + return mTextureUnits[mActiveTexture].mTextureArray; case GL_DRAW_FRAMEBUFFER: return mBindedFBO_W; case GL_READ_FRAMEBUFFER: @@ -138,4 +146,4 @@ protected: }; -#endif \ No newline at end of file +#endif diff --git a/Engine/source/gfx/gl/gfxGLTextureArray.cpp b/Engine/source/gfx/gl/gfxGLTextureArray.cpp new file mode 100644 index 000000000..f2b403bb0 --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLTextureArray.cpp @@ -0,0 +1,98 @@ +#include "gfxGLTextureArray.h" + +#include "gfxGLTextureObject.h" +#include "gfxGLUtils.h" +#include "core/util/tVector.h" + +GFXGLTextureArray::GFXGLTextureArray() +{ + mTextureArray = NULL; +} + +void GFXGLTextureArray::init() +{ + PRESERVE_2D_TEXTURE_ARRAY(); + glGenTextures(1, &mTextureArray); + glBindTexture(GL_TEXTURE_2D_ARRAY, mTextureArray); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, mMin(mMipLevels - 1, 1)); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glTexStorage3D(GL_TEXTURE_2D_ARRAY, mMipLevels, GFXGLTextureInternalFormat[mFormat], mWidth, mHeight, mArraySize); +} + +void GFXGLTextureArray::_setTexture(const GFXTexHandle& texture, U32 slot) +{ + PRESERVE_2D_TEXTURE_ARRAY(); + glBindTexture(GL_TEXTURE_2D_ARRAY, mTextureArray); + + GFXGLTextureObject* texObj = dynamic_cast(texture.getPointer()); + for (U32 mip = 0; mip < mMipLevels; ++mip) + { + U8* buf = texObj->getTextureData(mip); + const U32 mipWidth = getMax(U32(1), mWidth >> mip); + const U32 mipHeight = getMax(U32(1), mHeight >> mip); + if (mIsCompressed) + { + glCompressedTexSubImage3D( + GL_TEXTURE_2D_ARRAY, + mip, 0, 0, + slot, mipWidth, mipHeight, 1, + GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], buf + ); + } + else + { + glTexSubImage3D( + GL_TEXTURE_2D_ARRAY, + mip, 0, 0, + slot, mipWidth, mipHeight, 1, + GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], buf + ); + } + delete[] buf; + } +} + +void GFXGLTextureArray::setToTexUnit(U32 tuNum) +{ + dynamic_cast(getOwningDevice())->setTextureArrayInternal(tuNum, this); +} + +void GFXGLTextureArray::Release() +{ + glDeleteTextures(1, &mTextureArray); + mTextureArray = 0; + + GFXTextureArray::Release(); +} + +void GFXGLTextureArray::bind(U32 textureUnit) const +{ + glActiveTexture(GL_TEXTURE0 + textureUnit); + glBindTexture(GL_TEXTURE_2D_ARRAY, mTextureArray); + + dynamic_cast(getOwningDevice())->getOpenglCache()->setCacheBindedTex(textureUnit, GL_TEXTURE_2D_ARRAY, mTextureArray); + + GFXGLStateBlockRef sb = dynamic_cast(GFX)->getCurrentStateBlock(); + AssertFatal(sb, "GFXGLTextureArray::bind - No active stateblock!"); + if (!sb) + return; + + const GFXSamplerStateDesc& ssd = sb->getDesc().samplers[textureUnit]; + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, minificationFilter(ssd.minFilter, ssd.mipFilter, 0)); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GFXGLTextureFilter[ssd.magFilter]); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, mMin(mMipLevels - 1, 1)); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GFXGLTextureAddress[ssd.addressModeU]); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GFXGLTextureAddress[ssd.addressModeV]); +} + +void GFXGLTextureArray::zombify() +{ +} + +void GFXGLTextureArray::resurrect() +{ +} diff --git a/Engine/source/gfx/gl/gfxGLTextureArray.h b/Engine/source/gfx/gl/gfxGLTextureArray.h new file mode 100644 index 000000000..65e1d9729 --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLTextureArray.h @@ -0,0 +1,35 @@ +#ifndef _GFXGLTEXTUREARRAY_H_ +#define _GFXGLTEXTUREARRAY_H_ + +#include + +#include "gfx/gfxTextureArray.h" +#include "gfx/gfxTextureManager.h" + +class GFXGLTextureArray : public GFXTextureArray +{ +public: + GFXGLTextureArray(); + + ~GFXGLTextureArray() { Release(); }; + + void init(); + + void setToTexUnit(U32 tuNum) override; + + void bind(U32 textureUnit) const; + + // GFXResource interface + void zombify() override; + void resurrect() override; + void Release() override; + +protected: + void _setTexture(const GFXTexHandle& texture, U32 slot) override; + +private: + GLuint mTextureArray; +}; + + +#endif diff --git a/Engine/source/gfx/gl/gfxGLUtils.h b/Engine/source/gfx/gl/gfxGLUtils.h index 34eabcbd8..8d235d6b9 100644 --- a/Engine/source/gfx/gl/gfxGLUtils.h +++ b/Engine/source/gfx/gl/gfxGLUtils.h @@ -30,7 +30,7 @@ inline U32 getMaxMipmaps(U32 width, U32 height, U32 depth) { - return getMax( getBinLog2(depth), getMax(getBinLog2(width), getBinLog2(height))); + return getMax( getBinLog2(depth), getMax(getBinLog2(width), getBinLog2(height))) + 1; } inline GLenum minificationFilter(U32 minFilter, U32 mipFilter, U32 /*mipLevels*/) @@ -194,6 +194,9 @@ GFXGLPreserveTexture TORQUE_CONCAT(preserve_, __LINE__) (GL_TEXTURE_CUBE_MAP, GL #define PRESERVE_CUBEMAP_ARRAY_TEXTURE() \ GFXGLPreserveTexture TORQUE_CONCAT(preserve_, __LINE__) (GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_BINDING_CUBE_MAP_ARRAY, (GFXGLPreserveInteger::BindFn)glBindTexture) +#define PRESERVE_2D_TEXTURE_ARRAY() \ +GFXGLPreserveTexture TORQUE_CONCAT(preserve_, __LINE__) (GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BINDING_2D_ARRAY, (GFXGLPreserveInteger::BindFn)glBindTexture) + #define _GET_TEXTURE_BINDING(binding) \ binding == GL_TEXTURE_2D ? GL_TEXTURE_BINDING_2D : (binding == GL_TEXTURE_3D ? GL_TEXTURE_BINDING_3D : GL_TEXTURE_BINDING_1D ) diff --git a/Engine/source/shaderGen/shaderOp.cpp b/Engine/source/shaderGen/shaderOp.cpp index 7df224ed9..c6597b94a 100644 --- a/Engine/source/shaderGen/shaderOp.cpp +++ b/Engine/source/shaderGen/shaderOp.cpp @@ -80,6 +80,29 @@ void EchoOp::print( Stream &stream ) WRITESTR( mStatement ); } +//************************************************************************** +// Index operation +//************************************************************************** +IndexOp::IndexOp( Var* var, U32 index ) : Parent( NULL, NULL ) +{ + mInput[0] = var; + mIndex = index; +} + +//-------------------------------------------------------------------------- +// Print +//-------------------------------------------------------------------------- +void IndexOp::print( Stream &stream ) +{ + Var* var = dynamic_cast(mInput[0]); + + mInput[0]->print(stream); + if (var->arraySize > 1) + { + WRITESTR(String::ToString("[%d]", mIndex)); + } +} + //************************************************************************** // General operation diff --git a/Engine/source/shaderGen/shaderOp.h b/Engine/source/shaderGen/shaderOp.h index 6e8170d9a..2f2cbc4bf 100644 --- a/Engine/source/shaderGen/shaderOp.h +++ b/Engine/source/shaderGen/shaderOp.h @@ -110,6 +110,21 @@ public: virtual void print( Stream &stream ); }; +//---------------------------------------------------------------------------- +/*! + Accesses the given index on the variable +*/ +//---------------------------------------------------------------------------- +class IndexOp : public ShaderOp +{ + typedef ShaderOp Parent; + U32 mIndex; + +public: + IndexOp( Var* var, U32 index ); + virtual void print( Stream &stream ); +}; + //---------------------------------------------------------------------------- /*! diff --git a/Engine/source/terrain/glsl/terrFeatureGLSL.cpp b/Engine/source/terrain/glsl/terrFeatureGLSL.cpp index 7c80f9854..bf9cb9c46 100644 --- a/Engine/source/terrain/glsl/terrFeatureGLSL.cpp +++ b/Engine/source/terrain/glsl/terrFeatureGLSL.cpp @@ -48,7 +48,7 @@ namespace FEATUREMGR->registerFeature( MFT_TerrainMacroMap, new NamedFeatureGLSL("TerrainMacroMap Deprecated")); // new TerrainMacroMapFeatGLSL); FEATUREMGR->registerFeature( MFT_TerrainLightMap, new TerrainLightMapFeatGLSL ); FEATUREMGR->registerFeature( MFT_TerrainSideProject, new NamedFeatureGLSL( "Terrain Side Projection" ) ); - FEATUREMGR->registerFeature( MFT_TerrainAdditive, new TerrainAdditiveFeatGLSL ); + FEATUREMGR->registerFeature(MFT_TerrainHeightBlend, new TerrainHeightMapBlendGLSL); FEATUREMGR->registerFeature( MFT_TerrainORMMap, new TerrainORMMapFeatGLSL ); FEATUREMGR->registerFeature( MFT_DeferredTerrainBlankInfoMap, new TerrainBlankInfoMapFeatGLSL ); } @@ -124,69 +124,90 @@ Var* TerrainFeatGLSL::_getInMacroCoord( Vector &componentList return inDet; } -Var* TerrainFeatGLSL::_getNormalMapTex() +Var* TerrainFeatGLSL::_getDetailMapSampler() { - String name( String::ToString( "normalMap%d", getProcessIndex() ) ); - Var *normalMap = (Var*)LangElement::find( name ); - - if ( !normalMap ) + String name("detailMapSampler"); + Var* detailMapSampler = (Var*)LangElement::find(name); + + if (!detailMapSampler) { - normalMap = new Var; - normalMap->setType( "sampler2D" ); - normalMap->setName( name ); - normalMap->uniform = true; - normalMap->sampler = true; - normalMap->constNum = Var::getTexUnitNum(); + detailMapSampler = new Var; + detailMapSampler->setName(name); + detailMapSampler->setType("sampler2DArray"); + detailMapSampler->uniform = true; + detailMapSampler->sampler = true; + detailMapSampler->constNum = Var::getTexUnitNum(); } - - return normalMap; + + return detailMapSampler; } -Var* TerrainFeatGLSL::_getORMConfigMapTex() +Var* TerrainFeatGLSL::_getNormalMapSampler() { - String name(String::ToString("ormConfigMap%d", getProcessIndex())); - Var *ormConfigMap = (Var*)LangElement::find(name); + String name("normalMapSampler"); + Var* normalMapSampler = (Var*)LangElement::find(name); - if (!ormConfigMap) - { - ormConfigMap = new Var; - ormConfigMap->setType("sampler2D"); - ormConfigMap->setName(name); - ormConfigMap->uniform = true; - ormConfigMap->sampler = true; - ormConfigMap->constNum = Var::getTexUnitNum(); - } + if (!normalMapSampler) + { + normalMapSampler = new Var; + normalMapSampler->setName(name); + normalMapSampler->setType("sampler2DArray"); + normalMapSampler->uniform = true; + normalMapSampler->sampler = true; + normalMapSampler->constNum = Var::getTexUnitNum(); + } - return ormConfigMap; + return normalMapSampler; +} + +Var* TerrainFeatGLSL::_getOrmMapSampler() +{ + String name("ormMapSampler"); + Var* ormMapSampler = (Var*)LangElement::find(name); + + if (!ormMapSampler) + { + ormMapSampler = new Var; + ormMapSampler->setName(name); + ormMapSampler->setType("sampler2DArray"); + ormMapSampler->uniform = true; + ormMapSampler->sampler = true; + ormMapSampler->constNum = Var::getTexUnitNum(); + } + + return ormMapSampler; } Var* TerrainFeatGLSL::_getDetailIdStrengthParallax() { - String name( String::ToString( "detailIdStrengthParallax%d", getProcessIndex() ) ); - - Var *detailInfo = (Var*)LangElement::find( name ); - if ( !detailInfo ) + String name(String::ToString("detailIdStrengthParallax", getProcessIndex())); + + Var* detailInfo = (Var*)LangElement::find(name); + if (!detailInfo) { detailInfo = new Var; - detailInfo->setType( "vec3" ); - detailInfo->setName( name ); + detailInfo->setType("vec4"); + detailInfo->setName(name); detailInfo->uniform = true; detailInfo->constSortPos = cspPotentialPrimitive; + detailInfo->arraySize = getProcessIndex(); } - + + detailInfo->arraySize = mMax(detailInfo->arraySize, getProcessIndex() + 1); + return detailInfo; } Var* TerrainFeatGLSL::_getMacroIdStrengthParallax() { - String name( String::ToString( "macroIdStrengthParallax%d", getProcessIndex() ) ); + String name(String::ToString("macroIdStrengthParallax%d", getProcessIndex())); - Var *detailInfo = (Var*)LangElement::find( name ); - if ( !detailInfo ) + Var* detailInfo = (Var*)LangElement::find(name); + if (!detailInfo) { detailInfo = new Var; - detailInfo->setType( "vec3" ); - detailInfo->setName( name ); + detailInfo->setType("vec3"); + detailInfo->setName(name); detailInfo->uniform = true; detailInfo->constSortPos = cspPotentialPrimitive; } @@ -291,21 +312,42 @@ void TerrainBaseMapFeatGLSL::processPix( Vector &componentLis } meta->addStatement( new GenOp( " @;\r\n", assignColor( baseColor, Material::Mul,NULL,target ) ) ); + // Set base ORM info + Var* ormConfig; + OutputTarget targ = RenderTarget1; + if (fd.features[MFT_isDeferred]) + { + targ = RenderTarget2; + } + ormConfig = (Var*)LangElement::find(getOutputTargetVarName(targ)); + if (!ormConfig) + { + // create color var + ormConfig = new Var; + ormConfig->setType("fragout"); + ormConfig->setName(getOutputTargetVarName(targ)); + ormConfig->setStructName("OUT"); + } + + meta->addStatement(new GenOp(" @ = float4(0.0, 1.0, 1.0, 0.0);\r\n", ormConfig)); + output = meta; } ShaderFeature::Resources TerrainBaseMapFeatGLSL::getResources( const MaterialFeatureData &fd ) { - Resources res; + Resources res; + + // Sample base texture res.numTexReg = 1; - res.numTex = 1; + res.numTex = 1; return res; } U32 TerrainBaseMapFeatGLSL::getOutputTargets( const MaterialFeatureData &fd ) const { - return fd.features[MFT_isDeferred] ? ShaderFeature::RenderTarget1 : ShaderFeature::DefaultTarget; + return fd.features[MFT_isDeferred] ? ShaderFeature::RenderTarget1 | ShaderFeature::RenderTarget2 : ShaderFeature::DefaultTarget | ShaderFeature::RenderTarget1; } TerrainDetailMapFeatGLSL::TerrainDetailMapFeatGLSL() @@ -377,11 +419,17 @@ void TerrainDetailMapFeatGLSL::processVert( Vector &component outTex->setType( "vec4" ); // Get the detail scale and fade info. - Var *detScaleAndFade = new Var; - detScaleAndFade->setType( "vec4" ); - detScaleAndFade->setName( String::ToString( "detailScaleAndFade%d", detailIndex ) ); - detScaleAndFade->uniform = true; - detScaleAndFade->constSortPos = cspPotentialPrimitive; + Var *detScaleAndFade = (Var*)LangElement::find("detailScaleAndFade"); + if (!detScaleAndFade) + { + detScaleAndFade = new Var; + detScaleAndFade->setType("vec4"); + detScaleAndFade->setName("detailScaleAndFade"); + detScaleAndFade->uniform = true; + detScaleAndFade->constSortPos = cspPotentialPrimitive; + } + + detScaleAndFade->arraySize = mMax(detScaleAndFade->arraySize, detailIndex + 1); // Setup the detail coord. // @@ -392,11 +440,11 @@ void TerrainDetailMapFeatGLSL::processVert( Vector &component // // See TerrainBaseMapFeatGLSL::processVert(). // - meta->addStatement( new GenOp( " @.xyz = @ * @.xyx;\r\n", outTex, inTex, detScaleAndFade ) ); + meta->addStatement( new GenOp( " @.xyz = @ * @.xyx;\r\n", outTex, inTex, new IndexOp(detScaleAndFade, detailIndex) ) ); // 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 ) ); + outTex, new IndexOp(detScaleAndFade, detailIndex), dist, new IndexOp(detScaleAndFade, detailIndex)) ); output = meta; } @@ -473,186 +521,97 @@ void TerrainDetailMapFeatGLSL::processPix( Vector &component // Calculate the blend for this detail texture. meta->addStatement( new GenOp( " @ = calcBlend( @.x, @.xy, @, @ );\r\n", - new DecOp( detailBlend ), detailInfo, inTex, layerSize, layerSample ) ); - - // New terrain - - Var *lerpBlend = (Var*)LangElement::find("lerpBlend"); - if (!lerpBlend) - { - lerpBlend = new Var; - lerpBlend->setType("float"); - lerpBlend->setName("lerpBlend"); - lerpBlend->uniform = true; - lerpBlend->constSortPos = cspPrimitive; - } - - - Var *blendDepth = (Var*)LangElement::find(String::ToString("blendDepth%d", detailIndex)); - if (!blendDepth) - { - blendDepth = new Var; - blendDepth->setType("float"); - blendDepth->setName(String::ToString("blendDepth%d", detailIndex)); - blendDepth->uniform = true; - blendDepth->constSortPos = cspPrimitive; - } - - ShaderFeature::OutputTarget target = ShaderFeature::DefaultTarget; - - if (fd.features.hasFeature(MFT_isDeferred)) - target= ShaderFeature::RenderTarget1; - - Var *outColor = (Var*)LangElement::find( getOutputTargetVarName(target) ); - - if (!outColor) - { - // create color var - outColor = new Var; - outColor->setType("vec4"); - outColor->setName("col"); - outColor->setStructName("OUT"); - meta->addStatement(new GenOp(" @;\r\n", outColor)); - } - - Var *detailColor = (Var*)LangElement::find("detailColor"); - if (!detailColor) - { - detailColor = new Var; - detailColor->setType("vec4"); - detailColor->setName("detailColor"); - meta->addStatement(new GenOp(" @;\r\n", new DecOp(detailColor))); - } - - // Get the detail texture. - Var *detailMap = new Var; - detailMap->setType("sampler2D"); - detailMap->setName(String::ToString("detailMap%d", detailIndex)); - detailMap->uniform = true; - detailMap->sampler = true; - detailMap->constNum = Var::getTexUnitNum(); // used as texture unit num here - - // Get the normal map texture. - Var *normalMap = _getNormalMapTex(); - - // Issue happens somewhere here ----- - - // Sample the normal map. - // - // We take two normal samples and lerp between them for - // side projection layers... else a single sample. - LangElement *texOp; - - // Note that we're doing the standard greyscale detail - // map technique here which can darken and lighten the - // diffuse texture. - // - // We take two color samples and lerp between them for - // side projection layers... else a single sample. - // - if (fd.features.hasFeature(MFT_TerrainSideProject, detailIndex)) - { - meta->addStatement(new GenOp(" @ = ( lerp( tex2D( @, @.yz ), tex2D( @, @.xz ), @.z ) * 2.0 ) - 1.0;\r\n", - detailColor, detailMap, inDet, detailMap, inDet, inTex)); - - texOp = new GenOp("lerp( tex2D( @, @.yz ), tex2D( @, @.xz ), @.z )", - normalMap, inDet, normalMap, inDet, inTex); - } - else - { - meta->addStatement(new GenOp(" @ = ( tex2D( @, @.xy ) * 2.0 ) - 1.0;\r\n", - detailColor, detailMap, inDet)); - - texOp = new GenOp("tex2D(@, @.xy)", normalMap, inDet); - } - - // New terrain - - // Get a var and accumulate the blend amount. - Var *blendTotal = (Var*)LangElement::find( "blendTotal" ); - if ( !blendTotal ) - { - blendTotal = new Var; - blendTotal->setName( "blendTotal" ); - blendTotal->setType( "float" ); - meta->addStatement( new GenOp( " @ = 0;\r\n", new DecOp( blendTotal ) ) ); - } - - // Add to the blend total. - meta->addStatement(new GenOp(" @ = max( @, @ );\r\n", blendTotal, blendTotal, detailBlend)); + new DecOp( detailBlend ), new IndexOp(detailInfo, detailIndex), inTex, layerSize, layerSample ) ); // If we had a parallax feature... then factor in the parallax // amount so that it fades out with the layer blending. - if ( fd.features.hasFeature( MFT_TerrainParallaxMap, detailIndex ) ) + if (fd.features.hasFeature(MFT_TerrainParallaxMap, detailIndex)) { + // Get the normal map texture. + Var* normalMap = _getNormalMapSampler(); + // Call the library function to do the rest. if (fd.features.hasFeature(MFT_IsBC3nm, detailIndex)) { - meta->addStatement(new GenOp(" @.xy += parallaxOffsetDxtnm( @, @.xy, @, @.z * @ );\r\n", - inDet, normalMap, inDet, negViewTS, detailInfo, detailBlend)); + meta->addStatement(new GenOp(" @.xy += parallaxOffsetDxtnm( @, vec3(@.xy, @.x), @, @.z * @ );\r\n", + inDet, normalMap, inDet, new IndexOp(detailInfo, detailIndex), negViewTS, new IndexOp(detailInfo, detailIndex), detailBlend)); } else { - meta->addStatement(new GenOp(" @.xy += parallaxOffset( @, @.xy, @, @.z * @ );\r\n", - inDet, normalMap, inDet, negViewTS, detailInfo, detailBlend)); + meta->addStatement(new GenOp(" @.xy += parallaxOffset( @, vec3(@.xy, @.x), @, @.z * @ );\r\n", + inDet, normalMap, inDet, new IndexOp(detailInfo, detailIndex), negViewTS, new IndexOp(detailInfo, detailIndex), detailBlend)); } } - - // Check to see if we have a gbuffer normal. - Var *gbNormal = (Var*)LangElement::find( "gbNormal" ); - - // If we have a gbuffer normal and we don't have a - // normal map feature then we need to lerp in a - // default normal else the normals below this layer - // will show thru. - if (gbNormal && - !fd.features.hasFeature(MFT_TerrainNormalMap, detailIndex)) - { - Var *viewToTangent = getInViewToTangent(componentList); - meta->addStatement(new GenOp(" @ = lerp( @, tGetMatrix3Row(@, 2), min( @, @.w ) );\r\n", - gbNormal, gbNormal, viewToTangent, detailBlend, inDet)); + Var* detailColor = (Var*)LangElement::find(String::ToString("detailColor%d", detailIndex)); + if (!detailColor) + { + detailColor = new Var; + detailColor->setType("vec4"); + detailColor->setName(String::ToString("detailColor%d", detailIndex)); + meta->addStatement(new GenOp(" @;\r\n", new DecOp(detailColor))); } - // If we're using SM 3.0 then take advantage of - // dynamic branching to skip layers per-pixel. - + // Get the detail texture. + Var *detailMap = _getDetailMapSampler(); - if ( GFX->getPixelShaderVersion() >= 3.0f ) - meta->addStatement( new GenOp( " if ( @ > 0.0f )\r\n", detailBlend ) ); - - meta->addStatement( new GenOp( " {\r\n" ) ); - - // Note that we're doing the standard greyscale detail - // map technique here which can darken and lighten the - // diffuse texture. - // - // We take two color samples and lerp between them for - // side projection layers... else a single sample. - // - if ( fd.features.hasFeature( MFT_TerrainSideProject, detailIndex ) ) + // If we had a parallax feature... then factor in the parallax + // amount so that it fades out with the layer blending. + if (fd.features.hasFeature(MFT_TerrainSideProject, detailIndex)) { - meta->addStatement( new GenOp( " @ = ( lerp( tex2D( @, @.yz ), tex2D( @, @.xz ), @.z ) * 2.0 ) - 1.0;\r\n", - detailColor, detailMap, inDet, detailMap, inDet, inTex ) ); + meta->addStatement(new GenOp(" @ = ( lerp( tex2D( @, vec3(@.yz, @.x) ), tex2D( @, vec3(@.xz, @.x) ), @.z ) * 2.0 ) - 1.0;\r\n", + detailColor, detailMap, inDet, new IndexOp(detailInfo, detailIndex), detailMap, inDet, new IndexOp(detailInfo, detailIndex), inTex)); } else { - meta->addStatement( new GenOp( " @ = ( tex2D( @, @.xy ) * 2.0 ) - 1.0;\r\n", - detailColor, detailMap, inDet ) ); + meta->addStatement(new GenOp(" @ = ( tex2D( @, vec3(@.xy, @.x) ) * 2.0 ) - 1.0;\r\n", + detailColor, detailMap, inDet, new IndexOp(detailInfo, detailIndex))); } - meta->addStatement( new GenOp( " @ *= @.y * @.w;\r\n", - detailColor, detailInfo, inDet ) ); + meta->addStatement(new GenOp(" @ *= @.y * @.w;\r\n", + detailColor, new IndexOp(detailInfo, detailIndex), inDet)); + if (!fd.features.hasFeature(MFT_TerrainHeightBlend)) + { + // Check to see if we have a gbuffer normal. + Var* gbNormal = (Var*)LangElement::find("gbNormal"); - meta->addStatement(new GenOp(" @.rgb = toGamma(@.rgb);\r\n", outColor, outColor)); + // If we have a gbuffer normal and we don't have a + // normal map feature then we need to lerp in a + // default normal else the normals below this layer + // will show thru. + if (gbNormal && + !fd.features.hasFeature(MFT_TerrainNormalMap, detailIndex)) + { + Var* viewToTangent = getInViewToTangent(componentList); - meta->addStatement(new GenOp(" @ += @ * @;\r\n", - outColor, detailColor, detailBlend)); + meta->addStatement(new GenOp(" @ = lerp( @, tGetMatrix3Row(@, 2), min( @, @.w ) );\r\n", + gbNormal, gbNormal, viewToTangent, detailBlend, inDet)); + } - meta->addStatement(new GenOp(" @.rgb = toLinear(clamp(@.rgb, 0, 1));\r\n", outColor, outColor)); + // If we're using SM 3.0 then take advantage of + // dynamic branching to skip layers per-pixel. + if (GFX->getPixelShaderVersion() >= 3.0f) + meta->addStatement(new GenOp(" if ( @ > 0.0f )\r\n", detailBlend)); - meta->addStatement( new GenOp( " }\r\n" ) ); + meta->addStatement(new GenOp(" {\r\n")); + + ShaderFeature::OutputTarget target = ShaderFeature::DefaultTarget; + + if (fd.features.hasFeature(MFT_isDeferred)) + target = ShaderFeature::RenderTarget1; + + Var* outColor = (Var*)LangElement::find(getOutputTargetVarName(target)); + + meta->addStatement(new GenOp(" @.rgb = toGamma(@.rgb);\r\n", outColor, outColor)); + + meta->addStatement(new GenOp(" @ += @ * @;\r\n", + outColor, detailColor, detailBlend)); + + meta->addStatement(new GenOp(" @.rgb = toLinear(clamp(@.rgb, 0, 1));\r\n", outColor, outColor)); + + meta->addStatement(new GenOp(" }\r\n")); + } output = meta; } @@ -665,27 +624,14 @@ ShaderFeature::Resources TerrainDetailMapFeatGLSL::getResources( const MaterialF { // If this is the first detail pass then we // samples from the layer tex. - res.numTex += 1; + res.numTex = 1; + res.numTexReg = 1; - // If this material also does parallax then it - // will generate the negative view vector and the - // worldToTanget transform. - if ( fd.features.hasFeature( MFT_TerrainParallaxMap ) ) - res.numTexReg += 4; + // Add Detail TextureArray + res.numTex += 1; + res.numTexReg += 1; } - // sample from the detail texture for diffuse coloring. - res.numTex += 1; - - // If we have parallax for this layer then we'll also - // be sampling the normal map for the parallax heightmap. - if ( fd.features.hasFeature( MFT_TerrainParallaxMap, getProcessIndex() ) ) - res.numTex += 1; - - // Finally we always send the detail texture - // coord to the pixel shader. - res.numTexReg += 1; - return res; } @@ -836,20 +782,6 @@ void TerrainMacroMapFeatGLSL::processPix( Vector &componentL meta->addStatement( new GenOp( " @ = calcBlend( @.x, @.xy, @, @ );\r\n", new DecOp( detailBlend ), detailInfo, inTex, layerSize, layerSample ) ); - // Get a var and accumulate the blend amount. - Var *blendTotal = (Var*)LangElement::find( "blendTotal" ); - if ( !blendTotal ) - { - blendTotal = new Var; - //blendTotal->setName( "blendTotal" ); - blendTotal->setName( "blendTotal" ); - blendTotal->setType( "float" ); - meta->addStatement( new GenOp( " @ = 0;\r\n", new DecOp( blendTotal ) ) ); - } - - // Add to the blend total. - meta->addStatement(new GenOp(" @ = max( @, @ );\r\n", blendTotal, blendTotal, detailBlend)); - Var *detailColor = (Var*)LangElement::find( "macroColor" ); if ( !detailColor ) { @@ -950,9 +882,12 @@ void TerrainNormalMapFeatGLSL::processVert( Vector &component MultiLine *meta = new MultiLine; - // Make sure the world to tangent transform - // is created and available for the pixel shader. - getOutViewToTangent( componentList, meta, fd ); + if (!fd.features.hasFeature(MFT_TerrainHeightBlend)) + { + // Make sure the world to tangent transform + // is created and available for the pixel shader. + getOutViewToTangent(componentList, meta, fd); + } output = meta; } @@ -966,37 +901,18 @@ void TerrainNormalMapFeatGLSL::processPix( Vector &component MultiLine *meta = new MultiLine; - Var *viewToTangent = getInViewToTangent( componentList ); - - // This var is read from GBufferConditionerGLSL and - // used in the deferred output. - Var *gbNormal = (Var*)LangElement::find( "gbNormal" ); - if ( !gbNormal ) - { - gbNormal = new Var; - gbNormal->setName( "gbNormal" ); - gbNormal->setType( "vec3" ); - meta->addStatement( new GenOp( " @ = tGetMatrix3Row(@, 2);\r\n", new DecOp( gbNormal ), viewToTangent ) ); - } - const S32 normalIndex = getProcessIndex(); Var *detailBlend = (Var*)LangElement::find( String::ToString( "detailBlend%d", normalIndex ) ); AssertFatal( detailBlend, "The detail blend is missing!" ); - // If we're using SM 3.0 then take advantage of - // dynamic branching to skip layers per-pixel. - if ( GFX->getPixelShaderVersion() >= 3.0f ) - meta->addStatement( new GenOp( " if ( @ > 0.0f )\r\n", detailBlend ) ); - - meta->addStatement( new GenOp( " {\r\n" ) ); - // Get the normal map texture. - Var *normalMap = _getNormalMapTex(); + Var *normalMap = _getNormalMapSampler(); /// Get the texture coord. Var *inDet = _getInDetailCoord( componentList ); Var *inTex = getVertTexCoord( "texCoord" ); + Var* detailInfo = _getDetailIdStrengthParallax(); // Sample the normal map. // @@ -1005,42 +921,50 @@ void TerrainNormalMapFeatGLSL::processPix( Vector &component LangElement *texOp; if ( fd.features.hasFeature( MFT_TerrainSideProject, normalIndex ) ) { - texOp = new GenOp( "lerp( tex2D( @, @.yz ), tex2D( @, @.xz ), @.z )", - normalMap, inDet, normalMap, inDet, inTex ); + texOp = new GenOp( "lerp( tex2D( @, vec3(@.yz, @.x) ), tex2D( @, vec3(@.xz, @.x) ), @.z )", + normalMap, inDet, new IndexOp(detailInfo, normalIndex), normalMap, inDet, inTex, new IndexOp(detailInfo, normalIndex)); } else - texOp = new GenOp( "tex2D(@, @.xy)", normalMap, inDet ); + texOp = new GenOp( String::ToString("tex2D(@, vec3(@.xy, @.x))", normalIndex), normalMap, inDet, new IndexOp(detailInfo, normalIndex)); // create bump normal Var *bumpNorm = new Var; - bumpNorm->setName( "bumpNormal" ); + bumpNorm->setName( String::ToString("bumpNormal%d", normalIndex) ); bumpNorm->setType( "vec4" ); LangElement *bumpNormDecl = new DecOp( bumpNorm ); meta->addStatement( expandNormalMap( texOp, bumpNormDecl, bumpNorm, fd ) ); - // If this is the last normal map then we - // can test to see the total blend value - // to see if we should clip the result. - Var* blendTotal = (Var*)LangElement::find("blendTotal"); - if (blendTotal) + if (!fd.features.hasFeature(MFT_TerrainHeightBlend)) { - if (fd.features.getNextFeatureIndex(MFT_TerrainNormalMap, normalIndex) == -1) - meta->addStatement(new GenOp(" if ( @ > 0.0001f ){\r\n\r\n", blendTotal)); - } - // Normalize is done later... - // Note: The reverse mul order is intentional. Affine matrix. - meta->addStatement(new GenOp(" @ = lerp( @, tMul( @.xyz, @ ), min( @, @.w ) );\r\n", - gbNormal, gbNormal, bumpNorm, viewToTangent, detailBlend, inDet)); + Var* viewToTangent = getInViewToTangent(componentList); - if (blendTotal) - { - if (fd.features.getNextFeatureIndex(MFT_TerrainNormalMap, normalIndex) == -1) - meta->addStatement(new GenOp(" }\r\n")); - } + // This var is read from GBufferConditionerGLSL and + // used in the deferred output. + Var* gbNormal = (Var*)LangElement::find("gbNormal"); + if (!gbNormal) + { + gbNormal = new Var; + gbNormal->setName("gbNormal"); + gbNormal->setType("vec3"); + meta->addStatement(new GenOp(" @ = tGetMatrix3Row(@, 2);\r\n", new DecOp(gbNormal), viewToTangent)); + } - // End the conditional block. - meta->addStatement( new GenOp( " }\r\n" ) ); + // If we're using SM 3.0 then take advantage of + // dynamic branching to skip layers per-pixel. + if (GFX->getPixelShaderVersion() >= 3.0f) + meta->addStatement(new GenOp(" if ( @ > 0.0f )\r\n", detailBlend)); + + meta->addStatement(new GenOp(" {\r\n")); + + // Normalize is done later... + // Note: The reverse mul order is intentional. Affine matrix. + meta->addStatement(new GenOp(" @ = lerp( @, tMul( @.xyz, @ ), min( @, @.w ) );\r\n", + gbNormal, gbNormal, bumpNorm, viewToTangent, detailBlend, inDet)); + + // End the conditional block. + meta->addStatement(new GenOp(" }\r\n")); + } output = meta; } @@ -1050,16 +974,25 @@ ShaderFeature::Resources TerrainNormalMapFeatGLSL::getResources( const MaterialF Resources res; // We only need to process normals during the deferred. - if ( fd.features.hasFeature( MFT_DeferredConditioner ) ) + if (!fd.features.hasFeature(MFT_DeferredConditioner)) { - // If this is the first normal map and there - // are no parallax features then we will - // generate the worldToTanget transform. - if ( !fd.features.hasFeature( MFT_TerrainParallaxMap ) && - ( getProcessIndex() == 0 || !fd.features.hasFeature( MFT_TerrainNormalMap, getProcessIndex() - 1 ) ) ) - res.numTexReg = 3; + return res; + } - res.numTex = 1; + S32 featureIndex = 0, firstNormalMapIndex = 0; + for (int idx = 0; idx < fd.features.getCount(); ++idx) { + const FeatureType& type = fd.features.getAt(idx, &featureIndex); + if (type == MFT_TerrainNormalMap) { + firstNormalMapIndex = getMin(firstNormalMapIndex, featureIndex); + } + } + + // We only need to process normals during the deferred. + if (getProcessIndex() == firstNormalMapIndex) + { + // Normal Texture Array + res.numTexReg += 1; + res.numTex += 1; } return res; @@ -1107,35 +1040,6 @@ ShaderFeature::Resources TerrainLightMapFeatGLSL::getResources( const MaterialFe return res; } - -void TerrainAdditiveFeatGLSL::processPix( Vector &componentList, - const MaterialFeatureData &fd ) -{ - Var *color = NULL; - Var* norm = NULL; - if (fd.features[MFT_isDeferred]) - { - color = (Var*)LangElement::find(getOutputTargetVarName(ShaderFeature::RenderTarget1)); - norm = (Var*)LangElement::find(getOutputTargetVarName(ShaderFeature::DefaultTarget)); - } - color = (Var*)LangElement::find(getOutputTargetVarName(ShaderFeature::DefaultTarget)); - - Var *blendTotal = (Var*)LangElement::find( "blendTotal" ); - if ( !color || !blendTotal ) - return; - - MultiLine *meta = new MultiLine; - - meta->addStatement( new GenOp( " clip( @ - 0.0001 );\r\n", blendTotal ) ); - meta->addStatement( new GenOp( " @.a = @;\r\n", color, blendTotal ) ); - if (fd.features[MFT_isDeferred]) - { - meta->addStatement(new GenOp(" @.a = @;\r\n", norm, blendTotal)); - } - - output = meta; -} - //standard matInfo map contains data of the form .r = bitflags, .g = (will contain AO), //.b = specular strength, a= spec power. @@ -1197,21 +1101,25 @@ void TerrainORMMapFeatGLSL::processVert(Vector &componentList, Var *outTex = (Var*)LangElement::find(String::ToString("detCoord%d", detailIndex)); if (outTex == NULL) { + outTex = new Var; 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)); + Var *detScaleAndFade = (Var*)LangElement::find("detailScaleAndFade"); if (detScaleAndFade == NULL) { + detScaleAndFade = new Var; detScaleAndFade->setType("vec4"); - detScaleAndFade->setName(String::ToString("detailScaleAndFade%d", detailIndex)); + detScaleAndFade->setName("detailScaleAndFade"); detScaleAndFade->uniform = true; detScaleAndFade->constSortPos = cspPotentialPrimitive; } + detScaleAndFade->arraySize = mMax(detScaleAndFade->arraySize, detailIndex + 1); + // Setup the detail coord. // // NOTE: You see here we scale the texture coord by 'xyx' @@ -1221,11 +1129,11 @@ void TerrainORMMapFeatGLSL::processVert(Vector &componentList, // // See TerrainBaseMapFeatGLSL::processVert(). // - meta->addStatement(new GenOp(" @.xyz = @ * @.xyx;\r\n", outTex, inTex, detScaleAndFade)); + meta->addStatement(new GenOp(" @.xyz = @ * @.xyx;\r\n", outTex, inTex, new IndexOp(detScaleAndFade, detailIndex))); // 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)); + outTex, new IndexOp(detScaleAndFade, detailIndex), dist, new IndexOp(detScaleAndFade, detailIndex))); output = meta; } @@ -1241,9 +1149,10 @@ void TerrainORMMapFeatGLSL::processPix(Vector &componentList, /// Get the texture coord. Var *inDet = _getInDetailCoord(componentList); Var *inTex = getVertTexCoord("texCoord"); + Var* detailInfo = _getDetailIdStrengthParallax(); const S32 compositeIndex = getProcessIndex(); - Var *ormConfigMap = _getORMConfigMapTex(); + Var *ormConfigMap = _getOrmMapSampler(); // Sample the normal map. // // We take two normal samples and lerp between them for @@ -1252,11 +1161,11 @@ void TerrainORMMapFeatGLSL::processPix(Vector &componentList, if (fd.features.hasFeature(MFT_TerrainSideProject, compositeIndex)) { - texOp = new GenOp("lerp( tex2D( @, @.yz ), tex2D( @, @.xz ), @.z )", - ormConfigMap, inDet, ormConfigMap, inDet, inTex); + texOp = new GenOp("lerp( tex2D( @, vec3(@.yz, @.x) ), tex2D( @, vec3(@.xz, @.x) ), @.z )", + ormConfigMap, inDet, new IndexOp(detailInfo, compositeIndex), ormConfigMap, inDet, new IndexOp(detailInfo, compositeIndex), inTex); } else - texOp = new GenOp("tex2D(@, @.xy)", ormConfigMap, inDet); + texOp = new GenOp("tex2D(@, vec3(@.xy, @.x))", ormConfigMap, inDet, new IndexOp(detailInfo, compositeIndex)); // search for material var Var * ormConfig; @@ -1283,21 +1192,24 @@ void TerrainORMMapFeatGLSL::processPix(Vector &componentList, 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(" @ = @.rgb*@;\r\n", new DecOp(matinfoCol), texOp, detailBlend)); - meta->addStatement(new GenOp(" @.gba += @;\r\n", ormConfig, matinfoCol)); - } - else - { - meta->addStatement(new GenOp(" @ = lerp(vec3(1.0,1.0,0.0),@.rgb,@);\r\n", new DecOp(matinfoCol), texOp, detailBlend)); - meta->addStatement(new GenOp(" @ = vec4(0.0,@);\r\n", ormConfig, matinfoCol)); - } - - if (fd.features[MFT_InvertRoughness]) + if (compositeIndex == 0) { - meta->addStatement(new GenOp(" @.b = 1.0-@.b;\r\n", ormConfig, ormConfig)); + meta->addStatement(new GenOp(" @ = vec4(0.0, 0.0, 0.0, 0.0);\r\n", ormConfig)); + } + + meta->addStatement(new GenOp(" @ = @.rgb;\r\n", new DecOp(matinfoCol), texOp)); + + if (fd.features.hasFeature(MFT_InvertRoughness, compositeIndex)) + { + meta->addStatement(new GenOp(" @.b = 1.0 - @.b;\r\n", matinfoCol, matinfoCol)); + } + + + meta->addStatement(new GenOp(" @ = lerp(float3(1.0, 1.0, 0.0), @, @.y * @.w);\r\n", matinfoCol, matinfoCol, new IndexOp(detailInfo, compositeIndex), inDet)); + + if (!fd.features.hasFeature(MFT_TerrainHeightBlend)) + { + meta->addStatement(new GenOp(" @.gba += @ * @;\r\n", ormConfig, matinfoCol, detailBlend)); } output = meta; @@ -1305,10 +1217,23 @@ void TerrainORMMapFeatGLSL::processPix(Vector &componentList, ShaderFeature::Resources TerrainORMMapFeatGLSL::getResources(const MaterialFeatureData &fd) { - Resources res; - res.numTex = 1; - res.numTexReg += 1; - return res; + Resources res; + + S32 featureIndex = 0, firstOrmMapIndex = 0; + for (int idx = 0; idx < fd.features.getCount(); ++idx) { + const FeatureType& type = fd.features.getAt(idx, &featureIndex); + if (type == MFT_TerrainORMMap) { + firstOrmMapIndex = getMin(firstOrmMapIndex, featureIndex); + } + } + + // We only need to process normals during the deferred. + if (getProcessIndex() == firstOrmMapIndex) + { + res.numTexReg = 1; + res.numTex = 1; + } + return res; } @@ -1321,9 +1246,11 @@ U32 TerrainBlankInfoMapFeatGLSL::getOutputTargets(const MaterialFeatureData &fd) void TerrainBlankInfoMapFeatGLSL::processPix(Vector &componentList, const MaterialFeatureData &fd) { + S32 compositeIndex = getProcessIndex(); + // search for material var Var *material; - OutputTarget targ = RenderTarget1; + OutputTarget targ = DefaultTarget; if (fd.features[MFT_isDeferred]) { targ = RenderTarget2; @@ -1340,7 +1267,373 @@ void TerrainBlankInfoMapFeatGLSL::processPix(Vector &component material->setStructName("OUT"); } - meta->addStatement(new GenOp(" @ = vec4(0.0,1.0,1.0,0.0);\r\n", material)); + if (compositeIndex == 0) + { + meta->addStatement(new GenOp(" @ = vec4(0.0, 0.0, 0.0, 0.0);\r\n", material)); + } + + Var* detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", compositeIndex)); + AssertFatal(detailBlend, "The detail blend is missing!"); + + String matinfoName(String::ToString("matinfoCol%d", compositeIndex)); + + meta->addStatement(new GenOp(" @.gba += vec3(@, @, 0.0);\r\n", material, detailBlend, detailBlend)); + + output = meta; +} + +void TerrainHeightMapBlendGLSL::processVert( + Vector &componentList, const MaterialFeatureData &fd) { + // We only need to process normals during the deferred. + if (!fd.features.hasFeature(MFT_DeferredConditioner)) + return; + + MultiLine* meta = new MultiLine; + + // Make sure the world to tangent transform + // is created and available for the pixel shader. + getOutViewToTangent(componentList, meta, fd); + + output = meta; +} + +void TerrainHeightMapBlendGLSL::processPix(Vector& componentList, + const MaterialFeatureData& fd) +{ + + ShaderFeature::OutputTarget target = ShaderFeature::DefaultTarget; + + if (fd.features.hasFeature(MFT_isDeferred)) + target = ShaderFeature::RenderTarget1; + + Var* outColor = (Var*)LangElement::find(getOutputTargetVarName(target)); + + if (!outColor) + return; + + MultiLine* meta = new MultiLine; + + // Count the number of detail textures + int detailCount = 0; + while (true) + { + if (LangElement::find(String::ToString("detailBlend%d", detailCount)) == NULL) + { + break; + } + + ++detailCount; + } + + if (detailCount == 0) + { + return; + } + + // Compute the "height" of each detail layer and store it detailHX + for (S32 idx = 0; idx < detailCount; ++idx) + { + Var* detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", idx)); + Var* bumpNormal = (Var*)LangElement::find(String::ToString("bumpNormal%d", idx)); + Var* blendDepth = (Var*)LangElement::find(String::ToString("blendDepth%d", idx)); + if (!blendDepth) + { + blendDepth = new Var; + blendDepth->setType("float"); + blendDepth->setName(String::ToString("blendDepth%d", idx)); + blendDepth->uniform = true; + blendDepth->constSortPos = cspPrimitive; + } + + Var* blendContrast = (Var*)LangElement::find(String::ToString("blendContrast%d", idx)); + if (!blendContrast) + { + blendContrast = new Var; + blendContrast->setType("float"); + blendContrast->setName(String::ToString("blendContrast%d", idx)); + blendContrast->uniform = true; + blendContrast->constSortPos = cspPrimitive; + } + + Var* detailH = (Var*)LangElement::find(String::ToString("detailH%d", idx)); + if (!detailH) + { + detailH = new Var; + detailH->setType("float"); + detailH->setName(String::ToString("detailH%d", idx)); + + meta->addStatement(new GenOp(" @ = 0;\r\n", + new DecOp(detailH))); + meta->addStatement(new GenOp(" if (@ > 0.0f) {\r\n", detailBlend)); + if (bumpNormal != NULL) + { + meta->addStatement(new GenOp(" @ = clamp(@.a + @, 0.0, 1.0);\r\n", + detailH, bumpNormal, blendDepth)); + } + else + { + meta->addStatement(new GenOp(" @ = clamp(0.5 + @, 0.0, 1.0);\r\n", + detailH, blendDepth)); + } + + meta->addStatement(new GenOp(" @ = max((@ * 2.0f - 1.0f) * @ + 0.5f, 0.0f);\r\n", + detailH, detailH, blendContrast)); + + meta->addStatement(new GenOp(" }\r\n")); + } + } + + meta->addStatement(new GenOp("\r\n")); + + // Compute blending factors + Var* depth = (Var*)LangElement::find("baseBlendDepth"); + if (depth == NULL) + { + depth = new Var; + depth->setType("float"); + depth->setName("baseBlendDepth"); + depth->uniform = true; + depth->constSortPos = cspPrimitive; + } + + Var* ma = (Var*)LangElement::find("ma"); + if (ma == NULL) + { + ma = new Var; + ma->setType("float"); + ma->setName("ma"); + meta->addStatement(new GenOp(" @ = 0;\r\n", + new DecOp(ma))); + } + + for (S32 idx = 0; idx < detailCount; ++idx) + { + Var* detailH = (Var*)LangElement::find(String::ToString("detailH%d", idx)); + Var* detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", idx)); + + meta->addStatement(new GenOp(" @ = max(@, @ + @);\r\n", + ma, ma, detailH, detailBlend)); + } + + meta->addStatement(new GenOp(" @ -= @;\r\n", + ma, depth)); + + meta->addStatement(new GenOp("\r\n")); + + for (S32 idx = 0; idx < detailCount; ++idx) + { + Var* detailH = (Var*)LangElement::find(String::ToString("detailH%d", idx)); + Var* detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", idx)); + Var* detailB = (Var*)LangElement::find(String::ToString("detailB%d", idx)); + if (!detailB) + { + detailB = new Var; + detailB->setType("float"); + detailB->setName(String::ToString("detailB%d", idx)); + + meta->addStatement(new GenOp(" @ = max(@ + @ - @, 0);\r\n", + new DecOp(detailB), detailH, detailBlend, ma)); + } + } + + meta->addStatement(new GenOp("\r\n")); + + // Compute albedo + meta->addStatement(new GenOp(" @.rgb = toGamma(@.rgb);\r\n", + outColor, outColor)); + + meta->addStatement(new GenOp(" @.rgb += (", + outColor)); + + for (S32 idx = 0; idx < detailCount; ++idx) + { + Var* detailColor = (Var*)LangElement::find(String::ToString("detailColor%d", idx)); + Var* detailB = (Var*)LangElement::find(String::ToString("detailB%d", idx)); + + + if (idx > 0) + { + meta->addStatement(new GenOp(" + ")); + } + + meta->addStatement(new GenOp("@.rgb * @", detailColor, detailB)); + } + + meta->addStatement(new GenOp(") / (")); + + for (S32 idx = 0; idx < detailCount; ++idx) + { + Var* detailB = (Var*)LangElement::find(String::ToString("detailB%d", idx)); + + if (idx > 0) + { + meta->addStatement(new GenOp(" + ")); + } + + meta->addStatement(new GenOp("@", detailB)); + } + + + meta->addStatement(new GenOp(");\r\n")); + + meta->addStatement(new GenOp(" @.rgb = toLinear(clamp(@.rgb, 0, 1));\r\n", + outColor, outColor)); + + meta->addStatement(new GenOp("\r\n")); + + // Compute ORM + Var* ormOutput; + OutputTarget targ = DefaultTarget; + if (fd.features[MFT_isDeferred]) + { + targ = RenderTarget2; + } + ormOutput = (Var*)LangElement::find(getOutputTargetVarName(targ)); + + meta->addStatement(new GenOp(" @.gba = (", + ormOutput)); + + for (S32 idx = 0; idx < detailCount; ++idx) + { + Var* matinfoCol = (Var*)LangElement::find(String::ToString("matinfoCol%d", idx)); + Var* detailB = (Var*)LangElement::find(String::ToString("detailB%d", idx)); + + + if (idx > 0) + { + meta->addStatement(new GenOp(" + ")); + } + if (matinfoCol) + { + meta->addStatement(new GenOp("@ * @", matinfoCol, detailB)); + } + else + { + meta->addStatement(new GenOp("vec3(1.0, 1.0, 0.0) * @", detailB)); + } + } + + meta->addStatement(new GenOp(") / (")); + + for (S32 idx = 0; idx < detailCount; ++idx) + { + Var* detailB = (Var*)LangElement::find(String::ToString("detailB%d", idx)); + + if (idx > 0) + { + meta->addStatement(new GenOp(" + ")); + } + + meta->addStatement(new GenOp("@", detailB)); + } + + + meta->addStatement(new GenOp(");\r\n")); + + + meta->addStatement(new GenOp("\r\n")); + + // Compute normal-specific blending factors + // LukasPJ: I'm not sure why this is necessary, it might not be. + Var* normalMa = (Var*)LangElement::find("normalMa"); + if (normalMa == NULL) + { + normalMa = new Var; + normalMa->setType("float"); + normalMa->setName("normalMa"); + meta->addStatement(new GenOp(" @ = 0;\r\n", + new DecOp(normalMa))); + } + + for (S32 idx = 0; idx < detailCount; ++idx) + { + Var* detCoord = (Var*)LangElement::find(String::ToString("detCoord%d", idx)); + + Var* detailH = (Var*)LangElement::find(String::ToString("detailH%d", idx)); + Var* detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", idx)); + + meta->addStatement(new GenOp(" @ = max(@, @ + min(@, @.w));\r\n", + normalMa, normalMa, detailH, detailBlend, detCoord)); + } + + meta->addStatement(new GenOp(" @ -= @;\r\n", + normalMa, depth)); + + meta->addStatement(new GenOp("\r\n")); + + for (S32 idx = 0; idx < detailCount; ++idx) + { + Var* detCoord = (Var*)LangElement::find(String::ToString("detCoord%d", idx)); + + Var* detailH = (Var*)LangElement::find(String::ToString("detailH%d", idx)); + Var* detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", idx)); + Var* normalDetailB = (Var*)LangElement::find(String::ToString("normalDetailB%d", idx)); + if (!normalDetailB) + { + normalDetailB = new Var; + normalDetailB->setType("float"); + normalDetailB->setName(String::ToString("normalDetailB%d", idx)); + + meta->addStatement(new GenOp(" @ = max(@ + min(@, @.w) - @, 0);\r\n", + new DecOp(normalDetailB), detailH, detailBlend, detCoord, normalMa)); + } + } + + // Compute normals + Var* gbNormal = (Var*)LangElement::find("gbNormal"); + if (!gbNormal) + { + gbNormal = new Var; + gbNormal->setName("gbNormal"); + gbNormal->setType("vec3"); + meta->addStatement(new GenOp(" @;\r\n", new DecOp(gbNormal))); + } + + if (gbNormal != NULL) + { + meta->addStatement(new GenOp(" @ = (", + gbNormal)); + + for (S32 idx = 0; idx < detailCount; ++idx) + { + Var* normalDetailB = (Var*)LangElement::find(String::ToString("normalDetailB%d", idx)); + Var* bumpNormal = (Var*)LangElement::find(String::ToString("bumpNormal%d", idx)); + Var* viewToTangent = getInViewToTangent(componentList); + + + if (idx > 0) + { + meta->addStatement(new GenOp(" + ")); + } + + if (bumpNormal != NULL) + { + meta->addStatement(new GenOp("tMul(@.xyz, @) * @", bumpNormal, viewToTangent, normalDetailB)); + } + else + { + meta->addStatement(new GenOp("tGetMatrix3Row(@, 2) * @", viewToTangent, normalDetailB)); + } + } + + meta->addStatement(new GenOp(") / (")); + + for (S32 idx = 0; idx < detailCount; ++idx) + { + Var* normalDetailB = (Var*)LangElement::find(String::ToString("normalDetailB%d", idx)); + + if (idx > 0) + { + meta->addStatement(new GenOp(" + ")); + } + + meta->addStatement(new GenOp("@", normalDetailB)); + } + + + meta->addStatement(new GenOp(");\r\n")); + } + output = meta; } diff --git a/Engine/source/terrain/glsl/terrFeatureGLSL.h b/Engine/source/terrain/glsl/terrFeatureGLSL.h index 830e396d7..5b48c0a51 100644 --- a/Engine/source/terrain/glsl/terrFeatureGLSL.h +++ b/Engine/source/terrain/glsl/terrFeatureGLSL.h @@ -44,9 +44,9 @@ public: Var* _getInMacroCoord(Vector &componentList ); - Var* _getNormalMapTex(); - - Var* _getORMConfigMapTex(); + Var* _getDetailMapSampler(); + Var* _getNormalMapSampler(); + Var* _getOrmMapSampler(); static Var* _getUniformVar( const char *name, const char *type, ConstantSortPosition csp ); @@ -151,17 +151,6 @@ public: virtual String getName() { return "Terrain Lightmap Texture"; } }; - -class TerrainAdditiveFeatGLSL : public TerrainFeatGLSL -{ -public: - - virtual void processPix( Vector &componentList, - const MaterialFeatureData &fd ); - - virtual String getName() { return "Terrain Additive"; } -}; - class TerrainORMMapFeatGLSL : public TerrainFeatGLSL { public: @@ -189,4 +178,17 @@ public: virtual String getName() { return "Blank Matinfo map"; } }; +class TerrainHeightMapBlendGLSL : public TerrainFeatGLSL +{ +public: + + virtual void processVert(Vector& componentList, + const MaterialFeatureData& fd); + + virtual void processPix(Vector& componentList, + const MaterialFeatureData& fd); + + virtual String getName() { return "Terrain Heightmap Blend"; } +}; + #endif // _TERRFEATUREGLSL_H_ diff --git a/Engine/source/terrain/hlsl/terrFeatureHLSL.cpp b/Engine/source/terrain/hlsl/terrFeatureHLSL.cpp index 9b8b7debb..ba381887f 100644 --- a/Engine/source/terrain/hlsl/terrFeatureHLSL.cpp +++ b/Engine/source/terrain/hlsl/terrFeatureHLSL.cpp @@ -30,6 +30,7 @@ #include "gfx/gfxDevice.h" #include "shaderGen/langElement.h" #include "shaderGen/shaderOp.h" +#include "shaderGen/featureType.h" #include "shaderGen/featureMgr.h" #include "shaderGen/shaderGen.h" #include "core/module.h" @@ -48,7 +49,7 @@ namespace FEATUREMGR->registerFeature( MFT_TerrainMacroMap, new NamedFeatureHLSL("TerrainMacroMap Deprecated")); // new TerrainMacroMapFeatHLSL); FEATUREMGR->registerFeature( MFT_TerrainLightMap, new TerrainLightMapFeatHLSL ); FEATUREMGR->registerFeature( MFT_TerrainSideProject, new NamedFeatureHLSL( "Terrain Side Projection" ) ); - FEATUREMGR->registerFeature( MFT_TerrainAdditive, new TerrainAdditiveFeatHLSL ); + FEATUREMGR->registerFeature( MFT_TerrainHeightBlend, new TerrainHeightMapBlendHLSL ); FEATUREMGR->registerFeature( MFT_TerrainORMMap, new TerrainORMMapFeatHLSL ); FEATUREMGR->registerFeature( MFT_DeferredTerrainBlankInfoMap, new TerrainBlankInfoMapFeatHLSL ); } @@ -123,56 +124,131 @@ Var* TerrainFeatHLSL::_getInMacroCoord( Vector &componentList return inDet; } -Var* TerrainFeatHLSL::_getNormalMapTex() +Var* TerrainFeatHLSL::_getDetailMapSampler() { - String name(String::ToString("normalMap%d", getProcessIndex())); - Var *normalMap = (Var*)LangElement::find(name); + String name("detailMapSampler"); + Var* detailMapSampler = (Var*)LangElement::find(name); - if (!normalMap) + if(!detailMapSampler) { - normalMap = new Var; - normalMap->setType("SamplerState"); - normalMap->setName(name); - normalMap->uniform = true; - normalMap->sampler = true; - normalMap->constNum = Var::getTexUnitNum(); + detailMapSampler = new Var; + detailMapSampler->setName(name); + detailMapSampler->setType("SamplerState"); + detailMapSampler->uniform = true; + detailMapSampler->sampler = true; + detailMapSampler->constNum = Var::getTexUnitNum(); } - return normalMap; + return detailMapSampler; } -Var* TerrainFeatHLSL::_getORMConfigMapTex() +Var* TerrainFeatHLSL::_getDetailMapArray() { - String name(String::ToString("ormConfigMap%d", getProcessIndex())); - Var *ormConfigMap = (Var*)LangElement::find(name); + String name("detailMapArray"); + Var* detailMapArray = (Var*)LangElement::find(name); - if (!ormConfigMap) + if(!detailMapArray) { - ormConfigMap = new Var; - ormConfigMap->setType("SamplerState"); - ormConfigMap->setName(name); - ormConfigMap->uniform = true; - ormConfigMap->sampler = true; - ormConfigMap->constNum = Var::getTexUnitNum(); + detailMapArray = new Var; + detailMapArray->setName(name); + detailMapArray->setType("Texture2DArray"); + detailMapArray->uniform = true; + detailMapArray->texture = true; + detailMapArray->constNum = _getDetailMapSampler()->constNum; } - return ormConfigMap; + return detailMapArray; +} + +Var* TerrainFeatHLSL::_getNormalMapSampler() +{ + String name("normalMapSampler"); + Var* normalMapSampler = (Var*)LangElement::find(name); + + if (!normalMapSampler) + { + normalMapSampler = new Var; + normalMapSampler->setName(name); + normalMapSampler->setType("SamplerState"); + normalMapSampler->uniform = true; + normalMapSampler->sampler = true; + normalMapSampler->constNum = Var::getTexUnitNum(); + } + + return normalMapSampler; +} + +Var* TerrainFeatHLSL::_getNormalMapArray() +{ + String name("normalMapArray"); + Var* normalMapArray = (Var*)LangElement::find(name); + + if (!normalMapArray) + { + normalMapArray = new Var; + normalMapArray->setName(name); + normalMapArray->setType("Texture2DArray"); + normalMapArray->uniform = true; + normalMapArray->texture = true; + normalMapArray->constNum = _getNormalMapSampler()->constNum; + } + + return normalMapArray; +} + +Var* TerrainFeatHLSL::_getOrmMapSampler() +{ + String name("ormMapSampler"); + Var* ormMapSampler = (Var*)LangElement::find(name); + + if (!ormMapSampler) + { + ormMapSampler = new Var; + ormMapSampler->setName(name); + ormMapSampler->setType("SamplerState"); + ormMapSampler->uniform = true; + ormMapSampler->sampler = true; + ormMapSampler->constNum = Var::getTexUnitNum(); + } + + return ormMapSampler; +} + +Var* TerrainFeatHLSL::_getOrmMapArray() +{ + String name("ormMapArray"); + Var* ormMapArray = (Var*)LangElement::find(name); + + if (!ormMapArray) + { + ormMapArray = new Var; + ormMapArray->setName(name); + ormMapArray->setType("Texture2DArray"); + ormMapArray->uniform = true; + ormMapArray->texture = true; + ormMapArray->constNum = _getOrmMapSampler()->constNum; + } + + return ormMapArray; } Var* TerrainFeatHLSL::_getDetailIdStrengthParallax() { - String name( String::ToString( "detailIdStrengthParallax%d", getProcessIndex() ) ); + String name( String::ToString( "detailIdStrengthParallax", getProcessIndex() ) ); Var *detailInfo = (Var*)LangElement::find( name ); if ( !detailInfo ) { detailInfo = new Var; - detailInfo->setType( "float3" ); + detailInfo->setType( "float4" ); detailInfo->setName( name ); detailInfo->uniform = true; detailInfo->constSortPos = cspPotentialPrimitive; + detailInfo->arraySize = getProcessIndex(); } + detailInfo->arraySize = mMax(detailInfo->arraySize, getProcessIndex() + 1); + return detailInfo; } @@ -297,6 +373,26 @@ void TerrainBaseMapFeatHLSL::processPix( Vector &componentLis } meta->addStatement( new GenOp( " @;\r\n", assignColor( baseColor, Material::Mul,NULL,target ) ) ); + + if (fd.features[MFT_isDeferred]) + { + // Set base ORM info + Var* ormConfig; + OutputTarget targ = RenderTarget1; + targ = RenderTarget2; + ormConfig = (Var*)LangElement::find(getOutputTargetVarName(targ)); + if (!ormConfig) + { + // create color var + ormConfig = new Var; + ormConfig->setType("fragout"); + ormConfig->setName(getOutputTargetVarName(targ)); + ormConfig->setStructName("OUT"); + } + + meta->addStatement(new GenOp(" @ = float4(0.0, 1.0, 1.0, 0.0);\r\n", ormConfig)); + } + output = meta; } @@ -311,7 +407,7 @@ ShaderFeature::Resources TerrainBaseMapFeatHLSL::getResources( const MaterialFea U32 TerrainBaseMapFeatHLSL::getOutputTargets( const MaterialFeatureData &fd ) const { - return fd.features[MFT_isDeferred] ? ShaderFeature::RenderTarget1 : ShaderFeature::DefaultTarget; + return fd.features[MFT_isDeferred] ? ShaderFeature::RenderTarget1 | ShaderFeature::RenderTarget2 : ShaderFeature::DefaultTarget; } TerrainDetailMapFeatHLSL::TerrainDetailMapFeatHLSL() @@ -383,11 +479,17 @@ void TerrainDetailMapFeatHLSL::processVert( Vector &component outTex->setType( "float4" ); // Get the detail scale and fade info. - Var *detScaleAndFade = new Var; - detScaleAndFade->setType( "float4" ); - detScaleAndFade->setName( String::ToString( "detailScaleAndFade%d", detailIndex ) ); - detScaleAndFade->uniform = true; - detScaleAndFade->constSortPos = cspPotentialPrimitive; + Var* detScaleAndFade = (Var*)LangElement::find("detailScaleAndFade"); + if (detScaleAndFade == NULL) + { + detScaleAndFade = new Var; + detScaleAndFade->setType("float4"); + detScaleAndFade->setName("detailScaleAndFade"); + detScaleAndFade->uniform = true; + detScaleAndFade->constSortPos = cspPotentialPrimitive; + } + + detScaleAndFade->arraySize = mMax(detScaleAndFade->arraySize, detailIndex + 1); // Setup the detail coord. // @@ -398,11 +500,11 @@ void TerrainDetailMapFeatHLSL::processVert( Vector &component // // See TerrainBaseMapFeatHLSL::processVert(). // - meta->addStatement( new GenOp( " @.xyz = @ * @.xyx;\r\n", outTex, inTex, detScaleAndFade ) ); + meta->addStatement( new GenOp( " @.xyz = @ * @.xyx;\r\n", outTex, inTex, new IndexOp(detScaleAndFade, detailIndex) ) ); // 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 ) ); + outTex, new IndexOp(detScaleAndFade, detailIndex), dist, new IndexOp(detScaleAndFade, detailIndex)) ); output = meta; } @@ -485,143 +587,97 @@ void TerrainDetailMapFeatHLSL::processPix( Vector &component // Calculate the blend for this detail texture. meta->addStatement( new GenOp( " @ = calcBlend( @.x, @.xy, @, @ );\r\n", - new DecOp( detailBlend ), detailInfo, inTex, layerSize, layerSample ) ); - - // Get a var and accumulate the blend amount. - Var *blendTotal = (Var*)LangElement::find( "blendTotal" ); - if ( !blendTotal ) - { - blendTotal = new Var; - blendTotal->setName( "blendTotal" ); - blendTotal->setType( "float" ); - meta->addStatement( new GenOp( " @ = 0;\r\n", new DecOp( blendTotal ) ) ); - } - - // Add to the blend total. - - meta->addStatement(new GenOp(" @ = max( @, @ );\r\n", blendTotal, blendTotal, detailBlend)); + new DecOp( detailBlend ), new IndexOp(detailInfo, detailIndex), inTex, layerSize, layerSample ) ); // If we had a parallax feature... then factor in the parallax // amount so that it fades out with the layer blending. if (fd.features.hasFeature(MFT_TerrainParallaxMap, detailIndex)) { - // Get the rest of our inputs. - Var *normalMap = _getNormalMapTex(); - - String name(String::ToString("normalMapTex%d", getProcessIndex())); - Var *normalMapTex = (Var*)LangElement::find(name); - - if (!normalMapTex) - { - normalMapTex = new Var; - normalMapTex->setName(String::ToString("normalMapTex%d", getProcessIndex())); - normalMapTex->setType("Texture2D"); - normalMapTex->uniform = true; - normalMapTex->texture = true; - normalMapTex->constNum = normalMap->constNum; - } + Var* normalMapArray = _getNormalMapArray(); + Var* normalMapSampler = _getNormalMapSampler(); // Call the library function to do the rest. if (fd.features.hasFeature(MFT_IsBC3nm, detailIndex)) { - meta->addStatement(new GenOp(" @.xy += parallaxOffsetDxtnm( @, @, @.xy, @, @.z * @ );\r\n", - inDet, normalMapTex, normalMap, inDet, negViewTS, detailInfo, detailBlend)); + meta->addStatement(new GenOp(" @.xy += parallaxOffsetDxtnmTexArray( @, @, float3(@.xy, @.x), @, @.z * @ );\r\n", + inDet, normalMapArray, normalMapSampler, inDet, new IndexOp(detailInfo, detailIndex), negViewTS, new IndexOp(detailInfo, detailIndex), detailBlend)); } else { - meta->addStatement(new GenOp(" @.xy += parallaxOffset( @, @, @.xy, @, @.z * @ );\r\n", - inDet, normalMapTex, normalMap, inDet, negViewTS, detailInfo, detailBlend)); + meta->addStatement(new GenOp(" @.xy += parallaxOffsetTexArray( @, @, float3(@.xy, @.x), @, @.z * @ );\r\n", + inDet, normalMapArray, normalMapSampler, inDet, new IndexOp(detailInfo, detailIndex), negViewTS, new IndexOp(detailInfo, detailIndex), detailBlend)); } } - - // Check to see if we have a gbuffer normal. - Var *gbNormal = (Var*)LangElement::find( "gbNormal" ); - // If we have a gbuffer normal and we don't have a - // normal map feature then we need to lerp in a - // default normal else the normals below this layer - // will show thru. - if (gbNormal && - !fd.features.hasFeature(MFT_TerrainNormalMap, detailIndex)) - { - Var *viewToTangent = getInViewToTangent(componentList); - meta->addStatement(new GenOp(" @ = lerp( @, @[2], min( @, @.w ) );\r\n", - gbNormal, gbNormal, viewToTangent, detailBlend, inDet)); - } - - Var *detailColor = (Var*)LangElement::find( "detailColor" ); + Var *detailColor = (Var*)LangElement::find(String::ToString("detailColor%d", detailIndex)); if ( !detailColor ) { detailColor = new Var; detailColor->setType( "float4" ); - detailColor->setName( "detailColor" ); + detailColor->setName( String::ToString("detailColor%d", detailIndex) ); meta->addStatement( new GenOp( " @;\r\n", new DecOp( detailColor ) ) ); } - // Get the detail texture. - Var *detailMap = new Var; - detailMap->setType( "SamplerState" ); - detailMap->setName( String::ToString( "detailMap%d", detailIndex ) ); - detailMap->uniform = true; - detailMap->sampler = true; - detailMap->constNum = Var::getTexUnitNum(); // used as texture unit num here - - // If we're using SM 3.0 then take advantage of - // dynamic branching to skip layers per-pixel. - - - if ( GFX->getPixelShaderVersion() >= 3.0f ) - meta->addStatement( new GenOp( " if ( @ > 0.0f )\r\n", detailBlend ) ); - - meta->addStatement( new GenOp( " {\r\n" ) ); - - // Note that we're doing the standard greyscale detail - // map technique here which can darken and lighten the - // diffuse texture. - // - // We take two color samples and lerp between them for - // side projection layers... else a single sample. - // - //Sampled detail texture that is not expanded - Var* detailTex = new Var; - detailTex->setName(String::ToString("detailTex%d", detailIndex)); - detailTex->setType("Texture2D"); - detailTex->uniform = true; - detailTex->texture = true; - detailTex->constNum = detailMap->constNum; + Var* detailMapArray = _getDetailMapArray(); + Var* detailMapSampler = _getDetailMapSampler(); if (fd.features.hasFeature(MFT_TerrainSideProject, detailIndex)) { - - meta->addStatement(new GenOp(" @ = ( lerp( @.Sample( @, @.yz ), @.Sample( @, @.xz ), @.z ) * 2.0 ) - 1.0;\r\n", - detailColor, detailTex, detailMap, inDet, detailTex, detailMap, inDet, inTex)); + meta->addStatement(new GenOp(" @ = ( lerp( @.Sample( @, float3(@.yz, @.x) ), @.Sample( @, float3(@.xz, @.x) ), @.z ) * 2.0 ) - 1.0;\r\n", + detailColor, detailMapArray, detailMapSampler, inDet, new IndexOp(detailInfo, detailIndex), detailMapArray, detailMapSampler, inDet, new IndexOp(detailInfo, detailIndex), inTex)); } else { - meta->addStatement(new GenOp(" @ = ( @.Sample( @, @.xy ) * 2.0 ) - 1.0;\r\n", - detailColor, detailTex, detailMap, inDet)); + meta->addStatement(new GenOp(" @ = ( @.Sample( @, float3(@.xy, @.x) ) * 2.0 ) - 1.0;\r\n", + detailColor, detailMapArray, detailMapSampler, inDet, new IndexOp(detailInfo, detailIndex))); } - meta->addStatement( new GenOp( " @ *= @.y * @.w;\r\n", - detailColor, detailInfo, inDet ) ); + meta->addStatement(new GenOp(" @ *= @.y * @.w;\r\n", + detailColor, new IndexOp(detailInfo, detailIndex), inDet)); - ShaderFeature::OutputTarget target = ShaderFeature::DefaultTarget; + if (!fd.features.hasFeature(MFT_TerrainHeightBlend)) + { + // Check to see if we have a gbuffer normal. + Var* gbNormal = (Var*)LangElement::find("gbNormal"); - if (fd.features.hasFeature(MFT_isDeferred)) - target= ShaderFeature::RenderTarget1; + // If we have a gbuffer normal and we don't have a + // normal map feature then we need to lerp in a + // default normal else the normals below this layer + // will show thru. + if (gbNormal && + !fd.features.hasFeature(MFT_TerrainNormalMap, detailIndex)) + { + Var* viewToTangent = getInViewToTangent(componentList); - Var *outColor = (Var*)LangElement::find( getOutputTargetVarName(target) ); + meta->addStatement(new GenOp(" @ = lerp( @, @[2], min( @, @.w ) );\r\n", + gbNormal, gbNormal, viewToTangent, detailBlend, inDet)); + } - meta->addStatement(new GenOp(" @.rgb = toGamma(@.rgb);\r\n", outColor, outColor)); + // If we're using SM 3.0 then take advantage of + // dynamic branching to skip layers per-pixel. + if (GFX->getPixelShaderVersion() >= 3.0f) + meta->addStatement(new GenOp(" if ( @ > 0.0f )\r\n", detailBlend)); - meta->addStatement( new GenOp( " @ += @ * @;\r\n", - outColor, detailColor, detailBlend)); + meta->addStatement(new GenOp(" {\r\n")); - meta->addStatement(new GenOp(" @.rgb = toLinear(clamp(@.rgb, 0, 1));\r\n", outColor, outColor)); + ShaderFeature::OutputTarget target = ShaderFeature::DefaultTarget; - meta->addStatement( new GenOp( " }\r\n" ) ); + if (fd.features.hasFeature(MFT_isDeferred)) + target = ShaderFeature::RenderTarget1; + + Var* outColor = (Var*)LangElement::find(getOutputTargetVarName(target)); + + meta->addStatement(new GenOp(" @.rgb = toGamma(@.rgb);\r\n", outColor, outColor)); + + meta->addStatement(new GenOp(" @ += @ * @;\r\n", + outColor, detailColor, detailBlend)); + + meta->addStatement(new GenOp(" @.rgb = toLinear(clamp(@.rgb, 0, 1));\r\n", outColor, outColor)); + + meta->addStatement(new GenOp(" }\r\n")); + } output = meta; } @@ -635,26 +691,13 @@ ShaderFeature::Resources TerrainDetailMapFeatHLSL::getResources( const MaterialF // If this is the first detail pass then we // samples from the layer tex. res.numTex += 1; + res.numTexReg += 1; - // If this material also does parallax then it - // will generate the negative view vector and the - // worldToTanget transform. - if ( fd.features.hasFeature( MFT_TerrainParallaxMap ) ) - res.numTexReg += 4; + // Add Detail TextureArray + res.numTex += 1; + res.numTexReg += 1; } - // sample from the detail texture for diffuse coloring. - res.numTex += 1; - - // If we have parallax for this layer then we'll also - // be sampling the normal map for the parallax heightmap. - if ( fd.features.hasFeature( MFT_TerrainParallaxMap, getProcessIndex() ) ) - res.numTex += 1; - - // Finally we always send the detail texture - // coord to the pixel shader. - res.numTexReg += 1; - return res; } @@ -714,18 +757,24 @@ void TerrainMacroMapFeatHLSL::processVert( Vector &componentL outTex->setType( "float4" ); // Get the detail scale and fade info. - Var *detScaleAndFade = new Var; - detScaleAndFade->setType( "float4" ); - detScaleAndFade->setName( String::ToString( "macroScaleAndFade%d", detailIndex ) ); - detScaleAndFade->uniform = true; - detScaleAndFade->constSortPos = cspPotentialPrimitive; + Var* macroScaleAndFade = (Var*)LangElement::find("macroScaleAndFade"); + if (macroScaleAndFade == NULL) + { + macroScaleAndFade = new Var; + macroScaleAndFade->setType("float4"); + macroScaleAndFade->setName("macroScaleAndFade"); + macroScaleAndFade->uniform = true; + macroScaleAndFade->constSortPos = cspPotentialPrimitive; + } + + macroScaleAndFade->arraySize = mMax(macroScaleAndFade->arraySize, detailIndex + 1); // Setup the detail coord. - meta->addStatement( new GenOp( " @.xyz = @ * @.xyx;\r\n", outTex, inTex, detScaleAndFade ) ); + meta->addStatement( new GenOp( " @.xyz = @ * @.xyx;\r\n", outTex, inTex, new IndexOp(macroScaleAndFade, detailIndex)) ); // 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 ) ); + outTex, new IndexOp(macroScaleAndFade, detailIndex), dist, new IndexOp(macroScaleAndFade, detailIndex)) ); output = meta; } @@ -809,20 +858,7 @@ void TerrainMacroMapFeatHLSL::processPix( Vector &componentL // Calculate the blend for this detail texture. meta->addStatement( new GenOp( " @ = calcBlend( @.x, @.xy, @, @ );\r\n", - new DecOp( detailBlend ), detailInfo, inTex, layerSize, layerSample ) ); - - // Get a var and accumulate the blend amount. - Var *blendTotal = (Var*)LangElement::find( "blendTotal" ); - if ( !blendTotal ) - { - blendTotal = new Var; - blendTotal->setName( "blendTotal" ); - blendTotal->setType( "float" ); - meta->addStatement( new GenOp( " @ = 0;\r\n", new DecOp( blendTotal ) ) ); - } - - // Add to the blend total. - meta->addStatement(new GenOp(" @ = max( @, @ );\r\n", blendTotal, blendTotal, detailBlend)); + new DecOp( detailBlend ), new IndexOp(detailInfo, detailIndex), inTex, layerSize, layerSample ) ); // Check to see if we have a gbuffer normal. Var *gbNormal = (Var*)LangElement::find( "gbNormal" ); @@ -849,22 +885,6 @@ void TerrainMacroMapFeatHLSL::processPix( Vector &componentL meta->addStatement( new GenOp( " @;\r\n", new DecOp( detailColor ) ) ); } - // Get the detail texture. - Var *detailMap = new Var; - detailMap->setType( "SamplerState" ); - detailMap->setName( String::ToString( "macroMap%d", detailIndex ) ); - detailMap->uniform = true; - detailMap->sampler = true; - detailMap->constNum = Var::getTexUnitNum(); // used as texture unit num here - - //Create texture object for directx 11 - Var *detailTex = new Var; - detailTex->setName(String::ToString("macroMapTex%d", detailIndex)); - detailTex->setType("Texture2D"); - detailTex->uniform = true; - detailTex->texture = true; - detailTex->constNum = detailMap->constNum; - // If we're using SM 3.0 then take advantage of // dynamic branching to skip layers per-pixel. if ( GFX->getPixelShaderVersion() >= 3.0f ) @@ -872,6 +892,9 @@ void TerrainMacroMapFeatHLSL::processPix( Vector &componentL meta->addStatement( new GenOp( " {\r\n" ) ); + Var* detailMapArray = _getDetailMapArray(); + Var* detailMapSampler = _getDetailMapSampler(); + // Note that we're doing the standard greyscale detail // map technique here which can darken and lighten the // diffuse texture. @@ -881,17 +904,17 @@ void TerrainMacroMapFeatHLSL::processPix( Vector &componentL // if (fd.features.hasFeature(MFT_TerrainSideProject, detailIndex)) { - meta->addStatement(new GenOp(" @ = ( lerp( @.Sample( @, @.yz ), @.Sample( @, @.xz ), @.z ) * 2.0 ) - 1.0;\r\n", - detailColor, detailTex, detailMap, inDet, detailTex, detailMap, inDet, inTex)); + meta->addStatement(new GenOp(" @ = ( lerp( @.Sample( @, float3(@.yz, @.x) ), @.Sample( @, float3(@.xz, @.x) ), @.z ) * 2.0 ) - 1.0;\r\n", + detailColor, detailMapArray, detailMapSampler, inDet, new IndexOp(detailInfo, detailIndex), detailMapArray, detailMapSampler, inDet, new IndexOp(detailInfo, detailIndex), inTex)); } else { - meta->addStatement(new GenOp(" @ = ( @.Sample( @, @.xy ) * 2.0 ) - 1.0;\r\n", - detailColor, detailTex, detailMap, inDet)); + meta->addStatement(new GenOp(" @ = ( @.Sample( @, float3(@.xy, @.x) ) * 2.0 ) - 1.0;\r\n", + detailColor, detailMapArray, detailMapSampler, inDet, new IndexOp(detailInfo, detailIndex))); } - meta->addStatement( new GenOp( " @ *= @.y * @.w;\r\n", - detailColor, detailInfo, inDet ) ); + meta->addStatement( new GenOp( " @ *= @.y * @.w;\r\n", + detailColor, new IndexOp(detailInfo, detailIndex), inDet ) ); ShaderFeature::OutputTarget target = ShaderFeature::DefaultTarget; @@ -946,9 +969,12 @@ void TerrainNormalMapFeatHLSL::processVert( Vector &component MultiLine *meta = new MultiLine; - // Make sure the world to tangent transform - // is created and available for the pixel shader. - getOutViewToTangent( componentList, meta, fd ); + if ( !fd.features.hasFeature(MFT_TerrainHeightBlend) ) + { + // Make sure the world to tangent transform + // is created and available for the pixel shader. + getOutViewToTangent(componentList, meta, fd); + } output = meta; } @@ -962,93 +988,71 @@ void TerrainNormalMapFeatHLSL::processPix( Vector &component MultiLine *meta = new MultiLine; - Var *viewToTangent = getInViewToTangent( componentList ); - - // This var is read from GBufferConditionerHLSL and - // used in the deferred output. - Var *gbNormal = (Var*)LangElement::find( "gbNormal" ); - if ( !gbNormal ) - { - gbNormal = new Var; - gbNormal->setName( "gbNormal" ); - gbNormal->setType( "float3" ); - meta->addStatement( new GenOp( " @ = @[2];\r\n", new DecOp( gbNormal ), viewToTangent ) ); - } - const S32 normalIndex = getProcessIndex(); Var *detailBlend = (Var*)LangElement::find( String::ToString( "detailBlend%d", normalIndex ) ); AssertFatal( detailBlend, "The detail blend is missing!" ); - // If we're using SM 3.0 then take advantage of - // dynamic branching to skip layers per-pixel. - if ( GFX->getPixelShaderVersion() >= 3.0f ) - meta->addStatement( new GenOp( " if ( @ > 0.0f )\r\n", detailBlend ) ); - - meta->addStatement( new GenOp( " {\r\n" ) ); - - // Get the normal map texture. - Var *normalMap = _getNormalMapTex(); - /// Get the texture coord. - Var *inDet = _getInDetailCoord( componentList ); - Var *inTex = getVertTexCoord( "texCoord" ); + Var* inDet = _getInDetailCoord(componentList); + Var* inTex = getVertTexCoord("texCoord"); + Var* detailInfo = _getDetailIdStrengthParallax(); // Sample the normal map. // // We take two normal samples and lerp between them for // side projection layers... else a single sample. - LangElement *texOp; - - String name(String::ToString("normalMapTex%d", getProcessIndex())); - Var *normalMapTex = (Var*)LangElement::find(name); - if (!normalMapTex) - { - normalMapTex = new Var; - normalMapTex->setName(String::ToString("normalMapTex%d", getProcessIndex())); - normalMapTex->setType("Texture2D"); - normalMapTex->uniform = true; - normalMapTex->texture = true; - normalMapTex->constNum = normalMap->constNum; - } + LangElement* texOp; + + Var* normalMapSampler = _getNormalMapSampler(); + Var* normalMapArray = _getNormalMapArray(); if (fd.features.hasFeature(MFT_TerrainSideProject, normalIndex)) { - texOp = new GenOp("lerp( @.Sample( @, @.yz ), @.Sample( @, @.xz ), @.z )", - normalMapTex, normalMap, inDet, normalMapTex, normalMap, inDet, inTex); + texOp = new GenOp("lerp( @.Sample( @, float3(@.yz, @.x) ), @.Sample( @, float3(@.xz, @.x) ), @.z )", + normalMapArray, normalMapSampler, inDet, new IndexOp(detailInfo, normalIndex), normalMapArray, normalMapSampler, inDet, new IndexOp(detailInfo, normalIndex), inTex); } else - texOp = new GenOp("@.Sample(@, @.xy)", normalMapTex, normalMap, inDet); + texOp = new GenOp("@.Sample(@, float3(@.xy, @.x))", normalMapArray, normalMapSampler, inDet, new IndexOp(detailInfo, normalIndex)); // create bump normal - Var *bumpNorm = new Var; - bumpNorm->setName( "bumpNormal" ); - bumpNorm->setType( "float4" ); + Var* bumpNorm = new Var; + bumpNorm->setName(String::ToString("bumpNormal%d", normalIndex)); + bumpNorm->setType("float4"); - LangElement *bumpNormDecl = new DecOp( bumpNorm ); - meta->addStatement( expandNormalMap( texOp, bumpNormDecl, bumpNorm, fd ) ); + LangElement* bumpNormDecl = new DecOp(bumpNorm); + meta->addStatement(expandNormalMap(texOp, bumpNormDecl, bumpNorm, fd)); - // If this is the last normal map then we - // can test to see the total blend value - // to see if we should clip the result. - Var* blendTotal = (Var*)LangElement::find("blendTotal"); - if (blendTotal) + if (!fd.features.hasFeature(MFT_TerrainHeightBlend)) { - if (fd.features.getNextFeatureIndex(MFT_TerrainNormalMap, normalIndex) == -1) - meta->addStatement(new GenOp(" if ( @ > 0.0001f ){\r\n\r\n", blendTotal)); - } + Var* viewToTangent = getInViewToTangent(componentList); + + // This var is read from GBufferConditionerHLSL and + // used in the deferred output. + Var* gbNormal = (Var*)LangElement::find("gbNormal"); + if (!gbNormal) + { + gbNormal = new Var; + gbNormal->setName("gbNormal"); + gbNormal->setType("float3"); + meta->addStatement(new GenOp(" @ = @[2];\r\n", new DecOp(gbNormal), viewToTangent)); + } + + // If we're using SM 3.0 then take advantage of + // dynamic branching to skip layers per-pixel. + if (GFX->getPixelShaderVersion() >= 3.0f) + meta->addStatement(new GenOp(" if ( @ > 0.0f )\r\n", detailBlend)); + + meta->addStatement(new GenOp(" {\r\n")); + // Normalize is done later... // Note: The reverse mul order is intentional. Affine matrix. - meta->addStatement( new GenOp( " @ = lerp( @, mul( @.xyz, @ ), min( @, @.w ) );\r\n", - gbNormal, gbNormal, bumpNorm, viewToTangent, detailBlend, inDet ) ); + meta->addStatement(new GenOp(" @ = lerp( @, mul( @.xyz, @ ), min( @, @.w ) );\r\n", + gbNormal, gbNormal, bumpNorm, viewToTangent, detailBlend, inDet)); - if (blendTotal) - { - if (fd.features.getNextFeatureIndex(MFT_TerrainNormalMap, normalIndex) == -1) - meta->addStatement(new GenOp(" }\r\n")); + // End the conditional block. + meta->addStatement(new GenOp(" }\r\n")); } - // End the conditional block. - meta->addStatement( new GenOp( " }\r\n" ) ); output = meta; } @@ -1056,14 +1060,27 @@ void TerrainNormalMapFeatHLSL::processPix( Vector &component ShaderFeature::Resources TerrainNormalMapFeatHLSL::getResources( const MaterialFeatureData &fd ) { Resources res; - - // If this is the first normal map and there - // are no parallax features then we will - // generate the worldToTanget transform. - if ( !fd.features.hasFeature( MFT_TerrainParallaxMap ) && - ( getProcessIndex() == 0 || !fd.features.hasFeature( MFT_TerrainNormalMap, getProcessIndex() - 1 ) ) ) - res.numTexReg = 3; - res.numTex = 1; + + if (!fd.features.hasFeature(MFT_DeferredConditioner)) + { + return res; + } + + S32 featureIndex = 0, firstNormalMapIndex = 0; + for (int idx = 0; idx < fd.features.getCount(); ++idx) { + const FeatureType &type = fd.features.getAt(idx, &featureIndex); + if (type == MFT_TerrainNormalMap) { + firstNormalMapIndex = getMin(firstNormalMapIndex, featureIndex); + } + } + + // We only need to process normals during the deferred. + if (getProcessIndex() == firstNormalMapIndex) + { + res.numTexReg += 1; + res.numTex += 1; + } + return res; } @@ -1116,35 +1133,6 @@ ShaderFeature::Resources TerrainLightMapFeatHLSL::getResources( const MaterialFe return res; } -void TerrainAdditiveFeatHLSL::processPix( Vector &componentList, - const MaterialFeatureData &fd ) -{ - Var *color = NULL; - Var* norm = NULL; - if (fd.features[MFT_isDeferred]) - { - color = (Var*) LangElement::find( getOutputTargetVarName(ShaderFeature::RenderTarget1) ); - norm = (Var*) LangElement::find( getOutputTargetVarName(ShaderFeature::DefaultTarget) ); - } - else - color = (Var*) LangElement::find( getOutputTargetVarName(ShaderFeature::DefaultTarget) ); - - Var *blendTotal = (Var*)LangElement::find( "blendTotal" ); - if ( !color || !blendTotal ) - return; - - MultiLine *meta = new MultiLine; - - meta->addStatement( new GenOp( " clip( @ - 0.0001 );\r\n", blendTotal ) ); - meta->addStatement( new GenOp( " @.a = @;\r\n", color, blendTotal ) ); - if (fd.features[MFT_isDeferred]) - { - meta->addStatement(new GenOp(" @.a = @;\r\n", norm, blendTotal)); - } - - output = meta; -} - //standard matInfo map contains data of the form .r = bitflags, .g = (will contain AO), //.b = specular strength, a= spec power. @@ -1211,15 +1199,18 @@ void TerrainORMMapFeatHLSL::processVert(Vector &componentList, outTex->setType("float4"); } // Get the detail scale and fade info. - Var *detScaleAndFade = (Var*)LangElement::find(String::ToString("detailScaleAndFade%d", detailIndex)); + Var *detScaleAndFade = (Var*)LangElement::find("detailScaleAndFade"); if (detScaleAndFade == NULL) { + detScaleAndFade = new Var; detScaleAndFade->setType("float4"); - detScaleAndFade->setName(String::ToString("detailScaleAndFade%d", detailIndex)); + detScaleAndFade->setName("detailScaleAndFade"); detScaleAndFade->uniform = true; detScaleAndFade->constSortPos = cspPotentialPrimitive; } + detScaleAndFade->arraySize = mMax(detScaleAndFade->arraySize, detailIndex + 1); + // Setup the detail coord. // // NOTE: You see here we scale the texture coord by 'xyx' @@ -1229,11 +1220,11 @@ void TerrainORMMapFeatHLSL::processVert(Vector &componentList, // // See TerrainBaseMapFeatHLSL::processVert(). // - meta->addStatement(new GenOp(" @.xyz = @ * @.xyx;\r\n", outTex, inTex, detScaleAndFade)); + meta->addStatement(new GenOp(" @.xyz = @ * @.xyx;\r\n", outTex, inTex, new IndexOp(detScaleAndFade, detailIndex))); // 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)); + outTex, new IndexOp(detScaleAndFade, detailIndex), dist, new IndexOp(detScaleAndFade, detailIndex))); output = meta; } @@ -1249,36 +1240,28 @@ void TerrainORMMapFeatHLSL::processPix(Vector &componentList, /// Get the texture coord. Var *inDet = _getInDetailCoord(componentList); Var *inTex = getVertTexCoord("texCoord"); + Var* detailInfo = _getDetailIdStrengthParallax(); const S32 compositeIndex = getProcessIndex(); - Var *ormConfigMap = _getORMConfigMapTex(); // Sample the normal map. // // We take two normal samples and lerp between them for // side projection layers... else a single sample. LangElement *texOp; - String name(String::ToString("ormConfigMapTex%d", getProcessIndex())); - Var *ormConfigMapTex = (Var*)LangElement::find(name); - if (!ormConfigMapTex) - { - ormConfigMapTex = new Var; - ormConfigMapTex->setName(String::ToString("ormConfigMapTex%d", getProcessIndex())); - ormConfigMapTex->setType("Texture2D"); - ormConfigMapTex->uniform = true; - ormConfigMapTex->texture = true; - ormConfigMapTex->constNum = ormConfigMap->constNum; - } + + Var* ormMapArray = _getOrmMapArray(); + Var* ormMapSampler = _getOrmMapSampler(); if (fd.features.hasFeature(MFT_TerrainSideProject, compositeIndex)) { - texOp = new GenOp("lerp( @.Sample( @, @.yz ), @.Sample( @, @.xz ), @.z )", - ormConfigMapTex, ormConfigMap, inDet, ormConfigMapTex, ormConfigMap, inDet, inTex); + texOp = new GenOp("lerp( @.Sample( @, float3(@.yz, @.x) ), @.Sample( @, float3(@.xz, @.x) ), @.z )", + ormMapArray, ormMapSampler, inDet, new IndexOp(detailInfo, compositeIndex), ormMapArray, ormMapSampler, inDet, new IndexOp(detailInfo, compositeIndex), inTex); } else - texOp = new GenOp("@.Sample(@, @.xy)", ormConfigMapTex, ormConfigMap, inDet); + texOp = new GenOp("@.Sample(@, float3(@.xy, @.x))", ormMapArray, ormMapSampler, inDet, new IndexOp(detailInfo, compositeIndex)); // search for material var Var * ormConfig; - OutputTarget targ = DefaultTarget; + OutputTarget targ = RenderTarget1; if (fd.features[MFT_isDeferred]) { targ = RenderTarget2; @@ -1300,22 +1283,24 @@ void TerrainORMMapFeatHLSL::processPix(Vector &componentList, String matinfoName(String::ToString("matinfoCol%d", compositeIndex)); Var *matinfoCol = new Var(matinfoName, "float3"); - - Var *priorComp = (Var*)LangElement::find(String::ToString("matinfoCol%d", compositeIndex - 1)); - if (priorComp) + + if (compositeIndex == 0) { - meta->addStatement(new GenOp(" @ = @.rgb*@;\r\n", new DecOp(matinfoCol), texOp, detailBlend)); - meta->addStatement(new GenOp(" @.gba += @;\r\n", ormConfig, matinfoCol)); - } - else - { - meta->addStatement(new GenOp(" @ = lerp(float3(1.0,1.0,0.0),@.rgb,@);\r\n", new DecOp(matinfoCol), texOp, detailBlend)); - meta->addStatement(new GenOp(" @ = float4(0.0,@);\r\n", ormConfig, matinfoCol)); + meta->addStatement(new GenOp(" @ = float4(0.0, 0.0, 0.0, 0.0);\r\n", ormConfig)); } - if (fd.features[MFT_InvertRoughness]) + meta->addStatement(new GenOp(" @ = @.rgb;\r\n", new DecOp(matinfoCol), texOp)); + + if (fd.features.hasFeature(MFT_InvertRoughness, compositeIndex)) { - meta->addStatement(new GenOp(" @.b = 1.0-@.b;\r\n", ormConfig, ormConfig)); + meta->addStatement(new GenOp(" @.b = 1.0 - @.b;\r\n", matinfoCol, matinfoCol)); + } + + meta->addStatement(new GenOp(" @ = lerp(float3(1.0, 1.0, 0.0), @, @.y * @.w);\r\n", matinfoCol, matinfoCol, new IndexOp(detailInfo, compositeIndex), inDet)); + + if (!fd.features.hasFeature(MFT_TerrainHeightBlend)) + { + meta->addStatement(new GenOp(" @.gba += @ * @;\r\n", ormConfig, matinfoCol, detailBlend)); } output = meta; @@ -1331,20 +1316,22 @@ ShaderFeature::Resources TerrainORMMapFeatHLSL::getResources(const MaterialFeatu // reminder, the matinfo buffer is flags, smooth, ao, metal U32 TerrainBlankInfoMapFeatHLSL::getOutputTargets(const MaterialFeatureData &fd) const { - return fd.features[MFT_isDeferred] ? ShaderFeature::RenderTarget2 : ShaderFeature::DefaultTarget; + return fd.features[MFT_isDeferred] ? ShaderFeature::RenderTarget2 : ShaderFeature::RenderTarget1; } void TerrainBlankInfoMapFeatHLSL::processPix(Vector &componentList, const MaterialFeatureData &fd) { + S32 compositeIndex = getProcessIndex(); + // search for material var Var *material; - OutputTarget targ = DefaultTarget; + OutputTarget ormConfig = RenderTarget1; if (fd.features[MFT_isDeferred]) { - targ = RenderTarget2; + ormConfig = RenderTarget2; } - material = (Var*)LangElement::find(getOutputTargetVarName(targ)); + material = (Var*)LangElement::find(getOutputTargetVarName(ormConfig)); MultiLine * meta = new MultiLine; if (!material) @@ -1352,11 +1339,380 @@ void TerrainBlankInfoMapFeatHLSL::processPix(Vector &component // create color var material = new Var; material->setType("fragout"); - material->setName(getOutputTargetVarName(targ)); + material->setName(getOutputTargetVarName(ormConfig)); material->setStructName("OUT"); } - meta->addStatement(new GenOp(" @ = float4(0.0,1.0,1.0,0.0);\r\n", material)); + if (compositeIndex == 0) + { + meta->addStatement(new GenOp(" @ = float4(0.0, 0.0, 0.0, 0.0);\r\n", material)); + } + + Var* detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", compositeIndex)); + AssertFatal(detailBlend, "The detail blend is missing!"); + + String matinfoName(String::ToString("matinfoCol%d", compositeIndex)); + + if (!fd.features.hasFeature(MFT_TerrainHeightBlend)) + { + meta->addStatement(new GenOp(" @.gba += float3(@, @, 0.0);\r\n", material, detailBlend, detailBlend)); + } + + output = meta; +} + +void TerrainHeightMapBlendHLSL::processVert(Vector& componentList, + const MaterialFeatureData& fd) +{ + // We only need to process normals during the deferred. + if (!fd.features.hasFeature(MFT_DeferredConditioner)) + return; + + MultiLine* meta = new MultiLine; + + // Make sure the world to tangent transform + // is created and available for the pixel shader. + getOutViewToTangent(componentList, meta, fd); + + output = meta; +} + +void TerrainHeightMapBlendHLSL::processPix(Vector& componentList, + const MaterialFeatureData& fd) +{ + + ShaderFeature::OutputTarget target = ShaderFeature::DefaultTarget; + + if (fd.features.hasFeature(MFT_isDeferred)) + target = ShaderFeature::RenderTarget1; + + Var* outColor = (Var*)LangElement::find(getOutputTargetVarName(target)); + + if (!outColor) + return; + + MultiLine* meta = new MultiLine; + + // Count number of detail layers + int detailCount = 0; + while (true) + { + if(LangElement::find(String::ToString("detailBlend%d", detailCount)) == NULL) + { + break; + } + + ++detailCount; + } + + if ( detailCount == 0 ) + { + return; + } + + // Compute blend factors + for (S32 idx = 0; idx < detailCount; ++idx) + { + Var* detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", idx)); + Var* bumpNormal = (Var*)LangElement::find(String::ToString("bumpNormal%d", idx)); + Var* blendDepth = (Var*)LangElement::find(String::ToString("blendDepth%d", idx)); + if (!blendDepth) + { + blendDepth = new Var; + blendDepth->setType("float"); + blendDepth->setName(String::ToString("blendDepth%d", idx)); + blendDepth->uniform = true; + blendDepth->constSortPos = cspPrimitive; + } + + Var* blendContrast = (Var*)LangElement::find(String::ToString("blendContrast%d", idx)); + if (!blendContrast) + { + blendContrast = new Var; + blendContrast->setType("float"); + blendContrast->setName(String::ToString("blendContrast%d", idx)); + blendContrast->uniform = true; + blendContrast->constSortPos = cspPrimitive; + } + + Var* detailH = (Var*)LangElement::find(String::ToString("detailH%d", idx)); + if (!detailH) + { + detailH = new Var; + detailH->setType("float"); + detailH->setName(String::ToString("detailH%d", idx)); + + meta->addStatement(new GenOp(" @ = 0;\r\n", + new DecOp(detailH))); + meta->addStatement(new GenOp(" if (@ > 0.0f) {\r\n", detailBlend)); + if (bumpNormal != NULL) + { + meta->addStatement(new GenOp(" @ = clamp(@.a + @, 0.0, 1.0);\r\n", + detailH, bumpNormal, blendDepth)); + } + else + { + meta->addStatement(new GenOp(" @ = clamp(0.5 + @, 0.0, 1.0);\r\n", + detailH, blendDepth)); + } + + meta->addStatement(new GenOp(" @ = max((@ * 2.0f - 1.0f) * @ + 0.5f, 0.0f);\r\n", + detailH, detailH, blendContrast)); + + meta->addStatement(new GenOp(" }\r\n")); + } + } + + meta->addStatement(new GenOp("\r\n")); + + Var* depth = (Var*)LangElement::find("baseBlendDepth"); + if (depth == NULL) + { + depth = new Var; + depth->setType("float"); + depth->setName("baseBlendDepth"); + depth->uniform = true; + depth->constSortPos = cspPrimitive; + } + + Var* ma = (Var*)LangElement::find("ma"); + if (ma == NULL) + { + ma = new Var; + ma->setType("float"); + ma->setName("ma"); + meta->addStatement(new GenOp(" @ = 0;\r\n", + new DecOp(ma))); + } + + for (S32 idx = 0; idx < detailCount; ++idx) + { + Var* detailH = (Var*)LangElement::find(String::ToString("detailH%d", idx)); + Var* detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", idx)); + + meta->addStatement(new GenOp(" @ = max(@, @ + @);\r\n", + ma, ma, detailH, detailBlend)); + } + + meta->addStatement(new GenOp(" @ -= @;\r\n", + ma, depth)); + + meta->addStatement(new GenOp("\r\n")); + + for (S32 idx = 0; idx < detailCount; ++idx) + { + Var* detailH = (Var*)LangElement::find(String::ToString("detailH%d", idx)); + Var* detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", idx)); + Var* detailB = (Var*)LangElement::find(String::ToString("detailB%d", idx)); + if (!detailB) + { + detailB = new Var; + detailB->setType("float"); + detailB->setName(String::ToString("detailB%d", idx)); + + meta->addStatement(new GenOp(" @ = max(@ + @ - @, 0);\r\n", + new DecOp(detailB), detailH, detailBlend, ma)); + } + } + + meta->addStatement(new GenOp("\r\n")); + + // Compute albedo + meta->addStatement(new GenOp(" @.rgb = toGamma(@.rgb);\r\n", + outColor, outColor)); + + meta->addStatement(new GenOp(" @.rgb += (", + outColor)); + + for (S32 idx = 0; idx < detailCount; ++idx) + { + Var* detailColor = (Var*)LangElement::find(String::ToString("detailColor%d", idx)); + Var* detailB = (Var*)LangElement::find(String::ToString("detailB%d", idx)); + + + if (idx > 0) + { + meta->addStatement(new GenOp(" + ")); + } + + meta->addStatement(new GenOp("@.rgb * @", detailColor, detailB)); + } + + meta->addStatement(new GenOp(") / (")); + + for (S32 idx = 0; idx < detailCount; ++idx) + { + Var* detailB = (Var*)LangElement::find(String::ToString("detailB%d", idx)); + + if (idx > 0) + { + meta->addStatement(new GenOp(" + ")); + } + + meta->addStatement(new GenOp("@", detailB)); + } + + + meta->addStatement(new GenOp(");\r\n")); + + meta->addStatement(new GenOp(" @.rgb = toLinear(clamp(@.rgb, 0, 1));\r\n", + outColor, outColor)); + + meta->addStatement(new GenOp("\r\n")); + + // Compute ORM + Var* ormOutput; + OutputTarget targ = DefaultTarget; + if (fd.features[MFT_isDeferred]) + { + targ = RenderTarget2; + } + ormOutput = (Var*)LangElement::find(getOutputTargetVarName(targ)); + + meta->addStatement(new GenOp(" @.gba = (", + ormOutput)); + + for (S32 idx = 0; idx < detailCount; ++idx) + { + Var* matinfoCol = (Var*)LangElement::find(String::ToString("matinfoCol%d", idx)); + Var* detailB = (Var*)LangElement::find(String::ToString("detailB%d", idx)); + + + if (idx > 0) + { + meta->addStatement(new GenOp(" + ")); + } + if (matinfoCol) + { + meta->addStatement(new GenOp("@ * @", matinfoCol, detailB)); + } + else + { + meta->addStatement(new GenOp("float3(1.0, 1.0, 0.0) * @", detailB)); + } + } + + meta->addStatement(new GenOp(") / (")); + + for (S32 idx = 0; idx < detailCount; ++idx) + { + Var* detailB = (Var*)LangElement::find(String::ToString("detailB%d", idx)); + + if (idx > 0) + { + meta->addStatement(new GenOp(" + ")); + } + + meta->addStatement(new GenOp("@", detailB)); + } + + + meta->addStatement(new GenOp(");\r\n")); + + + meta->addStatement(new GenOp("\r\n")); + + // Compute normal-specific blending factors + // LukasPJ: I'm not sure why this is necessary, it might not be. + Var* normalMa = (Var*)LangElement::find("normalMa"); + if (normalMa == NULL) + { + normalMa = new Var; + normalMa->setType("float"); + normalMa->setName("normalMa"); + meta->addStatement(new GenOp(" @ = 0;\r\n", + new DecOp(normalMa))); + } + + for (S32 idx = 0; idx < detailCount; ++idx) + { + Var* detCoord = (Var*)LangElement::find(String::ToString("detCoord%d", idx)); + + Var* detailH = (Var*)LangElement::find(String::ToString("detailH%d", idx)); + Var* detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", idx)); + + meta->addStatement(new GenOp(" @ = max(@, @ + min(@, @.w));\r\n", + normalMa, normalMa, detailH, detailBlend, detCoord)); + } + + meta->addStatement(new GenOp(" @ -= @;\r\n", + normalMa, depth)); + + meta->addStatement(new GenOp("\r\n")); + + for (S32 idx = 0; idx < detailCount; ++idx) + { + Var* detCoord = (Var*)LangElement::find(String::ToString("detCoord%d", idx)); + + Var* detailH = (Var*)LangElement::find(String::ToString("detailH%d", idx)); + Var* detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", idx)); + Var* normalDetailB = (Var*)LangElement::find(String::ToString("normalDetailB%d", idx)); + if (!normalDetailB) + { + normalDetailB = new Var; + normalDetailB->setType("float"); + normalDetailB->setName(String::ToString("normalDetailB%d", idx)); + + meta->addStatement(new GenOp(" @ = max(@ + min(@, @.w) - @, 0);\r\n", + new DecOp(normalDetailB), detailH, detailBlend, detCoord, normalMa)); + } + } + + // Compute normals + Var* gbNormal = (Var*)LangElement::find("gbNormal"); + if (!gbNormal) + { + gbNormal = new Var; + gbNormal->setName("gbNormal"); + gbNormal->setType("float3"); + meta->addStatement(new GenOp(" @;\r\n", new DecOp(gbNormal))); + } + + if (gbNormal != NULL) + { + meta->addStatement(new GenOp(" @ = (", + gbNormal)); + + for (S32 idx = 0; idx < detailCount; ++idx) + { + Var* normalDetailB = (Var*)LangElement::find(String::ToString("normalDetailB%d", idx)); + Var* bumpNormal = (Var*)LangElement::find(String::ToString("bumpNormal%d", idx)); + Var* viewToTangent = getInViewToTangent(componentList); + + + if (idx > 0) + { + meta->addStatement(new GenOp(" + ")); + } + + if (bumpNormal != NULL) + { + meta->addStatement(new GenOp("mul(@.xyz, @) * @", bumpNormal, viewToTangent, normalDetailB)); + } + else + { + meta->addStatement(new GenOp("@[2] * @", viewToTangent, normalDetailB)); + } + } + + meta->addStatement(new GenOp(") / (")); + + for (S32 idx = 0; idx < detailCount; ++idx) + { + Var* normalDetailB = (Var*)LangElement::find(String::ToString("normalDetailB%d", idx)); + + if (idx > 0) + { + meta->addStatement(new GenOp(" + ")); + } + + meta->addStatement(new GenOp("@", normalDetailB)); + } + + + meta->addStatement(new GenOp(");\r\n")); + } + output = meta; } diff --git a/Engine/source/terrain/hlsl/terrFeatureHLSL.h b/Engine/source/terrain/hlsl/terrFeatureHLSL.h index e2b5fdb55..829af9c6f 100644 --- a/Engine/source/terrain/hlsl/terrFeatureHLSL.h +++ b/Engine/source/terrain/hlsl/terrFeatureHLSL.h @@ -45,6 +45,12 @@ public: Var* _getInMacroCoord(Vector &componentList ); + Var* _getDetailMapSampler(); + Var* _getDetailMapArray(); + Var* _getNormalMapSampler(); + Var* _getNormalMapArray(); + Var* _getOrmMapSampler(); + Var* _getOrmMapArray(); Var* _getNormalMapTex(); Var* _getORMConfigMapTex(); @@ -151,17 +157,6 @@ public: virtual String getName() { return "Terrain Lightmap Texture"; } }; - -class TerrainAdditiveFeatHLSL : public TerrainFeatHLSL -{ -public: - - virtual void processPix( Vector &componentList, - const MaterialFeatureData &fd ); - - virtual String getName() { return "Terrain Additive"; } -}; - class TerrainORMMapFeatHLSL : public TerrainFeatHLSL { public: @@ -189,4 +184,17 @@ public: virtual String getName() { return "Blank Matinfo map"; } }; +class TerrainHeightMapBlendHLSL : public TerrainFeatHLSL +{ +public: + + virtual void processVert(Vector& componentList, + const MaterialFeatureData& fd); + + virtual void processPix(Vector& componentList, + const MaterialFeatureData& fd); + + virtual String getName() { return "Terrain Heightmap Blend"; } +}; + #endif // _TERRFEATUREHLSL_H_ diff --git a/Engine/source/terrain/terrCellMaterial.cpp b/Engine/source/terrain/terrCellMaterial.cpp index c97a51682..0f3b64e14 100644 --- a/Engine/source/terrain/terrCellMaterial.cpp +++ b/Engine/source/terrain/terrCellMaterial.cpp @@ -23,6 +23,8 @@ #include "platform/platform.h" #include "terrain/terrCellMaterial.h" + +#include "core/util/safeRelease.h" #include "terrain/terrData.h" #include "terrain/terrCell.h" #include "materials/materialFeatureTypes.h" @@ -37,7 +39,7 @@ #include "gfx/util/screenspace.h" #include "lighting/advanced/advancedLightBinManager.h" -S32 sgMaxTerrainMaterialsPerPass = 3; +S32 sgMaxTerrainMaterialsPerPass = 32; AFTER_MODULE_INIT( MaterialManager ) { @@ -51,17 +53,13 @@ Vector _initSamplerNames() { Vector samplerNames; samplerNames.push_back("$baseTexMap"); - samplerNames.push_back("$layerTex"); - //samplerNames.push_back("$macrolayerTex"); + samplerNames.push_back("$layerTex"); samplerNames.push_back("$lightMapTex"); samplerNames.push_back("$lightInfoBuffer"); - for(int i = 0; i < sgMaxTerrainMaterialsPerPass; ++i) - { - 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("$ormConfigMap%d", i)); - } + samplerNames.push_back("$normalMapSampler"); + samplerNames.push_back("$detailMapSampler"); + samplerNames.push_back("$macroMapSampler"); + samplerNames.push_back("$ormMapSampler"); return samplerNames; } @@ -71,10 +69,11 @@ const Vector TerrainCellMaterial::mSamplerNames = _initSamplerNames(); TerrainCellMaterial::TerrainCellMaterial() : mTerrain( NULL ), - mCurrPass( 0 ), mDeferredMat( NULL ), mReflectMat( NULL ), - mMaterials(0) + mShader( NULL ), + mCurrPass( 0 ), + mMaterials( 0 ) { smAllMaterials.push_back( this ); } @@ -84,6 +83,9 @@ TerrainCellMaterial::~TerrainCellMaterial() SAFE_DELETE( mDeferredMat ); SAFE_DELETE( mReflectMat ); smAllMaterials.remove( this ); + + T3D::for_each(mMaterialInfos.begin(), mMaterialInfos.end(), T3D::delete_pointer()); + mMaterialInfos.clear(); } void TerrainCellMaterial::_updateDefaultAnisotropy() @@ -101,86 +103,74 @@ void TerrainCellMaterial::_updateDefaultAnisotropy() Vector::iterator iter = smAllMaterials.begin(); for ( ; iter != smAllMaterials.end(); iter++ ) { - for ( U32 p=0; p < (*iter)->mPasses.size(); p++ ) + // Start from the existing state block. + GFXStateBlockDesc desc = (*iter)->mStateBlock->getDesc(); + + if ((*iter)->mDetailTexArrayConst->isValid()) { - Pass &pass = (*iter)->mPasses[p]; + const S32 sampler = (*iter)->mDetailTexArrayConst->getSamplerRegister(); - // Start from the existing state block. - GFXStateBlockDesc desc = pass.stateBlock->getDesc(); - - for ( U32 m=0; m < pass.materials.size(); m++ ) + if (maxAnisotropy > 1) { - const MaterialInfo *matInfo = pass.materials[m]; + desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic; + desc.samplers[sampler].maxAnisotropy = maxAnisotropy; + } + else + desc.samplers[sampler].minFilter = GFXTextureFilterLinear; + } - if ( matInfo->detailTexConst->isValid() ) - { - const S32 sampler = matInfo->detailTexConst->getSamplerRegister(); + if ((*iter)->mMacroTexArrayConst->isValid()) + { + const S32 sampler = (*iter)->mMacroTexArrayConst->getSamplerRegister(); - if ( maxAnisotropy > 1 ) - { - desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic; - desc.samplers[sampler].maxAnisotropy = maxAnisotropy; - } - else - desc.samplers[sampler].minFilter = GFXTextureFilterLinear; - } + if (maxAnisotropy > 1) + { + desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic; + desc.samplers[sampler].maxAnisotropy = maxAnisotropy; + } + else + desc.samplers[sampler].minFilter = GFXTextureFilterLinear; + } - if ( matInfo->macroTexConst->isValid() ) - { - const S32 sampler = matInfo->macroTexConst->getSamplerRegister(); + if ((*iter)->mNormalTexArrayConst->isValid()) + { + const S32 sampler = (*iter)->mNormalTexArrayConst->getSamplerRegister(); - if ( maxAnisotropy > 1 ) - { - desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic; - desc.samplers[sampler].maxAnisotropy = maxAnisotropy; - } - else - desc.samplers[sampler].minFilter = GFXTextureFilterLinear; - } + if (maxAnisotropy > 1) + { + desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic; + desc.samplers[sampler].maxAnisotropy = maxAnisotropy; + } + else + desc.samplers[sampler].minFilter = GFXTextureFilterLinear; + } - if ( matInfo->normalTexConst->isValid() ) - { - const S32 sampler = matInfo->normalTexConst->getSamplerRegister(); + if ((*iter)->mOrmTexArrayConst->isValid()) + { + const S32 sampler = (*iter)->mOrmTexArrayConst->getSamplerRegister(); - if ( maxAnisotropy > 1 ) - { - desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic; - desc.samplers[sampler].maxAnisotropy = maxAnisotropy; - } - else - desc.samplers[sampler].minFilter = GFXTextureFilterLinear; - } + if (maxAnisotropy > 1) + { + desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic; + desc.samplers[sampler].maxAnisotropy = maxAnisotropy; + } + else + desc.samplers[sampler].minFilter = GFXTextureFilterLinear; + } - if (matInfo->ormTexConst->isValid()) - { - const S32 sampler = matInfo->ormTexConst->getSamplerRegister(); + // Set the updated stateblock. + desc.setCullMode(GFXCullCCW); + (*iter)->mStateBlock = GFX->createStateBlock(desc); - if (maxAnisotropy > 1) - { - desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic; - desc.samplers[sampler].maxAnisotropy = maxAnisotropy; - } - else - desc.samplers[sampler].minFilter = GFXTextureFilterLinear; - } + //reflection + desc.setCullMode(GFXCullCW); + (*iter)->mReflectionStateBlock = GFX->createStateBlock(desc); - } // for ( U32 m=0; m < pass.materials.size(); m++ ) - - // Set the updated stateblock. - desc.setCullMode( GFXCullCCW ); - pass.stateBlock = GFX->createStateBlock( desc ); - - //reflection - desc.setCullMode( GFXCullCW ); - pass.reflectionStateBlock = GFX->createStateBlock(desc); - - // Create the wireframe state blocks. - GFXStateBlockDesc wireframe( desc ); - wireframe.fillMode = GFXFillWireframe; - wireframe.setCullMode( GFXCullCCW ); - pass.wireframeStateBlock = GFX->createStateBlock( wireframe ); - - } // for ( U32 p=0; i < (*iter)->mPasses.size(); p++ ) + // Create the wireframe state blocks. + GFXStateBlockDesc wireframe(desc); + wireframe.fillMode = GFXFillWireframe; + wireframe.setCullMode(GFXCullCCW); + (*iter)->mWireframeStateBlock = GFX->createStateBlock(wireframe); } } @@ -207,31 +197,26 @@ void TerrainCellMaterial::setTransformAndEye( const MatrixF &modelXfm, VectorF vEye = invViewXfm.getForwardVector(); vEye.normalize( 1.0f / farPlane ); - for ( U32 i=0; i < mPasses.size(); i++ ) + mConsts->setSafe(mModelViewProjConst, modelViewProj); + + if (mViewToObjConst->isValid() || mWorldViewOnlyConst->isValid()) { - Pass &pass = mPasses[i]; + MatrixF worldViewOnly = viewXfm * modelXfm; - pass.consts->setSafe( pass.modelViewProjConst, modelViewProj ); + mConsts->setSafe(mWorldViewOnlyConst, worldViewOnly); - if( pass.viewToObj->isValid() || pass.worldViewOnly->isValid() ) + if (mViewToObjConst->isValid()) { - MatrixF worldViewOnly = viewXfm * modelXfm; - - pass.consts->setSafe( pass.worldViewOnly, worldViewOnly ); - - if( pass.viewToObj->isValid() ) - { - worldViewOnly.affineInverse(); - pass.consts->set( pass.viewToObj, worldViewOnly); - } + worldViewOnly.affineInverse(); + mConsts->set(mViewToObjConst, worldViewOnly); } - - pass.consts->setSafe( pass.eyePosWorldConst, eyePos ); - pass.consts->setSafe( pass.eyePosConst, objEyePos ); - pass.consts->setSafe( pass.objTransConst, modelXfm ); - pass.consts->setSafe( pass.worldToObjConst, invModelXfm ); - pass.consts->setSafe( pass.vEyeConst, vEye ); } + + mConsts->setSafe(mEyePosWorldConst, eyePos); + mConsts->setSafe(mEyePosConst, objEyePos); + mConsts->setSafe(mObjTransConst, modelXfm); + mConsts->setSafe(mWorldToObjConst, invModelXfm); + mConsts->setSafe(mVEyeConst, vEye); } TerrainCellMaterial* TerrainCellMaterial::getDeferredMat() @@ -268,8 +253,7 @@ void TerrainCellMaterial::init( TerrainBlock *block, mTerrain = block; mMaterials = activeMaterials; - Vector materials; - + mMaterialInfos.clear(); for ( U32 i = 0; i < 64; i++ ) { if ( !( mMaterials & ((U64)1 << i ) ) ) @@ -280,41 +264,20 @@ void TerrainCellMaterial::init( TerrainBlock *block, MaterialInfo *info = new MaterialInfo(); info->layerId = i; info->mat = mat; - materials.push_back( info ); + mMaterialInfos.push_back(info); } - mCurrPass = 0; - mPasses.clear(); - - // Ok... loop till we successfully generate all - // the shader passes for the materials. - while ( materials.size() > 0 || baseOnly ) + if (!_initShader(deferredMat, + reflectMat, + baseOnly)) { - mPasses.increment(); + Con::errorf("TerrainCellMaterial::init - Failed to init shader!"); - if ( !_createPass( &materials, - &mPasses.last(), - mPasses.size() == 1, - deferredMat, - reflectMat, - baseOnly ) ) - { - Con::errorf( "TerrainCellMaterial::init - Failed to create pass!" ); - - // The pass failed to be generated... give up. - mPasses.last().materials.clear(); - mPasses.clear(); - T3D::for_each( materials.begin(), materials.end(), T3D::delete_pointer() ); - return; - } - - if ( baseOnly ) - break; + T3D::for_each(mMaterialInfos.begin(), mMaterialInfos.end(), T3D::delete_pointer()); + mMaterialInfos.clear(); + return; } - // Cleanup any remaining matinfo. - T3D::for_each( materials.begin(), materials.end(), T3D::delete_pointer() ); - // If we have attached mats then update them too. if ( mDeferredMat ) mDeferredMat->init( mTerrain, mMaterials, true, false, baseOnly ); @@ -322,14 +285,11 @@ void TerrainCellMaterial::init( TerrainBlock *block, mReflectMat->init( mTerrain, mMaterials, false, true, baseOnly ); } -bool TerrainCellMaterial::_createPass( Vector *materials, - Pass *pass, - bool firstPass, - bool deferredMat, - bool reflectMat, - bool baseOnly ) +bool TerrainCellMaterial::_initShader(bool deferredMat, + bool reflectMat, + bool baseOnly) { - if ( GFX->getPixelShaderVersion() < 3.0f ) + if (GFX->getPixelShaderVersion() < 3.0f) baseOnly = true; // NOTE: At maximum we only try to combine sgMaxTerrainMaterialsPerPass materials @@ -337,7 +297,7 @@ bool TerrainCellMaterial::_createPass( Vector *materials, // cases, but the most common case results in much fewer // shader generation failures and permutations leading to // faster load time and less hiccups during gameplay. - U32 matCount = getMin( sgMaxTerrainMaterialsPerPass, materials->size() ); + U32 matCount = getMin(sgMaxTerrainMaterialsPerPass, mMaterialInfos.size()); Vector normalMaps; @@ -347,135 +307,139 @@ bool TerrainCellMaterial::_createPass( Vector *materials, // TODO: This seems ugly... we should trigger // features like this differently in the future. // - bool useBLM = String::compare( LIGHTMGR->getId(), "BLM" ) == 0; + bool useBLM = String::compare(LIGHTMGR->getId(), "BLM") == 0; // Do we need to disable normal mapping? - const bool disableNormalMaps = MATMGR->getExclusionFeatures().hasFeature( MFT_NormalMap ) || useBLM; + const bool disableNormalMaps = MATMGR->getExclusionFeatures().hasFeature(MFT_NormalMap) || useBLM; // How about parallax? - const bool disableParallaxMaps = GFX->getPixelShaderVersion() < 3.0f || - MATMGR->getExclusionFeatures().hasFeature( MFT_Parallax ); + const bool disableParallaxMaps = GFX->getPixelShaderVersion() < 3.0f || + MATMGR->getExclusionFeatures().hasFeature(MFT_Parallax); // Has advanced lightmap support been enabled for deferred. bool advancedLightmapSupport = false; - if ( deferredMat ) + if (deferredMat) { // This sucks... but it works. - AdvancedLightBinManager *lightBin; - if ( Sim::findObject( "AL_LightBinMgr", lightBin ) ) + AdvancedLightBinManager* lightBin; + if (Sim::findObject("AL_LightBinMgr", lightBin)) advancedLightmapSupport = lightBin->MRTLightmapsDuringDeferred(); } // Loop till we create a valid shader! - while( true ) + while (true) { FeatureSet features; - features.addFeature( MFT_VertTransform ); - features.addFeature( MFT_TerrainBaseMap ); + features.addFeature(MFT_VertTransform); + features.addFeature(MFT_TerrainBaseMap); - if ( deferredMat ) + if (deferredMat) { - features.addFeature( MFT_EyeSpaceDepthOut ); - features.addFeature( MFT_DeferredConditioner ); + features.addFeature(MFT_EyeSpaceDepthOut); + features.addFeature(MFT_DeferredConditioner); features.addFeature(MFT_isDeferred); - if ( advancedLightmapSupport ) - features.addFeature( MFT_RenderTarget3_Zero ); + if (advancedLightmapSupport) + features.addFeature(MFT_RenderTarget3_Zero); } else { - features.addFeature( MFT_RTLighting ); + features.addFeature(MFT_RTLighting); // The HDR feature is always added... it will compile out // if HDR is not enabled in the engine. - features.addFeature( MFT_HDROut ); + features.addFeature(MFT_HDROut); } - features.addFeature(MFT_DeferredTerrainBlankInfoMap); // Enable lightmaps and fogging if we're in BL. - if ( reflectMat || useBLM ) + if (reflectMat || useBLM) { - features.addFeature( MFT_Fog ); - features.addFeature( MFT_ForwardShading ); + features.addFeature(MFT_Fog); + features.addFeature(MFT_ForwardShading); } - if ( useBLM ) - features.addFeature( MFT_TerrainLightMap ); - - // The additional passes need to be lerp blended into the - // target to maintain the results of the previous passes. - if (!firstPass && deferredMat) - features.addFeature( MFT_TerrainAdditive ); + if (useBLM) + features.addFeature(MFT_TerrainLightMap); normalMaps.clear(); - pass->materials.clear(); + + S32 featureIndex = 0; // Now add all the material layer features. - - for ( U32 i=0; i < matCount && !baseOnly; i++ ) + for (U32 i = 0; i < matCount && !baseOnly; i++) { - TerrainMaterial *mat = (*materials)[i]->mat; + TerrainMaterial* mat = mMaterialInfos[i]->mat; - if ( mat == NULL ) + if (mat == NULL) continue; // We only include materials that // have more than a base texture. - if ( mat->getDetailSize() <= 0 || - mat->getDetailDistance() <= 0 || - mat->getDetailMap().isEmpty() ) - continue; + if (mat->getDetailSize() <= 0 || + mat->getDetailDistance() <= 0 || + mat->getDetailMap().isEmpty()) + continue; - S32 featureIndex = pass->materials.size(); - - // check for macro detail texture - if ( !(mat->getMacroSize() <= 0 || mat->getMacroDistance() <= 0 || mat->getMacroMap().isEmpty() ) ) - { - if(deferredMat) - features.addFeature(MFT_isDeferred, featureIndex); - features.addFeature( MFT_TerrainMacroMap, featureIndex ); - } - - if(deferredMat) - features.addFeature(MFT_isDeferred, featureIndex); - features.addFeature( MFT_TerrainDetailMap, featureIndex ); - - if (!(mat->getORMConfigMap().isEmpty())) + // check for macro detail texture + if (!(mat->getMacroSize() <= 0 || mat->getMacroDistance() <= 0 || mat->getMacroMap().isEmpty())) { if (deferredMat) features.addFeature(MFT_isDeferred, featureIndex); - features.addFeature(MFT_TerrainORMMap, featureIndex); - features.removeFeature(MFT_DeferredTerrainBlankInfoMap); + features.addFeature(MFT_TerrainMacroMap, featureIndex); } - if (mat->getInvertRoughness()) - features.addFeature(MFT_InvertRoughness); - pass->materials.push_back( (*materials)[i] ); + if (deferredMat) + features.addFeature(MFT_isDeferred, featureIndex); + features.addFeature(MFT_TerrainDetailMap, featureIndex); + + if (deferredMat) + { + if (!(mat->getORMConfigMap().isEmpty())) + { + features.addFeature(MFT_TerrainORMMap, featureIndex); + } + else + { + features.addFeature(MFT_DeferredTerrainBlankInfoMap, featureIndex); + } + } + + if (mat->getInvertRoughness()) + features.addFeature(MFT_InvertRoughness, featureIndex); + normalMaps.increment(); // Skip normal maps if we need to. - if ( !disableNormalMaps && mat->getNormalMap().isNotEmpty() ) + if (!disableNormalMaps && mat->getNormalMap().isNotEmpty()) { - features.addFeature( MFT_TerrainNormalMap, featureIndex ); + features.addFeature(MFT_TerrainNormalMap, featureIndex); - normalMaps.last().set( mat->getNormalMap(), - &GFXNormalMapProfile, "TerrainCellMaterial::_createPass() - NormalMap" ); + normalMaps.last().set(mat->getNormalMap(), + &GFXNormalMapProfile, "TerrainCellMaterial::_initShader() - NormalMap"); GFXFormat normalFmt = normalMaps.last().getFormat(); - if ( normalFmt == GFXFormatBC3 ) - features.addFeature( MFT_IsBC3nm, featureIndex ); - else if ( normalFmt == GFXFormatBC5) - features.addFeature( MFT_IsBC5nm, featureIndex); + if (normalFmt == GFXFormatBC3) + features.addFeature(MFT_IsBC3nm, featureIndex); + else if (normalFmt == GFXFormatBC5) + features.addFeature(MFT_IsBC5nm, featureIndex); // Do we need and can we do parallax mapping? - if ( !disableParallaxMaps && - mat->getParallaxScale() > 0.0f && - !mat->useSideProjection() ) - features.addFeature( MFT_TerrainParallaxMap, featureIndex ); + if (!disableParallaxMaps && + mat->getParallaxScale() > 0.0f && + !mat->useSideProjection()) + features.addFeature(MFT_TerrainParallaxMap, featureIndex); } // Is this layer got side projection? - if ( mat->useSideProjection() ) - features.addFeature( MFT_TerrainSideProject, featureIndex ); + if (mat->useSideProjection()) + features.addFeature(MFT_TerrainSideProject, featureIndex); + + featureIndex++; + } + + // New blending + if (matCount > 0 && !Con::getBoolVariable("$Terrain::LerpBlend", false)) + { + features.addFeature(MFT_TerrainHeightBlend); } MaterialFeatureData featureData; @@ -486,16 +450,16 @@ bool TerrainCellMaterial::_createPass( Vector *materials, // registers we're gonna need. U32 numTex = 0; U32 numTexReg = 0; - for ( U32 i=0; i < features.getCount(); i++ ) + for (U32 i = 0; i < features.getCount(); i++) { S32 index; - const FeatureType &type = features.getAt( i, &index ); - ShaderFeature* sf = FEATUREMGR->getByType( type ); - if ( !sf ) + const FeatureType& type = features.getAt(i, &index); + ShaderFeature* sf = FEATUREMGR->getByType(type); + if (!sf) continue; - sf->setProcessIndex( index ); - ShaderFeature::Resources res = sf->getResources( featureData ); + sf->setProcessIndex(index); + ShaderFeature::Resources res = sf->getResources(featureData); numTex += res.numTex; numTexReg += res.numTexReg; @@ -507,9 +471,9 @@ bool TerrainCellMaterial::_createPass( Vector *materials, // limit. Its really supposed to be 11, but that // always fails to compile so far. // - if ( numTex < GFX->getNumSamplers() && - numTexReg <= 10 ) - { + if (numTex < GFX->getNumSamplers() && + numTexReg <= 10) + { // NOTE: We really shouldn't be getting errors building the shaders, // but we can generate more instructions than the ps_2_x will allow. // @@ -522,18 +486,18 @@ bool TerrainCellMaterial::_createPass( Vector *materials, // 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 = true;// matCount == 1; - GFXShader::setLogging( logErrors, true ); + GFXShader::setLogging(logErrors, true); - pass->shader = SHADERGEN->getShader( featureData, getGFXVertexFormat(), NULL, mSamplerNames ); + mShader = SHADERGEN->getShader(featureData, getGFXVertexFormat(), NULL, mSamplerNames); } // If we got a shader then we can continue. - if ( pass->shader ) + if (mShader) break; // If the material count is already 1 then this // is a real shader error... give up! - if ( matCount <= 1 ) + if (matCount <= 1) return false; // If we failed we next try half the input materials @@ -542,180 +506,149 @@ bool TerrainCellMaterial::_createPass( Vector *materials, } // Setup the constant buffer. - pass->consts = pass->shader->allocConstBuffer(); + mConsts = mShader->allocConstBuffer(); // Prepare the basic constants. - pass->modelViewProjConst = pass->shader->getShaderConstHandle( "$modelview" ); - pass->worldViewOnly = pass->shader->getShaderConstHandle( "$worldViewOnly" ); - pass->viewToObj = pass->shader->getShaderConstHandle( "$viewToObj" ); - pass->eyePosWorldConst = pass->shader->getShaderConstHandle( "$eyePosWorld" ); - pass->eyePosConst = pass->shader->getShaderConstHandle( "$eyePos" ); - pass->vEyeConst = pass->shader->getShaderConstHandle( "$vEye" ); - pass->layerSizeConst = pass->shader->getShaderConstHandle( "$layerSize" ); - pass->objTransConst = pass->shader->getShaderConstHandle( "$objTrans" ); - pass->worldToObjConst = pass->shader->getShaderConstHandle( "$worldToObj" ); - pass->lightInfoBufferConst = pass->shader->getShaderConstHandle( "$lightInfoBuffer" ); - pass->baseTexMapConst = pass->shader->getShaderConstHandle( "$baseTexMap" ); - pass->layerTexConst = pass->shader->getShaderConstHandle( "$layerTex" ); - pass->fogDataConst = pass->shader->getShaderConstHandle( "$fogData" ); - pass->fogColorConst = pass->shader->getShaderConstHandle( "$fogColor" ); - pass->lightMapTexConst = pass->shader->getShaderConstHandle( "$lightMapTex" ); - pass->oneOverTerrainSize = pass->shader->getShaderConstHandle( "$oneOverTerrainSize" ); - pass->squareSize = pass->shader->getShaderConstHandle( "$squareSize" ); + mModelViewProjConst = mShader->getShaderConstHandle("$modelview"); + mWorldViewOnlyConst = mShader->getShaderConstHandle("$worldViewOnly"); + mViewToObjConst = mShader->getShaderConstHandle("$viewToObj"); + mEyePosWorldConst = mShader->getShaderConstHandle("$eyePosWorld"); + mEyePosConst = mShader->getShaderConstHandle("$eyePos"); + mVEyeConst = mShader->getShaderConstHandle("$vEye"); + mLayerSizeConst = mShader->getShaderConstHandle("$layerSize"); + mObjTransConst = mShader->getShaderConstHandle("$objTrans"); + mWorldToObjConst = mShader->getShaderConstHandle("$worldToObj"); + mLightInfoBufferConst = mShader->getShaderConstHandle("$lightInfoBuffer"); + mBaseTexMapConst = mShader->getShaderConstHandle("$baseTexMap"); + mLayerTexConst = mShader->getShaderConstHandle("$layerTex"); + mFogDataConst = mShader->getShaderConstHandle("$fogData"); + mFogColorConst = mShader->getShaderConstHandle("$fogColor"); + mLightMapTexConst = mShader->getShaderConstHandle("$lightMapTex"); + mOneOverTerrainSizeConst = mShader->getShaderConstHandle("$oneOverTerrainSize"); + mSquareSizeConst = mShader->getShaderConstHandle("$squareSize"); + mBlendDepthConst = mShader->getShaderConstHandle("$baseBlendDepth"); - pass->lightParamsConst = pass->shader->getShaderConstHandle( "$rtParamslightInfoBuffer" ); + mLightParamsConst = mShader->getShaderConstHandle("$rtParamslightInfoBuffer"); // Now prepare the basic stateblock. GFXStateBlockDesc desc; - if ( !firstPass ) - { - desc.setBlend( true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha ); - - // If this is the deferred then we don't want to - // write to the last two color channels (where - // depth is usually encoded). - // - // This trick works in combination with the - // MFT_TerrainAdditive feature to lerp the - // output normal with the previous pass. - // - if ( deferredMat ) - desc.setColorWrites( true, true, true, false ); - } // We write to the zbuffer if this is a deferred // material or if the deferred is disabled. - desc.setZReadWrite( true, !MATMGR->getDeferredEnabled() || - deferredMat || - reflectMat ); + desc.setZReadWrite(true, !MATMGR->getDeferredEnabled() || + deferredMat || + reflectMat); desc.samplersDefined = true; - if ( pass->baseTexMapConst->isValid() ) - desc.samplers[pass->baseTexMapConst->getSamplerRegister()] = GFXSamplerStateDesc::getWrapLinear(); + if (mBaseTexMapConst->isValid()) + desc.samplers[mBaseTexMapConst->getSamplerRegister()] = GFXSamplerStateDesc::getWrapLinear(); - if ( pass->layerTexConst->isValid() ) - desc.samplers[pass->layerTexConst->getSamplerRegister()] = GFXSamplerStateDesc::getClampPoint(); + if (mLayerTexConst->isValid()) + desc.samplers[mLayerTexConst->getSamplerRegister()] = GFXSamplerStateDesc::getClampPoint(); - if ( pass->lightInfoBufferConst->isValid() ) - desc.samplers[pass->lightInfoBufferConst->getSamplerRegister()] = GFXSamplerStateDesc::getClampPoint(); + if (mLightInfoBufferConst->isValid()) + desc.samplers[mLightInfoBufferConst->getSamplerRegister()] = GFXSamplerStateDesc::getClampPoint(); - if ( pass->lightMapTexConst->isValid() ) - desc.samplers[pass->lightMapTexConst->getSamplerRegister()] = GFXSamplerStateDesc::getWrapLinear(); + if (mLightMapTexConst->isValid()) + desc.samplers[mLightMapTexConst->getSamplerRegister()] = GFXSamplerStateDesc::getWrapLinear(); const U32 maxAnisotropy = MATMGR->getDefaultAnisotropy(); - // Finally setup the material specific shader - // constants and stateblock state. - // - // NOTE: If this changes be sure to check TerrainCellMaterial::_updateDefaultAnisotropy - // to see if it needs the same changes. - // - for ( U32 i=0; i < pass->materials.size(); i++ ) + mDetailInfoVArrayConst = mShader->getShaderConstHandle("$detailScaleAndFade"); + mDetailInfoPArrayConst = mShader->getShaderConstHandle("$detailIdStrengthParallax"); + mMacroInfoVArrayConst = mShader->getShaderConstHandle("$macroIdStrengthParallax"); + mMacroInfoPArrayConst = mShader->getShaderConstHandle("$macroIdStrengthParallax"); + + mDetailTexArrayConst = mShader->getShaderConstHandle("$detailMapSampler"); + if (mDetailTexArrayConst->isValid()) { - MaterialInfo *matInfo = pass->materials[i]; + const S32 sampler = mDetailTexArrayConst->getSamplerRegister(); - matInfo->detailInfoVConst = pass->shader->getShaderConstHandle( avar( "$detailScaleAndFade%d", i ) ); - matInfo->detailInfoPConst = pass->shader->getShaderConstHandle( avar( "$detailIdStrengthParallax%d", i ) ); + desc.samplers[sampler] = GFXSamplerStateDesc::getWrapLinear(); + desc.samplers[sampler].magFilter = GFXTextureFilterLinear; + desc.samplers[sampler].mipFilter = GFXTextureFilterLinear; - matInfo->detailTexConst = pass->shader->getShaderConstHandle( avar( "$detailMap%d", i ) ); - if ( matInfo->detailTexConst->isValid() ) + if (maxAnisotropy > 1) { - const S32 sampler = matInfo->detailTexConst->getSamplerRegister(); - - desc.samplers[sampler] = GFXSamplerStateDesc::getWrapLinear(); - desc.samplers[sampler].magFilter = GFXTextureFilterLinear; - desc.samplers[sampler].mipFilter = GFXTextureFilterLinear; - - if ( maxAnisotropy > 1 ) - { - desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic; - desc.samplers[sampler].maxAnisotropy = maxAnisotropy; - } - else - desc.samplers[sampler].minFilter = GFXTextureFilterLinear; - - matInfo->detailTex.set( matInfo->mat->getDetailMap(), - &GFXStaticTextureProfile, "TerrainCellMaterial::_createPass() - DetailMap" ); - } - - matInfo->ormTexConst = pass->shader->getShaderConstHandle(avar("$ormConfigMap%d", i)); - if (matInfo->ormTexConst->isValid()) - { - GFXTextureProfile* profile = &GFXStaticTextureProfile; - if (matInfo->mat->getIsSRGB()) - profile = &GFXStaticTextureSRGBProfile; - - matInfo->ormTex.set(matInfo->mat->getORMConfigMap(), - profile, "TerrainCellMaterial::_createPass() - CompositeMap"); - const S32 sampler = matInfo->ormTexConst->getSamplerRegister(); - - desc.samplers[sampler] = GFXSamplerStateDesc::getWrapLinear(); - desc.samplers[sampler].magFilter = GFXTextureFilterLinear; - desc.samplers[sampler].mipFilter = GFXTextureFilterLinear; - - if (maxAnisotropy > 1) - { - desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic; - desc.samplers[sampler].maxAnisotropy = maxAnisotropy; - } - else - desc.samplers[sampler].minFilter = GFXTextureFilterLinear; - } - - matInfo->macroInfoVConst = pass->shader->getShaderConstHandle( avar( "$macroScaleAndFade%d", i ) ); - matInfo->macroInfoPConst = pass->shader->getShaderConstHandle( avar( "$macroIdStrengthParallax%d", i ) ); - - matInfo->macroTexConst = pass->shader->getShaderConstHandle( avar( "$macroMap%d", i ) ); - if ( matInfo->macroTexConst->isValid() ) - { - const S32 sampler = matInfo->macroTexConst->getSamplerRegister(); - - desc.samplers[sampler] = GFXSamplerStateDesc::getWrapLinear(); - desc.samplers[sampler].magFilter = GFXTextureFilterLinear; - desc.samplers[sampler].mipFilter = GFXTextureFilterLinear; - - if ( maxAnisotropy > 1 ) - { - desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic; - desc.samplers[sampler].maxAnisotropy = maxAnisotropy; - } - else - desc.samplers[sampler].minFilter = GFXTextureFilterLinear; - - matInfo->macroTex.set( matInfo->mat->getMacroMap(), - &GFXStaticTextureProfile, "TerrainCellMaterial::_createPass() - MacroMap" ); - } - //end macro texture - - matInfo->normalTexConst = pass->shader->getShaderConstHandle( avar( "$normalMap%d", i ) ); - if ( matInfo->normalTexConst->isValid() ) - { - const S32 sampler = matInfo->normalTexConst->getSamplerRegister(); - - desc.samplers[sampler] = GFXSamplerStateDesc::getWrapLinear(); - desc.samplers[sampler].magFilter = GFXTextureFilterLinear; - desc.samplers[sampler].mipFilter = GFXTextureFilterLinear; - - if ( maxAnisotropy > 1 ) - { - desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic; - desc.samplers[sampler].maxAnisotropy = maxAnisotropy; - } - else - desc.samplers[sampler].minFilter = GFXTextureFilterLinear; - - matInfo->normalTex = normalMaps[i]; + desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic; + desc.samplers[sampler].maxAnisotropy = maxAnisotropy; } + else + desc.samplers[sampler].minFilter = GFXTextureFilterLinear; } - // Remove the materials we processed and leave the - // ones that remain for the next pass. - for ( U32 i=0; i < matCount; i++ ) + mMacroTexArrayConst = mShader->getShaderConstHandle("$macroMapSampler"); + if (mMacroTexArrayConst->isValid()) { - MaterialInfo *matInfo = materials->first(); - if ( baseOnly || pass->materials.find_next( matInfo ) == -1 ) - delete matInfo; - materials->pop_front(); + const S32 sampler = mMacroTexArrayConst->getSamplerRegister(); + + desc.samplers[sampler] = GFXSamplerStateDesc::getWrapLinear(); + desc.samplers[sampler].magFilter = GFXTextureFilterLinear; + desc.samplers[sampler].mipFilter = GFXTextureFilterLinear; + + if (maxAnisotropy > 1) + { + desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic; + desc.samplers[sampler].maxAnisotropy = maxAnisotropy; + } + else + desc.samplers[sampler].minFilter = GFXTextureFilterLinear; + } + + mNormalTexArrayConst = mShader->getShaderConstHandle("$normalMapSampler"); + if (mNormalTexArrayConst->isValid()) + { + const S32 sampler = mNormalTexArrayConst->getSamplerRegister(); + + desc.samplers[sampler] = GFXSamplerStateDesc::getWrapLinear(); + desc.samplers[sampler].magFilter = GFXTextureFilterLinear; + desc.samplers[sampler].mipFilter = GFXTextureFilterLinear; + + if (maxAnisotropy > 1) + { + desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic; + desc.samplers[sampler].maxAnisotropy = maxAnisotropy; + } + else + desc.samplers[sampler].minFilter = GFXTextureFilterLinear; + } + + mOrmTexArrayConst = mShader->getShaderConstHandle("$ormMapSampler"); + if (mOrmTexArrayConst->isValid()) + { + GFXTextureProfile* profile = &GFXStaticTextureProfile; + + const S32 sampler = mOrmTexArrayConst->getSamplerRegister(); + + desc.samplers[sampler] = GFXSamplerStateDesc::getWrapLinear(); + desc.samplers[sampler].magFilter = GFXTextureFilterLinear; + desc.samplers[sampler].mipFilter = GFXTextureFilterLinear; + + if (maxAnisotropy > 1) + { + desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic; + desc.samplers[sampler].maxAnisotropy = maxAnisotropy; + } + else + desc.samplers[sampler].minFilter = GFXTextureFilterLinear; + } + + for (U32 i = 0; i < matCount && !baseOnly; i++) + { + TerrainMaterial* mat = mMaterialInfos[i]->mat; + + if (mat == NULL) + continue; + + // We only include materials that + // have more than a base texture. + if (mat->getDetailSize() <= 0 || + mat->getDetailDistance() <= 0 || + mat->getDetailMap().isEmpty()) + continue; + + mMaterialInfos[i]->mBlendDepthConst = mShader->getShaderConstHandle(avar("$blendDepth%d", i)); + mMaterialInfos[i]->mBlendContrastConst = mShader->getShaderConstHandle(avar("$blendContrast%d", i)); } // If we're doing deferred it requires some @@ -724,28 +657,34 @@ bool TerrainCellMaterial::_createPass( Vector *materials, desc.addDesc( RenderDeferredMgr::getOpaqueStenciWriteDesc( false ) ); desc.setCullMode( GFXCullCCW ); - pass->stateBlock = GFX->createStateBlock(desc); + mStateBlock = GFX->createStateBlock(desc); //reflection stateblock desc.setCullMode( GFXCullCW ); - pass->reflectionStateBlock = GFX->createStateBlock(desc); + mReflectionStateBlock = GFX->createStateBlock(desc); // Create the wireframe state blocks. GFXStateBlockDesc wireframe( desc ); wireframe.fillMode = GFXFillWireframe; wireframe.setCullMode( GFXCullCCW ); - pass->wireframeStateBlock = GFX->createStateBlock( wireframe ); + mWireframeStateBlock = GFX->createStateBlock( wireframe ); return true; } -void TerrainCellMaterial::_updateMaterialConsts( Pass *pass ) +void TerrainCellMaterial::_updateMaterialConsts( ) { PROFILE_SCOPE( TerrainCellMaterial_UpdateMaterialConsts ); - - for ( U32 j=0; j < pass->materials.size(); j++ ) + if (mMaterialInfos.empty()) { - MaterialInfo *matInfo = pass->materials[j]; + return; + } + AlignedArray detailInfoArray(mMaterialInfos.size(), sizeof(Point4F)); + AlignedArray detailScaleAndFadeArray(mMaterialInfos.size(), sizeof(Point4F)); + + for ( U32 j=0; j < mMaterialInfos.size(); j++ ) + { + MaterialInfo *matInfo = mMaterialInfos[j]; F32 detailSize = matInfo->mat->getDetailSize(); F32 detailScale = 1.0f; @@ -773,38 +712,20 @@ void TerrainCellMaterial::_updateMaterialConsts( Pass *pass ) if ( !mIsZero( distance ) ) detailScaleAndFade.w = 1.0f / distance; - Point3F detailIdStrengthParallax( matInfo->layerId, + Point4F detailIdStrengthParallax( matInfo->layerId, matInfo->mat->getDetailStrength(), - matInfo->mat->getParallaxScale() ); + matInfo->mat->getParallaxScale(), 0 ); - pass->consts->setSafe( matInfo->detailInfoVConst, detailScaleAndFade ); - pass->consts->setSafe( matInfo->detailInfoPConst, detailIdStrengthParallax ); + detailScaleAndFadeArray[j] = detailScaleAndFade; + detailInfoArray[j] = detailIdStrengthParallax; - // macro texture info - - F32 macroSize = matInfo->mat->getMacroSize(); - F32 macroScale = 1.0f; - if ( !mIsZero( macroSize ) ) - macroScale = mTerrain->getWorldBlockSize() / macroSize; - - // Scale the distance by the global scalar. - const F32 macroDistance = mTerrain->smDetailScale * matInfo->mat->getMacroDistance(); - - Point4F macroScaleAndFade( macroScale, - -macroScale, - macroDistance, - 0 ); - - if ( !mIsZero( macroDistance ) ) - macroScaleAndFade.w = 1.0f / macroDistance; - - Point3F macroIdStrengthParallax( matInfo->layerId, - matInfo->mat->getMacroStrength(), - 0 ); - - pass->consts->setSafe( matInfo->macroInfoVConst, macroScaleAndFade ); - pass->consts->setSafe( matInfo->macroInfoPConst, macroIdStrengthParallax ); + mConsts->setSafe(matInfo->mBlendDepthConst, matInfo->mat->getBlendDepth()); + mConsts->setSafe(matInfo->mBlendContrastConst, matInfo->mat->getBlendContrast()); } + + mConsts->setSafe(mDetailInfoVArrayConst, detailScaleAndFadeArray); + mConsts->setSafe(mDetailInfoPArrayConst, detailInfoArray); + } bool TerrainCellMaterial::setupPass( const SceneRenderState *state, @@ -812,80 +733,89 @@ bool TerrainCellMaterial::setupPass( const SceneRenderState *state, { PROFILE_SCOPE( TerrainCellMaterial_SetupPass ); - if ( mCurrPass >= mPasses.size() ) + if (mCurrPass > 0) { mCurrPass = 0; return false; } - Pass &pass = mPasses[mCurrPass]; + if (mMaterialInfos.size() > 4) + { + int a = 2 + 2; + } - _updateMaterialConsts( &pass ); + mCurrPass++; - if ( pass.baseTexMapConst->isValid() ) - GFX->setTexture( pass.baseTexMapConst->getSamplerRegister(), mTerrain->mBaseTex.getPointer() ); + _updateMaterialConsts(); - if ( pass.layerTexConst->isValid() ) - GFX->setTexture( pass.layerTexConst->getSamplerRegister(), mTerrain->mLayerTex.getPointer() ); + if ( mBaseTexMapConst->isValid() ) + GFX->setTexture( mBaseTexMapConst->getSamplerRegister(), mTerrain->mBaseTex.getPointer() ); - if ( pass.lightMapTexConst->isValid() ) - GFX->setTexture( pass.lightMapTexConst->getSamplerRegister(), mTerrain->getLightMapTex() ); + if ( mLayerTexConst->isValid() ) + GFX->setTexture( mLayerTexConst->getSamplerRegister(), mTerrain->mLayerTex.getPointer() ); + + if ( mLightMapTexConst->isValid() ) + GFX->setTexture( mLightMapTexConst->getSamplerRegister(), mTerrain->getLightMapTex() ); if ( sceneData.wireframe ) - GFX->setStateBlock( pass.wireframeStateBlock ); + GFX->setStateBlock( mWireframeStateBlock ); else if ( state->isReflectPass( )) - GFX->setStateBlock( pass.reflectionStateBlock ); + GFX->setStateBlock( mReflectionStateBlock ); else - GFX->setStateBlock( pass.stateBlock ); + GFX->setStateBlock( mStateBlock ); - GFX->setShader( pass.shader ); - GFX->setShaderConstBuffer( pass.consts ); + GFX->setShader( mShader ); + GFX->setShaderConstBuffer( mConsts ); // Let the light manager prepare any light stuff it needs. LIGHTMGR->setLightInfo( NULL, NULL, sceneData, state, - mCurrPass, - pass.consts ); + 0, + mConsts ); - for ( U32 i=0; i < pass.materials.size(); i++ ) - { - MaterialInfo *matInfo = pass.materials[i]; + if (mDetailTexArrayConst->isValid() && mTerrain->getDetailTextureArray().isValid()) + GFX->setTextureArray(mDetailTexArrayConst->getSamplerRegister(), mTerrain->getDetailTextureArray()); + if (mMacroTexArrayConst->isValid() && mTerrain->getMacroTextureArray().isValid()) + GFX->setTextureArray(mMacroTexArrayConst->getSamplerRegister(), mTerrain->getMacroTextureArray()); + if (mNormalTexArrayConst->isValid() && mTerrain->getNormalTextureArray().isValid()) + GFX->setTextureArray(mNormalTexArrayConst->getSamplerRegister(), mTerrain->getNormalTextureArray()); + if (mOrmTexArrayConst->isValid() && mTerrain->getOrmTextureArray().isValid()) + GFX->setTextureArray(mOrmTexArrayConst->getSamplerRegister(), mTerrain->getOrmTextureArray()); - if ( matInfo->detailTexConst->isValid() ) - GFX->setTexture( matInfo->detailTexConst->getSamplerRegister(), matInfo->detailTex ); - if ( matInfo->macroTexConst->isValid() ) - GFX->setTexture( matInfo->macroTexConst->getSamplerRegister(), matInfo->macroTex ); - if ( matInfo->normalTexConst->isValid() ) - GFX->setTexture( matInfo->normalTexConst->getSamplerRegister(), matInfo->normalTex ); - if ( matInfo->ormTexConst->isValid() ) - GFX->setTexture( matInfo->ormTexConst->getSamplerRegister(), matInfo->ormTex ); - } + mConsts->setSafe( mLayerSizeConst, (F32)mTerrain->mLayerTex.getWidth() ); - pass.consts->setSafe( pass.layerSizeConst, (F32)mTerrain->mLayerTex.getWidth() ); - - if ( pass.oneOverTerrainSize->isValid() ) + if ( mOneOverTerrainSizeConst->isValid() ) { F32 oneOverTerrainSize = 1.0f / mTerrain->getWorldBlockSize(); - pass.consts->set( pass.oneOverTerrainSize, oneOverTerrainSize ); + mConsts->set( mOneOverTerrainSizeConst, oneOverTerrainSize ); } - pass.consts->setSafe( pass.squareSize, mTerrain->getSquareSize() ); + mConsts->setSafe( mSquareSizeConst, mTerrain->getSquareSize() ); - if ( pass.fogDataConst->isValid() ) + if ( mFogDataConst->isValid() ) { Point3F fogData; fogData.x = sceneData.fogDensity; fogData.y = sceneData.fogDensityOffset; fogData.z = sceneData.fogHeightFalloff; - pass.consts->set( pass.fogDataConst, fogData ); + mConsts->set( mFogDataConst, fogData ); } - pass.consts->setSafe( pass.fogColorConst, sceneData.fogColor ); + if (String::isEmpty(Con::getVariable("$Terrain::BlendDepth"))) + { + mConsts->setSafe(mBlendDepthConst, 0.2f); + } + else + { + mConsts->setSafe(mBlendDepthConst, Con::getFloatVariable("$Terrain::BlendDepth")); + } - if ( pass.lightInfoBufferConst->isValid() && - pass.lightParamsConst->isValid() ) + mConsts->setSafe( mFogColorConst, sceneData.fogColor ); + + if ( mLightInfoBufferConst->isValid() && + mLightParamsConst->isValid() ) { if ( !mLightInfoTarget ) mLightInfoTarget = NamedTexTarget::find( "diffuseLighting" ); @@ -898,17 +828,16 @@ bool TerrainCellMaterial::setupPass( const SceneRenderState *state, if ( texObject ) { - GFX->setTexture( pass.lightInfoBufferConst->getSamplerRegister(), texObject ); + GFX->setTexture( mLightInfoBufferConst->getSamplerRegister(), texObject ); const Point3I &targetSz = texObject->getSize(); const RectI &targetVp = mLightInfoTarget->getViewport(); Point4F rtParams; ScreenSpace::RenderTargetParameters(targetSz, targetVp, rtParams); - pass.consts->setSafe( pass.lightParamsConst, rtParams ); + mConsts->setSafe( mLightParamsConst, rtParams ); } } - ++mCurrPass; return true; } diff --git a/Engine/source/terrain/terrCellMaterial.h b/Engine/source/terrain/terrCellMaterial.h index 91d1f40e3..c23f19e29 100644 --- a/Engine/source/terrain/terrCellMaterial.h +++ b/Engine/source/terrain/terrCellMaterial.h @@ -40,6 +40,7 @@ #endif +class GFXTextureArray; class SceneRenderState; struct SceneData; class TerrainMaterial; @@ -58,8 +59,7 @@ protected: public: MaterialInfo() - :mat(NULL), layerId(0), detailTexConst(NULL), macroTexConst(NULL), normalTexConst(NULL), - ormTexConst(NULL), detailInfoVConst(NULL), detailInfoPConst(NULL), macroInfoVConst(NULL), macroInfoPConst(NULL) + :mat(NULL), layerId(0) { } @@ -69,95 +69,64 @@ protected: TerrainMaterial *mat; U32 layerId; - - GFXShaderConstHandle *detailTexConst; - GFXTexHandle detailTex; - - GFXShaderConstHandle *macroTexConst; - GFXTexHandle macroTex; - - GFXShaderConstHandle *normalTexConst; - GFXTexHandle normalTex; - - GFXShaderConstHandle *ormTexConst; - GFXTexHandle ormTex; - - GFXShaderConstHandle *detailInfoVConst; - GFXShaderConstHandle *detailInfoPConst; - - GFXShaderConstHandle *macroInfoVConst; - GFXShaderConstHandle *macroInfoPConst; + GFXShaderConstHandle* mBlendDepthConst; + GFXShaderConstHandle* mBlendContrastConst; }; - class Pass - { - public: + /// + GFXShader *mShader; - Pass() - : shader( NULL ), - modelViewProjConst(NULL), worldViewOnly(NULL), viewToObj(NULL), - eyePosWorldConst(NULL), eyePosConst(NULL), - objTransConst(NULL), worldToObjConst(NULL), vEyeConst(NULL), - layerSizeConst(NULL), lightParamsConst(NULL), lightInfoBufferConst(NULL), - baseTexMapConst(NULL), layerTexConst(NULL), - lightMapTexConst(NULL), - squareSize(NULL), oneOverTerrainSize(NULL), - fogDataConst(NULL), fogColorConst(NULL) - { - } + GFXShaderConstBufferRef mConsts; - ~Pass() - { - for ( U32 i=0; i < materials.size(); i++ ) - delete materials[i]; - } + GFXStateBlockRef mStateBlock; + GFXStateBlockRef mWireframeStateBlock; + GFXStateBlockRef mReflectionStateBlock; - Vector materials; + GFXShaderConstHandle *mModelViewProjConst; + GFXShaderConstHandle *mWorldViewOnlyConst; + GFXShaderConstHandle *mViewToObjConst; - /// - GFXShader *shader; + GFXShaderConstHandle *mEyePosWorldConst; + GFXShaderConstHandle *mEyePosConst; - GFXShaderConstBufferRef consts; + GFXShaderConstHandle *mObjTransConst; + GFXShaderConstHandle *mWorldToObjConst; + GFXShaderConstHandle *mVEyeConst; - GFXStateBlockRef stateBlock; - GFXStateBlockRef wireframeStateBlock; - GFXStateBlockRef reflectionStateBlock; + GFXShaderConstHandle *mLayerSizeConst; + GFXShaderConstHandle *mLightParamsConst; + GFXShaderConstHandle *mLightInfoBufferConst; - GFXShaderConstHandle *modelViewProjConst; - GFXShaderConstHandle *worldViewOnly; - GFXShaderConstHandle *viewToObj; + GFXShaderConstHandle *mBaseTexMapConst; + GFXShaderConstHandle *mLayerTexConst; - GFXShaderConstHandle *eyePosWorldConst; - GFXShaderConstHandle *eyePosConst; + GFXShaderConstHandle *mLightMapTexConst; - GFXShaderConstHandle *objTransConst; - GFXShaderConstHandle *worldToObjConst; - GFXShaderConstHandle *vEyeConst; + GFXShaderConstHandle *mSquareSizeConst; + GFXShaderConstHandle *mOneOverTerrainSizeConst; - GFXShaderConstHandle *layerSizeConst; - GFXShaderConstHandle *lightParamsConst; - GFXShaderConstHandle *lightInfoBufferConst; + GFXShaderConstHandle* mDetailInfoVArrayConst; + GFXShaderConstHandle* mDetailInfoPArrayConst; + GFXShaderConstHandle* mMacroInfoVArrayConst; + GFXShaderConstHandle* mMacroInfoPArrayConst; - GFXShaderConstHandle *baseTexMapConst; - GFXShaderConstHandle *layerTexConst; + GFXShaderConstHandle *mFogDataConst; + GFXShaderConstHandle *mFogColorConst; - GFXShaderConstHandle *lightMapTexConst; + GFXShaderConstHandle *mDetailTexArrayConst; + GFXShaderConstHandle *mMacroTexArrayConst; + GFXShaderConstHandle *mNormalTexArrayConst; + GFXShaderConstHandle *mOrmTexArrayConst; - GFXShaderConstHandle *squareSize; - GFXShaderConstHandle *oneOverTerrainSize; - - GFXShaderConstHandle *fogDataConst; - GFXShaderConstHandle *fogColorConst; - }; + GFXShaderConstHandle* mBlendDepthConst; TerrainBlock *mTerrain; - U64 mMaterials; - - Vector mPasses; - U32 mCurrPass; + U64 mMaterials; + Vector mMaterialInfos; + static const Vector mSamplerNames; GFXTexHandle mBaseMapTexture; @@ -175,14 +144,11 @@ protected: /// A vector of all terrain cell materials loaded in the system. static Vector smAllMaterials; - bool _createPass( Vector *materials, - Pass *pass, - bool firstPass, - bool deferredMat, + bool _initShader( bool deferredMat, bool reflectMat, bool baseOnly ); - void _updateMaterialConsts( Pass *pass ); + void _updateMaterialConsts(); public: diff --git a/Engine/source/terrain/terrData.cpp b/Engine/source/terrain/terrData.cpp index dc7f44e76..90fd7c2f2 100644 --- a/Engine/source/terrain/terrData.cpp +++ b/Engine/source/terrain/terrData.cpp @@ -47,11 +47,13 @@ #include "materials/baseMatInstance.h" #include "gfx/gfxTextureManager.h" #include "gfx/gfxCardProfile.h" +#include "gfx/gfxAPI.h" #include "core/resourceManager.h" #include "T3D/physics/physicsPlugin.h" #include "T3D/physics/physicsBody.h" #include "T3D/physics/physicsCollision.h" #include "console/engineAPI.h" +#include "core/util/safeRelease.h" #include "T3D/assets/TerrainMaterialAsset.h" using namespace Torque; @@ -203,7 +205,11 @@ TerrainBlock::TerrainBlock() mScreenError( 16 ), mCastShadows( true ), mZoningDirty( false ), - mUpdateBasetex ( true ) + mUpdateBasetex ( true ), + mDetailTextureArray( NULL ), + mMacroTextureArray( NULL ), + mOrmTextureArray( NULL ), + mNormalTextureArray( NULL ) { mTypeMask = TerrainObjectType | StaticObjectType | StaticShapeObjectType; mNetFlags.set(Ghostable | ScopeAlways); @@ -231,6 +237,11 @@ TerrainBlock::~TerrainBlock() editor->detachTerrain(this); #endif deleteZodiacPrimitiveBuffer(); + + SAFE_RELEASE(mDetailTextureArray); + SAFE_RELEASE(mMacroTextureArray); + SAFE_RELEASE(mNormalTextureArray); + SAFE_RELEASE(mOrmTextureArray); } void TerrainBlock::_onTextureEvent( GFXTexCallbackCode code ) @@ -1461,6 +1472,11 @@ DefineEngineMethod(TerrainBlock, saveAsset, bool, (), , return static_cast(object)->saveAsset(); } +DefineEngineMethod( TerrainBlock, setMaterialsDirty, void, (),, "") +{ + static_cast(object)->setMaterialsDirty(); +} + //ConsoleMethod(TerrainBlock, save, bool, 3, 3, "(string fileName) - saves the terrain block's terrain file to the specified file name.") //{ // char filename[256]; diff --git a/Engine/source/terrain/terrData.h b/Engine/source/terrain/terrData.h index 021a11dae..cfef5c29d 100644 --- a/Engine/source/terrain/terrData.h +++ b/Engine/source/terrain/terrData.h @@ -135,6 +135,11 @@ protected: /// Vector mBaseTextures; + GFXTextureArrayHandle mDetailTextureArray; + GFXTextureArrayHandle mMacroTextureArray; + GFXTextureArrayHandle mNormalTextureArray; + GFXTextureArrayHandle mOrmTextureArray; + /// GFXTexHandle mLayerTex; @@ -308,6 +313,8 @@ public: /// Deletes all the materials on the terrain. void deleteAllMaterials(); + void setMaterialsDirty() { mDetailsDirty = true; }; + //void setMaterialName( U32 index, const String &name ); /// Accessors and mutators for TerrainMaterialUndoAction. @@ -324,6 +331,11 @@ public: U32 getMaterialCount() const; + GFXTextureArrayHandle getDetailTextureArray() const { return mDetailTextureArray; } + GFXTextureArrayHandle getMacroTextureArray() const { return mMacroTextureArray; } + GFXTextureArrayHandle getNormalTextureArray() const { return mNormalTextureArray; } + GFXTextureArrayHandle getOrmTextureArray() const { return mOrmTextureArray; } + //BaseMatInstance* getMaterialInst( U32 x, U32 y ); void setHeight( const Point2I &pos, F32 height ); diff --git a/Engine/source/terrain/terrFeatureTypes.cpp b/Engine/source/terrain/terrFeatureTypes.cpp index 816414cce..a1c28a08d 100644 --- a/Engine/source/terrain/terrFeatureTypes.cpp +++ b/Engine/source/terrain/terrFeatureTypes.cpp @@ -33,7 +33,7 @@ ImplementFeatureType( MFT_TerrainNormalMap, MFG_Texture, 103.0f, false ); ImplementFeatureType( MFT_TerrainMacroMap, MFG_Texture, 104.0f, false ); ImplementFeatureType( MFT_TerrainLightMap, MFG_Texture, 105.0f, false ); ImplementFeatureType( MFT_TerrainSideProject, MFG_Texture, 106.0f, false ); -ImplementFeatureType( MFT_TerrainAdditive, MFG_PostProcess, 999.0f, false ); +ImplementFeatureType( MFT_TerrainHeightBlend, MFG_PreLighting, 200.0f, false ); //Deferred Shading ImplementFeatureType( MFT_DeferredTerrainBlankInfoMap, MFG_Texture, 104.1f, false); ImplementFeatureType( MFT_TerrainORMMap, MFG_Texture, 104.2f, false); diff --git a/Engine/source/terrain/terrFeatureTypes.h b/Engine/source/terrain/terrFeatureTypes.h index 043e052cf..825758af9 100644 --- a/Engine/source/terrain/terrFeatureTypes.h +++ b/Engine/source/terrain/terrFeatureTypes.h @@ -34,7 +34,7 @@ DeclareFeatureType( MFT_TerrainNormalMap ); DeclareFeatureType( MFT_TerrainParallaxMap ); DeclareFeatureType( MFT_TerrainLightMap ); DeclareFeatureType( MFT_TerrainSideProject ); -DeclareFeatureType( MFT_TerrainAdditive ); +DeclareFeatureType( MFT_TerrainHeightBlend ); //Deferred Shading DeclareFeatureType( MFT_TerrainORMMap ); DeclareFeatureType( MFT_DeferredTerrainBlankInfoMap ); diff --git a/Engine/source/terrain/terrMaterial.cpp b/Engine/source/terrain/terrMaterial.cpp index 74a0c51e0..47f0b3190 100644 --- a/Engine/source/terrain/terrMaterial.cpp +++ b/Engine/source/terrain/terrMaterial.cpp @@ -68,6 +68,8 @@ TerrainMaterial::TerrainMaterial() mMacroStrength( 0.7f ), mMacroDistance( 500.0f ), mParallaxScale( 0.0f ), + mBlendDepth( 0.0f ), + mBlendContrast( 1.0f ), mIsSRGB(false), mInvertRoughness(false) { @@ -91,6 +93,12 @@ 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("blendHeightBase", TypeF32, Offset(mBlendDepth, TerrainMaterial), "A fixed value to add while blending using heightmap-based blending." + "Higher numbers = larger blend radius."); + + addField("blendHeightContrast", TypeF32, Offset(mBlendContrast, TerrainMaterial), "A fixed value to add while blending using heightmap-based blending." + "Higher numbers = larger blend radius."); + scriptBindMapSlot(DetailMap, TerrainMaterial, "Raises and lowers the RGB result of the Base Albedo up close."); addField( "detailSize", TypeF32, Offset( mDetailSize, TerrainMaterial ), "Used to scale the detail map to the material square" ); addField( "detailStrength", TypeF32, Offset( mDetailStrength, TerrainMaterial ), "Exponentially sharpens or lightens the detail map rendering on the material" ); diff --git a/Engine/source/terrain/terrMaterial.h b/Engine/source/terrain/terrMaterial.h index cc9b7d8b1..7596714ba 100644 --- a/Engine/source/terrain/terrMaterial.h +++ b/Engine/source/terrain/terrMaterial.h @@ -85,6 +85,13 @@ protected: /// F32 mParallaxScale; + /// Depth for blending the textures using the new + /// blending method. Higher numbers = larger blend + /// radius. + F32 mBlendDepth; + + F32 mBlendContrast; + public: TerrainMaterial(); @@ -122,6 +129,10 @@ public: F32 getParallaxScale() const { return mParallaxScale; } + F32 getBlendDepth() const { return mBlendDepth; } + + F32 getBlendContrast() const { return mBlendContrast; } + bool getIsSRGB() const { return mIsSRGB; } bool getInvertRoughness() const { return mInvertRoughness; } diff --git a/Engine/source/terrain/terrRender.cpp b/Engine/source/terrain/terrRender.cpp index d75196778..ee3856eef 100644 --- a/Engine/source/terrain/terrRender.cpp +++ b/Engine/source/terrain/terrRender.cpp @@ -96,7 +96,7 @@ void TerrainBlock::_updateMaterials() { TerrainMaterial *mat = mFile->mMaterials[i]; - if (!mat->getDiffuseMap().isEmpty()) + if (mat->getDiffuseMap().isNotEmpty()) { mBaseTextures[i].set(mat->getDiffuseMap(), &GFXStaticTextureSRGBProfile, "TerrainBlock::_updateMaterials() - DiffuseMap"); @@ -114,6 +114,133 @@ void TerrainBlock::_updateMaterials() mMaxDetailDistance = mat->getMacroDistance(); } + Vector detailTexArray; + detailTexArray.setSize(mFile->mMaterials.size()); + Vector macroTexArray; + macroTexArray.setSize(mFile->mMaterials.size()); + Vector normalTexArray; + normalTexArray.setSize(mFile->mMaterials.size()); + Vector ormTexArray; + ormTexArray.setSize(mFile->mMaterials.size()); + + for (U32 i = 0; i < mFile->mMaterials.size(); i++) + { + TerrainMaterial* mat = mFile->mMaterials[i]; + GFXTextureProfile* profile = &GFXStaticTextureProfile; + if (mat->getIsSRGB()) + profile = &GFXStaticTextureSRGBProfile; + + if (mat->getDetailMap().isNotEmpty()) + detailTexArray[i] = TEXMGR->createTexture(mat->getDetailMap(), profile); + if (mat->getMacroMap().isNotEmpty()) + macroTexArray[i] = TEXMGR->createTexture(mat->getMacroMap(), profile); + if (mat->getNormalMap().isNotEmpty()) + normalTexArray[i] = TEXMGR->createTexture(mat->getNormalMap(), profile); + if (mat->getORMConfigMap().isNotEmpty()) + ormTexArray[i] = TEXMGR->createTexture(mat->getORMConfigMap(), profile); + } + + if (mDetailTextureArray.isNull()) + { + mDetailTextureArray = GFX->createTextureArray(); + } + + if (mMacroTextureArray.isNull()) + { + mMacroTextureArray = GFX->createTextureArray(); + } + + if (mNormalTextureArray.isNull()) + { + mNormalTextureArray = GFX->createTextureArray(); + } + + if (mOrmTextureArray.isNull()) + { + mOrmTextureArray = GFX->createTextureArray(); + } + + U32 detailTexArraySize = detailTexArray.size(); + U32 macroTexArraySize = macroTexArray.size(); + U32 normalTexArraySize = normalTexArray.size(); + U32 ormTexArraySize = ormTexArray.size(); +#ifdef TORQUE_TOOLS + // For performance improvement when adding terrain layers, we always allocate at least 32 textures to the arrays in tool builds + detailTexArraySize = mMax(32, detailTexArraySize); + macroTexArraySize = mMax(32, macroTexArraySize); + normalTexArraySize = mMax(32, normalTexArraySize); + ormTexArraySize = mMax(32, ormTexArraySize); +#endif + + // Format has been explicitly set + const U32 detailTexSize = Con::getIntVariable("Terrain::DetailTextureSize"); + const GFXFormat detailTexFormat = static_cast(Con::getIntVariable("Terrain::DetailTextureFormat")); + if (detailTexSize != 0) + { + GFXFormat format = GFXFormatR8G8B8A8; + if (detailTexFormat < GFXFormat_COUNT) + { + format = detailTexFormat; + } + mDetailTextureArray->set(detailTexSize, detailTexSize, detailTexArraySize, format); + } + + const U32 macroTexSize = Con::getIntVariable("Terrain::MacroTextureSize"); + const GFXFormat macroTexFormat = static_cast(Con::getIntVariable("Terrain::MacroTextureFormat")); + if (macroTexSize != 0) + { + GFXFormat format = GFXFormatR8G8B8A8; + if (macroTexFormat < GFXFormat_COUNT) + { + format = macroTexFormat; + } + mMacroTextureArray->set(macroTexSize, macroTexSize, macroTexArraySize, format); + } + + const U32 normalTexSize = Con::getIntVariable("Terrain::NormalTextureSize"); + const GFXFormat normalTexFormat = static_cast(Con::getIntVariable("Terrain::NormalTextureFormat")); + if (normalTexSize != 0) + { + GFXFormat format = GFXFormatR8G8B8A8; + if (normalTexFormat < GFXFormat_COUNT) + { + format = normalTexFormat; + } + mNormalTextureArray->set(normalTexSize, normalTexSize, normalTexArraySize, format); + } + + const U32 ormTexSize = Con::getIntVariable("Terrain::OrmTextureSize"); + const GFXFormat ormTexFormat = static_cast(Con::getIntVariable("Terrain::OrmTextureFormat")); + if (ormTexSize != 0) + { + GFXFormat format = GFXFormatR8G8B8A8; + if (ormTexFormat < GFXFormat_COUNT) + { + format = ormTexFormat; + } + mOrmTextureArray->set(ormTexSize, ormTexSize, ormTexArraySize, format); + } + + if (!mDetailTextureArray->fromTextureArray(detailTexArray, detailTexArraySize)) + { + Con::errorf("TerrainBlock::_updateMaterials - an issue with the diffuse terrain materials was detected. Please ensure they are all of the same size and format!"); + } + + if (!mMacroTextureArray->fromTextureArray(macroTexArray, macroTexArraySize)) + { + Con::errorf("TerrainBlock::_updateMaterials - an issue with the detail terrain materials was detected. Please ensure they are all of the same size and format!"); + } + + if (!mNormalTextureArray->fromTextureArray(normalTexArray, normalTexArraySize)) + { + Con::errorf("TerrainBlock::_updateMaterials - an issue with the normal terrain materials was detected. Please ensure they are all of the same size and format!"); + } + + if (!mOrmTextureArray->fromTextureArray(ormTexArray, ormTexArraySize)) + { + Con::errorf("TerrainBlock::_updateMaterials - an issue with the orm terrain materials was detected. Please ensure they are all of the same size and format!"); + } + if ( mCell ) mCell->deleteMaterials(); } diff --git a/Templates/BaseGame/game/core/rendering/Core_Rendering.cs b/Templates/BaseGame/game/core/rendering/Core_Rendering.cs index 653482d43..805331463 100644 --- a/Templates/BaseGame/game/core/rendering/Core_Rendering.cs +++ b/Templates/BaseGame/game/core/rendering/Core_Rendering.cs @@ -11,7 +11,22 @@ function Core_Rendering::onCreate(%this) $pref::ReflectionProbes::BakeResolution = ProjectSettings.value("Rendering/ProbeCaptureResolution", "64"); + $Terrain::LerpBlend = ProjectSettings.value("Terrain/LerpBlend"); + $Terrain::BlendDepth = ProjectSettings.value("Terrain/BlendDepth"); + + $Terrain::DetailTextureSize = ProjectSettings.value("Terrain/DetailTextureSize"); + $Terrain::MacroTextureSize = ProjectSettings.value("Terrain/MacroTextureSize"); + $Terrain::NormalTextureSize = ProjectSettings.value("Terrain/NormalTextureSize"); + $Terrain::OrmTextureSize = ProjectSettings.value("Terrain/OrmTextureSize"); + + // Default to R8G8B8A8 for all textures + $Terrain::DetailTextureFormat = ProjectSettings.value("Terrain/DetailTextureFormat", 12); + $Terrain::MacroTextureFormat = ProjectSettings.value("Terrain/MacroTextureFormat", 12); + $Terrain::NormalTextureFormat = ProjectSettings.value("Terrain/NormalTextureFormat", 12); + $Terrain::OrmTextureFormat = ProjectSettings.value("Terrain/OrmTextureFormat", 12); + exec("./scripts/graphicsOptions.cs"); + exec("./scripts/terrainSettings.cs"); exec("./scripts/renderManager.cs"); exec("./scripts/gfxData/clouds.cs"); exec("./scripts/gfxData/commonMaterialData.cs"); @@ -20,6 +35,8 @@ function Core_Rendering::onCreate(%this) exec("./scripts/gfxData/terrainBlock.cs"); exec("./scripts/gfxData/water.cs"); exec("./scripts/gfxData/warningTerrainMat.cs"); + + loadTerrainSettings(); } function Core_Rendering::onDestroy(%this) diff --git a/Templates/BaseGame/game/core/rendering/shaders/gl/torque.glsl b/Templates/BaseGame/game/core/rendering/shaders/gl/torque.glsl index 75d79bd66..5482bdaf3 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/gl/torque.glsl +++ b/Templates/BaseGame/game/core/rendering/shaders/gl/torque.glsl @@ -166,6 +166,35 @@ vec2 parallaxOffsetDxtnm(sampler2D texMap, vec2 texCoord, vec3 negViewTS, float return offset; } +/// Same as the above but for arrays +vec2 parallaxOffset( sampler2DArray texMap, vec3 texCoord, vec3 negViewTS, float depthScale ) +{ + float depth = texture( texMap, texCoord ).a/(PARALLAX_REFINE_STEPS*2); + vec2 offset = negViewTS.xy * vec2( depth * depthScale )/vec2(PARALLAX_REFINE_STEPS*2); + + for ( int i=0; i < PARALLAX_REFINE_STEPS; i++ ) + { + depth = ( depth + texture( texMap, texCoord + vec3(offset, 0.0) ).a )/(PARALLAX_REFINE_STEPS*2); + offset = negViewTS.xy * vec2( depth * depthScale )/vec2(PARALLAX_REFINE_STEPS*2); + } + + return offset; +} + +vec2 parallaxOffsetDxtnm(sampler2DArray texMap, vec3 texCoord, vec3 negViewTS, float depthScale) +{ + float depth = texture(texMap, texCoord).r/(PARALLAX_REFINE_STEPS*2); + vec2 offset = negViewTS.xy * vec2(depth * depthScale)/vec2(PARALLAX_REFINE_STEPS*2); + + for (int i = 0; i < PARALLAX_REFINE_STEPS; i++) + { + depth = (depth + texture(texMap, texCoord + vec3(offset, 0.0)).r)/(PARALLAX_REFINE_STEPS*2); + offset = negViewTS.xy * vec2(depth * depthScale)/vec2(PARALLAX_REFINE_STEPS*2); + } + + return offset; +} + /// The maximum value for 10bit per component integer HDR encoding. const float HDR_RGB10_MAX = 4.0; diff --git a/Templates/BaseGame/game/core/rendering/shaders/shaderModel.hlsl b/Templates/BaseGame/game/core/rendering/shaders/shaderModel.hlsl index d3fcd946a..d34869c3b 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/shaderModel.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/shaderModel.hlsl @@ -60,6 +60,7 @@ //helper if you want to pass sampler/texture in a function //2D #define TORQUE_SAMPLER2D(tex) Texture2D texture_##tex, SamplerState tex +#define TORQUE_SAMPLER2DARRAY(tex) Texture2DArray texture_##tex, SamplerState tex #define TORQUE_SAMPLER2D_MAKEARG(tex) texture_##tex, tex // Sampler comparison state - use above MAKEARG with this #define TORQUE_SAMPLER2DCMP(tex) Texture2D texture_##tex, SamplerComparisonState tex diff --git a/Templates/BaseGame/game/core/rendering/shaders/torque.hlsl b/Templates/BaseGame/game/core/rendering/shaders/torque.hlsl index 27a04a093..6365d547a 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/torque.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/torque.hlsl @@ -167,6 +167,35 @@ float2 parallaxOffsetDxtnm(TORQUE_SAMPLER2D(texMap), float2 texCoord, float3 neg return offset; } +/// Copy of the above to functions, but for arrays +float2 parallaxOffsetTexArray(TORQUE_SAMPLER2DARRAY(texMap), float3 texCoord, float3 negViewTS, float depthScale) +{ + float depth = TORQUE_TEX2D(texMap, texCoord).a/(PARALLAX_REFINE_STEPS*2); + float2 offset = negViewTS.xy * (depth * depthScale)/(PARALLAX_REFINE_STEPS); + + for (int i = 0; i < PARALLAX_REFINE_STEPS; i++) + { + depth = (depth + TORQUE_TEX2D(texMap, texCoord + float3(offset, 0.0)).a)/(PARALLAX_REFINE_STEPS*2); + offset = negViewTS.xy * (depth * depthScale)/(PARALLAX_REFINE_STEPS); + } + + return offset; +} + +float2 parallaxOffsetDxtnmTexArray(TORQUE_SAMPLER2DARRAY(texMap), float3 texCoord, float3 negViewTS, float depthScale) +{ + float depth = TORQUE_TEX2D(texMap, texCoord).r/(PARALLAX_REFINE_STEPS*2); + float2 offset = negViewTS.xy * (depth * depthScale)/(PARALLAX_REFINE_STEPS*2); + + for (int i = 0; i < PARALLAX_REFINE_STEPS; i++) + { + depth = (depth + TORQUE_TEX2D(texMap, texCoord + float3(offset, 0.0)).r)/(PARALLAX_REFINE_STEPS*2); + offset = negViewTS.xy * (depth * depthScale)/(PARALLAX_REFINE_STEPS*2); + } + + return offset; +} + /// The maximum value for 10bit per component integer HDR encoding. static const float HDR_RGB10_MAX = 4.0; diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/TerrainPainterToolbar.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/TerrainPainterToolbar.ed.gui index 8cdba481d..ca4126330 100644 --- a/Templates/BaseGame/game/tools/worldEditor/gui/TerrainPainterToolbar.ed.gui +++ b/Templates/BaseGame/game/tools/worldEditor/gui/TerrainPainterToolbar.ed.gui @@ -210,7 +210,7 @@ new GuiBitmapCtrl() { Enabled = "1"; Profile = "ToolsGuiDefaultProfile"; - position = "270 3"; + position = "230 3"; Extent = "2 26"; MinExtent = "1 1"; bitmap = "tools/gui/images/separator-h.png"; @@ -222,7 +222,7 @@ Profile = "ToolsGuiDefaultProfile"; HorizSizing = "right"; VertSizing = "bottom"; - Position = "262 5"; + Position = "222 5"; Extent = "256 50"; MinExtent = "8 2"; canSave = "1"; @@ -370,7 +370,7 @@ new GuiBitmapCtrl() { Enabled = "1"; Profile = "ToolsGuiDefaultProfile"; - position = "525 3"; + position = "445 3"; Extent = "2 26"; MinExtent = "1 1"; bitmap = "tools/gui/images/separator-h.png"; @@ -382,7 +382,7 @@ Profile = "ToolsGuiTransparentProfile"; HorizSizing = "right"; VertSizing = "bottom"; - position = "540 5"; + position = "480 5"; Extent = "120 50"; MinExtent = "8 2"; canSave = "1"; @@ -454,6 +454,43 @@ bitmap = "tools/gui/images/dropslider"; }; }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "618 3"; + Extent = "2 26"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-h.png"; + }; + + new GuiControl(TerrainTextureSettingsButtonContainer,EditorGuiGroup) { + position = "628 5"; + extent = "90 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTransparentProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiButtonCtrl() { + text = "Texture Settings"; + buttonType = "pushButton"; + profile = "ToolsGuiButtonProfile"; + command = "TerrainTextureSettingsDlg.show();"; + tooltipProfile = "ToolsGuiToolTipProfile"; + position = "0 0"; + extent = "90 18"; + horizSizing = "right"; + vertSizing = "bottom"; + }; + }; }; }; //--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/guiTerrainMaterialDlg.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/guiTerrainMaterialDlg.ed.gui index c50cb370b..0d4339eae 100644 --- a/Templates/BaseGame/game/tools/worldEditor/gui/guiTerrainMaterialDlg.ed.gui +++ b/Templates/BaseGame/game/tools/worldEditor/gui/guiTerrainMaterialDlg.ed.gui @@ -32,9 +32,9 @@ anchorBottom = "0"; anchorLeft = "0"; anchorRight = "0"; - position = "315 168"; - extent = "394 494"; - minExtent = "358 432"; + position = "315 127"; + extent = "394 514"; + minExtent = "358 452"; horizSizing = "center"; vertSizing = "center"; profile = "ToolsGuiWindowProfile"; @@ -149,7 +149,7 @@ anchorLeft = "1"; anchorRight = "0"; position = "202 26"; - extent = "185 425"; + extent = "185 445"; minExtent = "8 2"; horizSizing = "left"; vertSizing = "height"; @@ -511,7 +511,7 @@ anchorLeft = "1"; anchorRight = "0"; position = "6 122"; - extent = "185 50"; + extent = "185 100"; minExtent = "8 2"; horizSizing = "width"; vertSizing = "bottom"; @@ -712,12 +712,162 @@ canSave = "1"; canSaveDynamicFields = "0"; }; + new GuiSliderCtrl(TerrainMaterialDlgBlendHeightBaseSlider) { + range = "-0.5 0.5"; + ticks = "0"; + snap = "0"; + value = "0.5"; + useFillBar = "0"; + fillBarColor = "255 255 255 255"; + renderTicks = "1"; + position = "39 61"; + extent = "70 14"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiSliderProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "blendHeightBaseSliderCtrl"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Blend Height"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "115 61"; + extent = "58 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl(TerrainMaterialDlgBlendHeightBaseTextEdit) { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + text = "0.3"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "0"; + anchorBottom = "0"; + anchorLeft = "0"; + anchorRight = "0"; + position = "1 59"; + extent = "35 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextEditProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "blendHeightBaseTextEditCtrl"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(TerrainMaterialDlgBlendHeightContrastSlider) { + range = "0.0 5.0"; + ticks = "0"; + snap = "0"; + value = "1.0"; + useFillBar = "0"; + fillBarColor = "255 255 255 255"; + renderTicks = "1"; + position = "39 81"; + extent = "70 14"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiSliderProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "blendHeightContrastSliderCtrl"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Blend Contrast"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "115 81"; + extent = "58 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl(TerrainMaterialDlgBlendHeightContrastTextEdit) { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + text = "0.3"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "0"; + anchorBottom = "0"; + anchorLeft = "0"; + anchorRight = "0"; + position = "1 79"; + extent = "35 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextEditProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "blendHeightContrastTextEditCtrl"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; }; new GuiBitmapCtrl() { bitmap = "tools/gui/images/separator-v"; color = "255 255 255 255"; wrap = "0"; - position = "6 177"; + position = "6 222"; extent = "175 2"; minExtent = "8 2"; horizSizing = "width"; @@ -738,7 +888,7 @@ anchorBottom = "0"; anchorLeft = "1"; anchorRight = "0"; - position = "6 184"; + position = "6 229"; extent = "185 64"; minExtent = "8 2"; horizSizing = "width"; @@ -781,7 +931,7 @@ anchorLeft = "1"; anchorRight = "0"; position = "56 -3"; - extent = "60 18"; + extent = "64 18"; minExtent = "8 2"; horizSizing = "right"; vertSizing = "bottom"; @@ -933,7 +1083,7 @@ bitmap = "tools/gui/images/separator-v"; color = "255 255 255 255"; wrap = "0"; - position = "6 254"; + position = "6 299"; extent = "175 2"; minExtent = "8 2"; horizSizing = "width"; @@ -954,7 +1104,7 @@ anchorBottom = "0"; anchorLeft = "1"; anchorRight = "0"; - position = "6 261"; + position = "6 306"; extent = "185 72"; minExtent = "8 2"; horizSizing = "width"; @@ -1438,7 +1588,7 @@ bitmap = "tools/gui/images/separator-v"; color = "255 255 255 255"; wrap = "0"; - position = "6 336"; + position = "6 381"; extent = "175 2"; minExtent = "8 2"; horizSizing = "width"; @@ -1459,7 +1609,7 @@ anchorBottom = "0"; anchorLeft = "1"; anchorRight = "0"; - position = "6 343"; + position = "6 388"; extent = "185 72"; minExtent = "8 2"; horizSizing = "width"; @@ -1766,7 +1916,7 @@ }; new GuiControl() { position = "6 42"; - extent = "189 435"; + extent = "189 455"; minExtent = "8 2"; horizSizing = "width"; vertSizing = "height"; @@ -1831,7 +1981,7 @@ canRenameObjects = "1"; renameInternal = "0"; position = "1 1"; - extent = "8 2"; + extent = "136 798"; minExtent = "8 2"; horizSizing = "right"; vertSizing = "bottom"; @@ -1853,7 +2003,7 @@ groupNum = "-1"; buttonType = "PushButton"; useMouseEvents = "0"; - position = "202 456"; + position = "202 476"; extent = "98 22"; minExtent = "8 2"; horizSizing = "left"; @@ -1873,7 +2023,7 @@ groupNum = "-1"; buttonType = "PushButton"; useMouseEvents = "0"; - position = "307 456"; + position = "307 476"; extent = "80 22"; minExtent = "8 2"; horizSizing = "left"; @@ -1893,7 +2043,7 @@ color = "255 255 255 255"; wrap = "0"; position = "199 23"; - extent = "190 329"; + extent = "190 349"; minExtent = "8 2"; horizSizing = "left"; vertSizing = "height"; diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/guiTerrainTextureSettingsDlg.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/guiTerrainTextureSettingsDlg.ed.gui new file mode 100644 index 000000000..47c3549a2 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/guiTerrainTextureSettingsDlg.ed.gui @@ -0,0 +1,309 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(TerrainTextureSettingsDlg, EditorGuiGroup) { + position = "0 0"; + extent = "1024 768"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new GuiWindowCtrl() { + canSaveDynamicFields = "0"; + internalName = "TerrainTextureSettings"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + position = "342 184"; + extent = "340 400"; + minExtent = "340 400"; + horizSizing = "center"; + vertSizing = "center"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "1"; + AnchorLeft = "1"; + AnchorRight = "1"; + resizeWidth = "1"; + resizeHeight = "0"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "4 4"; + closeCommand = "TerrainTextureSettingsDlg.cancel();"; + EdgeSnap = "0"; + text = "Global Terrain Texture Settings"; + + new GuiCheckBoxCtrl() { + internalName = "lerpBlendCheckBox"; + text = "LerpBlend"; + profile = "ToolsGuiCheckBoxProfile"; + tooltipProfile = "ToolsGuiToolTipProfile"; + tooltip = "If enabled, terrain textures will use a simple linear interpolation blending method."; + + command = "TerrainTextureSettingsDlg.apply();"; + + position = "20 40"; + extent = "300 20"; + }; + + new GuiControl() { + position = "20 70"; + profile = "ToolsGuiDefaultProfile"; + extent = "300 20"; + + new GuiTextCtrl() { + text = "Global Blend Depth:"; + profile = "ToolsGuiTextProfile"; + tooltipProfile = "ToolsGuiToolTipProfile"; + tooltip = "Controls the general level of bleding across all textures, has no effect if Lerp Blend is enabled."; + + position = "0 0"; + extent = "120 20"; + }; + + new GuiSliderCtrl() { + internalName = "blendDepthSlider"; + profile = "ToolsGuiSliderProfile"; + + command = "TerrainTextureSettingsDlg.apply();"; + altCommand = "TerrainTextureSettingsDlg.updateBlendDepth();"; + + position = "130 0"; + extent = "170 20"; + + range = "0.01 1.0"; + }; + }; + + new GuiControl() { + position = "20 100"; + profile = "ToolsGuiDefaultProfile"; + extent = "300 20"; + + new GuiTextCtrl() { + text = "Detail Texture Size:"; + profile = "ToolsGuiTextProfile"; + + command = "TerrainTextureSettingsDlg.apply();"; + + position = "0 0"; + extent = "120 20"; + }; + + new GuiTextEditCtrl() { + internalName = "detailTextureSizeTextEdit"; + profile = "ToolsGuiTextEditProfile"; + + command = "TerrainTextureSettingsDlg.apply();"; + + position = "130 0"; + extent = "170 20"; + }; + }; + + new GuiControl() { + position = "20 130"; + profile = "ToolsGuiDefaultProfile"; + extent = "300 20"; + + new GuiTextCtrl() { + text = "Detail Texture Format:"; + profile = "ToolsGuiTextProfile"; + + position = "0 0"; + extent = "120 20"; + }; + + new GuiPopUpMenuCtrl() { + internalName = "detailTextureFormatPopUpMenu"; + profile = "ToolsGuiPopUpMenuProfile"; + + command = "TerrainTextureSettingsDlg.apply();"; + + position = "130 0"; + extent = "170 20"; + }; + }; + + new GuiControl() { + position = "20 160"; + profile = "ToolsGuiDefaultProfile"; + extent = "300 20"; + + new GuiTextCtrl() { + text = "Macro Texture Size:"; + profile = "ToolsGuiTextProfile"; + + position = "0 0"; + extent = "120 20"; + }; + + new GuiTextEditCtrl() { + internalName = "macroTextureSizeTextEdit"; + profile = "ToolsGuiTextEditProfile"; + + command = "TerrainTextureSettingsDlg.apply();"; + + position = "130 0"; + extent = "170 20"; + }; + }; + + new GuiControl() { + position = "20 190"; + profile = "ToolsGuiDefaultProfile"; + extent = "300 20"; + + new GuiTextCtrl() { + text = "Macro Texture Format:"; + profile = "ToolsGuiTextProfile"; + + position = "0 0"; + extent = "120 20"; + }; + + new GuiPopUpMenuCtrl() { + internalName = "macroTextureFormatPopUpMenu"; + profile = "ToolsGuiPopUpMenuProfile"; + + command = "TerrainTextureSettingsDlg.apply();"; + + position = "130 0"; + extent = "170 20"; + }; + }; + + new GuiControl() { + position = "20 220"; + profile = "ToolsGuiDefaultProfile"; + extent = "300 20"; + + new GuiTextCtrl() { + text = "Normal Texture Size:"; + profile = "ToolsGuiTextProfile"; + + position = "0 0"; + extent = "120 20"; + }; + + new GuiTextEditCtrl() { + internalName = "normalTextureSizeTextEdit"; + profile = "ToolsGuiTextEditProfile"; + + command = "TerrainTextureSettingsDlg.apply();"; + + position = "130 0"; + extent = "170 20"; + }; + }; + + new GuiControl() { + position = "20 250"; + profile = "ToolsGuiDefaultProfile"; + extent = "300 20"; + + new GuiTextCtrl() { + text = "Normal Texture Format:"; + profile = "ToolsGuiTextProfile"; + + position = "0 0"; + extent = "120 20"; + }; + + new GuiPopUpMenuCtrl() { + internalName = "normalTextureFormatPopUpMenu"; + profile = "ToolsGuiPopUpMenuProfile"; + + command = "TerrainTextureSettingsDlg.apply();"; + + position = "130 0"; + extent = "170 20"; + }; + }; + + new GuiControl() { + position = "20 280"; + profile = "ToolsGuiDefaultProfile"; + extent = "300 20"; + + new GuiTextCtrl() { + text = "ORM Texture Size:"; + profile = "ToolsGuiTextProfile"; + + position = "0 0"; + extent = "120 20"; + }; + + new GuiTextEditCtrl() { + internalName = "ormTextureSizeTextEdit"; + profile = "ToolsGuiTextEditProfile"; + + position = "130 0"; + extent = "170 20"; + }; + }; + + new GuiControl() { + position = "20 310"; + profile = "ToolsGuiDefaultProfile"; + extent = "300 20"; + + new GuiTextCtrl() { + text = "ORM Texture Format:"; + profile = "ToolsGuiTextProfile"; + + position = "0 0"; + extent = "120 20"; + }; + + new GuiPopUpMenuCtrl() { + internalName = "ormTextureFormatPopUpMenu"; + profile = "ToolsGuiPopUpMenuProfile"; + + position = "130 0"; + extent = "170 20"; + }; + }; + + new GuiControl() { + position = "20 350"; + profile = "ToolsGuiDefaultProfile"; + extent = "300 30"; + + new GuiButtonCtrl() { + text = "Apply & Save"; + profile = "ToolsGuiButtonProfile"; + position = "0 0"; + + command = "TerrainTextureSettingsDlg.applyAndSave();"; + + extent = "145 30"; + }; + + new GuiButtonCtrl() { + text = "Cancel"; + profile = "ToolsGuiButtonProfile"; + position = "155 0"; + + command = "TerrainTextureSettingsDlg.cancel();"; + + extent = "145 30"; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/EditorGui.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/EditorGui.ed.cs index b39839e8d..dd1693405 100644 --- a/Templates/BaseGame/game/tools/worldEditor/scripts/EditorGui.ed.cs +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/EditorGui.ed.cs @@ -119,7 +119,8 @@ function EditorGui::init(%this) } exec("~/worldEditor/gui/guiTerrainMaterialDlg.ed.gui"); - exec("~/worldEditor/gui/TerrainBrushSoftnessCurveDlg.ed.gui"); + exec("~/worldEditor/gui/TerrainBrushSoftnessCurveDlg.ed.gui"); + exec("~/worldEditor/gui/guiTerrainTextureSettingsDlg.ed.gui"); } if ( !isObject( %this-->TerrainPainterToolbar) ) { diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/interfaces/terrainMaterialDlg.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/interfaces/terrainMaterialDlg.ed.cs index ff6209946..ca6a04d91 100644 --- a/Templates/BaseGame/game/tools/worldEditor/scripts/interfaces/terrainMaterialDlg.ed.cs +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/interfaces/terrainMaterialDlg.ed.cs @@ -443,6 +443,14 @@ function TerrainMaterialDlg::setActiveMaterial( %this, %mat ) %this-->detDistanceCtrl.setText( %mat.detailDistance ); %this-->sideProjectionCtrl.setValue( %mat.useSideProjection ); %this-->parallaxScaleCtrl.setText( %mat.parallaxScale ); + + %blendHeightBase = mFloor(%mat.blendHeightBase * 1000)/1000; + %this-->blendHeightBaseTextEditCtrl.setText( %blendHeightBase ); + %this-->blendHeightBaseSliderCtrl.setValue( %mat.blendHeightBase ); + + %blendHeightContrast = mFloor(%mat.blendHeightContrast * 1000)/1000; + %this-->blendHeightContrastTextEditCtrl.setText( %blendHeightContrast ); + %this-->blendHeightContrastSliderCtrl.setValue( %mat.blendHeightContrast ); %this-->macroSizeCtrl.setText( %mat.macroSize ); %this-->macroStrengthCtrl.setText( %mat.macroStrength ); @@ -504,6 +512,8 @@ function TerrainMaterialDlg::saveDirtyMaterial( %this, %mat ) %detailDistance = %this-->detDistanceCtrl.getText(); %useSideProjection = %this-->sideProjectionCtrl.getValue(); %parallaxScale = %this-->parallaxScaleCtrl.getText(); + %blendHeightBase = %this-->blendHeightBaseTextEditCtrl.getText(); + %blendHeightContrast = %this-->blendHeightContrastTextEditCtrl.getText(); %macroSize = %this-->macroSizeCtrl.getText(); %macroStrength = %this-->macroStrengthCtrl.getText(); @@ -529,11 +539,13 @@ function TerrainMaterialDlg::saveDirtyMaterial( %this, %mat ) %mat.macroSize == %macroSize && %mat.macroStrength == %macroStrength && %mat.macroDistance == %macroDistance && - %mat.parallaxScale == %parallaxScale && + %mat.parallaxScale == %parallaxScale && + %mat.blendHeightBase == %blendHeightBase && + %mat.blendHeightContrast == %blendHeightContrast && %mat.isSRGB == %isSRGB && - %mat.invertRoughness == %invertRoughness) + %mat.invertRoughness == %invertRoughness && false) return; - + // Make sure the material name is unique. if( %mat.internalName !$= %newName ) @@ -567,6 +579,8 @@ function TerrainMaterialDlg::saveDirtyMaterial( %this, %mat ) %mat.macroDistance = %macroDistance; %mat.useSideProjection = %useSideProjection; %mat.parallaxScale = %parallaxScale; + %mat.blendHeightBase = %blendHeightBase; + %mat.blendHeightContrast = %blendHeightContrast; %mat.isSRGB = %isSRGB; %mat.invertRoughness = %invertRoughness; @@ -619,6 +633,8 @@ function TerrainMaterialDlg::snapshotMaterials( %this ) macroDistance = %mat.macroDistance; useSideProjection = %mat.useSideProjection; parallaxScale = %mat.parallaxScale; + blendHeightBase = %mat.blendHeightBase; + blendHeightContrast = %mat.blendHeightContrast; isSRGB = %mat.isSRGB; invertRoughness = %mat.invertRoughness; }; @@ -656,6 +672,8 @@ function TerrainMaterialDlg::restoreMaterials( %this ) %mat.macroDistance = %obj.macroDistance; %mat.useSideProjection = %obj.useSideProjection; %mat.parallaxScale = %obj.parallaxScale; + %mat.blendHeightBase = %obj.blendHeightBase; + %mat.blendHeightContrast = %obj.blendHeightContrast; %mat.isSRGB = %obj.isSRGB; %mat.invertRoughness = %obj.invertRoughness; } @@ -693,3 +711,31 @@ function TerrainMaterialDlg::_selectTextureFileDialog( %this, %defaultFileName ) return %file; } + +function TerrainMaterialDlgBlendHeightBaseSlider::onMouseDragged(%this) +{ + %value = mFloor(%this.value * 1000)/1000; + TerrainMaterialDlgBlendHeightBaseTextEdit.setText(%value); + TerrainMaterialDlg.activeMat.blendHeightBase = %this.value; + +} + +function TerrainMaterialDlgBlendHeightBaseTextEdit::onValidate(%this) +{ + TerrainMaterialDlgBlendHeightBaseSlider.setValue(%this.getText()); + TerrainMaterialDlg.activeMat.blendHeightBase = %this.getText(); +} + +function TerrainMaterialDlgBlendHeightContrastSlider::onMouseDragged(%this) +{ + %value = mFloor(%this.value * 1000)/1000; + TerrainMaterialDlgBlendHeightContrastTextEdit.setText(%value); + TerrainMaterialDlg.activeMat.blendHeightContrast = %this.value; + +} + +function TerrainMaterialDlgBlendHeightContrastTextEdit::onValidate(%this) +{ + TerrainMaterialDlgBlendHeightContrastSlider.setValue(%this.getText()); + TerrainMaterialDlg.activeMat.blendHeightContrast = %this.getText(); +} diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/interfaces/terrainTextureSettingsDlg.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/interfaces/terrainTextureSettingsDlg.ed.cs new file mode 100644 index 000000000..9d9478060 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/interfaces/terrainTextureSettingsDlg.ed.cs @@ -0,0 +1,158 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- + +$TerrainTextureSettingsDlg::TerrainTextureFormat = + "R8G8B8 10" TAB + "R8G8B8_SRGB 11" TAB + "R8G8B8A8 12" TAB + "R8G8B8A8_SRGB 15" TAB + "BC5 33"; + +//----------------------------------------------------------------------------- + +function TerrainTextureSettingsDlg::show( %this ) +{ + Canvas.pushDialog( %this ); +} + +function TerrainTextureSettingsDlg::onWake( %this ) { + %this-->lerpBlendCheckBox.setStateOn(ProjectSettings.value("Terrain/LerpBlend")); + %this-->blendDepthSlider.setValue(ProjectSettings.value("Terrain/BlendDepth")); + + %this-->detailTextureFormatPopUpMenu.clear(); + %this-->macroTextureFormatPopUpMenu.clear(); + %this-->normalTextureFormatPopUpMenu.clear(); + %this-->ormTextureFormatPopUpMenu.clear(); + + for(%i = 0; %i < getFieldCount($TerrainTextureSettingsDlg::TerrainTextureFormat); %i++) { + %field = getField($TerrainTextureSettingsDlg::TerrainTextureFormat, %i); + + %this-->detailTextureFormatPopUpMenu.add(getWord(%field, 0), getWord(%field, 1)); + %this-->macroTextureFormatPopUpMenu.add(getWord(%field, 0), getWord(%field, 1)); + %this-->normalTextureFormatPopUpMenu.add(getWord(%field, 0), getWord(%field, 1)); + %this-->ormTextureFormatPopUpMenu.add(getWord(%field, 0), getWord(%field, 1)); + } + + %this-->detailTextureFormatPopUpMenu.setSelected(ProjectSettings.value("Terrain/DetailTextureFormat", 12), false); + %this-->macroTextureFormatPopUpMenu.setSelected(ProjectSettings.value("Terrain/MacroTextureFormat", 12), false); + %this-->normalTextureFormatPopUpMenu.setSelected(ProjectSettings.value("Terrain/NormalTextureFormat", 12), false); + %this-->ormTextureFormatPopUpMenu.setSelected(ProjectSettings.value("Terrain/OrmTextureFormat", 12), false); + + %this-->detailTextureSizeTextEdit.setText(ProjectSettings.value("Terrain/DetailTextureSize")); + %this-->macroTextureSizeTextEdit.setText(ProjectSettings.value("Terrain/MacroTextureSize")); + %this-->normalTextureSizeTextEdit.setText(ProjectSettings.value("Terrain/NormalTextureSize")); + %this-->ormTextureSizeTextEdit.setText(ProjectSettings.value("Terrain/OrmTextureSize")); +} + +function TerrainTextureSettingsDlg::updateBlendDepth( %this ) { + $Terrain::BlendDepth = %this-->blendDepthSlider.getValue(); +} + +function TerrainTextureSettingsDlg::apply( %this ) { + $Terrain::LerpBlend = %this-->lerpBlendCheckBox.isStateOn(); + $Terrain::BlendDepth = %this-->blendDepthSlider.getValue(); + + $Terrain::DetailTextureFormat = %this-->detailTextureFormatPopUpMenu.getSelected(); + $Terrain::MacroTextureFormat = %this-->macroTextureFormatPopUpMenu.getSelected(); + $Terrain::NormalTextureFormat = %this-->normalTextureFormatPopUpMenu.getSelected(); + $Terrain::OrmTextureFormat = %this-->ormTextureFormatPopUpMenu.getSelected(); + + if (%this-->detailTextureSizeTextEdit.getText() $= "" || mIsPow2(%this-->detailTextureSizeTextEdit.getText())) { + $Terrain::DetailTextureSize = %this-->detailTextureSizeTextEdit.getText(); + } + if (%this-->macroTextureSizeTextEdit.getText() $= "" || mIsPow2(%this-->macroTextureSizeTextEdit.getText())) { + $Terrain::MacroTextureSize = %this-->macroTextureSizeTextEdit.getText(); + } + if (%this-->normalTextureSizeTextEdit.getText() $= "" || mIsPow2(%this-->normalTextureSizeTextEdit.getText())) { + $Terrain::NormalTextureSize = %this-->normalTextureSizeTextEdit.getText(); + } + if (%this-->ormTextureSizeTextEdit.getText() $= "" || mIsPow2(%this-->ormTextureSizeTextEdit.getText())) { + $Terrain::OrmTextureSize = %this-->ormTextureSizeTextEdit.getText(); + } + + ETerrainEditor.getActiveTerrain().getClientObject().setMaterialsDirty(); +} + + +function TerrainTextureSettingsDlg::validate( %this ) { + if (%this-->detailTextureSizeTextEdit.getText() !$= "" && !mIsPow2(%this-->detailTextureSizeTextEdit.getText())) { + toolsMessageBoxOK("Detail Texture Error!", "Detail texture resolution must be a power of 2"); + return false; + } + if (%this-->macroTextureSizeTextEdit.getText() !$= "" && !mIsPow2(%this-->macroTextureSizeTextEdit.getText())) { + toolsMessageBoxOK("Macro Texture Error!", "Macro texture resolution must be a power of 2"); + return false; + } + if (%this-->normalTextureSizeTextEdit.getText() !$= "" && !mIsPow2(%this-->normalTextureSizeTextEdit.getText())) { + toolsMessageBoxOK("Normal Texture Error!", "Normal texture resolution must be a power of 2"); + return false; + } + if (%this-->ormTextureSizeTextEdit.getText() !$= "" && !mIsPow2(%this-->ormTextureSizeTextEdit.getText())) { + toolsMessageBoxOK("ORM Texture Error!", "ORM texture resolution must be a power of 2"); + return false; + } + + return true; +} + + +function TerrainTextureSettingsDlg::cancel( %this ) { + $Terrain::LerpBlend = ProjectSettings.value("Terrain/LerpBlend"); + $Terrain::BlendDepth = ProjectSettings.value("Terrain/BlendDepth"); + + $Terrain::DetailTextureFormat = ProjectSettings.value("Terrain/DetailTextureFormat"); + $Terrain::MacroTextureFormat = ProjectSettings.value("Terrain/MacroTextureFormat"); + $Terrain::NormalTextureFormat = ProjectSettings.value("Terrain/NormalTextureFormat"); + $Terrain::OrmTextureFormat = ProjectSettings.value("Terrain/OrmTextureFormat"); + + $Terrain::DetailTextureSize = ProjectSettings.value("Terrain/DetailTextureSize"); + $Terrain::MacroTextureSize = ProjectSettings.value("Terrain/MacroTextureSize"); + $Terrain::NormalTextureSize = ProjectSettings.value("Terrain/NormalTextureSize"); + $Terrain::OrmTextureSize = ProjectSettings.value("Terrain/OrmTextureSize"); + + ETerrainEditor.getActiveTerrain().getClientObject().setMaterialsDirty(); + Canvas.popDialog(%this); +} + +function TerrainTextureSettingsDlg::applyAndSave( %this ) { + if (!%this.validate()) { + return; + } + %this.apply(); + + ProjectSettings.setValue("Terrain/LerpBlend", $Terrain::LerpBlend); + ProjectSettings.setValue("Terrain/BlendDepth", $Terrain::BlendDepth); + + ProjectSettings.setValue("Terrain/DetailTextureFormat", $Terrain::DetailTextureFormat); + ProjectSettings.setValue("Terrain/MacroTextureFormat", $Terrain::MacroTextureFormat); + ProjectSettings.setValue("Terrain/NormalTextureFormat", $Terrain::NormalTextureFormat); + ProjectSettings.setValue("Terrain/OrmTextureFormat", $Terrain::OrmTextureFormat); + + ProjectSettings.setValue("Terrain/DetailTextureSize", $Terrain::DetailTextureSize); + ProjectSettings.setValue("Terrain/MacroTextureSize", $Terrain::MacroTextureSize); + ProjectSettings.setValue("Terrain/NormalTextureSize", $Terrain::NormalTextureSize); + ProjectSettings.setValue("Terrain/OrmTextureSize", $Terrain::OrmTextureSize); + + ProjectSettings.write(); + + Canvas.popDialog(%this); +} \ No newline at end of file