diff --git a/CMakeLists.txt b/CMakeLists.txt index d17f24b2a..e8c4efa19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,7 @@ ign_configure_project(VERSION_SUFFIX pre1) # Set project-specific options #============================================================================ +set(CMAKE_CXX_STANDARD 14) option(USE_UNOFFICAL_OGRE_VERSIONS "Accept unsupported Ogre versions in the build" OFF) #============================================================================ @@ -153,9 +154,18 @@ if (HAVE_OGRE2) list(APPEND RENDERING_COMPONENTS ogre2) endif() +configure_file("${PROJECT_SOURCE_DIR}/cppcheck.suppress.in" + ${PROJECT_BINARY_DIR}/cppcheck.suppress) + ign_configure_build(QUIT_IF_BUILD_ERRORS COMPONENTS ${RENDERING_COMPONENTS}) +if (HAVE_OGRE2) + # Must be done after ign_configure_build or else Terra + # won't see IGN_ADD_fPIC_TO_LIBRARIES + add_subdirectory(ogre2/src/terrain/Terra) +endif() + #============================================================================ # Create package information #============================================================================ diff --git a/cppcheck.suppress.in b/cppcheck.suppress.in new file mode 100644 index 000000000..f1e4878fb --- /dev/null +++ b/cppcheck.suppress.in @@ -0,0 +1 @@ +*:*/Terra/*.h diff --git a/examples/heightmap/Main.cc b/examples/heightmap/Main.cc index 679daf9c2..67e50692d 100644 --- a/examples/heightmap/Main.cc +++ b/examples/heightmap/Main.cc @@ -170,8 +170,8 @@ void buildScene(ScenePtr _scene) CameraPtr camera = _scene->CreateCamera("camera"); camera->SetLocalPosition(1.441, 25.787, 17.801); camera->SetLocalRotation(0.0, 0.588, -1.125); - camera->SetImageWidth(800); - camera->SetImageHeight(600); + camera->SetImageWidth(1600); + camera->SetImageHeight(900); camera->SetAntiAliasing(2); camera->SetAspectRatio(1.333); camera->SetHFOV(IGN_PI / 2); @@ -207,7 +207,15 @@ int main(int _argc, char** _argv) std::vector engineNames; std::vector cameras; - engineNames.push_back("ogre"); + // Expose engine name to command line because we can't instantiate both + // ogre and ogre2 at the same time + std::string ogreEngineName("ogre2"); + if (_argc > 1) + { + ogreEngineName = _argv[1]; + } + + engineNames.push_back(ogreEngineName); for (auto engineName : engineNames) { diff --git a/ogre2/include/ignition/rendering/ogre2/Ogre2Heightmap.hh b/ogre2/include/ignition/rendering/ogre2/Ogre2Heightmap.hh new file mode 100644 index 000000000..057948fa7 --- /dev/null +++ b/ogre2/include/ignition/rendering/ogre2/Ogre2Heightmap.hh @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#ifndef IGNITION_RENDERING_OGRE2_OGRE2HEIGHTMAP_HH_ +#define IGNITION_RENDERING_OGRE2_OGRE2HEIGHTMAP_HH_ + +#include + +#include "ignition/rendering/base/BaseHeightmap.hh" +#include "ignition/rendering/ogre2/Ogre2Geometry.hh" + +// Ignoring warning: "non dll-interface class +// 'ignition::rendering::v5::Heightmap' used as base for dll-interface class" +// because `Heightmap` and `BaseHeightmap` are header-only +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4275) +#endif + +namespace Ogre +{ + class Camera; + class Terra; +} + +namespace ignition +{ + namespace rendering + { + inline namespace IGNITION_RENDERING_VERSION_NAMESPACE { + // + // Forward declaration + class Ogre2HeightmapPrivate; + + /// \brief Ogre implementation of a heightmap geometry. + class IGNITION_RENDERING_OGRE2_VISIBLE Ogre2Heightmap + : public BaseHeightmap + { + /// \brief Constructor + /// \param[in] _desc Parameters describing how a + /// heightmap should be loaded + public: explicit Ogre2Heightmap(const HeightmapDescriptor &_desc); + + /// \brief Destructor + public: virtual ~Ogre2Heightmap() override; + + // Documentation inherited. + public: virtual void Init() override; + + // Documentation inherited. + public: virtual void PreRender() override; + + /// \brief Returns the Terra pointer as it is a movable object that + /// must be attached to a regular SceneNode + /// \remarks This behavior is different from ogre1 + /// \return Terra pointer + public: virtual Ogre::MovableObject *OgreObject() const override; + + /// \brief Returns NULL, heightmap materials don't inherit from + /// MaterialPtr. + /// \return Null pointer. + public: virtual MaterialPtr Material() const override; + + /// \brief Has no effect for heightmaps. The material is set through a + /// HeightmapDescriptor. + /// \param[in] _material Not used. + /// \param[in] _unique Not used. + public: virtual void SetMaterial(MaterialPtr _material, bool _unique) + override; + + /// \internal + /// \brief Retrieves the internal Terra pointer + /// \return internal Terra pointer + public: Ogre::Terra* Terra(); + + /// \internal + /// \brief Must be called before rendering with the camera + /// that will perform rendering. + /// + /// May update shadows if light direction changed + /// \param[in] _camera Camera about to be used for rendering + public: void UpdateForRender(Ogre::Camera *_activeCamera); + + /// \brief Heightmap should only be created by scene. + private: friend class OgreScene; + + /// \brief Private data class + private: std::unique_ptr dataPtr; + }; + } + } +} +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +#endif diff --git a/ogre2/include/ignition/rendering/ogre2/Ogre2RenderEngine.hh b/ogre2/include/ignition/rendering/ogre2/Ogre2RenderEngine.hh index d81755187..42d1f53ab 100644 --- a/ogre2/include/ignition/rendering/ogre2/Ogre2RenderEngine.hh +++ b/ogre2/include/ignition/rendering/ogre2/Ogre2RenderEngine.hh @@ -39,6 +39,9 @@ namespace Ogre { class OverlaySystem; } + + class HlmsPbsTerraShadows; + class CompositorWorkspaceListener; } namespace ignition @@ -175,6 +178,25 @@ namespace ignition /// \return Pointer to the ogre overlay system. public: Ogre::v1::OverlaySystem *OverlaySystem() const; + /// \internal + /// \brief Get a pointer to the Pbs listener that adds terra shadows. + /// Do NOT assume HlmsPbs::getListener() == HlmsPbsTerraShadows() + /// as there may be more than one listener in the future with + /// a master listener coordinating them + /// \return Pointer to the Pbs listener that adds terra shadows. + public: Ogre::HlmsPbsTerraShadows *HlmsPbsTerraShadows() const; + + /// \internal + /// \brief Get a pointer to the workspace listener that adds terra + /// casting shadows from spot and point lights. + /// + /// This listener needs to be added to each workspace that wants + /// terrain shadows from spot/point lights. If no terrains are in scene + /// then the workspace's overhead is negligible / almost 0. + /// \return Pointer to the CompositorWorkspaceListener + public: Ogre::CompositorWorkspaceListener + *TerraWorkspaceListener() const; + /// \brief Pointer to the ogre's overlay system private: Ogre::v1::OverlaySystem *ogreOverlaySystem = nullptr; diff --git a/ogre2/include/ignition/rendering/ogre2/Ogre2RenderTypes.hh b/ogre2/include/ignition/rendering/ogre2/Ogre2RenderTypes.hh index 64048814f..73dd3ca40 100644 --- a/ogre2/include/ignition/rendering/ogre2/Ogre2RenderTypes.hh +++ b/ogre2/include/ignition/rendering/ogre2/Ogre2RenderTypes.hh @@ -39,6 +39,7 @@ namespace ignition class Ogre2GizmoVisual; class Ogre2GpuRays; class Ogre2Grid; + class Ogre2Heightmap; class Ogre2InertiaVisual; class Ogre2JointVisual; class Ogre2Light; @@ -87,6 +88,7 @@ namespace ignition typedef shared_ptr Ogre2GizmoVisualPtr; typedef shared_ptr Ogre2GpuRaysPtr; typedef shared_ptr Ogre2GridPtr; + typedef shared_ptr Ogre2HeightmapPtr; typedef shared_ptr Ogre2InertiaVisualPtr; typedef shared_ptr Ogre2JointVisualPtr; typedef shared_ptr Ogre2LightPtr; diff --git a/ogre2/include/ignition/rendering/ogre2/Ogre2Scene.hh b/ogre2/include/ignition/rendering/ogre2/Ogre2Scene.hh index fb1c854b0..401f6bfbf 100644 --- a/ogre2/include/ignition/rendering/ogre2/Ogre2Scene.hh +++ b/ogre2/include/ignition/rendering/ogre2/Ogre2Scene.hh @@ -19,6 +19,7 @@ #include #include +#include #include "ignition/rendering/Storage.hh" #include "ignition/rendering/base/BaseScene.hh" @@ -135,7 +136,12 @@ namespace ignition /// \brief When LegacyAutoGpuFlush(), this function mimics /// legacy behavior. /// When not, it verifies PreRender has been called - public: void StartRendering(); + /// It also performs necessary updates for all heightmaps + /// + /// \param _camera camera that is about to render, used + /// by heightmaps (Terra). See Ogre2Scene::UpdateAllHeightmaps + /// Can be null + public: void StartRendering(Ogre::Camera *_camera); /// \internal /// \brief Every Render() function calls this function with @@ -334,6 +340,12 @@ namespace ignition protected: virtual bool InitObject(Ogre2ObjectPtr _object, unsigned int _id, const std::string &_name); + /// \internal + /// \brief Iterates through all Heightmaps and calls + /// Ogre2Heightmap::UpdateForRender on each of them + /// \param[in] _camera Camera about to be used for rendering + public: void UpdateAllHeightmaps(Ogre::Camera *_camera); + /// \brief Create a compositor shadow node with the same number of shadow /// textures as the number of shadow casting lights protected: void UpdateShadowNode(); @@ -405,6 +417,9 @@ namespace ignition /// \brief A list of ogre materials protected: Ogre2MaterialMapPtr materials; + /// \brief A list of ogre heightmaps + protected: std::vector> heightmaps; + /// \brief Pointer to the ogre scene manager protected: Ogre::SceneManager *ogreSceneManager = nullptr; diff --git a/ogre2/src/CMakeLists.txt b/ogre2/src/CMakeLists.txt index 9685ecc7d..e807cce1a 100644 --- a/ogre2/src/CMakeLists.txt +++ b/ogre2/src/CMakeLists.txt @@ -33,6 +33,7 @@ target_link_libraries(${ogre2_target} PRIVATE ignition-plugin${IGN_PLUGIN_VER}::register ${OPENGL_LIBRARIES} + terra IgnOGRE2::IgnOGRE2) set (versioned ${CMAKE_SHARED_LIBRARY_PREFIX}${PROJECT_NAME_LOWER}-${engine_name}${CMAKE_SHARED_LIBRARY_SUFFIX}) diff --git a/ogre2/src/Ogre2DepthCamera.cc b/ogre2/src/Ogre2DepthCamera.cc index 7e7ab6a2a..59bd496af 100644 --- a/ogre2/src/Ogre2DepthCamera.cc +++ b/ogre2/src/Ogre2DepthCamera.cc @@ -929,6 +929,9 @@ void Ogre2DepthCamera::CreateWorkspaceInstance() this->dataPtr->ogreCompositorWorkspaceDef, false); + this->dataPtr->ogreCompositorWorkspace->addListener( + engine->TerraWorkspaceListener()); + // add the listener Ogre::CompositorNode *node = this->dataPtr->ogreCompositorWorkspace->getNodeSequence()[0]; @@ -959,7 +962,7 @@ void Ogre2DepthCamera::Render() glEnable(GL_DEPTH_CLAMP); #endif - this->scene->StartRendering(); + this->scene->StartRendering(this->ogreCamera); // update the compositors this->dataPtr->ogreCompositorWorkspace->_validateFinalTarget(); diff --git a/ogre2/src/Ogre2GpuRays.cc b/ogre2/src/Ogre2GpuRays.cc index 21a218226..ea2d28103 100644 --- a/ogre2/src/Ogre2GpuRays.cc +++ b/ogre2/src/Ogre2GpuRays.cc @@ -1213,6 +1213,7 @@ void Ogre2GpuRays::UpdateRenderTarget1stPass() // update the compositors for (auto i : this->dataPtr->cubeFaceIdx) { + this->scene->UpdateAllHeightmaps(this->dataPtr->cubeCam[i]); this->dataPtr->ogreCompositorWorkspace1st[i]->setEnabled(true); this->dataPtr->ogreCompositorWorkspace1st[i]->_validateFinalTarget(); @@ -1244,7 +1245,7 @@ void Ogre2GpuRays::UpdateRenderTarget2ndPass() ////////////////////////////////////////////////// void Ogre2GpuRays::Render() { - this->scene->StartRendering(); + this->scene->StartRendering(nullptr); this->UpdateRenderTarget1stPass(); this->UpdateRenderTarget2ndPass(); diff --git a/ogre2/src/Ogre2Heightmap.cc b/ogre2/src/Ogre2Heightmap.cc new file mode 100644 index 000000000..0252fc2ed --- /dev/null +++ b/ogre2/src/Ogre2Heightmap.cc @@ -0,0 +1,441 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#include + +#include +#include + +#include "ignition/rendering/ogre2/Ogre2Heightmap.hh" +#include "ignition/rendering/ogre2/Ogre2Conversions.hh" +#include "ignition/rendering/ogre2/Ogre2Light.hh" +#include "ignition/rendering/ogre2/Ogre2RenderEngine.hh" +#include "ignition/rendering/ogre2/Ogre2Scene.hh" + +#include "Terra/Terra.h" + +#ifdef _MSC_VER + #pragma warning(push, 0) +#endif +#include +#include +#include +#include +#include +#include +#include "Terra/Hlms/OgreHlmsTerra.h" +#include "Terra/Hlms/OgreHlmsTerraDatablock.h" +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +////////////////////////////////////////////////// +class ignition::rendering::Ogre2HeightmapPrivate +{ + /// \brief Skirt min height. Leave it at -1 for automatic. + /// Leave it at 0 for maximum skirt size (high performance hit) + public: float skirtMinHeight{-1}; + + /// \brief Cached value of skirtMinHeight auto-calculated by Terra + /// so we can use it if skirtMinHeight becomes -1 again + public: float autoSkirtValue; + + /// \brief The raw height values. + public: std::vector heights; + + /// \brief Size of the heightmap data. + public: unsigned int dataSize{0u}; + + public: std::unique_ptr terra{nullptr}; +}; + +using namespace ignition; +using namespace rendering; + +////////////////////////////////////////////////// +Ogre2Heightmap::Ogre2Heightmap(const HeightmapDescriptor &_desc) + : BaseHeightmap(_desc), dataPtr(std::make_unique()) +{ +} + +////////////////////////////////////////////////// +Ogre2Heightmap::~Ogre2Heightmap() +{ +} + +////////////////////////////////////////////////// +void Ogre2Heightmap::Init() +{ + Ogre2Object::Init(); + + if (this->descriptor.Data() == nullptr) + { + ignerr << "Failed to initialize: null heightmap data." << std::endl; + return; + } + + if (this->descriptor.Name().empty()) + this->descriptor.SetName(this->Name()); + + // Add paths + for (auto i = 0u; i < this->descriptor.TextureCount(); ++i) + { + auto texture = this->descriptor.TextureByIndex(i); + Ogre2RenderEngine::Instance()->AddResourcePath(texture->Diffuse()); + Ogre2RenderEngine::Instance()->AddResourcePath(texture->Normal()); + } + + // \todo These parameters shouldn't be hardcoded, and instead parametrized so + // that they can be made consistent across different libraries (like + // ign-physics) + bool flipY = false; + // sampling size along image width and height + const bool needsOgre1Compat = + math::isPowerOfTwo(this->descriptor.Data()->Width() - 1u); + const unsigned int srcWidth = + needsOgre1Compat + ? ((this->descriptor.Data()->Width() * this->descriptor.Sampling()) - + this->descriptor.Sampling() + 1) + : (this->descriptor.Data()->Width() * this->descriptor.Sampling()); + + if (needsOgre1Compat) + { + ignwarn << "Heightmap final sampling should be 2^n" + << std::endl << " which differs from ogre1's 2^n+1" + << std::endl << "The last row and column will be cropped" + << std::endl << "size = (width * sampling) - sampling + 1" + << std::endl << "[" << srcWidth << "] = ([" + << this->descriptor.Data()->Width() << "] * [" + << this->descriptor.Sampling() << "]) - [" + << this->descriptor.Sampling() << "] + 1" + << std::endl; + } + else if (!math::isPowerOfTwo(srcWidth)) + { + ignerr << "Heightmap final sampling must satisfy 2^n." + << std::endl << "size = width * sampling" + << std::endl << "[" << srcWidth << "] = [" + << this->descriptor.Data()->Width() << "] * [" + << this->descriptor.Sampling() << "]" + << std::endl; + return; + } + + const unsigned int newWidth = + math::isPowerOfTwo(srcWidth) ? srcWidth : (srcWidth - 1u); + + math::Vector3d scale; + scale.X(this->descriptor.Size().X() / newWidth); + scale.Y(this->descriptor.Size().Y() / newWidth); + scale.Z(1.0); + + // Construct the heightmap lookup table + std::vector lookup; + this->descriptor.Data()->FillHeightMap(this->descriptor.Sampling(), + srcWidth, this->descriptor.Size(), scale, flipY, lookup); + this->dataPtr->heights.reserve(newWidth * newWidth); + + // Terra is optimized to work with UNORM heightmaps, therefore it assumes + // lowest height is 0. + // So we move the heightmap so that its min elevation = 0 before feeding to + // ogre. It is later translated back by the setOrigin call. + // + // Obtain min and max elevation and bring everything to range [0; 1] + // Terra should support non-normalized ranges but there are a couple + // bugs preventing that, so it's just easier to normalize the data + float minElevation = 0.0; + float maxElevation = 0.0; + + for (unsigned int y = 0; y < newWidth; ++y) + { + for (unsigned int x = 0; x < newWidth; ++x) + { + const size_t index = y * srcWidth + x; + const float heightVal = lookup[index]; + minElevation = std::min(minElevation, heightVal); + maxElevation = std::max(maxElevation, heightVal); + this->dataPtr->heights.push_back(heightVal); + } + } + + // min and max elevations collected. Now normalize + const float heightDiff = maxElevation - minElevation; + const float invHeightDiff = + fabsf( heightDiff ) < 1e-6f ? 1.0f : (1.0f / heightDiff); + for (float &heightVal : this->dataPtr->heights) + { + heightVal = (heightVal - minElevation) * invHeightDiff; + assert( heightVal >= 0 ); + } + + this->dataPtr->dataSize = newWidth; + + if (this->dataPtr->heights.empty()) + { + ignerr << "Failed to load terrain. Heightmap data is empty" << std::endl; + return; + } + + // Create terrain group, which holds all the individual terrain instances. + // Param 1: Pointer to the scene manager + // Param 2: Alignment plane + // Param 3: Number of vertices along one edge of the terrain (2^n+1). + // Terrains must be square, with each side a power of 2 in size + // Param 4: World size of each terrain instance, in meters. + + auto ogreScene = std::dynamic_pointer_cast(this->Scene()); + + Ogre::Image2 image; + image.loadDynamicImage(this->dataPtr->heights.data(), newWidth, newWidth, + 1u, Ogre::TextureTypes::Type2D, + Ogre::PFG_R32_FLOAT, false); + + const math::Vector3d newSize = this->descriptor.Size() * + math::Vector3d(1.0, 1.0, heightDiff); + + math::Vector3d center( + this->descriptor.Position().X(), + this->descriptor.Position().Y(), + this->descriptor.Position().Z() + newSize.Z() * 0.5 + minElevation); + + Ogre::Root *ogreRoot = Ogre2RenderEngine::Instance()->OgreRoot(); + Ogre::SceneManager *ogreSceneManager = ogreScene->OgreSceneManager(); + Ogre::CompositorManager2 *ogreCompMgr = ogreRoot->getCompositorManager2(); + + // TODO(anyone): Gazebo doesn't support SCENE_STATIC scene nodes + this->dataPtr->terra = + std::make_unique( + Ogre::Id::generateNewId(), + &ogreSceneManager->_getEntityMemoryManager( + Ogre::/*SCENE_STATIC*/SCENE_DYNAMIC), + ogreSceneManager, 11u, ogreCompMgr, nullptr, true ); + // Does not cast shadows because it uses a raymarching implementation + // instead of shadow maps. It does receive shadows from shadow maps though + this->dataPtr->terra->setCastShadows(false); + this->dataPtr->terra->load( + image, + Ogre2Conversions::Convert(center), + Ogre2Conversions::Convert(newSize), + this->descriptor.Name()); + this->dataPtr->autoSkirtValue = + this->dataPtr->terra->getCustomSkirtMinHeight(); + this->dataPtr->terra->setDatablock( + ogreRoot->getHlmsManager()-> + getHlms(Ogre::HLMS_USER3)->getDefaultDatablock()); + + Ogre::Hlms *hlmsTerra = + ogreRoot->getHlmsManager()->getHlms(Ogre::HLMS_USER3); + + IGN_ASSERT(dynamic_cast(hlmsTerra), + "HlmsTerra incorrectly setup, memory corrupted, or " + "HlmsTerra::getType changed while this code is out of sync"); + + Ogre::String datablockName = "IGN Terra " + this->name; + + Ogre::HlmsDatablock *datablockBase = hlmsTerra->createDatablock( + datablockName, datablockName, Ogre::HlmsMacroblock(), + Ogre::HlmsBlendblock(), Ogre::HlmsParamVec(), false); + + IGN_ASSERT(dynamic_cast(datablockBase) != nullptr, + "Corruption detected. This is impossible."); + + Ogre::HlmsTerraDatablock *datablock = + static_cast(datablockBase); + + Ogre::HlmsSamplerblock samplerblock; + samplerblock.setAddressingMode(Ogre::TAM_WRAP); + samplerblock.setFiltering(Ogre::TFO_ANISOTROPIC); + samplerblock.mMaxAnisotropy = 8u; + + size_t numTextures = static_cast(this->descriptor.TextureCount()); + + if (numTextures >= 1u) + { + bool bCanUseFirstAsBase = false; + + using namespace Ogre; + const HeightmapTexture *texture0 = this->descriptor.TextureByIndex(0); + if (texture0->Normal().empty() && + abs(newSize.X() - texture0->Size()) < 1e-6 && + abs(newSize.Y() - texture0->Size()) < 1e-6 ) + { + bCanUseFirstAsBase = true; + } + + if ((numTextures > 4u && !bCanUseFirstAsBase) || + (numTextures > 5u && bCanUseFirstAsBase)) + { + ignwarn << "Ogre2Heightmap currently supports up to 4 textures, " + "5 textures if the first one is diffuse-only & " + "texture size = terrain size. " + "The rest are ignored. Supplied: " + << numTextures << std::endl; + numTextures = bCanUseFirstAsBase ? 5u : 4u; + } + + if (bCanUseFirstAsBase) + { + datablock->setTexture(static_cast(TERRA_DIFFUSE), + texture0->Diffuse(), &samplerblock); + } + else + { + datablock->setTexture(static_cast(TERRA_DETAIL0), + texture0->Diffuse(), &samplerblock); + + datablock->setTexture(static_cast(TERRA_DETAIL0_NM), + texture0->Normal(), &samplerblock); + + const float sizeX = + static_cast(newSize.X() / texture0->Size()); + const float sizeY = + static_cast(newSize.Y() / texture0->Size()); + if (!texture0->Diffuse().empty() || !texture0->Normal().empty()) + datablock->setDetailMapOffsetScale(0, Vector4(0, 0, sizeX, sizeY)); + } + + for (size_t i = 1u; i < numTextures; ++i) + { + const size_t idxOffset = bCanUseFirstAsBase ? 1 : 0; + const HeightmapTexture *texture = this->descriptor.TextureByIndex(i); + + datablock->setTexture(static_cast( + TERRA_DETAIL0 + i - idxOffset), + texture->Diffuse(), &samplerblock); + + datablock->setTexture(static_cast( + TERRA_DETAIL0_NM + i - idxOffset), + texture->Normal(), &samplerblock); + + const float sizeX = + static_cast(newSize.X() / texture->Size()); + const float sizeY = + static_cast(newSize.Y() / texture->Size()); + if (!texture->Diffuse().empty() || !texture->Normal().empty()) + { + datablock->setDetailMapOffsetScale( + static_cast(i - idxOffset), + Vector4(0, 0, sizeX, sizeY)); + } + } + + + size_t numBlends = static_cast(this->descriptor.BlendCount()); + if ((numBlends > 3u && !bCanUseFirstAsBase) || + (numBlends > 4u && bCanUseFirstAsBase)) + { + ignwarn << "Ogre2Heightmap currently supports up to 3 blends, " + "4 blends if the first one is diffuse-only & " + "texture size = terrain size. " + "The rest are ignored. Supplied: " + << numBlends << std::endl; + numBlends = bCanUseFirstAsBase ? 4u : 3u; + } + + Ogre::Vector4 minBlendHeights(0.0f); + Ogre::Vector4 maxBlendHeights(0.0f); + for (size_t i = 0; i < numBlends; ++i) + { + const size_t idxOffset = bCanUseFirstAsBase ? 0u : 1u; + const HeightmapBlend *blend = this->descriptor.BlendByIndex(i); + minBlendHeights[i + idxOffset] = + static_cast(blend->MinHeight()); + maxBlendHeights[i + idxOffset] = + static_cast(blend->MinHeight()+ + blend->FadeDistance()); + } + datablock->setIgnWeightsHeights(minBlendHeights, maxBlendHeights); + } + + this->dataPtr->terra->setDatablock(datablock); + + ignmsg << "Loading heightmap: " << this->descriptor.Name() << std::endl; + auto time = std::chrono::steady_clock::now(); + + ignmsg << "Heightmap loaded. Process took " + << std::chrono::duration_cast( + std::chrono::steady_clock::now() - time).count() + << " ms." << std::endl; +} + +////////////////////////////////////////////////// +void Ogre2Heightmap::PreRender() +{ +} + +/////////////////////////////////////////////////// +void Ogre2Heightmap::UpdateForRender(Ogre::Camera *_activeCamera) +{ + if (this->dataPtr->skirtMinHeight >= 0) + { + this->dataPtr->terra->setCustomSkirtMinHeight( + this->dataPtr->skirtMinHeight); + } + else + { + this->dataPtr->terra->setCustomSkirtMinHeight( + this->dataPtr->autoSkirtValue); + } + + // Get the first directional light + Ogre2DirectionalLightPtr directionalLight; + for (unsigned int i = 0; i < this->Scene()->LightCount(); ++i) + { + auto light = std::dynamic_pointer_cast( + this->Scene()->LightByIndex(i)); + if (nullptr != light) + { + directionalLight = light; + break; + } + } + + this->dataPtr->terra->setCamera(_activeCamera); + if (directionalLight) + { + this->dataPtr->terra->update( + Ogre2Conversions::Convert(directionalLight->Direction())); + } + else + { + this->dataPtr->terra->update(Ogre::Vector3::NEGATIVE_UNIT_Y); + } +} + +////////////////////////////////////////////////// +Ogre::MovableObject *Ogre2Heightmap::OgreObject() const +{ + return this->dataPtr->terra.get(); +} + +////////////////////////////////////////////////// +void Ogre2Heightmap::SetMaterial(MaterialPtr, bool) +{ + // no-op +} + +////////////////////////////////////////////////// +MaterialPtr Ogre2Heightmap::Material() const +{ + return nullptr; +} + +////////////////////////////////////////////////// +Ogre::Terra* Ogre2Heightmap::Terra() +{ + return this->dataPtr->terra.get(); +} diff --git a/ogre2/src/Ogre2RenderEngine.cc b/ogre2/src/Ogre2RenderEngine.cc index bcfafd5c0..c5e25830f 100644 --- a/ogre2/src/Ogre2RenderEngine.cc +++ b/ogre2/src/Ogre2RenderEngine.cc @@ -41,6 +41,9 @@ #include "ignition/rendering/ogre2/Ogre2Scene.hh" #include "ignition/rendering/ogre2/Ogre2Storage.hh" +#include "Terra/Hlms/OgreHlmsTerra.h" +#include "Terra/Hlms/PbsListener/OgreHlmsPbsTerraShadows.h" +#include "Terra/TerraWorkspaceListener.h" class ignition::rendering::Ogre2RenderEnginePrivate { @@ -50,6 +53,13 @@ class ignition::rendering::Ogre2RenderEnginePrivate /// \brief A list of supported fsaa levels public: std::vector fsaaLevels; + + /// \brief Pbs listener that adds terra shadows + public: std::unique_ptr hlmsPbsTerraShadows; + + /// \brief Listener that needs to be in every workspace + /// that wants terrain to cast shadows from spot and point lights + public: std::unique_ptr terraWorkspaceListener; }; using namespace ignition; @@ -112,6 +122,8 @@ void Ogre2RenderEngine::Destroy() delete this->ogreOverlaySystem; this->ogreOverlaySystem = nullptr; + this->dataPtr->hlmsPbsTerraShadows.reset(); + if (this->ogreRoot) { // Clean up any textures that may still be in flight. @@ -635,6 +647,14 @@ void Ogre2RenderEngine::RegisterHlms() rootHlmsFolder, "2.0", "scripts", "materials", "Common", "GLSLES"); Ogre::ResourceGroupManager::getSingleton().addResourceLocation( commonGLSLESMaterialFolder, "FileSystem", "General"); + Ogre::String terraMaterialFolder = common::joinPaths( + rootHlmsFolder, "2.0", "scripts", "materials", "Terra"); + Ogre::ResourceGroupManager::getSingleton().addResourceLocation( + terraMaterialFolder, "FileSystem", "General"); + Ogre::String terraGLSLMaterialFolder = common::joinPaths( + rootHlmsFolder, "2.0", "scripts", "materials", "Terra", "GLSL"); + Ogre::ResourceGroupManager::getSingleton().addResourceLocation( + terraGLSLMaterialFolder, "FileSystem", "General"); // The following code is taken from the registerHlms() function in ogre2 // samples framework @@ -702,13 +722,57 @@ void Ogre2RenderEngine::RegisterHlms() ++libraryFolderPathIt; } + { + archivePbsLibraryFolders.push_back( archiveManager.load( + rootHlmsFolder + "Hlms/Terra/" + "GLSL" + "/PbsTerraShadows", + "FileSystem", true ) ); + this->dataPtr->hlmsPbsTerraShadows.reset(new Ogre::HlmsPbsTerraShadows()); + } + // Create and register hlmsPbs = OGRE_NEW Ogre::HlmsPbs(archivePbs, &archivePbsLibraryFolders); + hlmsPbs->setListener(this->dataPtr->hlmsPbsTerraShadows.get()); Ogre::Root::getSingleton().getHlmsManager()->registerHlms(hlmsPbs); // disable writting debug output to disk hlmsPbs->setDebugOutputPath(false, false); } + + { + Ogre::HlmsTerra *hlmsTerra = 0; + // Create & Register HlmsPbs + // Do the same for HlmsPbs: + Ogre::HlmsTerra::getDefaultPaths(mainFolderPath, libraryFoldersPaths); + Ogre::Archive *archiveTerra = archiveManager.load( + rootHlmsFolder + mainFolderPath, "FileSystem", true); + + // Add ignition's customizations + libraryFoldersPaths.push_back("Hlms/Terra/ign"); + + // Get the library archive(s) + Ogre::ArchiveVec archiveTerraLibraryFolders; + libraryFolderPathIt = libraryFoldersPaths.begin(); + libraryFolderPathEn = libraryFoldersPaths.end(); + while (libraryFolderPathIt != libraryFolderPathEn) + { + Ogre::Archive *archiveLibrary = + archiveManager.load(rootHlmsFolder + *libraryFolderPathIt, + "FileSystem", true); + archiveTerraLibraryFolders.push_back(archiveLibrary); + ++libraryFolderPathIt; + } + + // Create and register + hlmsTerra = OGRE_NEW Ogre::HlmsTerra(archiveTerra, + &archiveTerraLibraryFolders); + Ogre::Root::getSingleton().getHlmsManager()->registerHlms(hlmsTerra); + + // disable writting debug output to disk + hlmsTerra->setDebugOutputPath(false, false); + + this->dataPtr->terraWorkspaceListener.reset( + new Ogre::TerraWorkspaceListener(hlmsTerra)); + } } ////////////////////////////////////////////////// @@ -873,6 +937,19 @@ Ogre::v1::OverlaySystem *Ogre2RenderEngine::OverlaySystem() const return this->ogreOverlaySystem; } +///////////////////////////////////////////////// +Ogre::HlmsPbsTerraShadows *Ogre2RenderEngine::HlmsPbsTerraShadows() const +{ + return this->dataPtr->hlmsPbsTerraShadows.get(); +} + +///////////////////////////////////////////////// +Ogre::CompositorWorkspaceListener *Ogre2RenderEngine::TerraWorkspaceListener() + const +{ + return this->dataPtr->terraWorkspaceListener.get(); +} + // Register this plugin IGNITION_ADD_PLUGIN(ignition::rendering::Ogre2RenderEnginePlugin, ignition::rendering::RenderEnginePlugin) diff --git a/ogre2/src/Ogre2RenderTarget.cc b/ogre2/src/Ogre2RenderTarget.cc index fd1d75c16..867544d3a 100644 --- a/ogre2/src/Ogre2RenderTarget.cc +++ b/ogre2/src/Ogre2RenderTarget.cc @@ -290,6 +290,7 @@ void Ogre2RenderTarget::BuildCompositor() this->dataPtr->rtListener = new Ogre2RenderTargetCompositorListener(this); this->ogreCompositorWorkspace->addListener(this->dataPtr->rtListener); + this->ogreCompositorWorkspace->addListener(engine->TerraWorkspaceListener()); } ////////////////////////////////////////////////// @@ -450,7 +451,7 @@ void Ogre2RenderTarget::PostRender() ////////////////////////////////////////////////// void Ogre2RenderTarget::Render() { - this->scene->StartRendering(); + this->scene->StartRendering(this->ogreCamera); this->ogreCompositorWorkspace->_validateFinalTarget(); this->ogreCompositorWorkspace->_beginUpdate(false); diff --git a/ogre2/src/Ogre2Scene.cc b/ogre2/src/Ogre2Scene.cc index 428afd97b..160876f29 100644 --- a/ogre2/src/Ogre2Scene.cc +++ b/ogre2/src/Ogre2Scene.cc @@ -28,6 +28,7 @@ #include "ignition/rendering/ogre2/Ogre2GizmoVisual.hh" #include "ignition/rendering/ogre2/Ogre2GpuRays.hh" #include "ignition/rendering/ogre2/Ogre2Grid.hh" +#include "ignition/rendering/ogre2/Ogre2Heightmap.hh" #include "ignition/rendering/ogre2/Ogre2InertiaVisual.hh" #include "ignition/rendering/ogre2/Ogre2JointVisual.hh" #include "ignition/rendering/ogre2/Ogre2Light.hh" @@ -64,6 +65,9 @@ #include #include #endif + +#include "Terra/Terra.h" +#include "Terra/Hlms/PbsListener/OgreHlmsPbsTerraShadows.h" #ifdef _MSC_VER #pragma warning(pop) #endif @@ -256,8 +260,11 @@ void Ogre2Scene::EndForcedRender() } ////////////////////////////////////////////////// -void Ogre2Scene::StartRendering() +void Ogre2Scene::StartRendering(Ogre::Camera *_camera) { + if (_camera) + this->UpdateAllHeightmaps(_camera); + if (this->LegacyAutoGpuFlush()) { auto engine = Ogre2RenderEngine::Instance(); @@ -405,6 +412,93 @@ bool Ogre2Scene::InitImpl() return true; } +////////////////////////////////////////////////// +void Ogre2Scene::UpdateAllHeightmaps(Ogre::Camera *_camera) +{ + auto engine = Ogre2RenderEngine::Instance(); + Ogre::HlmsPbsTerraShadows *pbsTerraShadows = engine->HlmsPbsTerraShadows(); + + Ogre::Real closestTerraSqDist = std::numeric_limits::max(); + Ogre::Terra *closestTerra = 0; + Ogre::Terra *insideTerra = 0; + + const Ogre::Vector2 cameraPos2d(_camera->getDerivedPosition().xy()); + + auto itor = this->heightmaps.begin(); + auto endt = this->heightmaps.end(); + + while (itor != endt) + { + Ogre2HeightmapPtr heightmap = itor->lock(); + if (!heightmap) + { + // Heightmap has been destroyed. Remove it from our list. + // Swap and pop trick + itor = Ogre::efficientVectorRemove(this->heightmaps, itor); + endt = this->heightmaps.end(); + } + else + { + heightmap->UpdateForRender(_camera); + Ogre::Terra *terra = heightmap->Terra(); + + const Ogre::Vector2 origin2d = terra->getTerrainOrigin().xy() + + terra->getXZDimensions() * 0.5f; + const Ogre::Vector2 end2d = origin2d + terra->getXZDimensions(); + + if (!(cameraPos2d.x < origin2d.x || cameraPos2d.x > end2d.x || + cameraPos2d.y < origin2d.y || cameraPos2d.x > end2d.y) ) + { + // Give preference to the Terra we're currently inside of + insideTerra = terra; + } + else + { + auto sqDist = cameraPos2d.squaredDistance((origin2d + end2d) * 0.5f); + if( sqDist < closestTerraSqDist ) + { + closestTerraSqDist = sqDist; + closestTerra = terra; + } + } + + ++itor; + } + } + + // If we're not inside any Terra, then prefer the one that is + // "closest" to camera. Both may be nullptrs though. + if (insideTerra) + pbsTerraShadows->setTerra(insideTerra); + else + pbsTerraShadows->setTerra(closestTerra); + + +#if OGRE_VERSION_MAJOR == 2 && OGRE_VERSION_MINOR == 2 + if (!this->heightmaps.empty()) + { + // Ogre 2.2 expects ign to provide Terra's shadow texture + // to each compositor that may use it to properly set barriers + // (otherwise GPU may start rendering before the Compute Shader + // is done ray marching terrain shadows) + // + // This is insane with so many possible compositors ign has, + // so we do a brute-force approach here (not that expensive actually) + // + // Ogre 2.3 got rid of this requirement due to being very user-hostile + Ogre::RenderSystem *renderSys = + this->ogreSceneManager->getDestinationRenderSystem(); + + Ogre::ResourceTransition resourceTransition; + resourceTransition.readBarrierBits = Ogre::ReadBarrier::Uav; + resourceTransition.writeBarrierBits = Ogre::WriteBarrier::Uav; + renderSys->_resourceTransitionCreated(&resourceTransition); + renderSys->_executeResourceTransition(&resourceTransition); + renderSys->_resourceTransitionDestroyed(&resourceTransition); + } +#endif +} + ////////////////////////////////////////////////// void Ogre2Scene::UpdateShadowNode() { @@ -1027,13 +1121,13 @@ CapsulePtr Ogre2Scene::CreateCapsuleImpl(unsigned int _id, } ////////////////////////////////////////////////// -HeightmapPtr Ogre2Scene::CreateHeightmapImpl(unsigned int, - const std::string &, const HeightmapDescriptor &) +HeightmapPtr Ogre2Scene::CreateHeightmapImpl(unsigned int _id, + const std::string &_name, const HeightmapDescriptor &_desc) { - ignerr << "Ogre 2 doesn't support heightmaps yet, see " << - "https://github.com/ignitionrobotics/ign-rendering/issues/187" - << std::endl; - return nullptr; + Ogre2HeightmapPtr heightmap(new Ogre2Heightmap(_desc)); + heightmaps.push_back(heightmap); + bool result = this->InitObject(heightmap, _id, _name); + return (result) ? heightmap : nullptr; } ////////////////////////////////////////////////// diff --git a/ogre2/src/Ogre2ThermalCamera.cc b/ogre2/src/Ogre2ThermalCamera.cc index 29894b315..ce4ea5356 100644 --- a/ogre2/src/Ogre2ThermalCamera.cc +++ b/ogre2/src/Ogre2ThermalCamera.cc @@ -842,7 +842,7 @@ void Ogre2ThermalCamera::Render() #endif // update the compositors - this->scene->StartRendering(); + this->scene->StartRendering(this->ogreCamera); this->dataPtr->ogreCompositorWorkspace->_validateFinalTarget(); this->dataPtr->ogreCompositorWorkspace->_beginUpdate(false); diff --git a/ogre2/src/media/2.0/scripts/materials/Terra/GLSL/GpuNormalMapper_ps.glsl b/ogre2/src/media/2.0/scripts/materials/Terra/GLSL/GpuNormalMapper_ps.glsl new file mode 100644 index 000000000..fe433177a --- /dev/null +++ b/ogre2/src/media/2.0/scripts/materials/Terra/GLSL/GpuNormalMapper_ps.glsl @@ -0,0 +1,88 @@ +// Our terrain has the following pattern: +// +// 1N 10 11 +// o-o-o +// |/|/| +// 0N o-+-o 01 +// |/|/| +// o-o-o +// NN N0 N1 +// +// We need to calculate the normal of the vertex in +// the center '+', which is shared by 6 triangles. + +#version 330 + +uniform sampler2D heightMap; + +uniform vec2 heightMapResolution; +uniform vec3 vScale; + +out vec4 fragColour; + +in block +{ + vec2 uv0; +} inPs; + +void main() +{ + ivec2 iCoord = ivec2( inPs.uv0 * heightMapResolution ); + + ivec3 xN01; + xN01.x = max( iCoord.x - 1, 0 ); + xN01.y = iCoord.x; + xN01.z = min( iCoord.x + 1, int(heightMapResolution.x) ); + ivec3 yN01; + yN01.x = max( iCoord.y - 1, 0 ); + yN01.y = iCoord.y; + yN01.z = min( iCoord.y + 1, int(heightMapResolution.y) ); + + //Watch out! It's heightXY, but texelFetch uses YX. + float heightNN = texelFetch( heightMap, ivec2( xN01.x, yN01.x ), 0 ).x * vScale.y; + float heightN0 = texelFetch( heightMap, ivec2( xN01.y, yN01.x ), 0 ).x * vScale.y; + //float heightN1 = texelFetch( heightMap, ivec2( xN01.z, yN01.x ), 0 ).x * vScale.y; + + float height0N = texelFetch( heightMap, ivec2( xN01.x, yN01.y ), 0 ).x * vScale.y; + float height00 = texelFetch( heightMap, ivec2( xN01.y, yN01.y ), 0 ).x * vScale.y; + float height01 = texelFetch( heightMap, ivec2( xN01.z, yN01.y ), 0 ).x * vScale.y; + + //float height1N = texelFetch( heightMap, ivec2( xN01.x, yN01.z ), 0 ).x * vScale.y; + float height10 = texelFetch( heightMap, ivec2( xN01.y, yN01.z ), 0 ).x * vScale.y; + float height11 = texelFetch( heightMap, ivec2( xN01.z, yN01.z ), 0 ).x * vScale.y; + + vec3 vNN = vec3( -vScale.x, heightNN, -vScale.z ); + vec3 vN0 = vec3( -vScale.x, heightN0, 0 ); + //vec3 vN1 = vec3( -vScale.x, heightN1, vScale.z ); + + vec3 v0N = vec3( 0, height0N, -vScale.z ); + vec3 v00 = vec3( 0, height00, 0 ); + vec3 v01 = vec3( 0, height01, vScale.z ); + + //vec3 v1N = vec3( vScale.x, height1N, -vScale.z ); + vec3 v10 = vec3( vScale.x, height10, 0 ); + vec3 v11 = vec3( vScale.x, height11, vScale.z ); + + vec3 vNormal = vec3( 0, 0, 0 ); + + vNormal += cross( (v01 - v00), (v11 - v00) ); + vNormal += cross( (v11 - v00), (v10 - v00) ); + vNormal += cross( (v10 - v00), (v0N - v00) ); + vNormal += cross( (v0N - v00), (vNN - v00) ); + vNormal += cross( (vNN - v00), (vN0 - v00) ); + vNormal += cross( (vN0 - v00), (v01 - v00) ); + +// vNormal += cross( (v01 - v00), (v11 - v00) ); +// vNormal += cross( (v11 - v00), (v10 - v00) ); +// vNormal += cross( (v10 - v00), (v1N - v00) ); +// vNormal += cross( (v1N - v00), (v0N - v00) ); +// vNormal += cross( (v0N - v00), (vNN - v00) ); +// vNormal += cross( (vNN - v00), (vN0 - v00) ); +// vNormal += cross( (vN0 - v00), (vN1 - v00) ); +// vNormal += cross( (vN1 - v00), (v01 - v00) ); + + vNormal = normalize( vNormal ); + + //fragColour.xy = vNormal.zx; + fragColour = vec4( vNormal.zyx * 0.5 + 0.5, 1.0f ); +} diff --git a/ogre2/src/media/2.0/scripts/materials/Terra/GLSL/TerraGaussianBlur_cs.glsl b/ogre2/src/media/2.0/scripts/materials/Terra/GLSL/TerraGaussianBlur_cs.glsl new file mode 100644 index 000000000..5b7c02a75 --- /dev/null +++ b/ogre2/src/media/2.0/scripts/materials/Terra/GLSL/TerraGaussianBlur_cs.glsl @@ -0,0 +1,22 @@ +//Based on GPUOpen's samples SeparableFilter11 +//https://github.com/GPUOpen-LibrariesAndSDKs/SeparableFilter11 +//For better understanding, read "Efficient Compute Shader Programming" from Bill Bilodeau +//http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2012/10/Efficient%20Compute%20Shader%20Programming.pps + +//TL;DR: +// * Each thread works on 4 pixels at a time (for VLIW hardware, i.e. Radeon HD 5000 & 6000 series). +// * 256 pixels per threadgroup. Each threadgroup works on 2 rows of 128 pixels each. +// That means 32x2 threads = 64. 64 threads x 4 pixels per thread = 256 + +@piece( data_type )vec3@end +@piece( lds_data_type )vec3@end +@piece( lds_definition )shared vec3 g_f3LDS[ 2 ] [ @value( samples_per_threadgroup ) ];@end + +@piece( image_sample ) + return textureLod( inputImage, f2SamplePosition, 0 ).xyz; +@end + +@piece( image_store ) + @foreach( 4, iPixel ) + imageStore( outputImage, ivec2( i2Center + @iPixel * i2Inc ), vec4( outColour[ @iPixel ], 1.0 ) );@end +@end diff --git a/ogre2/src/media/2.0/scripts/materials/Terra/GLSL/TerraShadowGenerator.glsl b/ogre2/src/media/2.0/scripts/materials/Terra/GLSL/TerraShadowGenerator.glsl new file mode 100644 index 000000000..ba2d33123 --- /dev/null +++ b/ogre2/src/media/2.0/scripts/materials/Terra/GLSL/TerraShadowGenerator.glsl @@ -0,0 +1,152 @@ +#version 430 + +layout(std140) uniform; + +layout (rgb10_a2) uniform restrict writeonly image2D shadowMap; +uniform sampler2D heightMap; + +layout( local_size_x = @value( threads_per_group_x ), + local_size_y = @value( threads_per_group_y ), + local_size_z = @value( threads_per_group_z ) ) in; + +//in uvec3 gl_NumWorkGroups; +//in uvec3 gl_WorkGroupID; +//in uvec3 gl_LocalInvocationID; +//in uvec3 gl_GlobalInvocationID; +//in uint gl_LocalInvocationIndex; + +//Bresenham algorithm uniforms +uniform vec2 delta; + +uniform ivec2 xyStep; //(y0 < y1) ? 1 : -1; +uniform int isSteep; + +layout(binding = 0) uniform StartsBuffer +{ + ivec4 startXY[4096]; +}; + +struct PerGroupData +{ + int iterations; + float deltaErrorStart; + float padding0; + float padding1; +}; + +layout(binding = 1) uniform PerGroupDataBuffer +{ + PerGroupData perGroupData[4096]; +}; + +//Rendering uniforms +uniform float heightDelta; + +vec2 calcShadow( ivec2 xyPos, vec2 prevHeight ) +{ + prevHeight.x -= heightDelta; + prevHeight.y = prevHeight.y * 0.985 - heightDelta; //Used for the penumbra region + + float currHeight = texelFetch( heightMap, xyPos, 0 ).x; + + //float shadowValue = smoothstep( prevHeight.y, prevHeight.x, clamp( currHeight, prevHeight.y, prevHeight.x ) ); + float shadowValue = smoothstep( prevHeight.y, prevHeight.x, currHeight + 0.001 ); + //float shadowValue = currHeight + 0.001 < prevHeight.x ? 0.0 : 1.0; + prevHeight.x = currHeight >= prevHeight.x ? currHeight : prevHeight.x; + prevHeight.y = currHeight >= prevHeight.y ? currHeight : prevHeight.y; + + //We store shadow's height in 10 bits, but the actual heightmap is in 16 bits. + //If we have a height of 0.9775, it will translate to 999.98 rounding to 1000 + //Thus when sampling, the objects on top of the terrain will be shadowed by the + //terrain at that spot. Thus we subtract 1 from the height, and add 1 to + //invHeightLength for a smooth gradient (which also prevents div. by zero). + vec2 roundedHeight = floor( clamp( prevHeight.xy, 0, 1 ) * 1023.0 + 0.5 ) - 1.0; + float invHeightLength = 1.0 / (roundedHeight.x - roundedHeight.y + 1); //+1 Avoids div. by zero + roundedHeight.y *= 0.000977517; + + imageStore( shadowMap, xyPos, vec4( shadowValue, roundedHeight.y, invHeightLength, 1.0 ) ); + + return prevHeight; +} + +void main() +{ + vec2 prevHeight = vec2( 0.0, 0.0 ); + float error = delta.x * 0.5 + perGroupData[gl_WorkGroupID.x].deltaErrorStart; + + int x, y; + if( gl_GlobalInvocationID.x < 4096u ) + { + x = startXY[gl_GlobalInvocationID.x].x; + y = startXY[gl_GlobalInvocationID.x].y; + } + else + { + //Due to alignment nightmares, instead of doing startXY[8192]; + //we perform startXY[4096] and store the values in .zw instead of .xy + //It only gets used if the picture is very big. This branch is coherent as + //long as 4096 is multiple of threads_per_group_x. + x = startXY[gl_GlobalInvocationID.x - 4096u].z; + y = startXY[gl_GlobalInvocationID.x - 4096u].w; + } + + int numIterations = perGroupData[gl_WorkGroupID.x].iterations; + for( int i=0; i fabs(x1 - x0)); +// if(steep) +// { +// std::swap(x0, y0); +// std::swap(x1, y1); +// } + +// if(x0 > x1) +// { +// std::swap(x0, x1); +// std::swap(y0, y1); +// } + +// const float dx = x1 - x0; +// const float dy = fabs(y1 - y0); + +// float error = dx / 2.0f; +// const int ystep = (y0 < y1) ? 1 : -1; +// int y = (int)y0; + +// const int maxX = (int)x1; + +// for(int x=(int)x0; x 1 ) INTERPOLANT( float2 zwDepth, @counter(texcoord) ); diff --git a/ogre2/src/media/Hlms/Pbs/Any/ShadowMapping_piece_ps.any b/ogre2/src/media/Hlms/Pbs/Any/ShadowMapping_piece_ps.any index 58b8b7e01..29de66f33 100644 --- a/ogre2/src/media/Hlms/Pbs/Any/ShadowMapping_piece_ps.any +++ b/ogre2/src/media/Hlms/Pbs/Any/ShadowMapping_piece_ps.any @@ -131,6 +131,54 @@ /// Declare getShadow twice (two overloads). The second one manually /// performs clamp to border to deal with UV atlases. @piece( DeclShadowSamplingFuncs ) + + @property( shadows_receive_on_ps ) + @foreach( hlms_num_shadow_map_lights, n ) + @property( hlms_shadowmap@n_is_directional_light ) + #define inPs_posL@n worldPosToDirLightSpace( inPs.worldPos, passBuf.shadowRcv[@n] ) + @end + @property( hlms_shadowmap@n_is_spot ) + #define inPs_posL@n worldPosToSpotLightSpace( inPs.worldPos, passBuf.shadowRcv[@n] ) + @end + @end + + @foreach( 2, m ) + @property( @m == 0 ) + INLINE float4 worldPosToDirLightSpace( float3 worldPos, ShadowReceiverData shadowRcv ) + @else + INLINE float4 worldPosToSpotLightSpace( float3 worldPos, ShadowReceiverData shadowRcv ) + @end + { + @property( !exponential_shadow_maps ) + float4 lightSpacePos = mul( float4(worldPos.xyz, 1.0f), shadowRcv.texViewProj ); + @property( @m == 1 ) + // Spotlights only + lightSpacePos.z = lightSpacePos.z * shadowRcv.shadowDepthRange.y; + @end + @property( syntax == glsl || syntax == glsles ) + lightSpacePos.z = ( lightSpacePos.z * 0.5 ) + 0.5; + @end + @else + float4 lightSpacePos = mul( float4(worldPos.xyz, 1.0f), shadowRcv.texViewProj ); + + // It's the same as (float4( worldPos.xyz, 1 ) * texViewMatrix).z + lightSpacePos.z = + -( dot( worldPos.xyz, shadowRcv.texViewZRow.xyz ) + shadowRcv.texViewZRow.w ); + lightSpacePos.z = ( lightSpacePos.z - shadowRcv.shadowDepthRange.x ) * + shadowRcv.shadowDepthRange.y; + @end + + return lightSpacePos; + } + @end + @else + @foreach( hlms_num_shadow_map_lights, n ) + @property( !hlms_shadowmap@n_is_point_light ) + #define inPs_posL@n inPs.posL@n + @end + @end + @end + @foreach( 4, m ) @property( @m == 0 ) INLINE float getShadow( @insertpiece( TEXTURE2DSHADOW ) shadowMap, @insertpiece( SamplerShadow ) @@ -327,14 +375,14 @@ if( inPs.depth <= passBuf.pssmSplitPoints@value(CurrentShadowMap) ) { fShadow = getShadow( hlms_shadowmap@value(CurrentShadowMap), @insertpiece( UseSamplerShadow ) - inPs.posL0, + inPs_posL0, passBuf.shadowRcv[@value(CurrentShadowMap)].invShadowMapSize hlms_shadowmap@counter(CurrentShadowMap)_uv_param ); @property( hlms_pssm_blend ) if( inPs.depth > passBuf.pssmBlendPoints@value(CurrentShadowMapBlend) ) { fShadowBlend = getShadow( hlms_shadowmap@value(CurrentShadowMap), @insertpiece( UseSamplerShadow ) - inPs.posL1, + inPs_posL1, passBuf.shadowRcv[@value(CurrentShadowMap)].invShadowMapSize hlms_shadowmap@value(CurrentShadowMap)_uv_param ); fShadow = lerp( fShadow, fShadowBlend, @@ -350,14 +398,14 @@ else if( inPs.depth <= passBuf.pssmSplitPoints@value(CurrentShadowMap) ) { fShadow = getShadow( hlms_shadowmap@value(CurrentShadowMap), @insertpiece( UseSamplerShadow ) - inPs.posL@n, + inPs_posL@n, passBuf.shadowRcv[@value(CurrentShadowMap)].invShadowMapSize hlms_shadowmap@counter(CurrentShadowMap)_uv_param ); @property( hlms_pssm_blend && @n < hlms_pssm_splits_minus_one ) if( inPs.depth > passBuf.pssmBlendPoints@value(CurrentShadowMapBlend) ) { fShadowBlend = getShadow( hlms_shadowmap@value(CurrentShadowMap), @insertpiece( UseSamplerShadow ) - inPs.posL@value(CurrentShadowMap), + inPs_posL@value(CurrentShadowMap), passBuf.shadowRcv[@value(CurrentShadowMap)].invShadowMapSize hlms_shadowmap@value(CurrentShadowMap)_uv_param ); fShadow = lerp( fShadow, fShadowBlend, @@ -386,7 +434,7 @@ @end @property( !hlms_pssm_splits && hlms_num_shadow_map_lights && hlms_lights_directional ) @property( receive_shadows ) float fShadow = getShadow( hlms_shadowmap@value(CurrentShadowMap), @insertpiece( UseSamplerShadow ) - inPs.posL0, + inPs_posL0, passBuf.shadowRcv[@value(CurrentShadowMap)].invShadowMapSize hlms_shadowmap@counter(CurrentShadowMap)_uv_param ); @end @property( !receive_shadows ) @@ -399,7 +447,7 @@ @piece( DarkenWithShadowFirstLight )* fShadow@end @piece( DarkenWithShadow ) * getShadow( hlms_shadowmap@value(CurrentShadowMap), @insertpiece( UseSamplerShadow ) - inPs.posL@value(CurrentShadowMap), + inPs_posL@value(CurrentShadowMap), passBuf.shadowRcv[@value(CurrentShadowMap)].invShadowMapSize hlms_shadowmap@counter(CurrentShadowMap)_uv_param ) @end diff --git a/ogre2/src/media/Hlms/Pbs/Any/ShadowMapping_piece_vs.any b/ogre2/src/media/Hlms/Pbs/Any/ShadowMapping_piece_vs.any index ee2df78fe..677ac5883 100644 --- a/ogre2/src/media/Hlms/Pbs/Any/ShadowMapping_piece_vs.any +++ b/ogre2/src/media/Hlms/Pbs/Any/ShadowMapping_piece_vs.any @@ -2,40 +2,46 @@ //#include "SyntaxHighlightingMisc.h" @property( !hlms_shadowcaster ) - @property( !exponential_shadow_maps ) - @piece( DoShadowReceiveVS ) - @foreach( hlms_num_shadow_map_lights, n ) - @property( !hlms_shadowmap@n_is_point_light ) - outVs.posL@n = mul( float4(worldPos.xyz, 1.0f), passBuf.shadowRcv[@n].texViewProj );@end @end + @property( !shadows_receive_on_ps ) + @property( !exponential_shadow_maps ) + @piece( DoShadowReceiveVS ) + @foreach( hlms_num_shadow_map_lights, n ) + @property( !hlms_shadowmap@n_is_point_light ) + outVs.posL@n = mul( float4(worldPos.xyz, 1.0f), passBuf.shadowRcv[@n].texViewProj ); - @foreach( hlms_num_shadow_map_lights, n ) - @property( !hlms_shadowmap@n_is_point_light ) - @property( !hlms_shadowmap@n_is_directional_light && hlms_no_reverse_depth ) - outVs.posL@n.z = outVs.posL@n.z * passBuf.shadowRcv[@n].shadowDepthRange.y; + @property( !hlms_shadowmap@n_is_directional_light && hlms_no_reverse_depth ) + outVs.posL@n.z = outVs.posL@n.z * passBuf.shadowRcv[@n].shadowDepthRange.y; + @end + @property( hlms_no_reverse_depth && (syntax == glsl || syntax == glsles) ) + outVs.posL@n.z = (outVs.posL@n.z * 0.5) + 0.5; + @end @end - @property( hlms_no_reverse_depth && (syntax == glsl || syntax == glsles) )outVs.posL@n.z = (outVs.posL@n.z * 0.5) + 0.5;@end @end + + @property( hlms_pssm_splits )outVs.depth = outVs_Position.w;@end @end + @else + @piece( DoShadowReceiveVS ) + @foreach( hlms_num_shadow_map_lights, n ) + @property( !hlms_shadowmap@n_is_point_light ) + outVs.posL@n = mul( float4(worldPos.xyz, 1.0f), passBuf.shadowRcv[@n].texViewProj ); - @property( hlms_pssm_splits )outVs.depth = outVs_Position.w;@end - @end - @end - @property( exponential_shadow_maps ) - @piece( DoShadowReceiveVS ) - @foreach( hlms_num_shadow_map_lights, n ) - @property( !hlms_shadowmap@n_is_point_light ) - outVs.posL@n = mul( float4(worldPos.xyz, 1.0f), passBuf.shadowRcv[@n].texViewProj );@end @end + //It's the same as (float4( worldPos.xyz, 1 ) * texViewMatrix).z + outVs.posL@n.z = -(dot( worldPos.xyz, passBuf.shadowRcv[@n].texViewZRow.xyz ) + passBuf.shadowRcv[@n].texViewZRow.w); + outVs.posL@n.z = (outVs.posL@n.z - passBuf.shadowRcv[@n].shadowDepthRange.x) + * passBuf.shadowRcv[@n].shadowDepthRange.y; + @end + @end - @foreach( hlms_num_shadow_map_lights, n ) - @property( !hlms_shadowmap@n_is_point_light ) - //It's the same as (float4( worldPos.xyz, 1 ) * texViewMatrix).z - outVs.posL@n.z = -(dot( worldPos.xyz, passBuf.shadowRcv[@n].texViewZRow.xyz ) + passBuf.shadowRcv[@n].texViewZRow.w); - outVs.posL@n.z = (outVs.posL@n.z - passBuf.shadowRcv[@n].shadowDepthRange.x) - * passBuf.shadowRcv[@n].shadowDepthRange.y; + @property( hlms_pssm_splits )outVs.depth = outVs_Position.w;@end + @end + @end + @else + @property( hlms_num_shadow_map_lights && !hlms_all_point_lights ) + @piece( DoShadowReceiveVS ) + outVs.worldPos.xyz = worldPos.xyz; + @property( hlms_pssm_splits )outVs.depth = outVs_Position.z;@end @end @end - - @property( hlms_pssm_splits )outVs.depth = outVs_Position.w;@end - @end @end @end diff --git a/ogre2/src/media/Hlms/Pbs/GLSL/VertexShader_vs.glsl b/ogre2/src/media/Hlms/Pbs/GLSL/VertexShader_vs.glsl index 044f9fa54..65cce86a7 100644 --- a/ogre2/src/media/Hlms/Pbs/GLSL/VertexShader_vs.glsl +++ b/ogre2/src/media/Hlms/Pbs/GLSL/VertexShader_vs.glsl @@ -4,8 +4,8 @@ out gl_PerVertex { vec4 gl_Position; -@property( hlms_global_clip_planes ) - float gl_ClipDistance[@value(hlms_global_clip_planes)]; +@property( hlms_pso_clip_distances ) + float gl_ClipDistance[@value(hlms_pso_clip_distances)]; @end }; diff --git a/ogre2/src/media/Hlms/Terra/Any/500.Structs_piece_vs_piece_ps.any b/ogre2/src/media/Hlms/Terra/Any/500.Structs_piece_vs_piece_ps.any new file mode 100644 index 000000000..c30ac6824 --- /dev/null +++ b/ogre2/src/media/Hlms/Terra/Any/500.Structs_piece_vs_piece_ps.any @@ -0,0 +1,99 @@ +@piece( TerraMaterialStructDecl ) +//Uniforms that change per Item/Entity, but change very infrequently +struct Material +{ + /* kD is already divided by PI to make it energy conserving. + (formula is finalDiffuse = NdotL * surfaceDiffuse / PI) + */ + float4 kD; //kD.w is shadowConstantBias + float4 roughness; + float4 metalness; + float4 detailOffsetScale[4]; + + @property( syntax != metal ) + uint4 indices0_7; + uint4 indices8_15; + uint4 indices16_24; + @else + ushort diffuseIdx; + ushort weightMapIdx; + ushort detailMapIdx0; + ushort detailMapIdx1; + ushort detailMapIdx2; + ushort detailMapIdx3; + ushort detailNormMapIdx0; + ushort detailNormMapIdx1; + ushort detailNormMapIdx2; + ushort detailNormMapIdx3; + ushort detailRoughnessIdx0; + ushort detailRoughnessIdx1; + ushort detailRoughnessIdx2; + ushort detailRoughnessIdx3; + ushort detailMetalnessIdx0; + ushort detailMetalnessIdx1; + ushort detailMetalnessIdx2; + ushort detailMetalnessIdx3; + ushort envMapIdx; + @end + + @insertpiece( custom_materialBuffer ) +}; + + @property( syntax != metal ) + CONST_BUFFER( MaterialBuf, 1 ) + { + Material materialArray[1]; + }; + @end +@end + +@property( syntax == metal ) + @piece( TerraMaterialDecl ) + , constant Material *materialArray [[buffer(CONST_SLOT_START+1)]] + @end +@end + +@piece( TerraInstanceStructDecl ) +struct CellData +{ + //.x = numVertsPerLine + //.y = lodLevel + //.z = vao->getPrimitiveCount() / m_verticesPerLine - 2u + //.w = skirtY (float) + uint4 numVertsPerLine; + int4 xzTexPosBounds; //XZXZ + float4 pos; //.w contains 1.0 / xzTexPosBounds.z + float4 scale; //.w contains 1.0 / xzTexPosBounds.w +}; + @property( syntax != metal ) + //Uniforms that change per Item/Entity + CONST_BUFFER( InstanceBuffer, 2 ) + { + CellData cellDataArray[256]; + }; + @end +@end + +@property( syntax == metal ) + @piece( TerraInstanceDecl ) + , constant CellData *cellDataArray [[buffer(CONST_SLOT_START+2)]] + @end +@end + +//Reset texcoord to 0 for every shader stage (since values are preserved). +@pset( texcoord, 0 ) + +@piece( Terra_VStoPS_block ) + INTERPOLANT( float3 pos, @counter(texcoord) ); + INTERPOLANT( float2 uv0, @counter(texcoord) ); + @insertpiece( VStoPS_block ) +@end + +@undefpiece( DeclareObjLightMask ) +@property( hlms_fine_light_mask || hlms_forwardplus_fine_light_mask ) + @property( syntax == metal ) + @piece( DeclareObjLightMask )uint objLightMask = 0xFFFFFFFFu;@end + @else + @piece( DeclareObjLightMask )uint objLightMask = 0xFFFFFFFFu;@end + @end +@end diff --git a/ogre2/src/media/Hlms/Terra/Any/800.PixelShader_piece_ps.any b/ogre2/src/media/Hlms/Terra/Any/800.PixelShader_piece_ps.any new file mode 100644 index 000000000..1e3272840 --- /dev/null +++ b/ogre2/src/media/Hlms/Terra/Any/800.PixelShader_piece_ps.any @@ -0,0 +1,401 @@ + +#include "/media/matias/Datos/SyntaxHighlightingMisc.h" + +@piece( DefaultTerraHeaderPS ) + @insertpiece( DefaultHeaderPS ) + @property( !hlms_prepass && needs_view_dir ) + @insertpiece( DeclareBRDF ) + @insertpiece( DeclareBRDF_InstantRadiosity ) + @insertpiece( DeclareBRDF_AreaLightApprox ) + @end + + #define SampleMetalness0( tex, sampler, uv, arrayIdx ) OGRE_SampleArray2D( tex, sampler, uv, arrayIdx ) + #define SampleMetalness1( tex, sampler, uv, arrayIdx ) OGRE_SampleArray2D( tex, sampler, uv, arrayIdx ) + #define SampleMetalness2( tex, sampler, uv, arrayIdx ) OGRE_SampleArray2D( tex, sampler, uv, arrayIdx ) + #define SampleMetalness3( tex, sampler, uv, arrayIdx ) OGRE_SampleArray2D( tex, sampler, uv, arrayIdx ) + + #define SampleRoughness0( tex, sampler, uv, arrayIdx ) OGRE_SampleArray2D( tex, sampler, uv, arrayIdx ) + #define SampleRoughness1( tex, sampler, uv, arrayIdx ) OGRE_SampleArray2D( tex, sampler, uv, arrayIdx ) + #define SampleRoughness2( tex, sampler, uv, arrayIdx ) OGRE_SampleArray2D( tex, sampler, uv, arrayIdx ) + #define SampleRoughness3( tex, sampler, uv, arrayIdx ) OGRE_SampleArray2D( tex, sampler, uv, arrayIdx ) + + #define OGRE_UNPACK_X_2x16( packedInt ) ((packedInt) & 0x0000FFFFu) + #define OGRE_UNPACK_Y_2x16( packedInt ) ((packedInt) >> 16u) +@end + +//----------------------------------------------------------------------------- +// BODY CODE +//----------------------------------------------------------------------------- + +@undefpiece( UnpackTextureIndices0 ) + +@piece( UnpackTextureIndices0 ) + @property( syntax == metal ) + @property( diffuse_map ) ushort texIndex_diffuseIdx = material.diffuseIdx;@end + @property( detail_weight_map ) ushort texIndex_weightMapIdx = material.weightMapIdx;@end + + @property( detail_map0 ) ushort texIndex_detailMapIdx0 = material.detailMapIdx0;@end + @property( detail_map1 ) ushort texIndex_detailMapIdx1 = material.detailMapIdx1;@end + @property( detail_map2 ) ushort texIndex_detailMapIdx2 = material.detailMapIdx2;@end + @property( detail_map3 ) ushort texIndex_detailMapIdx3 = material.detailMapIdx3;@end + + @property( detail_map_nm0 ) ushort texIndex_detailNormMapIdx0 = material.detailNormMapIdx0;@end + @property( detail_map_nm1 ) ushort texIndex_detailNormMapIdx1 = material.detailNormMapIdx1;@end + @property( detail_map_nm2 ) ushort texIndex_detailNormMapIdx2 = material.detailNormMapIdx2;@end + @property( detail_map_nm3 ) ushort texIndex_detailNormMapIdx3 = material.detailNormMapIdx3;@end + + @property( roughness_map0 ) ushort texIndex_detailRoughnessIdx0 = material.detailRoughnessIdx0;@end + @property( roughness_map1 ) ushort texIndex_detailRoughnessIdx1 = material.detailRoughnessIdx1;@end + @property( roughness_map2 ) ushort texIndex_detailRoughnessIdx2 = material.detailRoughnessIdx2;@end + @property( roughness_map3 ) ushort texIndex_detailRoughnessIdx3 = material.detailRoughnessIdx3;@end + + @property( metalness_map0 ) ushort texIndex_detailMetalnessIdx0 = material.detailMetalnessIdx0;@end + @property( metalness_map1 ) ushort texIndex_detailMetalnessIdx1 = material.detailMetalnessIdx1;@end + @property( metalness_map2 ) ushort texIndex_detailMetalnessIdx2 = material.detailMetalnessIdx2;@end + @property( metalness_map3 ) ushort texIndex_detailMetalnessIdx3 = material.detailMetalnessIdx3;@end + @else + @property( diffuse_map ) ushort texIndex_diffuseIdx = OGRE_UNPACK_X_2x16( material.indices0_7.x );@end + @property( detail_weight_map ) ushort texIndex_weightMapIdx = OGRE_UNPACK_Y_2x16( material.indices0_7.x );@end + + @property( detail_map0 ) ushort texIndex_detailMapIdx0 = OGRE_UNPACK_X_2x16( material.indices0_7.y );@end + @property( detail_map1 ) ushort texIndex_detailMapIdx1 = OGRE_UNPACK_Y_2x16( material.indices0_7.y );@end + @property( detail_map2 ) ushort texIndex_detailMapIdx2 = OGRE_UNPACK_X_2x16( material.indices0_7.z );@end + @property( detail_map3 ) ushort texIndex_detailMapIdx3 = OGRE_UNPACK_Y_2x16( material.indices0_7.z );@end + + @property( detail_map_nm0 ) ushort texIndex_detailNormMapIdx0 = OGRE_UNPACK_X_2x16( material.indices0_7.w );@end + @property( detail_map_nm1 ) ushort texIndex_detailNormMapIdx1 = OGRE_UNPACK_Y_2x16( material.indices0_7.w );@end + @property( detail_map_nm2 ) ushort texIndex_detailNormMapIdx2 = OGRE_UNPACK_X_2x16( material.indices8_15.x );@end + @property( detail_map_nm3 ) ushort texIndex_detailNormMapIdx3 = OGRE_UNPACK_Y_2x16( material.indices8_15.x );@end + + @property( roughness_map0 ) ushort texIndex_detailRoughnessIdx0 = OGRE_UNPACK_X_2x16( material.indices8_15.y );@end + @property( roughness_map1 ) ushort texIndex_detailRoughnessIdx1 = OGRE_UNPACK_Y_2x16( material.indices8_15.y );@end + @property( roughness_map2 ) ushort texIndex_detailRoughnessIdx2 = OGRE_UNPACK_X_2x16( material.indices8_15.z );@end + @property( roughness_map3 ) ushort texIndex_detailRoughnessIdx3 = OGRE_UNPACK_Y_2x16( material.indices8_15.z );@end + + @property( metalness_map0 ) ushort texIndex_detailMetalnessIdx0 = OGRE_UNPACK_X_2x16( material.indices8_15.w );@end + @property( metalness_map1 ) ushort texIndex_detailMetalnessIdx1 = OGRE_UNPACK_Y_2x16( material.indices8_15.w );@end + @property( metalness_map2 ) ushort texIndex_detailMetalnessIdx2 = OGRE_UNPACK_X_2x16( material.indices16_24.x );@end + @property( metalness_map3 ) ushort texIndex_detailMetalnessIdx3 = OGRE_UNPACK_Y_2x16( material.indices16_24.x );@end + @end +@end +@undefpiece( UnpackTextureIndices1 ) + +@undefpiece( SampleDiffuseMap ) +@piece( SampleDiffuseMap ) + /// DIFFUSE MAP + @property( diffuse_map ) + pixelData.diffuse = SampleDiffuse( textureMaps@value( diffuse_map_idx ), + samplerState@value(diffuse_map_sampler), + UV_DIFFUSE( inPs.uv@value(uv_diffuse).xy ), + texIndex_diffuseIdx ); + @else + /// If there are no diffuse maps, we must initialize it to some value. + pixelData.diffuse.xyzw = float4( 1, 1, 1, 1 ); + @end + + @foreach( detail_maps_diffuse, n ) + @property( !detail_map@n )float3 detailCol@n = float3( 0.0f, 0.0f, 0.0f );@end + @end + + + @property( !detail_maps_diffuse && !detail_maps_normal ) + float4 detailWeights = float4( 0.25f, 0.25f, 0.25f, 0.25f ); + @insertpiece( ign_weights ) + @else + // IGN CUSTOMIZE BEGIN + //pixelData.diffuse.xyz *= (detailCol0.xyz * detailWeights.x + detailCol1.xyz * detailWeights.y) + + // (detailCol2.xyz * detailWeights.z + detailCol3.xyz * detailWeights.w); + @insertpiece( ign_weights ) + // IGN CUSTOMIZE END + @end + + /// Apply the material's diffuse over the textures + pixelData.diffuse.xyz *= material.kD.xyz; +@end + +@undefpiece( SampleSpecularMap ) +@piece( SampleSpecularMap ) + /// SPECUlAR MAP + @foreach( 4, n ) + @property( metalness_map@n ) + float metalness@n = SampleMetalness@n( textureMaps@value( metalness_map@n_idx ), + samplerState@value(metalness_map@n_sampler), + inPs.uv0.xy * material.detailOffsetScale[@n].zw + + material.detailOffsetScale[@n].xy, + texIndex_detailMetalnessIdx@n ).x; + @else + float metalness@n = 0; + @end + @end + + pixelData.specular.xyz = float3( 1.0f, 1.0f, 1.0f ); + + // IGN CUSTOMIZE BEGIN + // float metalness = (metalness0 * detailWeights.x * material.metalness.x + + // metalness1 * detailWeights.y * material.metalness.y) + + // (metalness2 * detailWeights.z * material.metalness.z + + // metalness3 * detailWeights.w * material.metalness.w); + float metalness = 0.0f; + metalness = lerp( metalness, metalness0, detailWeights.x ); + metalness = lerp( metalness, metalness1, detailWeights.y ); + metalness = lerp( metalness, metalness2, detailWeights.z ); + metalness = lerp( metalness, metalness3, detailWeights.w ); + // IGN CUSTOMIZE END + + pixelData.F0 = lerp( float3( 0.03f, 0.03f, 0.03f ), pixelData.diffuse.xyz * 3.14159f, metalness ); + pixelData.diffuse.xyz = pixelData.diffuse.xyz - pixelData.diffuse.xyz * metalness; +@end + +@undefpiece( SampleRoughnessMap ) +@piece( SampleRoughnessMap ) + /// ROUGHNESS MAP + @foreach( 4, n ) + @property( roughness_map@n ) + float roughness@n = SampleRoughness@n( textureMaps@value( roughness_map@n_idx ), + samplerState@value(roughness_map@n_sampler), + inPs.uv0.xy * material.detailOffsetScale[@n].zw + + material.detailOffsetScale[@n].xy, + texIndex_detailRoughnessIdx@n ).x; + @else + float roughness@n = 0; + @end + @end + + // IGN CUSTOMIZE BEGIN + // pixelData.roughness = (roughness0 * detailWeights.x * material.roughness.x + + // roughness1 * detailWeights.y * material.roughness.y) + + // (roughness2 * detailWeights.z * material.roughness.z + + // roughness3 * detailWeights.w * material.roughness.w); + float roughness = 1.0f; + roughness = lerp( roughness, roughness0, detailWeights.x ); + roughness = lerp( roughness, roughness1, detailWeights.y ); + roughness = lerp( roughness, roughness2, detailWeights.z ); + roughness = lerp( roughness, roughness3, detailWeights.w ); + // IGN CUSTOMIZE END + pixelData.roughness = max( pixelData.roughness, 0.001f ); +@end + +@undefpiece( LoadNormalData ) +@piece( LoadNormalData ) + // Geometric normal + pixelData.geomNormal = OGRE_Sample( terrainNormals, samplerStateTerra, inPs.uv0.xy ).xyz * 2.0 - 1.0; + @property( z_up ) + pixelData.geomNormal.yz = float2( -pixelData.geomNormal.z, pixelData.geomNormal.y ); + @end + pixelData.geomNormal = mul( pixelData.geomNormal, toFloat3x3( passBuf.view ) ); + @property( normal_map ) + //Get the TBN matrix + float3 viewSpaceUnitX = mul( float3( 1, 0, 0 ) , toFloat3x3( passBuf.view ) ); + float3 vTangent = normalize( cross( pixelData.geomNormal, viewSpaceUnitX ) ); + float3 vBinormal = cross( vTangent, pixelData.geomNormal ); + float3x3 TBN = buildFloat3x3( vBinormal, vTangent, pixelData.geomNormal ); + @end +@end + +@piece( DefaultTerraBodyPS ) + PixelData pixelData; + + @insertpiece( LoadMaterial ) + @insertpiece( UnpackTextureIndices0 ) + @insertpiece( UnpackTextureIndices1 ) + @insertpiece( DeclareObjLightMask ) + @insertpiece( custom_ps_posMaterialLoad ) + + @insertpiece( LoadDetailWeights ) + + @insertpiece( SampleDetailMaps ) + + @property( !hlms_prepass ) + @insertpiece( SampleDiffuseMap ) + @end + + @insertpiece( SampleSpecularMap ) + @insertpiece( SampleRoughnessMap ) + + @insertpiece( forwardPlusDoDecals ) + + @property( !hlms_use_prepass ) + @insertpiece( LoadNormalData ) + @insertpiece( SampleAndApplyDetailNormalMaps ) + + @insertpiece( custom_ps_posSampleNormal ) + + @insertpiece( forwardPlusApplyDecalsNormal ) + + @property( normal_map ) + pixelData.normal = normalize( mul( TBN, pixelData.normal ) ); + @end + + @insertpiece( DoDirectionalShadowMaps ) + + @end @property( hlms_use_prepass ) + rshort2 iFragCoord = rshort2( gl_FragCoord.x, + @property( !hlms_forwardplus_flipY && syntax == glsl )passBuf.windowHeight.x - @end + gl_FragCoord.y ); + + @property( hlms_use_prepass_msaa ) + //SV_Coverage/gl_SampleMaskIn is always before depth & stencil tests, + //so we need to perform the test ourselves + //See http://www.yosoygames.com.ar/wp/2017/02/beware-of-sv_coverage/ + uint sampleMask = uint( gl_SampleMaskIn0 ); + float msaaDepth; + uint subsampleDepthMask; + float pixelDepthZ; + float pixelDepthW; + float2 pixelDepthZW; + float pixelDepth; + int intPixelDepth; + int intMsaaDepth; + //Unfortunately there are precision errors, so we allow some ulp errors. + //200 & 5 are arbitrary, but were empirically found to be very good values. + int ulpError = int( lerp( 200.0, 5.0, gl_FragCoord.z ) ); + @foreach( hlms_use_prepass_msaa, n ) + pixelDepthZW = interpolateAtSample( inPs.zwDepth, @n ); + pixelDepthZ = pixelDepthZW.x; + pixelDepthW = pixelDepthZW.y; + pixelDepth = pixelDepthZ / pixelDepthW; + msaaDepth = OGRE_Load2DMS( gBuf_depthTexture, iFragCoord.xy, @n ).x; + intPixelDepth = floatBitsToInt( pixelDepth ); + intMsaaDepth = floatBitsToInt( msaaDepth ); + subsampleDepthMask = (abs( intPixelDepth - intMsaaDepth ) <= ulpError) ? 0xffffffffu : ~(1u << @nu); + //subsampleDepthMask = int( (pixelDepth <= msaaDepth) ? 0xffffffffu : ~(1u << @nu) ); + sampleMask &= subsampleDepthMask; + @end + + sampleMask = sampleMask == 0u ? 1u : sampleMask; + + int gBufSubsample = int( findLSB( sampleMask ) ); + + pixelData.normal = normalize( OGRE_Load2DMS( gBuf_normals, iFragCoord, gBufSubsample ).xyz * 2.0 - 1.0 ); + float2 shadowRoughness = OGRE_Load2DMS( gBuf_shadowRoughness, iFragCoord, gBufSubsample ).xy; + @else + pixelData.normal = normalize( OGRE_Load2D( gBuf_normals, iFragCoord, 0 ).xyz * 2.0 - 1.0 ); + float2 shadowRoughness = OGRE_Load2D( gBuf_shadowRoughness, iFragCoord, 0 ).xy; + @end + + float fShadow = shadowRoughness.x; + + @property( roughness_map ) + pixelData.roughness = shadowRoughness.y * 0.98 + 0.02; + @end + @end + + @property( !hlms_prepass ) + @insertpiece( LightingHeader ) + + @insertpiece( custom_ps_preLights ) + + @property( !custom_disable_directional_lights ) + float fTerrainShadow = OGRE_Sample( terrainShadows, samplerStateTerra, inPs.uv0.xy ).x; + @property( !(hlms_pssm_splits || (!hlms_pssm_splits && hlms_num_shadow_map_lights && hlms_lights_directional)) ) + float fShadow = 1.0f; + @end + fShadow *= fTerrainShadow; + + @insertpiece( DoDirectionalLights ) + @end + + @insertpiece( DoPointLights ) + @insertpiece( DoSpotLights ) + + @insertpiece( DoAreaApproxLights ) + @insertpiece( DoAreaLtcLights ) + + @insertpiece( forward3dLighting ) + + @property( needs_env_brdf && (use_envprobe_map || hlms_use_ssr || use_planar_reflections || vct_num_probes) ) + pixelData.envColourS = float3( 0, 0, 0 ); + pixelData.envColourD = float3( 0, 0, 0 ); + @end + + @insertpiece( applyVoxelConeTracing ) + + @insertpiece( forwardPlusDoCubemaps ) + @insertpiece( applyIrradianceVolumes ) + + @insertpiece( DoEmissiveLight ) + + @property( use_envprobe_map ) + @property( use_parallax_correct_cubemaps && !hlms_enable_cubemaps_auto ) + @insertpiece( CubemapManualPcc ) + @end @property( !use_parallax_correct_cubemaps ) + @insertpiece( CubemapGlobal ) + @end + @end + + @property( hlms_use_ssr ) + //TODO: SSR pass should be able to combine global & local cubemap. + float4 ssrReflection = OGRE_Load2D( ssrTexture, iFragCoord, 0 ).xyzw; + @property( use_envprobe_map ) + pixelData.envColourS = lerp( pixelData.envColourS.xyz, ssrReflection.xyz, ssrReflection.w ); + @else + pixelData.envColourS += ssrReflection.xyz * ssrReflection.w; + @end + @end + + @insertpiece( DoPlanarReflectionsPS ) + + @property( ambient_hemisphere ) + @property( use_envprobe_map || hlms_use_ssr || use_planar_reflections || vct_num_probes ) + @property( vct_num_probes ) + //Only use ambient lighting if object is outside any VCT probe + if( vctSpecular.w == 0 ) + { + @end + pixelData.envColourS += lerp( passBuf.ambientLowerHemi.xyz, passBuf.ambientUpperHemi.xyz, ambientWD ); + pixelData.envColourD += lerp( passBuf.ambientLowerHemi.xyz, passBuf.ambientUpperHemi.xyz, ambientWS ); + @property( vct_num_probes ) + } + @end + @else + pixelData.envColourS = lerp( passBuf.ambientLowerHemi.xyz, passBuf.ambientUpperHemi.xyz, ambientWD ); + pixelData.envColourD = lerp( passBuf.ambientLowerHemi.xyz, passBuf.ambientUpperHemi.xyz, ambientWS ); + @end + @end + @property( ambient_fixed && vct_num_probes ) + //Only use ambient lighting if object is outside any VCT probe + finalColour += vctSpecular.w == 0 ? float3( 0, 0, 0 ) : + (passBuf.ambientUpperHemi.xyz * pixelData.diffuse.xyz); + @end + + @property( needs_env_brdf ) + @insertpiece( BRDF_EnvMap ) + @end + @end ///!hlms_prepass + + @property( !hlms_render_depth_only ) + @property( !hlms_prepass ) + @property( !hw_gamma_write ) + //Linear to Gamma space + outPs_colour0.xyz = sqrt( finalColour ); + @else + outPs_colour0.xyz = finalColour; + @end + + @property( hlms_alphablend ) + @property( use_texture_alpha ) + outPs_colour0.w = material.F0.w * pixelData.diffuse.w; + @else + outPs_colour0.w = material.F0.w; + @end + @else + outPs_colour0.w = 1.0; + @end + + @property( debug_pssm_splits ) + outPs_colour0.xyz = mix( outPs_colour0.xyz, debugPssmSplit.xyz, 0.2f ); + @end + + @property( hlms_gen_normals_gbuffer ) + outPs_normals = float4( pixelData.normal * 0.5 + 0.5, 1.0 ); + @end + @else + outPs_normals = float4( pixelData.normal * 0.5 + 0.5, 1.0 ); + @property( hlms_pssm_splits ) + outPs_shadowRoughness = float2( fShadow, (pixelData.roughness - 0.02) * 1.02040816 ); + @end @property( !hlms_pssm_splits ) + outPs_shadowRoughness = float2( 1.0, (pixelData.roughness - 0.02) * 1.02040816 ); + @end + @end + @end +@end ///DefaultTerraBodyPS diff --git a/ogre2/src/media/Hlms/Terra/Any/800.VertexShader_piece_vs.any b/ogre2/src/media/Hlms/Terra/Any/800.VertexShader_piece_vs.any new file mode 100644 index 000000000..2a01fb965 --- /dev/null +++ b/ogre2/src/media/Hlms/Terra/Any/800.VertexShader_piece_vs.any @@ -0,0 +1,145 @@ + +#include "/media/matias/Datos/SyntaxHighlightingMisc.h" + +//To render a 2x2 (quads) terrain: +//You'll normally need 6 vertices per line + 2 for degenerates. +//You'll need 8 vertices per line. +//So you'll need a total of 16 vertices. + +//To render a 4x2 (quads) terrain: +//You'll need 10 vertices per line. +//If we include degenerate vertices, you'll need 12 per line +//So you'll need a total of 24 vertices. +//in int inVs_vertexId; + +@piece( DefaultTerraHeaderVS ) + // START UNIFORM DECLARATION + @insertpiece( PassStructDecl ) + @insertpiece( TerraInstanceStructDecl ) + @property( hlms_shadowcaster ) + @insertpiece( TerraMaterialStructDecl ) + #define material materialArray[0] + @end + @insertpiece( custom_vs_uniformStructDeclaration ) + // END UNIFORM DECLARATION +@end + +@piece( VertexTerraTransform ) + //Lighting is in view space + @property( !hlms_shadowcaster ) + outVs.pos = mul( float4(worldPos.xyz, 1.0f), passBuf.view ).xyz; + @end + @property( !hlms_dual_paraboloid_mapping ) + @property( !hlms_instanced_stereo ) + outVs_Position = mul( float4(worldPos.xyz, 1.0f), passBuf.viewProj ); + @else + outVs_Position = mul( float4(worldPos.xyz, 1.0f), passBuf.viewProj[(inVs_stereoDrawId & 0x01u)] ); + @property( hlms_forwardplus ) + outVs.cullCamPosXY.xyz = mul( float4( outVs.pos.xyz, 1.0f ), + passBuf.leftEyeViewSpaceToCullCamClipSpace ).xyw; + @end + @end + @else + //Dual Paraboloid Mapping + outVs_Position.w = 1.0f; + outVs_Position.xyz = outVs.pos; + float L = length( outVs_Position.xyz ); + outVs_Position.z += 1.0f; + outVs_Position.xy /= outVs_Position.z; + outVs_Position.z = (L - NearPlane) / (FarPlane - NearPlane); + @end +@end + +@piece( DefaultTerraBodyVS ) + CellData cellData = cellDataArray[inVs_drawId]; + + //Map pointInLine from range [0; 12) to range [0; 9] so that it reads: + // 0 0 1 2 3 4 5 6 7 8 9 9 + uint pointInLine = uint(inVs_vertexId) % (cellData.numVertsPerLine.x); //cellData.numVertsPerLine.x = 12 + pointInLine = uint(clamp( int(pointInLine) - 1, 0, int(cellData.numVertsPerLine.x - 3u) )); + + uint2 uVertexPos; + + uVertexPos.x = pointInLine >> 1u; + //Even numbers are the next line, odd numbers are current line. + uVertexPos.y = (pointInLine & 0x01u) == 0u ? 1u : 0u; + uVertexPos.y += uint(inVs_vertexId) / cellData.numVertsPerLine.x; + //uVertexPos.y += floor( (float)inVs_vertexId / (float)cellData.numVertsPerLine ); Could be faster on GCN. + + @property( use_skirts ) + //Apply skirt. + bool isSkirt =( pointInLine <= 1u || + pointInLine >= (cellData.numVertsPerLine.x - 4u) || + uVertexPos.y == 0u || + uVertexPos.y == (cellData.numVertsPerLine.z + 2u) ); + + //Now shift X position for the left & right skirts + uVertexPos.x = uint( max( int(uVertexPos.x) - 1, 0 ) ); + uVertexPos.x = min( uVertexPos.x, ((cellData.numVertsPerLine.x - 7u) >> 1u) ); + // uVertexPos.x becomes: + // 0 0 0 1 1 2 2 3 3 4 4 4 + // 0 0 0 0 0 1 1 2 2 3 3 3 + // 0 0 0 0 0 1 1 2 2 2 2 2 + + //Now shift Y position for the front & back skirts + uVertexPos.y = uint( max( int(uVertexPos.y) - 1, 0 ) ); + uVertexPos.y = min( uVertexPos.y, cellData.numVertsPerLine.z ); + @end + + uint lodLevel = cellData.numVertsPerLine.y; + uVertexPos = uVertexPos << lodLevel; + + uVertexPos.xy = uint2( clamp( int2(uVertexPos.xy) + cellData.xzTexPosBounds.xy, + int2( 0, 0 ), cellData.xzTexPosBounds.zw ) ); + + float3 worldPos; + worldPos.y = OGRE_Load2D( heightMap, rshort2( uVertexPos.xy ), 0 ).x; +@property( use_skirts ) + float skirtHeight = uintBitsToFloat( cellData.numVertsPerLine.w ); + if( isSkirt ) + { + if( worldPos.y <= skirtHeight ) + worldPos.y = worldPos.y - skirtHeight; + else + worldPos.y = skirtHeight; + } +@end + worldPos.xz = float2( uVertexPos.xy ); + worldPos.xyz = worldPos.xyz * cellData.scale.xyz + cellData.pos.xyz; + + @property( z_up ) + worldPos.yz = float2( -worldPos.z, worldPos.y ); + @end + + @insertpiece( VertexTerraTransform ) + + outVs.uv0.xy = float2( uVertexPos.xy ) * float2( cellData.pos.w, cellData.scale.w ); + + @insertpiece( DoShadowReceiveVS ) + @property( hlms_shadowcaster ) + float shadowConstantBias = material.kD.w; + @insertpiece( DoShadowCasterVS ) + @end + +@property( syntax != metal ) + @property( (!hlms_shadowcaster || alpha_test) && !lower_gpu_overhead ) + outVs.drawId = inVs_drawId; + @end +@else + @property( hlms_fine_light_mask || hlms_forwardplus_fine_light_mask ) + outVs.objLightMask = worldMaterialIdx[inVs_drawId].z; + @end + + @property( use_planar_reflections ) + outVs.planarReflectionIdx = (ushort)(worldMaterialIdx[inVs_drawId].w); + @end +@end + + @property( hlms_use_prepass_msaa > 1 ) + outVs.zwDepth.xy = outVs_Position.zw; + @end + + @property( hlms_instanced_stereo ) + outVs_viewportIndex = int( inVs_stereoDrawId & 0x01u ); + @end +@end diff --git a/ogre2/src/media/Hlms/Terra/GLSL/PbsTerraShadows/PbsTerraShadows_piece_vs_piece_ps.glsl b/ogre2/src/media/Hlms/Terra/GLSL/PbsTerraShadows/PbsTerraShadows_piece_vs_piece_ps.glsl new file mode 100644 index 000000000..9162c3617 --- /dev/null +++ b/ogre2/src/media/Hlms/Terra/GLSL/PbsTerraShadows/PbsTerraShadows_piece_vs_piece_ps.glsl @@ -0,0 +1,44 @@ +@property( !hlms_shadowcaster && terra_enabled ) + +@piece( custom_VStoPS ) + float terrainShadow; +@end + +/// Extra per-pass global data we need for applying our +/// shadows to regular objects, passed to all PBS shaders. +@piece( custom_passBuffer ) + vec4 terraOrigin; //Normalized. i.e. -terrainOrigin / terrainDimensions + //.xz = terrain 1.0 / XZ dimensions. + //.y = 1.0 / terrainHeight; + vec4 invTerraBounds; +@end + +/// Add the shadows' texture to the vertex shader +@piece( custom_vs_uniformDeclaration ) + uniform sampler2D terrainShadows; +@end + +/// Evaluate the shadow based on world XZ position & height in the vertex shader. +/// Doing it at the pixel shader level would be more accurate, but the difference +/// is barely noticeable, and slower +@piece( custom_vs_posExecution ) + @property( z_up ) + vec3 terraWorldPos = vec3( worldPos.x, -worldPos.z, worldPos.y ); + @else + vec3 terraWorldPos = worldPos.xyz; + @end + vec3 terraShadowData = textureLod( terrainShadows, terraWorldPos.xz * passBuf.invTerraBounds.xz + + passBuf.terraOrigin.xz, 0 ).xyz; + float terraHeightWeight = terraWorldPos.y * passBuf.invTerraBounds.y + passBuf.terraOrigin.y; + terraHeightWeight = (terraHeightWeight - terraShadowData.y) * terraShadowData.z * 1023.0; + outVs.terrainShadow = mix( terraShadowData.x, 1.0, clamp( terraHeightWeight, 0.0, 1.0 ) ); +@end + +@property( hlms_num_shadow_map_lights ) + @piece( custom_ps_preLights )fShadow *= inPs.terrainShadow;@end +@else + @piece( custom_ps_preLights )float fShadow = inPs.terrainShadow;@end + @piece( DarkenWithShadowFirstLight )* fShadow@end +@end + +@end diff --git a/ogre2/src/media/Hlms/Terra/GLSL/PixelShader_ps.glsl b/ogre2/src/media/Hlms/Terra/GLSL/PixelShader_ps.glsl new file mode 100644 index 000000000..44a846adc --- /dev/null +++ b/ogre2/src/media/Hlms/Terra/GLSL/PixelShader_ps.glsl @@ -0,0 +1,132 @@ +@insertpiece( SetCrossPlatformSettings ) +@property( GL3+ < 430 ) + @property( hlms_tex_gather )#extension GL_ARB_texture_gather: require@end +@end +@insertpiece( SetCompatibilityLayer ) + +@insertpiece( DeclareUvModifierMacros ) + +layout(std140) uniform; +#define FRAG_COLOR 0 + + +@insertpiece( DefaultTerraHeaderPS ) + +// START UNIFORM DECLARATION +@property( !hlms_shadowcaster ) + @insertpiece( PassStructDecl ) + @insertpiece( TerraMaterialStructDecl ) + @insertpiece( TerraInstanceStructDecl ) +@end +@insertpiece( custom_ps_uniformDeclaration ) +// END UNIFORM DECLARATION + +in block +{ + @insertpiece( Terra_VStoPS_block ) +} inPs; + +@property( !hlms_render_depth_only ) + @property( !hlms_shadowcaster ) + @property( !hlms_prepass ) + layout(location = @counter(rtv_target), index = 0) out vec4 outColour; + @end + @property( hlms_gen_normals_gbuffer ) + #define outPs_normals outNormals + layout(location = @counter(rtv_target)) out vec4 outNormals; + @end + @property( hlms_prepass ) + #define outPs_shadowRoughness outShadowRoughness + layout(location = @counter(rtv_target)) out vec2 outShadowRoughness; + @end + @else + layout(location = @counter(rtv_target), index = 0) out float outColour; + @end +@end + +@property( !hlms_shadowcaster ) + +@property( hlms_use_prepass ) + @property( !hlms_use_prepass_msaa ) + uniform sampler2D gBuf_normals; + uniform sampler2D gBuf_shadowRoughness; + @else + uniform sampler2DMS gBuf_normals; + uniform sampler2DMS gBuf_shadowRoughness; + uniform sampler2DMS gBuf_depthTexture; + @end + + @property( hlms_use_ssr ) + uniform sampler2D ssrTexture; + @end +@end + +@insertpiece( DeclPlanarReflTextures ) +@insertpiece( DeclAreaApproxTextures ) + +@property( hlms_vpos ) +in vec4 gl_FragCoord; +@end + +uniform sampler2D terrainNormals; +uniform sampler2D terrainShadows; + +@property( hlms_forwardplus ) +/*layout(binding = 1) */uniform usamplerBuffer f3dGrid; +/*layout(binding = 2) */uniform samplerBuffer f3dLightList; +@end +@property( irradiance_volumes ) + uniform sampler3D irradianceVolume; +@end + +@foreach( num_textures, n ) + uniform sampler2DArray textureMaps@n;@end + +@property( !hlms_enable_cubemaps_auto ) + @property( use_envprobe_map )uniform samplerCube texEnvProbeMap;@end +@else + @property( !hlms_cubemaps_use_dpm ) + @property( use_envprobe_map )uniform samplerCubeArray texEnvProbeMap;@end + @else + @property( use_envprobe_map )uniform sampler2DArray texEnvProbeMap;@end + @insertpiece( DeclDualParaboloidFunc ) + @end +@end + +@property( use_parallax_correct_cubemaps ) + @insertpiece( DeclParallaxLocalCorrect ) +@end + +@insertpiece( DeclDecalsSamplers ) + +@insertpiece( DeclShadowMapMacros ) +@insertpiece( DeclShadowSamplers ) +@insertpiece( DeclShadowSamplingFuncs ) + +@insertpiece( DeclAreaLtcTextures ) +@insertpiece( DeclAreaLtcLightFuncs ) + +@insertpiece( DeclVctTextures ) + +@insertpiece( custom_ps_functions ) + +void main() +{ + @insertpiece( custom_ps_preExecution ) + @insertpiece( DefaultTerraBodyPS ) + @insertpiece( custom_ps_posExecution ) +} +@else /// !hlms_shadowcaster + +@insertpiece( DeclShadowCasterMacros ) +@property( hlms_shadowcaster_point || exponential_shadow_maps ) + @insertpiece( PassStructDecl ) +@end + +void main() +{ + @insertpiece( custom_ps_preExecution ) + @insertpiece( DefaultBodyPS ) + @insertpiece( custom_ps_posExecution ) +} +@end diff --git a/ogre2/src/media/Hlms/Terra/GLSL/VertexShader_vs.glsl b/ogre2/src/media/Hlms/Terra/GLSL/VertexShader_vs.glsl new file mode 100644 index 000000000..2fa0da73e --- /dev/null +++ b/ogre2/src/media/Hlms/Terra/GLSL/VertexShader_vs.glsl @@ -0,0 +1,41 @@ +@insertpiece( SetCrossPlatformSettings ) +@insertpiece( SetCompatibilityLayer ) + +out gl_PerVertex +{ + vec4 gl_Position; +@property( hlms_global_clip_planes ) + float gl_ClipDistance[@value(hlms_global_clip_planes)]; +@end +}; + +layout(std140) uniform; + +@insertpiece( DefaultTerraHeaderVS ) +@insertpiece( custom_vs_uniformDeclaration ) + +@property( GL_ARB_base_instance ) + in uint drawId; +@end + +@insertpiece( custom_vs_attributes ) + +out block +{ + @insertpiece( Terra_VStoPS_block ) +} outVs; + +// START UNIFORM GL DECLARATION +/*layout(binding = 0) */uniform sampler2D heightMap; +@property( !GL_ARB_base_instance )uniform uint baseInstance;@end +// END UNIFORM GL DECLARATION + +void main() +{ +@property( !GL_ARB_base_instance ) + uint drawId = baseInstance + uint( gl_InstanceID ); +@end + @insertpiece( custom_vs_preExecution ) + @insertpiece( DefaultTerraBodyVS ) + @insertpiece( custom_vs_posExecution ) +} diff --git a/ogre2/src/media/Hlms/Terra/GLSLES/PbsTerraShadows/PbsTerraShadows_piece_vs_piece_ps.glsl b/ogre2/src/media/Hlms/Terra/GLSLES/PbsTerraShadows/PbsTerraShadows_piece_vs_piece_ps.glsl new file mode 100644 index 000000000..f0436a884 --- /dev/null +++ b/ogre2/src/media/Hlms/Terra/GLSLES/PbsTerraShadows/PbsTerraShadows_piece_vs_piece_ps.glsl @@ -0,0 +1,38 @@ +@property( !hlms_shadowcaster && terra_enabled ) + +@piece( custom_VStoPS ) + float terrainShadow; +@end + +/// Extra per-pass global data we need for applying our +/// shadows to regular objects, passed to all PBS shaders. +@piece( custom_passBuffer ) + vec4 terraOrigin; //Normalized. i.e. -terrainOrigin / terrainDimensions + //.xz = terrain 1.0 / XZ dimensions. + //.y = 1.0 / terrainHeight; + vec4 invTerraBounds; +@end + +/// Add the shadows' texture to the vertex shader +@piece( custom_vs_uniformDeclaration ) + uniform sampler2D terrainShadows; +@end + +/// Evaluate the shadow based on world XZ position & height in the vertex shader. +/// Doing it at the pixel shader level would be more accurate, but the difference +/// is barely noticeable, and slower +@piece( custom_vs_posExecution ) + vec3 terraShadowData = textureLod( terrainShadows, worldPos.xz * passBuf.invTerraBounds.xz + passBuf.terraOrigin.xz, 0 ).xyz; + float terraHeightWeight = worldPos.y * passBuf.invTerraBounds.y + passBuf.terraOrigin.y; + terraHeightWeight = (terraHeightWeight - terraShadowData.y) * terraShadowData.z * 1023.0; + outVs.terrainShadow = mix( terraShadowData.x, 1.0, clamp( terraHeightWeight, 0.0, 1.0 ) ); +@end + +@property( hlms_num_shadow_map_lights ) + @piece( custom_ps_preLights )fShadow *= inPs.terrainShadow;@end +@end @property( !hlms_num_shadow_map_lights ) + @piece( custom_ps_preLights )float fShadow = inPs.terrainShadow;@end + @piece( DarkenWithShadowFirstLight )* fShadow@end +@end + +@end diff --git a/ogre2/src/media/Hlms/Terra/GLSLES/PixelShader_ps.glsl b/ogre2/src/media/Hlms/Terra/GLSLES/PixelShader_ps.glsl new file mode 100644 index 000000000..a30680158 --- /dev/null +++ b/ogre2/src/media/Hlms/Terra/GLSLES/PixelShader_ps.glsl @@ -0,0 +1,334 @@ +@property( false ) +@insertpiece( SetCrossPlatformSettings ) +@insertpiece( SetCompatibilityLayer ) + +layout(std140) uniform; +#define FRAG_COLOR 0 +layout(location = FRAG_COLOR) out vec4 outColour; + +uniform sampler2D terrainNormals; + +in block +{ +@insertpiece( Terra_VStoPS_block ) +} inPs; + +void main() +{ + outColour = vec4( inPs.uv0.xy, 0.0, 1.0 ); +} + +@end +@property( !false ) +@insertpiece( SetCrossPlatformSettings ) +@property( GL3+ < 430 ) + @property( hlms_tex_gather )#extension GL_ARB_texture_gather: require@end +@end + +@property( hlms_amd_trinary_minmax )#extension GL_AMD_shader_trinary_minmax: require@end +@insertpiece( SetCompatibilityLayer ) + +layout(std140) uniform; +#define FRAG_COLOR 0 +layout(location = FRAG_COLOR) out vec4 outColour; + +@property( hlms_vpos ) +in vec4 gl_FragCoord; +@end + +// START UNIFORM DECLARATION +@insertpiece( PassDecl ) +@insertpiece( TerraMaterialDecl ) +@insertpiece( TerraInstanceDecl ) +@insertpiece( custom_ps_uniformDeclaration ) +// END UNIFORM DECLARATION +in block +{ +@insertpiece( Terra_VStoPS_block ) +} inPs; + +uniform sampler2D terrainNormals; +uniform sampler2D terrainShadows; + +@property( hlms_forwardplus ) +/*layout(binding = 1) */uniform usamplerBuffer f3dGrid; +/*layout(binding = 2) */uniform samplerBuffer f3dLightList; +@end +@property( num_textures )uniform sampler2DArray textureMaps[@value( num_textures )];@end +@property( envprobe_map )uniform samplerCube texEnvProbeMap;@end + +vec4 diffuseCol; +@insertpiece( FresnelType ) F0; +float ROUGHNESS; + +vec3 nNormal; + +@property( hlms_lights_spot_textured )@insertpiece( DeclQuat_zAxis ) +vec3 qmul( vec4 q, vec3 v ) +{ + return v + 2.0 * cross( cross( v, q.xyz ) + q.w * v, q.xyz ); +} +@end + +@property( detail_maps_normal )vec3 getTSDetailNormal( sampler2DArray normalMap, vec3 uv ) +{ + vec3 tsNormal; +@property( signed_int_textures ) + //Normal texture must be in U8V8 or BC5 format! + tsNormal.xy = texture( normalMap, uv ).xy; +@end @property( !signed_int_textures ) + //Normal texture must be in LA format! + tsNormal.xy = texture( normalMap, uv ).xw * 2.0 - 1.0; +@end + tsNormal.z = sqrt( max( 0.0, 1.0 - tsNormal.x * tsNormal.x - tsNormal.y * tsNormal.y ) ); + + return tsNormal; +} + @foreach( 4, n ) + @property( normal_weight_detail@n ) + @piece( detail@n_nm_weight_mul ) * material.normalWeights.@insertpiece( detail_swizzle@n )@end + @end + @end +@end + +@insertpiece( DeclareBRDF ) + +@insertpiece( DeclShadowMapMacros ) +@insertpiece( DeclShadowSamplers ) +@insertpiece( DeclShadowSamplingFuncs ) + +void main() +{ + @insertpiece( custom_ps_preExecution ) + + @insertpiece( custom_ps_posMaterialLoad ) + +//Prepare weight map for the detail maps. +@property( detail_weight_map ) + vec4 detailWeights = texture( textureMaps[@value( detail_weight_map_idx )], + vec3( inPs.uv0.xy, @value(detail_weight_map_idx_slice) ) ); +@end @property( !detail_weight_map ) + vec4 detailWeights = vec4( 1.0, 1.0, 1.0, 1.0 ); +@end + +@property( diffuse_map ) + diffuseCol = texture( textureMaps[@value( diffuse_map_idx )], vec3( inPs.uv0.xy, @value(diffuse_map_idx_slice) ) ); +@end + + /// Sample detail maps +@foreach( 4, n ) + @property( detail_map@n ) + vec3 detailCol@n = texture( textureMaps[@value(detail_map@n_idx)], + vec3( inPs.uv0.xy * material.detailOffsetScale[@value(currOffsetDetail)].zw + + material.detailOffsetScale[@value(currOffsetDetail)].xy, + @value(detail_map@n_idx_slice) ) ).xyz; + @end @property( !detail_map@n ) + vec3 detailCol@n = vec3( 0, 0, 0 ); + @end + + @property( metalness_map@n ) + float metalness@n = texture( textureMaps[@value( metalness_map@n_idx )], + vec3( inPs.uv0.xy * material.detailOffsetScale[@value(currOffsetDetail)].zw + + material.detailOffsetScale[@value(currOffsetDetail)].xy, + @value( metalness_map@n_idx_slice ) ) ).x; + @end @property( !metalness_map@n ) + float metalness@n = 0; + @end + + @property( roughness_map@n ) + float roughness@n = texture( textureMaps[@value( roughness_map@n_idx )], + vec3( inPs.uv0.xy * material.detailOffsetScale[@value(currOffsetDetail)].zw + + material.detailOffsetScale[@value(currOffsetDetail)].xy, + @value( roughness_map@n_idx_slice ) ) ).x; + @end @property( !roughness_map@n ) + float roughness@n = 0; + @end + + @add( currOffsetDetail, 1 ) +@end + + float metalness = (metalness0 * detailWeights.x * material.metalness.x + + metalness1 * detailWeights.y * material.metalness.y) + + (metalness2 * detailWeights.z * material.metalness.z + + metalness3 * detailWeights.w * material.metalness.w); + + ROUGHNESS = (roughness0 * detailWeights.x * material.roughness.x + + roughness1 * detailWeights.y * material.roughness.y) + + (roughness2 * detailWeights.z * material.roughness.z + + roughness3 * detailWeights.w * material.roughness.w); + ROUGHNESS = max( ROUGHNESS, 0.02 ); + +@property( diffuse_map ) + diffuseCol.xyz *= (detailCol0 * detailWeights.x + detailCol1 * detailWeights.y) + + (detailCol2 * detailWeights.z + detailCol3 * detailWeights.w); +@end @property( !diffuse_map ) + @property( detail_maps_diffuse ) + diffuseCol.xyz = (detailCol0 * detailWeights.x + detailCol1 * detailWeights.y) + + (detailCol2 * detailWeights.z + detailCol3 * detailWeights.w); + @end @property( !detail_maps_diffuse ) + diffuseCol.xyzw = vec4( 1, 1, 1, 1 ); + @end +@end + + /// Apply the material's diffuse over the textures + diffuseCol.xyz *= material.kD.xyz; + + //Calculate F0 from metalness, and dim kD as metalness gets bigger. + F0 = mix( vec3( 0.03f ), @insertpiece( kD ).xyz * 3.14159f, metalness ); + @insertpiece( kD ).xyz = @insertpiece( kD ).xyz - @insertpiece( kD ).xyz * metalness; + +@property( !detail_maps_normal ) + // Geometric normal + nNormal = texture( terrainNormals, inPs.uv0.xy ).xyz * 2.0 - 1.0; + //nNormal.xz = texture( terrainNormals, inPs.uv0.xy ).xy; + //nNormal.y = sqrt( max( 1.0 - nNormal.x * nNormal.x - nNormal.z * nNormal.z, 0.0 ) ); + nNormal = nNormal * mat3(passBuf.view); +@end @property( detail_maps_normal ) + vec3 geomNormal = texture( terrainNormals, inPs.uv0.xy ).xyz * 2.0 - 1.0; + geomNormal = geomNormal * mat3(passBuf.view); + + //Get the TBN matrix + vec3 viewSpaceUnitX = vec3( passBuf.view[0].x, passBuf.view[1].x, passBuf.view[2].x ); + vec3 vTangent = normalize( cross( geomNormal, viewSpaceUnitX ) ); + vec3 vBinormal = cross( vTangent, geomNormal ); + mat3 TBN = mat3( vBinormal, vTangent, geomNormal ); +@end + + float fTerrainShadow = texture( terrainShadows, inPs.uv0.xy ).x; + @property( !(hlms_pssm_splits || (!hlms_pssm_splits && hlms_num_shadow_map_lights && hlms_lights_directional)) ) + float fShadow = 1.0f; + @end + @insertpiece( DoDirectionalShadowMaps ) + fShadow *= fTerrainShadow; + + /// The first iteration must initialize nNormal instead of try to merge with it. + /// Blend the detail normal maps with the main normal. +@foreach( second_valid_detail_map_nm, n, first_valid_detail_map_nm ) + vec3 vDetail = @insertpiece( SampleDetailMapNm@n ) * detailWeights.@insertpiece(detail_swizzle@n); + nNormal.xy = vDetail.xy; + nNormal.z = vDetail.z + 1.0 - detailWeights.@insertpiece(detail_swizzle@n);@end +@foreach( detail_maps_normal, n, second_valid_detail_map_nm )@property( detail_map_nm@n ) + vDetail = @insertpiece( SampleDetailMapNm@n ) * detailWeights.@insertpiece(detail_swizzle@n); + nNormal.xy += vDetail.xy; + nNormal.z *= vDetail.z + 1.0 - detailWeights.@insertpiece(detail_swizzle@n);@end @end + +@property( detail_maps_normal ) + nNormal = normalize( TBN * nNormal ); +@end + + //Everything's in Camera space +@property( hlms_lights_spot || ambient_hemisphere || envprobe_map || hlms_forwardplus ) + vec3 viewDir = normalize( -inPs.pos ); + float NdotV = clamp( dot( nNormal, viewDir ), 0.0, 1.0 );@end + +@property( !ambient_fixed ) + vec3 finalColour = vec3(0); +@end @property( ambient_fixed ) + vec3 finalColour = passBuf.ambientUpperHemi.xyz * @insertpiece( kD ).xyz; +@end + + @insertpiece( custom_ps_preLights ) + +@property( !custom_disable_directional_lights ) +@property( hlms_lights_directional ) + finalColour += BRDF( passBuf.lights[0].position.xyz, viewDir, NdotV, passBuf.lights[0].diffuse, passBuf.lights[0].specular ) @insertpiece(DarkenWithShadowFirstLight); +@end +@foreach( hlms_lights_directional, n, 1 ) + finalColour += BRDF( passBuf.lights[@n].position.xyz, viewDir, NdotV, passBuf.lights[@n].diffuse, passBuf.lights[@n].specular )@insertpiece( DarkenWithShadow );@end +@foreach( hlms_lights_directional_non_caster, n, hlms_lights_directional ) + finalColour += BRDF( passBuf.lights[@n].position.xyz, viewDir, NdotV, passBuf.lights[@n].diffuse, passBuf.lights[@n].specular );@end +@end + +@property( hlms_lights_point || hlms_lights_spot ) vec3 lightDir; + float fDistance; + vec3 tmpColour; + float spotCosAngle;@end + + //Point lights +@foreach( hlms_lights_point, n, hlms_lights_directional_non_caster ) + lightDir = passBuf.lights[@n].position.xyz - inPs.pos; + fDistance= length( lightDir ); + if( fDistance <= passBuf.lights[@n].attenuation.x ) + { + lightDir *= 1.0 / fDistance; + tmpColour = BRDF( lightDir, viewDir, NdotV, passBuf.lights[@n].diffuse, passBuf.lights[@n].specular )@insertpiece( DarkenWithShadowPoint ); + float atten = 1.0 / (0.5 + (passBuf.lights[@n].attenuation.y + passBuf.lights[@n].attenuation.z * fDistance) * fDistance ); + finalColour += tmpColour * atten; + }@end + + //Spot lights + //spotParams[@value(spot_params)].x = 1.0 / cos( InnerAngle ) - cos( OuterAngle ) + //spotParams[@value(spot_params)].y = cos( OuterAngle / 2 ) + //spotParams[@value(spot_params)].z = falloff +@foreach( hlms_lights_spot, n, hlms_lights_point ) + lightDir = passBuf.lights[@n].position.xyz - inPs.pos; + fDistance= length( lightDir ); +@property( !hlms_lights_spot_textured ) spotCosAngle = dot( normalize( inPs.pos - passBuf.lights[@n].position.xyz ), passBuf.lights[@n].spotDirection );@end +@property( hlms_lights_spot_textured ) spotCosAngle = dot( normalize( inPs.pos - passBuf.lights[@n].position.xyz ), zAxis( passBuf.lights[@n].spotQuaternion ) );@end + if( fDistance <= passBuf.lights[@n].attenuation.x && spotCosAngle >= passBuf.lights[@n].spotParams.y ) + { + lightDir *= 1.0 / fDistance; + @property( hlms_lights_spot_textured ) + vec3 posInLightSpace = qmul( spotQuaternion[@value(spot_params)], inPs.pos ); + float spotAtten = texture( texSpotLight, normalize( posInLightSpace ).xy ).x; + @end + @property( !hlms_lights_spot_textured ) + float spotAtten = clamp( (spotCosAngle - passBuf.lights[@n].spotParams.y) * passBuf.lights[@n].spotParams.x, 0.0, 1.0 ); + spotAtten = pow( spotAtten, passBuf.lights[@n].spotParams.z ); + @end + tmpColour = BRDF( lightDir, viewDir, NdotV, passBuf.lights[@n].diffuse, passBuf.lights[@n].specular )@insertpiece( DarkenWithShadow ); + float atten = 1.0 / (0.5 + (passBuf.lights[@n].attenuation.y + passBuf.lights[@n].attenuation.z * fDistance) * fDistance ); + finalColour += tmpColour * (atten * spotAtten); + }@end + +@insertpiece( forward3dLighting ) + +@property( envprobe_map || ambient_hemisphere ) + vec3 reflDir = 2.0 * dot( viewDir, nNormal ) * nNormal - viewDir; + + @property( envprobe_map ) + vec3 envColourS = textureLod( texEnvProbeMap, reflDir * passBuf.invViewMatCubemap, ROUGHNESS * 12.0 ).xyz @insertpiece( ApplyEnvMapScale );// * 0.0152587890625; + vec3 envColourD = textureLod( texEnvProbeMap, nNormal * passBuf.invViewMatCubemap, 11.0 ).xyz @insertpiece( ApplyEnvMapScale );// * 0.0152587890625; + @property( !hw_gamma_read ) //Gamma to linear space + envColourS = envColourS * envColourS; + envColourD = envColourD * envColourD; + @end + @end + @property( ambient_hemisphere ) + float ambientWD = dot( passBuf.ambientHemisphereDir.xyz, nNormal ) * 0.5 + 0.5; + float ambientWS = dot( passBuf.ambientHemisphereDir.xyz, reflDir ) * 0.5 + 0.5; + + @property( envprobe_map ) + envColourS += mix( passBuf.ambientLowerHemi.xyz, passBuf.ambientUpperHemi.xyz, ambientWD ); + envColourD += mix( passBuf.ambientLowerHemi.xyz, passBuf.ambientUpperHemi.xyz, ambientWS ); + @end @property( !envprobe_map ) + vec3 envColourS = mix( passBuf.ambientLowerHemi.xyz, passBuf.ambientUpperHemi.xyz, ambientWD ); + vec3 envColourD = mix( passBuf.ambientLowerHemi.xyz, passBuf.ambientUpperHemi.xyz, ambientWS ); + @end + @end + + @insertpiece( BRDF_EnvMap ) +@end +@property( !hw_gamma_write ) + //Linear to Gamma space + outColour.xyz = sqrt( finalColour ); +@end @property( hw_gamma_write ) + outColour.xyz = finalColour; +@end + +@property( hlms_alphablend ) + @property( use_texture_alpha ) + outColour.w = material.F0.w * diffuseCol.w; + @end @property( !use_texture_alpha ) + outColour.w = material.F0.w; + @end +@end @property( !hlms_alphablend ) + outColour.w = 1.0;@end + + @property( debug_pssm_splits ) + outColour.xyz = mix( outColour.xyz, debugPssmSplit.xyz, 0.2f ); + @end + + @insertpiece( custom_ps_posExecution ) +} +@end diff --git a/ogre2/src/media/Hlms/Terra/GLSLES/Structs_piece_vs_piece_ps.glsl b/ogre2/src/media/Hlms/Terra/GLSLES/Structs_piece_vs_piece_ps.glsl new file mode 100644 index 000000000..21f7c4fea --- /dev/null +++ b/ogre2/src/media/Hlms/Terra/GLSLES/Structs_piece_vs_piece_ps.glsl @@ -0,0 +1,46 @@ + +@piece( TerraMaterialDecl ) +layout_constbuffer(binding = 1) uniform MaterialBuf +{ + /* kD is already divided by PI to make it energy conserving. + (formula is finalDiffuse = NdotL * surfaceDiffuse / PI) + */ + vec4 kD; //kD.w is padding + vec4 roughness; + vec4 metalness; + vec4 detailOffsetScale[4]; + + @insertpiece( custom_materialBuffer ) +} material; +@end + +@piece( TerraInstanceDecl ) +struct CellData +{ + //.x = numVertsPerLine + //.y = lodLevel + //.z = vao->getPrimitiveCount() / m_verticesPerLine - 2u + //.w = skirtY (float) + uvec4 numVertsPerLine; + ivec4 xzTexPosBounds; //XZXZ + vec4 pos; //.w contains 1.0 / xzTexPosBounds.z + vec4 scale; //.w contains 1.0 / xzTexPosBounds.w +}; + +layout_constbuffer(binding = 2) uniform InstanceBuffer +{ + CellData cellData[256]; +} instance; +@end + +@piece( Terra_VStoPS_block ) + //flat uint drawId; + vec3 pos; + vec2 uv0; + + @foreach( hlms_num_shadow_map_lights, n ) + @property( !hlms_shadowmap@n_is_point_light ) + vec4 posL@n;@end @end + @property( hlms_pssm_splits )float depth;@end + @insertpiece( custom_VStoPS ) +@end diff --git a/ogre2/src/media/Hlms/Terra/GLSLES/Textures_piece_ps.glsl b/ogre2/src/media/Hlms/Terra/GLSLES/Textures_piece_ps.glsl new file mode 100644 index 000000000..5ae13440d --- /dev/null +++ b/ogre2/src/media/Hlms/Terra/GLSLES/Textures_piece_ps.glsl @@ -0,0 +1,12 @@ + +@undefpiece( kD ) +@piece( kD )diffuseCol@end + +@undefpiece( kS ) +@piece( kS )vec3( 1, 1, 1 )@end + +@foreach( detail_maps_normal, n ) + @undefpiece( SampleDetailMapNm@n ) + @piece( SampleDetailMapNm@n )getTSDetailNormal( textureMaps[@value(detail_map_nm@n_idx)], vec3( inPs.uv0.xy * material.detailOffsetScale[@n].zw + + material.detailOffsetScale[@n].xy , @value(detail_map_nm@n_idx_slice) ) )@end +@end diff --git a/ogre2/src/media/Hlms/Terra/GLSLES/VertexShader_vs.glsl b/ogre2/src/media/Hlms/Terra/GLSLES/VertexShader_vs.glsl new file mode 100644 index 000000000..2377a38b5 --- /dev/null +++ b/ogre2/src/media/Hlms/Terra/GLSLES/VertexShader_vs.glsl @@ -0,0 +1,126 @@ +@insertpiece( SetCrossPlatformSettings ) +@insertpiece( SetCompatibilityLayer ) + +@property( GL3+ ) +out gl_PerVertex +{ + vec4 gl_Position; +}; +@end + +layout(std140) uniform; + +//To render a 2x2 (quads) terrain: +//You'll normally need 6 vertices per line + 2 for degenerates. +//You'll need 8 vertices per line. +//So you'll need a total of 16 vertices. + +//To render a 4x2 (quads) terrain: +//You'll need 10 vertices per line. +//If we include degenerate vertices, you'll need 12 per line +//So you'll need a total of 24 vertices. +//in int gl_VertexID; + +@property( GL_ARB_base_instance ) + in uint drawId; +@end + +@insertpiece( custom_vs_attributes ) + +out block +{ +@insertpiece( Terra_VStoPS_block ) +} outVs; + +// START UNIFORM DECLARATION +@insertpiece( PassDecl ) +@insertpiece( TerraInstanceDecl ) +uniform sampler2D heightMap; +@insertpiece( custom_vs_uniformDeclaration ) +@property( !GL_ARB_base_instance )uniform uint baseInstance;@end +// END UNIFORM DECLARATION + +@piece( VertexTransform ) + //Lighting is in view space + outVs.pos = ( vec4(worldPos.xyz, 1.0f) * passBuf.view ).xyz; +@property( !hlms_dual_paraboloid_mapping ) + gl_Position = vec4(worldPos.xyz, 1.0f) * passBuf.viewProj;@end +@property( hlms_dual_paraboloid_mapping ) + //Dual Paraboloid Mapping + gl_Position.w = 1.0f; + gl_Position.xyz = outVs.pos; + float L = length( gl_Position.xyz ); + gl_Position.z += 1.0f; + gl_Position.xy /= gl_Position.z; + gl_Position.z = (L - NearPlane) / (FarPlane - NearPlane);@end +@end + +void main() +{ +@property( !GL_ARB_base_instance ) + uint drawId = baseInstance + uint( gl_InstanceID ); +@end + + @insertpiece( custom_vs_preExecution ) + + CellData cellData = instance.cellData[drawId]; + + //Map pointInLine from range [0; 12) to range [0; 9] so that it reads: + // 0 0 1 2 3 4 5 6 7 8 9 9 + uint pointInLine = uint(gl_VertexID) % (cellData.numVertsPerLine.x); //cellData.numVertsPerLine.x = 12 + pointInLine = uint(clamp( int(pointInLine) - 1, 0, int(cellData.numVertsPerLine.x - 3u) )); + + uvec2 uVertexPos; + + uVertexPos.x = pointInLine >> 1u; + //Even numbers are the next line, odd numbers are current line. + uVertexPos.y = (pointInLine & 0x01u) == 0u ? 1u : 0u; + uVertexPos.y += uint(gl_VertexID) / cellData.numVertsPerLine.x; + //uVertexPos.y += floor( (float)gl_VertexID / (float)cellData.numVertsPerLine ); Could be faster on GCN. + +@property( use_skirts ) + //Apply skirt. + bool isSkirt =( pointInLine.x <= 1u || + pointInLine.x >= (cellData.numVertsPerLine.x - 4u) || + uVertexPos.y == 0u || + uVertexPos.y == (cellData.numVertsPerLine.z + 2u) ); + + //Now shift X position for the left & right skirts + uVertexPos.x = uint( max( int(uVertexPos.x) - 1, 0 ) ); + uVertexPos.x = min( uVertexPos.x, ((cellData.numVertsPerLine.x - 7u) >> 1u) ); + // uVertexPos.x becomes: + // 0 0 0 1 1 2 2 3 3 4 4 4 + // 0 0 0 0 0 1 1 2 2 3 3 3 + // 0 0 0 0 0 1 1 2 2 2 2 2 + + //Now shift Y position for the front & back skirts + uVertexPos.y = uint( max( int(uVertexPos.y) - 1, 0 ) ); + uVertexPos.y = min( uVertexPos.y, cellData.numVertsPerLine.z ); +@end + + uint lodLevel = cellData.numVertsPerLine.y; + uVertexPos = uVertexPos << lodLevel; + + uVertexPos.xy = uvec2( clamp( ivec2(uVertexPos.xy) + cellData.xzTexPosBounds.xy, + ivec2( 0, 0 ), cellData.xzTexPosBounds.zw ) ); + + vec3 worldPos; + worldPos.y = texelFetch( heightMap, ivec2( uVertexPos.xy ), 0 ).x; +@property( use_skirts ) + worldPos.y = isSkirt ? uintBitsToFloat(cellData.numVertsPerLine.w) : worldPos.y; +@end + worldPos.xz = uVertexPos.xy; + worldPos.xyz = worldPos.xyz * cellData.scale.xyz + cellData.pos.xyz; + + @insertpiece( VertexTransform ) + + outVs.uv0.xy = vec2( uVertexPos.xy ) * vec2( cellData.pos.w, cellData.scale.w ); + + @insertpiece( DoShadowReceiveVS ) + +@property( hlms_pssm_splits ) outVs.depth = gl_Position.z;@end + + //outVs.drawId = drawId; + + @insertpiece( custom_vs_posExecution ) +} diff --git a/ogre2/src/media/Hlms/Terra/HLSL/PbsTerraShadows/PbsTerraShadows_piece_vs_piece_ps.hlsl b/ogre2/src/media/Hlms/Terra/HLSL/PbsTerraShadows/PbsTerraShadows_piece_vs_piece_ps.hlsl new file mode 100644 index 000000000..3e26c06e4 --- /dev/null +++ b/ogre2/src/media/Hlms/Terra/HLSL/PbsTerraShadows/PbsTerraShadows_piece_vs_piece_ps.hlsl @@ -0,0 +1,46 @@ +@property( !hlms_shadowcaster && terra_enabled ) + +@piece( custom_VStoPS ) + float terrainShadow : TEXCOORD@counter(texcoord); +@end + +/// Extra per-pass global data we need for applying our +/// shadows to regular objects, passed to all PBS shaders. +@piece( custom_passBuffer ) + float4 terraOrigin; //Normalized. i.e. -terrainOrigin / terrainDimensions + //.xz = terrain 1.0 / XZ dimensions. + //.y = 1.0 / terrainHeight; + float4 invTerraBounds; +@end + +/// Add the shadows' texture to the vertex shader +@piece( custom_vs_uniformDeclaration ) + SamplerState terrainShadowSampler : register(s12); + Texture2D terrainShadows : register(t12); +@end + +/// Evaluate the shadow based on world XZ position & height in the vertex shader. +/// Doing it at the pixel shader level would be more accurate, but the difference +/// is barely noticeable, and slower +@piece( custom_vs_posExecution ) + @property( z_up ) + float3 terraWorldPos = float3( worldPos.x, -worldPos.z, worldPos.y ); + @else + float3 terraWorldPos = worldPos.xyz; + @end + float3 terraShadowData = terrainShadows.SampleLevel( terrainShadowSampler, + terraWorldPos.xz * passBuf.invTerraBounds.xz + passBuf.terraOrigin.xz, + 0 ).xyz; + float terraHeightWeight = terraWorldPos.y * passBuf.invTerraBounds.y + passBuf.terraOrigin.y; + terraHeightWeight = (terraHeightWeight - terraShadowData.y) * terraShadowData.z * 1023.0; + outVs.terrainShadow = lerp( terraShadowData.x, 1.0, saturate( terraHeightWeight ) ); +@end + +@property( hlms_num_shadow_map_lights ) + @piece( custom_ps_preLights )fShadow *= inPs.terrainShadow;@end +@else + @piece( custom_ps_preLights )float fShadow = inPs.terrainShadow;@end + @piece( DarkenWithShadowFirstLight )* fShadow@end +@end + +@end diff --git a/ogre2/src/media/Hlms/Terra/HLSL/PixelShader_ps.hlsl b/ogre2/src/media/Hlms/Terra/HLSL/PixelShader_ps.hlsl new file mode 100644 index 000000000..4ebe7fc2e --- /dev/null +++ b/ogre2/src/media/Hlms/Terra/HLSL/PixelShader_ps.hlsl @@ -0,0 +1,155 @@ +@insertpiece( SetCrossPlatformSettings ) +@insertpiece( DeclareUvModifierMacros ) + +@insertpiece( DefaultTerraHeaderPS ) + +// START UNIFORM DECLARATION +@property( !hlms_shadowcaster ) + @insertpiece( PassStructDecl ) + @insertpiece( TerraMaterialStructDecl ) + @insertpiece( TerraInstanceStructDecl ) + @insertpiece( PccManualProbeDecl ) +@end +@insertpiece( custom_ps_uniformDeclaration ) +// END UNIFORM DECLARATION +struct PS_INPUT +{ + @insertpiece( Terra_VStoPS_block ) +}; + +@padd( roughness_map0_sampler, samplerStateStart ) +@padd( roughness_map1_sampler, samplerStateStart ) +@padd( roughness_map2_sampler, samplerStateStart ) +@padd( roughness_map3_sampler, samplerStateStart ) + +@padd( metalness_map0_sampler, samplerStateStart ) +@padd( metalness_map1_sampler, samplerStateStart ) +@padd( metalness_map2_sampler, samplerStateStart ) +@padd( metalness_map3_sampler, samplerStateStart ) + +@property( !hlms_shadowcaster ) + +@property( !hlms_render_depth_only ) + @property( hlms_gen_normals_gbuffer ) + #define outPs_normals outPs.normals + @end + @property( hlms_prepass ) + #define outPs_shadowRoughness outPs.shadowRoughness + @end +@end + +@property( hlms_use_prepass ) + @property( !hlms_use_prepass_msaa ) + Texture2D gBuf_normals : register(t@value(gBuf_normals)); + Texture2D gBuf_shadowRoughness : register(t@value(gBuf_shadowRoughness)); + @else + Texture2DMS gBuf_normals : register(t@value(gBuf_normals)); + Texture2DMS gBuf_shadowRoughness : register(t@value(gBuf_shadowRoughness)); + Texture2DMS gBuf_depthTexture : register(t@value(gBuf_depthTexture)); + @end + + @property( hlms_use_ssr ) + Texture2D ssrTexture : register(t@value(ssrTexture)); + @end +@end + +@insertpiece( DeclPlanarReflTextures ) +@insertpiece( DeclAreaApproxTextures ) + +Texture2D terrainNormals : register(t@value(terrainNormals)); +Texture2D terrainShadows : register(t@value(terrainShadows)); +SamplerState samplerStateTerra : register(s@value(terrainNormals)); + +@property( hlms_forwardplus ) + Buffer f3dGrid : register(t@value(f3dGrid)); + Buffer f3dLightList : register(t@value(f3dLightList)); +@end + +@property( irradiance_volumes ) + Texture3D irradianceVolume : register(t@value(irradianceVolume)); + SamplerState irradianceVolumeSampler : register(s@value(irradianceVolume)); +@end + +@foreach( num_textures, n ) + Texture2DArray textureMaps@n : register(t@value(textureMaps@n));@end + +@property( use_envprobe_map ) + @property( !hlms_enable_cubemaps_auto ) + TextureCube texEnvProbeMap : register(t@value(texEnvProbeMap)); + @else + @property( !hlms_cubemaps_use_dpm ) + TextureCubeArray texEnvProbeMap : register(t@value(texEnvProbeMap)); + @else + @property( use_envprobe_map )Texture2DArray texEnvProbeMap : register(t@value(texEnvProbeMap));@end + @insertpiece( DeclDualParaboloidFunc ) + @end + @end + @property( envMapRegSampler < samplerStateStart ) + SamplerState samplerState@value(envMapRegSampler) : register(s@value(envMapRegSampler)); + @end +@end + +@foreach( num_samplers, n ) + SamplerState samplerState@value(samplerStateStart) : register(s@counter(samplerStateStart));@end + +@property( use_parallax_correct_cubemaps ) + @insertpiece( DeclParallaxLocalCorrect ) +@end + +@insertpiece( DeclDecalsSamplers ) + +@insertpiece( DeclShadowMapMacros ) +@insertpiece( DeclShadowSamplers ) +@insertpiece( DeclShadowSamplingFuncs ) + +@insertpiece( DeclAreaLtcTextures ) +@insertpiece( DeclAreaLtcLightFuncs ) + +@insertpiece( DeclVctTextures ) + +@insertpiece( DeclOutputType ) + +@insertpiece( custom_ps_functions ) + +@insertpiece( output_type ) main +( + PS_INPUT inPs + @property( hlms_vpos ), float4 gl_FragCoord : SV_Position@end + @property( two_sided_lighting ), bool gl_FrontFacing : SV_IsFrontFace@end + @property( hlms_use_prepass_msaa && hlms_use_prepass ), uint gl_SampleMask : SV_Coverage@end +) +{ + PS_OUTPUT outPs; + @insertpiece( custom_ps_preExecution ) + @insertpiece( DefaultTerraBodyPS ) + @insertpiece( custom_ps_posExecution ) + +@property( !hlms_render_depth_only ) + return outPs; +@end +} +@else ///!hlms_shadowcaster + +@insertpiece( DeclShadowCasterMacros ) + +@property( hlms_shadowcaster_point || exponential_shadow_maps ) + @insertpiece( PassStructDecl ) +@end + +@insertpiece( DeclOutputType ) + +@insertpiece( output_type ) main( PS_INPUT inPs ) +{ +@property( !hlms_render_depth_only || exponential_shadow_maps || hlms_shadowcaster_point ) + PS_OUTPUT outPs; +@end + + @insertpiece( custom_ps_preExecution ) + @insertpiece( DefaultBodyPS ) + @insertpiece( custom_ps_posExecution ) + +@property( !hlms_render_depth_only || exponential_shadow_maps || hlms_shadowcaster_point ) + return outPs; +@end +} +@end diff --git a/ogre2/src/media/Hlms/Terra/HLSL/VertexShader_vs.hlsl b/ogre2/src/media/Hlms/Terra/HLSL/VertexShader_vs.hlsl new file mode 100644 index 000000000..1fdc76e96 --- /dev/null +++ b/ogre2/src/media/Hlms/Terra/HLSL/VertexShader_vs.hlsl @@ -0,0 +1,45 @@ + +//#include "SyntaxHighlightingMisc.h" + +@insertpiece( SetCrossPlatformSettings ) +@insertpiece( SetCompatibilityLayer ) + +struct VS_INPUT +{ + uint vertexId : SV_VertexID; + uint drawId : DRAWID; + @insertpiece( custom_vs_attributes ) +}; + +struct PS_INPUT +{ + @insertpiece( Terra_VStoPS_block ) + float4 gl_Position: SV_Position; + + @pdiv( full_pso_clip_distances, hlms_pso_clip_distances, 4 ) + @pmod( partial_pso_clip_distances, hlms_pso_clip_distances, 4 ) + @foreach( full_pso_clip_distances, n ) + float4 gl_ClipDistance@n : SV_ClipDistance@n; + @end + @property( partial_pso_clip_distances ) + float@value( partial_pso_clip_distances ) gl_ClipDistance@value( full_pso_clip_distances ) : SV_ClipDistance@value( full_pso_clip_distances ); + @end +}; + +@insertpiece( DefaultTerraHeaderVS ) +@insertpiece( custom_vs_uniformDeclaration ) + +// START UNIFORM DECLARATION +Texture2D heightMap: register(t0); +// END UNIFORM DECLARATION + +PS_INPUT main( VS_INPUT input ) +{ + PS_INPUT outVs; + + @insertpiece( custom_vs_preExecution ) + @insertpiece( DefaultTerraBodyVS ) + @insertpiece( custom_vs_posExecution ) + + return outVs; +} diff --git a/ogre2/src/media/Hlms/Terra/Metal/PbsTerraShadows/PbsTerraShadows_piece_vs_piece_ps.metal b/ogre2/src/media/Hlms/Terra/Metal/PbsTerraShadows/PbsTerraShadows_piece_vs_piece_ps.metal new file mode 100644 index 000000000..28c99327d --- /dev/null +++ b/ogre2/src/media/Hlms/Terra/Metal/PbsTerraShadows/PbsTerraShadows_piece_vs_piece_ps.metal @@ -0,0 +1,46 @@ +@property( !hlms_shadowcaster && terra_enabled ) + +@piece( custom_VStoPS ) + float terrainShadow; +@end + +/// Extra per-pass global data we need for applying our +/// shadows to regular objects, passed to all PBS shaders. +@piece( custom_passBuffer ) + float4 terraOrigin; //Normalized. i.e. -terrainOrigin / terrainDimensions + //.xz = terrain 1.0 / XZ dimensions. + //.y = 1.0 / terrainHeight; + float4 invTerraBounds; +@end + +/// Add the shadows' texture to the vertex shader +@piece( custom_vs_uniformDeclaration ) + , sampler terrainShadowSampler [[sampler(12)]] + , texture2d terrainShadows [[texture(12)]] +@end + +/// Evaluate the shadow based on world XZ position & height in the vertex shader. +/// Doing it at the pixel shader level would be more accurate, but the difference +/// is barely noticeable, and slower +@piece( custom_vs_posExecution ) + @property( z_up ) + float3 terraWorldPos = float3( worldPos.x, -worldPos.z, worldPos.y ); + @else + float3 terraWorldPos = worldPos.xyz; + @end + float3 terraShadowData = terrainShadows.sample( terrainShadowSampler, + terraWorldPos.xz * passBuf.invTerraBounds.xz + passBuf.terraOrigin.xz, + level(0) ).xyz; + float terraHeightWeight = terraWorldPos.y * passBuf.invTerraBounds.y + passBuf.terraOrigin.y; + terraHeightWeight = (terraHeightWeight - terraShadowData.y) * terraShadowData.z * 1023.0; + outVs.terrainShadow = lerp( terraShadowData.x, 1.0, saturate( terraHeightWeight ) ); +@end + +@property( hlms_num_shadow_map_lights ) + @piece( custom_ps_preLights )fShadow *= inPs.terrainShadow;@end +@else + @piece( custom_ps_preLights )float fShadow = inPs.terrainShadow;@end + @piece( DarkenWithShadowFirstLight )* fShadow@end +@end + +@end diff --git a/ogre2/src/media/Hlms/Terra/Metal/PixelShader_ps.metal b/ogre2/src/media/Hlms/Terra/Metal/PixelShader_ps.metal new file mode 100644 index 000000000..f27bf814d --- /dev/null +++ b/ogre2/src/media/Hlms/Terra/Metal/PixelShader_ps.metal @@ -0,0 +1,192 @@ + +//#include "SyntaxHighlightingMisc.h" + +@insertpiece( SetCrossPlatformSettings ) +@insertpiece( DeclareUvModifierMacros ) + +@insertpiece( DefaultTerraHeaderPS ) + +// START UNIFORM STRUCT DECLARATION +@property( !hlms_shadowcaster ) + @insertpiece( PassStructDecl ) + @insertpiece( TerraMaterialStructDecl ) + @insertpiece( TerraInstanceStructDecl ) +@end +@insertpiece( custom_ps_uniformStructDeclaration ) +// END UNIFORM STRUCT DECLARATION +struct PS_INPUT +{ + @insertpiece( Terra_VStoPS_block ) +}; + +@padd( roughness_map0_sampler, samplerStateStart ) +@padd( roughness_map1_sampler, samplerStateStart ) +@padd( roughness_map2_sampler, samplerStateStart ) +@padd( roughness_map3_sampler, samplerStateStart ) + +@padd( metalness_map0_sampler, samplerStateStart ) +@padd( metalness_map1_sampler, samplerStateStart ) +@padd( metalness_map2_sampler, samplerStateStart ) +@padd( metalness_map3_sampler, samplerStateStart ) + +@property( !hlms_shadowcaster ) + +@property( !hlms_render_depth_only ) + @property( hlms_gen_normals_gbuffer ) + #define outPs_normals outPs.normals + @end + @property( hlms_prepass ) + #define outPs_shadowRoughness outPs.shadowRoughness + @end +@end + +@property( use_parallax_correct_cubemaps ) + @insertpiece( DeclParallaxLocalCorrect ) +@end + + +@insertpiece( DeclShadowMapMacros ) +@insertpiece( DeclShadowSamplingFuncs ) + +@insertpiece( DeclAreaLtcLightFuncs ) + +@property( hlms_enable_cubemaps_auto && hlms_cubemaps_use_dpm ) + @insertpiece( DeclDualParaboloidFunc ) +@end + +constexpr sampler shadowSampler = sampler( coord::normalized, + address::clamp_to_edge, + filter::linear, + @property( hlms_no_reverse_depth ) + compare_func::less_equal ); + @else + compare_func::greater_equal ); + @end + +@insertpiece( DeclOutputType ) + +@insertpiece( custom_ps_functions ) + +fragment @insertpiece( output_type ) main_metal +( + PS_INPUT inPs [[stage_in]] + @property( hlms_vpos ) + , float4 gl_FragCoord [[position]] + @end + @property( two_sided_lighting ) + , bool gl_FrontFacing [[front_facing]] + @end + @property( hlms_use_prepass_msaa && hlms_use_prepass ) + , uint gl_SampleMask [[sample_mask]] + @end + // START UNIFORM DECLARATION + @property( !hlms_shadowcaster || alpha_test ) + @insertpiece( PassDecl ) + @insertpiece( TerraMaterialDecl ) + @insertpiece( PccManualProbeDecl ) + @end + @insertpiece( custom_ps_uniformDeclaration ) + // END UNIFORM DECLARATION + + , texture2d terrainNormals [[texture(@value(terrainNormals))]] + , texture2d terrainShadows [[texture(@value(terrainShadows))]] + , sampler samplerStateTerra [[sampler(@value(terrainNormals))]] + + @property( hlms_forwardplus ) + , device const ushort *f3dGrid [[buffer(TEX_SLOT_START+@value(f3dGrid))]] + , device const float4 *f3dLightList [[buffer(TEX_SLOT_START+@value(f3dLightList))]] + @end + + @property( hlms_use_prepass ) + @property( !hlms_use_prepass_msaa ) + , texture2d gBuf_normals [[texture(@value(gBuf_normals))]] + , texture2d gBuf_shadowRoughness [[texture(@value(gBuf_shadowRoughness))]] + @end @property( hlms_use_prepass_msaa ) + , texture2d_ms gBuf_normals [[texture(@value(gBuf_normals))]] + , texture2d_ms gBuf_shadowRoughness[[texture(@value(gBuf_shadowRoughness))]] + @end + + @property( hlms_use_ssr ) + , texture2d ssrTexture [[texture(@value(ssrTexture))]] + @end + @end + + @insertpiece( DeclPlanarReflTextures ) + @insertpiece( DeclAreaApproxTextures ) + + + @property( irradiance_volumes ) + , texture3d irradianceVolume [[texture(@value(irradianceVolume))]] + , sampler irradianceVolumeSampler [[sampler(@value(irradianceVolume))]] + @end + + @foreach( num_textures, n ) + , texture2d_array textureMaps@n [[texture(@value(textureMaps@n))]]@end + @property( use_envprobe_map ) + @property( !hlms_enable_cubemaps_auto ) + , texturecube texEnvProbeMap [[texture(@value(texEnvProbeMap))]] + @end + @property( hlms_enable_cubemaps_auto ) + @property( !hlms_cubemaps_use_dpm ) + , texturecube_array texEnvProbeMap [[texture(@value(texEnvProbeMap))]] + @end + @property( hlms_cubemaps_use_dpm ) + , texture2d_array texEnvProbeMap [[texture(@value(texEnvProbeMap))]] + @end + @end + @property( envMapRegSampler < samplerStateStart ) + , sampler samplerState@value(envMapRegSampler) [[sampler(@value(envMapRegSampler))]] + @end + @end + @foreach( num_samplers, n ) + , sampler samplerState@value(samplerStateStart) [[sampler(@counter(samplerStateStart))]]@end + @insertpiece( DeclDecalsSamplers ) + @insertpiece( DeclShadowSamplers ) + @insertpiece( DeclAreaLtcTextures ) + @insertpiece( DeclVctTextures ) +) +{ + PS_OUTPUT outPs; + @insertpiece( custom_ps_preExecution ) + @insertpiece( DefaultTerraBodyPS ) + @insertpiece( custom_ps_posExecution ) + +@property( !hlms_render_depth_only ) + return outPs; +@end +} +@else ///!hlms_shadowcaster + +@insertpiece( DeclShadowCasterMacros ) + +@property( hlms_shadowcaster_point || exponential_shadow_maps ) + @insertpiece( PassStructDecl ) +@end + +@insertpiece( DeclOutputType ) + +fragment @insertpiece( output_type ) main_metal +( + PS_INPUT inPs [[stage_in]] + + // START UNIFORM DECLARATION + @property( hlms_shadowcaster_point ) + @insertpiece( PassDecl ) + @end + @insertpiece( custom_ps_uniformDeclaration ) + // END UNIFORM DECLARATION +) +{ +@property( !hlms_render_depth_only || exponential_shadow_maps || hlms_shadowcaster_point ) + PS_OUTPUT outPs; +@end + + @insertpiece( custom_ps_preExecution ) + @insertpiece( DefaultBodyPS ) + @insertpiece( custom_ps_posExecution ) + +@property( !hlms_render_depth_only || exponential_shadow_maps || hlms_shadowcaster_point ) + return outPs; +@end +} +@end diff --git a/ogre2/src/media/Hlms/Terra/Metal/VertexShader_vs.metal b/ogre2/src/media/Hlms/Terra/Metal/VertexShader_vs.metal new file mode 100644 index 000000000..700c69626 --- /dev/null +++ b/ogre2/src/media/Hlms/Terra/Metal/VertexShader_vs.metal @@ -0,0 +1,52 @@ + +//#include "SyntaxHighlightingMisc.h" + +@insertpiece( SetCrossPlatformSettings ) +@insertpiece( SetCompatibilityLayer ) + +struct VS_INPUT +{ +@property( !iOS ) + ushort drawId [[attribute(15)]]; +@end + @insertpiece( custom_vs_attributes ) +}; + +struct PS_INPUT +{ + @insertpiece( Terra_VStoPS_block ) + float4 gl_Position [[position]]; + @foreach( hlms_pso_clip_distances, n ) + float gl_ClipDistance@n [[clip_distance]]; + @end +}; + +@insertpiece( DefaultTerraHeaderVS ) + +vertex PS_INPUT main_metal +( + VS_INPUT input [[stage_in]] + , uint inVs_vertexId [[vertex_id]] + @property( iOS ) + , ushort instanceId [[instance_id]] + , constant ushort &baseInstance [[buffer(15)]] + @end + // START UNIFORM DECLARATION + @insertpiece( PassDecl ) + @insertpiece( TerraInstanceDecl ) + @property( hlms_shadowcaster ) + @insertpiece( MaterialDecl ) + @end + , texture2d heightMap [[texture(0)]] + @insertpiece( custom_vs_uniformDeclaration ) + // END UNIFORM DECLARATION +) +{ + PS_INPUT outVs; + + @insertpiece( custom_vs_preExecution ) + @insertpiece( DefaultTerraBodyVS ) + @insertpiece( custom_vs_posExecution ) + + return outVs; +} diff --git a/ogre2/src/media/Hlms/Terra/ign/100.ign_CustomVs_piece_vs.any b/ogre2/src/media/Hlms/Terra/ign/100.ign_CustomVs_piece_vs.any new file mode 100644 index 000000000..5e423888f --- /dev/null +++ b/ogre2/src/media/Hlms/Terra/ign/100.ign_CustomVs_piece_vs.any @@ -0,0 +1,4 @@ + +@piece( custom_vs_posExecution ) + outVs.localHeight = worldPos.z - cellData.pos.y; +@end diff --git a/ogre2/src/media/Hlms/Terra/ign/100.ign_CustomWeights_piece_ps.any b/ogre2/src/media/Hlms/Terra/ign/100.ign_CustomWeights_piece_ps.any new file mode 100644 index 000000000..5fe900a76 --- /dev/null +++ b/ogre2/src/media/Hlms/Terra/ign/100.ign_CustomWeights_piece_ps.any @@ -0,0 +1,52 @@ + +#include "/media/matias/Datos/SyntaxHighlightingMisc.h" + +// Look for all pieces with "ign_" prefix when updating Terra to +// a new version + +@piece( ign_weights ) + detailWeights = float4( 0.0f, 0.0f, 0.0f, 0.0f ); + @property( ign_weight0 ) + detailWeights.x = smoothstep( material.ignWeightsMinHeight.x, material.ignWeightsMaxHeight.x, inPs.localHeight ); + @end + @property( ign_weight1 ) + detailWeights.y = smoothstep( material.ignWeightsMinHeight.y, material.ignWeightsMaxHeight.y, inPs.localHeight ); + @end + @property( ign_weight2 ) + detailWeights.z = smoothstep( material.ignWeightsMinHeight.z, material.ignWeightsMaxHeight.z, inPs.localHeight ); + @end + @property( ign_weight3 ) + detailWeights.w = smoothstep( material.ignWeightsMinHeight.w, material.ignWeightsMaxHeight.w, inPs.localHeight ); + @end + + @property( diffuse_map ) + @property( detail_map0 ) + // detailWeights.x *= detailCol0.w; + pixelData.diffuse.xyz = lerp( pixelData.diffuse.xyz, detailCol0.xyz, detailWeights.x ); + @else + // When there's no diffuse map; then first detail map couldn't be diffuse map + // because it contained a normal map; hence we must force it to be the + // base layer + detailWeights.x = 1.0f; + pixelData.diffuse.xyz = detailCol0.xyz; + @end + @else + // When there's no diffuse map; then first detail map couldn't be diffuse map + // because it contained a normal map; hence we must force it to be the + // base layer + detailWeights.x = 1.0f; + pixelData.diffuse.xyz = detailCol0.xyz; + @end + @property( detail_map1 ) + //detailWeights.y *= detailCol1.w; + pixelData.diffuse.xyz = lerp( pixelData.diffuse.xyz, detailCol1.xyz, detailWeights.y ); + @end + @property( detail_map2 ) + //detailWeights.z *= detailCol2.w; + pixelData.diffuse.xyz = lerp( pixelData.diffuse.xyz, detailCol2.xyz, detailWeights.z ); + @end + @property( detail_map3 ) + //detailWeights.w *= detailCol3.w; + pixelData.diffuse.xyz = lerp( pixelData.diffuse.xyz, detailCol3.xyz, detailWeights.w ); + @end +@end diff --git a/ogre2/src/media/Hlms/Terra/ign/500.ign_Structs_piece_all.any b/ogre2/src/media/Hlms/Terra/ign/500.ign_Structs_piece_all.any new file mode 100644 index 000000000..cf9b114d1 --- /dev/null +++ b/ogre2/src/media/Hlms/Terra/ign/500.ign_Structs_piece_all.any @@ -0,0 +1,11 @@ + +#include "/media/matias/Datos/SyntaxHighlightingMisc.h" + +@piece( custom_materialBuffer ) + float4 ignWeightsMinHeight; + float4 ignWeightsMaxHeight; +@end + +@piece( custom_VStoPS ) + INTERPOLANT( float localHeight, @counter(texcoord) ); +@end diff --git a/ogre2/src/terrain/Terra/CMakeLists.txt b/ogre2/src/terrain/Terra/CMakeLists.txt new file mode 100644 index 000000000..83c9ac907 --- /dev/null +++ b/ogre2/src/terrain/Terra/CMakeLists.txt @@ -0,0 +1,41 @@ + +project(terra) + +file( GLOB_RECURSE TERRA_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/include/*.h + ${CMAKE_CURRENT_LIST_DIR}/src/*.cpp +) + +add_library(${PROJECT_NAME} STATIC ${TERRA_SOURCES}) + +if(IGN_ADD_fPIC_TO_LIBRARIES AND NOT _ign_add_library_INTERFACE) + target_compile_options(${PROJECT_NAME} PRIVATE -fPIC) +endif() + +# disable all warnings for Terra +if (UNIX) + target_compile_options(${PROJECT_NAME} PRIVATE + -Wno-unused-parameter + -Wno-float-equal + -Wno-sign-compare + -Wno-strict-aliasing) +elseif (MSVC) + set_source_files_properties(${TERRA_SOURCES} COMPILE_FLAGS "/wd4244") +endif() + +add_definitions(-DOGRE_IGNORE_UNKNOWN_DEBUG) + +#target_compile_definitions(${PROJECT_NAME} PUBLIC +# $<$:DEBUG=1 _DEBUG=1>) + +target_include_directories(${PROJECT_NAME} + PRIVATE + # Hlms files inside Hlms/Pbs do not have #include thus + # we must add this one manually for this to build correctly + ${OGRE2_INCLUDE}/Hlms/Pbs + ${OGRE2_INCLUDE}/Hlms/Common + PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/include +) + +target_link_libraries(${PROJECT_NAME} PRIVATE IgnOGRE2::IgnOGRE2) diff --git a/ogre2/src/terrain/Terra/include/Terra/Hlms/OgreHlmsJsonTerra.h b/ogre2/src/terrain/Terra/include/Terra/Hlms/OgreHlmsJsonTerra.h new file mode 100644 index 000000000..7158ee7af --- /dev/null +++ b/ogre2/src/terrain/Terra/include/Terra/Hlms/OgreHlmsJsonTerra.h @@ -0,0 +1,100 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE + (Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org/ + +Copyright (c) 2000-2014 Torus Knot Software Ltd + +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. +----------------------------------------------------------------------------- +*/ + +#if !OGRE_NO_JSON && defined( IGN_TERRA_JSON_ENABLED ) +#ifndef _OgreHlmsJsonTerra_H_ +#define _OgreHlmsJsonTerra_H_ + +#include "Terra/Hlms/OgreHlmsTerraPrerequisites.h" +#include "Terra/Hlms/OgreHlmsTerraDatablock.h" +#include "OgreHlmsJson.h" +#include "OgreHeaderPrefix.h" + +namespace Ogre +{ + /** \addtogroup Component + * @{ + */ + /** \addtogroup Material + * @{ + */ + + class HlmsJsonTerra + { + HlmsManager *mHlmsManager; + TextureGpuManager *mTextureManager; + + static TerraBrdf::TerraBrdf parseBrdf( const char *value ); + static void parseOffset( const rapidjson::Value &jsonArray, Vector4 &offsetScale ); + static void parseScale( const rapidjson::Value &jsonArray, Vector4 &offsetScale ); + + static inline Vector3 parseVector3Array( const rapidjson::Value &jsonArray ); + + void loadTexture( const rapidjson::Value &json, const char *keyName, + TerraTextureTypes textureType, HlmsTerraDatablock *datablock, + const String &resourceGroup ); + + void loadTexture( const rapidjson::Value &json, const HlmsJson::NamedBlocks &blocks, + TerraTextureTypes textureType, HlmsTerraDatablock *datablock, + const String &resourceGroup ); + + void saveTexture( const char *blockName, + TerraTextureTypes textureType, + const HlmsTerraDatablock *datablock, String &outString, + bool writeTexture=true ); + void saveTexture( const Vector3 &value, const char *blockName, + TerraTextureTypes textureType, + const HlmsTerraDatablock *datablock, String &outString, + bool writeTexture=true ); + + void saveTexture( const Vector3 &value, const char *blockName, + TerraTextureTypes textureType, + bool writeValue, bool writeTexture, + const HlmsTerraDatablock *datablock, String &outString ); + + public: + HlmsJsonTerra( HlmsManager *hlmsManager, TextureGpuManager *textureManager ); + + void loadMaterial( const rapidjson::Value &json, const HlmsJson::NamedBlocks &blocks, + HlmsDatablock *datablock , const String &resourceGroup ); + void saveMaterial( const HlmsDatablock *datablock, String &outString ); + + static void collectSamplerblocks( const HlmsDatablock *datablock, + set::type &outSamplerblocks ); + }; + + /** @} */ + /** @} */ + +} + +#include "OgreHeaderSuffix.h" + +#endif + +#endif diff --git a/ogre2/src/terrain/Terra/include/Terra/Hlms/OgreHlmsTerra.h b/ogre2/src/terrain/Terra/include/Terra/Hlms/OgreHlmsTerra.h new file mode 100644 index 000000000..3ec887339 --- /dev/null +++ b/ogre2/src/terrain/Terra/include/Terra/Hlms/OgreHlmsTerra.h @@ -0,0 +1,151 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE + (Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org/ + +Copyright (c) 2000-2014 Torus Knot Software Ltd + +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 _OgreHlmsTerra_H_ +#define _OgreHlmsTerra_H_ + +#include "Terra/Hlms/OgreHlmsTerraPrerequisites.h" +#include "OgreHlmsBufferManager.h" +#include "OgreConstBufferPool.h" +#include "OgreMatrix4.h" +#include "Hlms/Pbs/OgreHlmsPbs.h" +#include "OgreHeaderPrefix.h" + +namespace Ogre +{ + class CompositorShadowNode; + struct QueuedRenderable; + + class Terra; + + /** \addtogroup Component + * @{ + */ + /** \addtogroup Material + * @{ + */ + + class HlmsTerraDatablock; + + /** Physically based shading implementation specfically designed for + OpenGL 3+, D3D11 and other RenderSystems which support uniform buffers. + */ + class HlmsTerra : public HlmsPbs + { + MovableObject const *mLastMovableObject; + DescriptorSetSampler const *mTerraDescSetSampler; + + FastArray mLinkedTerras; + + virtual HlmsDatablock* createDatablockImpl( IdString datablockName, + const HlmsMacroblock *macroblock, + const HlmsBlendblock *blendblock, + const HlmsParamVec ¶mVec ); + + void setDetailMapProperties( HlmsTerraDatablock *datablock, PiecesMap *inOutPieces ); + void setTextureProperty( const char *propertyName, HlmsTerraDatablock *datablock, + TerraTextureTypes texType ); + void setDetailTextureProperty( const char *propertyName, HlmsTerraDatablock *datablock, + TerraTextureTypes baseTexType, uint8 detailIdx ); + + virtual void calculateHashFor( Renderable *renderable, uint32 &outHash, uint32 &outCasterHash ); + virtual void calculateHashForPreCreate( Renderable *renderable, PiecesMap *inOutPieces ); + virtual void calculateHashForPreCaster( Renderable *renderable, PiecesMap *inOutPieces ); + + virtual void notifyPropertiesMergedPreGenerationStep(void); + + FORCEINLINE uint32 fillBuffersFor( const HlmsCache *cache, + const QueuedRenderable &queuedRenderable, + bool casterPass, uint32 lastCacheHash, + CommandBuffer *commandBuffer, bool isV1 ); + + public: + HlmsTerra( Archive *dataFolder, ArchiveVec *libraryFolders ); + virtual ~HlmsTerra(); + + const FastArray &getLinkedTerras( void ) const { return mLinkedTerras; } + + void _linkTerra( Terra *terra ); + void _unlinkTerra( Terra *terra ); + + virtual void _changeRenderSystem( RenderSystem *newRs ); + + virtual uint32 fillBuffersFor( const HlmsCache *cache, const QueuedRenderable &queuedRenderable, + bool casterPass, uint32 lastCacheHash, + uint32 lastTextureHash ); + + virtual uint32 fillBuffersForV1( const HlmsCache *cache, + const QueuedRenderable &queuedRenderable, + bool casterPass, uint32 lastCacheHash, + CommandBuffer *commandBuffer ); + virtual uint32 fillBuffersForV2( const HlmsCache *cache, + const QueuedRenderable &queuedRenderable, + bool casterPass, uint32 lastCacheHash, + CommandBuffer *commandBuffer ); + + static void getDefaultPaths( String& outDataFolderPath, StringVector& outLibraryFoldersPaths ); + +#if !OGRE_NO_JSON && defined( IGN_TERRA_JSON_ENABLED ) + /// @copydoc Hlms::_loadJson + virtual void _loadJson( const rapidjson::Value &jsonValue, const HlmsJson::NamedBlocks &blocks, + HlmsDatablock *datablock, const String &resourceGroup, + HlmsJsonListener *listener, + const String &additionalTextureExtension ) const; + /// @copydoc Hlms::_saveJson + virtual void _saveJson( const HlmsDatablock *datablock, String &outString, + HlmsJsonListener *listener, + const String &additionalTextureExtension ) const; + + /// @copydoc Hlms::_collectSamplerblocks + virtual void _collectSamplerblocks( set::type &outSamplerblocks, + const HlmsDatablock *datablock ) const; +#endif + }; + + struct TerraProperty + { + static const IdString UseSkirts; + static const IdString ZUp; + + static const IdString NumTextures; + static const char *DiffuseMap; + static const char *EnvProbeMap; + static const char *DetailWeightMap; + static const char *DetailMapN; + static const char *DetailMapNmN; + static const char *RoughnessMap; + static const char *MetalnessMap; + }; + + /** @} */ + /** @} */ + +} + +#include "OgreHeaderSuffix.h" + +#endif diff --git a/ogre2/src/terrain/Terra/include/Terra/Hlms/OgreHlmsTerraDatablock.h b/ogre2/src/terrain/Terra/include/Terra/Hlms/OgreHlmsTerraDatablock.h new file mode 100644 index 000000000..27c3a2d5c --- /dev/null +++ b/ogre2/src/terrain/Terra/include/Terra/Hlms/OgreHlmsTerraDatablock.h @@ -0,0 +1,280 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE + (Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org/ + +Copyright (c) 2000-2014 Torus Knot Software Ltd + +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 _OgreHlmsTerraDatablock_H_ +#define _OgreHlmsTerraDatablock_H_ + +#include "Terra/Hlms/OgreHlmsTerraPrerequisites.h" +#include "OgreHlmsDatablock.h" +#include "OgreConstBufferPool.h" +#include "OgreVector4.h" + +#define _OgreHlmsTextureBaseClassExport +#define OGRE_HLMS_TEXTURE_BASE_CLASS HlmsTerraBaseTextureDatablock +#define OGRE_HLMS_TEXTURE_BASE_MAX_TEX NUM_TERRA_TEXTURE_TYPES +#define OGRE_HLMS_CREATOR_CLASS HlmsTerra + #include "OgreHlmsTextureBaseClass.h" +#undef _OgreHlmsTextureBaseClassExport +#undef OGRE_HLMS_TEXTURE_BASE_CLASS +#undef OGRE_HLMS_TEXTURE_BASE_MAX_TEX +#undef OGRE_HLMS_CREATOR_CLASS + +#include "OgreHeaderPrefix.h" + +namespace Ogre +{ + /** \addtogroup Core + * @{ + */ + /** \addtogroup Resources + * @{ + */ + namespace TerraBrdf + { + enum TerraBrdf + { + FLAG_UNCORRELATED = 0x80000000, + FLAG_SPERATE_DIFFUSE_FRESNEL = 0x40000000, + FLAG_LEGACY_MATH = 0x20000000, + FLAG_FULL_LEGACY = 0x08000000, + BRDF_MASK = 0x00000FFF, + + /// Most physically accurate BRDF we have. Good for representing + /// the majority of materials. + /// Uses: + /// * Roughness/Distribution/NDF term: GGX + /// * Geometric/Visibility term: Smith GGX Height-Correlated + /// * Normalized Disney Diffuse BRDF,see + /// "Moving Frostbite to Physically Based Rendering" from + /// Sebastien Lagarde & Charles de Rousiers + Default = 0x00000000, + + /// Implements Cook-Torrance BRDF. + /// Uses: + /// * Roughness/Distribution/NDF term: Beckmann + /// * Geometric/Visibility term: Cook-Torrance + /// * Lambertian Diffuse. + /// + /// Ideal for silk (use high roughness values), synthetic fabric + CookTorrance = 0x00000001, + + /// Implements Normalized Blinn Phong using a normalization + /// factor of (n + 8) / (8 * pi) + /// The main reason to use this BRDF is performance. It's cheaper, + /// while still looking somewhat similar to Default. + /// If you still need more performance, see BlinnPhongLegacy + BlinnPhong = 0x00000002, + + /// Same as Default, but the geometry term is not height-correlated + /// which most notably causes edges to be dimmer and is less correct. + /// Unity (Marmoset too?) use an uncorrelated term, so you may want to + /// use this BRDF to get the closest look for a nice exchangeable + /// pipeline workflow. + DefaultUncorrelated = Default|FLAG_UNCORRELATED, + + /// Same as Default but the fresnel of the diffuse is calculated + /// differently. Normally the diffuse component would be multiplied against + /// the inverse of the specular's fresnel to maintain energy conservation. + /// This has the nice side effect that to achieve a perfect mirror effect, + /// you just need to raise the fresnel term to 1; which is very intuitive + /// to artists (specially if using coloured fresnel) + /// + /// When using this BRDF, the diffuse fresnel will be calculated differently, + /// causing the diffuse component to still affect the colour even when + /// the fresnel = 1 (although subtly). To achieve a perfect mirror you will + /// have to set the fresnel to 1 *and* the diffuse colour to black; + /// which can be unintuitive for artists. + /// + /// This BRDF is very useful for representing surfaces with complex refractions + /// and reflections like glass, transparent plastics, fur, and surface with + /// refractions and multiple rescattering that cannot be represented well + /// with the default BRDF. + DefaultSeparateDiffuseFresnel = Default|FLAG_SPERATE_DIFFUSE_FRESNEL, + + /// @see DefaultSeparateDiffuseFresnel. This is the same + /// but the Cook Torrance model is used instead. + /// + /// Ideal for shiny objects like glass toy marbles, some types of rubber. + /// silk, synthetic fabric. + CookTorranceSeparateDiffuseFresnel = CookTorrance|FLAG_SPERATE_DIFFUSE_FRESNEL, + + /// Like DefaultSeparateDiffuseFresnel, but uses BlinnPhong as base. + BlinnPhongSeparateDiffuseFresnel = BlinnPhong|FLAG_SPERATE_DIFFUSE_FRESNEL, + + /// Implements traditional / the original non-PBR blinn phong: + /// * Looks more like a 2000-2005's game + /// * Ignores fresnel completely. + /// * Works with Roughness in range (0; 1]. We automatically convert + /// this parameter for you to shininess. + /// * Assumes your Light power is set to PI (or a multiple) like with + /// most other Brdfs. + /// * Diffuse & Specular will automatically be + /// multiplied/divided by PI for you (assuming you + /// set your Light power to PI). + /// The main scenario to use this BRDF is: + /// * Performance. This is the fastest BRDF. + /// * You were using Default, but are ok with how this one looks, + /// so you switch to this one instead. + BlinnPhongLegacyMath = BlinnPhong|FLAG_LEGACY_MATH, + + /// Implements traditional / the original non-PBR blinn phong: + /// * Looks more like a 2000-2005's game + /// * Ignores fresnel completely. + /// * Roughness is actually the shininess parameter; which is in range (0; inf) + /// although most used ranges are in (0; 500]. + /// * Assumes your Light power is set to 1.0. + /// * Diffuse & Specular is unmodified. + /// There are two possible reasons to use this BRDF: + /// * Performance. This is the fastest BRDF. + /// * You're porting your app from Ogre 1.x and want to maintain that + /// Fixed-Function look for some odd reason, and your materials + /// already dealt in shininess, and your lights are already calibrated. + /// + /// Important: If switching from Default to BlinnPhongFullLegacy, you'll probably see + /// that your scene is too bright. This is probably because Default divides diffuse + /// by PI and you usually set your lights' power to a multiple of PI to compensate. + /// If your scene is too bright, kist divide your lights by PI. + /// BlinnPhongLegacyMath performs that conversion for you automatically at + /// material level instead of doing it at light level. + BlinnPhongFullLegacy = BlinnPhongLegacyMath|FLAG_FULL_LEGACY, + }; + } + + /** Contains information needed by TERRA (Physically Based Shading) for OpenGL 3+ & D3D11+ + */ + class HlmsTerraDatablock : public HlmsTerraBaseTextureDatablock + { + friend class HlmsTerra; + + protected: + float mkDr, mkDg, mkDb; //kD + float mShadowConstantBiasGpu; + float mRoughness[4]; + float mMetalness[4]; + Vector4 mDetailsOffsetScale[4]; + //uint16 mTexIndices[NUM_TERRA_TEXTURE_TYPES]; + + // IGN CUSTOMIZE BEGIN + float mIgnWeightsMinHeight[4]; + float mIgnWeightsMaxHeight[4]; + // IGN CUSTOMIZE END + + /// @see TerraBrdf::TerraBrdf + uint32 mBrdf; + + virtual void cloneImpl( HlmsDatablock *datablock ) const; + + void scheduleConstBufferUpdate(void); + virtual void uploadToConstBuffer( char *dstPtr, uint8 dirtyFlags ); + + public: + HlmsTerraDatablock( IdString name, HlmsTerra *creator, + const HlmsMacroblock *macroblock, + const HlmsBlendblock *blendblock, + const HlmsParamVec ¶ms ); + virtual ~HlmsTerraDatablock(); + + /// Sets overall diffuse colour. The colour will be divided by PI for energy conservation. + void setDiffuse( const Vector3 &diffuseColour ); + Vector3 getDiffuse(void) const; + + /// Sets the roughness + void setRoughness( uint8 detailMapIdx, float roughness ); + float getRoughness( uint8 detailMapIdx ) const; + + /** Sets the metalness in a metallic workflow. + @remarks + Overrides any fresnel value. + Should be in Metallic mode. @see setWorkflow; + @param metalness + Value in range [0; 1] + */ + void setMetalness( uint8 detailMapIdx, float metalness ); + float getMetalness( uint8 detailMapIdx ) const; + + /** Sets the scale and offset of the detail map. + @remarks + A value of Vector4( 0, 0, 1, 1 ) will cause a flushRenderables as we + remove the code from the shader. + @param detailMap + Value in the range [0; 8) + Range [0; 4) affects diffuse maps. + @param offsetScale + XY = Contains the UV offset. + ZW = Constains the UV scale. + Default value is Vector4( 0, 0, 1, 1 ) + */ + void setDetailMapOffsetScale( uint8 detailMap, const Vector4 &offsetScale ); + const Vector4& getDetailMapOffsetScale( uint8 detailMap ) const; + + /// Overloaded to tell it's unsupported + virtual void setAlphaTestThreshold( float threshold ); + + /// Unlike most Hlms implementations, directly modifying mShadowConstantBias is not enough + /// Call this function instead + void setShadowConstantBias( float shadowConstantBias ); + + /// Changes the BRDF in use. Calling this function may trigger an + /// HlmsDatablock::flushRenderables + void setBrdf( TerraBrdf::TerraBrdf brdf ); + uint32 getBrdf(void) const; + + // IGN CUSTOMIZE BEGIN + void setIgnWeightsHeights( const Vector4 &ignWeightsMinHeight, + const Vector4 &ignWeightsMaxHeight ); + using HlmsTerraBaseTextureDatablock::setTexture; + void setTexture( TerraTextureTypes texUnit, const String &name, + const HlmsSamplerblock *refParams ); + // IGN CUSTOMIZE END + + /** Suggests the TextureMapType (aka texture category) for each type of texture + (i.e. normals should load from TEXTURE_TYPE_NORMALS). + @remarks + Remember that if "myTexture" was loaded as TEXTURE_TYPE_DIFFUSE and then you try + to load it as TEXTURE_TYPE_NORMALS, the first one will prevail until it's removed. + You could create an alias however, and thus have two copies of the same texture with + different loading parameters. + */ +// static HlmsTextureManager::TextureMapType suggestMapTypeBasedOnTextureType( +// TerraTextureTypes type ); + bool suggestUsingSRGB( TerraTextureTypes type ) const; + uint32 suggestFiltersForType( TerraTextureTypes type ) const; + + virtual void calculateHash(); + + static const size_t MaterialSizeInGpu; + static const size_t MaterialSizeInGpuAligned; + }; + + /** @} */ + /** @} */ + +} + +#include "OgreHeaderSuffix.h" + +#endif diff --git a/ogre2/src/terrain/Terra/include/Terra/Hlms/OgreHlmsTerraPrerequisites.h b/ogre2/src/terrain/Terra/include/Terra/Hlms/OgreHlmsTerraPrerequisites.h new file mode 100644 index 000000000..bbee394fd --- /dev/null +++ b/ogre2/src/terrain/Terra/include/Terra/Hlms/OgreHlmsTerraPrerequisites.h @@ -0,0 +1,65 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE + (Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org/ + +Copyright (c) 2000-2014 Torus Knot Software Ltd + +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 _OgreHlmsTerraPrerequisites_H_ +#define _OgreHlmsTerraPrerequisites_H_ + +namespace Ogre +{ + enum TerraTextureTypes + { + TERRA_DIFFUSE, + TERRA_DETAIL_WEIGHT, + TERRA_DETAIL0, + TERRA_DETAIL1, + TERRA_DETAIL2, + TERRA_DETAIL3, + TERRA_DETAIL0_NM, + TERRA_DETAIL1_NM, + TERRA_DETAIL2_NM, + TERRA_DETAIL3_NM, + TERRA_DETAIL_ROUGHNESS0, + TERRA_DETAIL_ROUGHNESS1, + TERRA_DETAIL_ROUGHNESS2, + TERRA_DETAIL_ROUGHNESS3, + TERRA_DETAIL_METALNESS0, + TERRA_DETAIL_METALNESS1, + TERRA_DETAIL_METALNESS2, + TERRA_DETAIL_METALNESS3, + TERRA_REFLECTION, + TERRA_RESERVED0, + TERRA_RESERVED1, + TERRA_RESERVED2, + TERRA_RESERVED3, + TERRA_RESERVED4, + NUM_TERRA_TEXTURE_TYPES + }; + + class HlmsTerra; +} + +#endif diff --git a/ogre2/src/terrain/Terra/include/Terra/Hlms/PbsListener/OgreHlmsPbsTerraShadows.h b/ogre2/src/terrain/Terra/include/Terra/Hlms/PbsListener/OgreHlmsPbsTerraShadows.h new file mode 100644 index 000000000..df7257a63 --- /dev/null +++ b/ogre2/src/terrain/Terra/include/Terra/Hlms/PbsListener/OgreHlmsPbsTerraShadows.h @@ -0,0 +1,81 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE + (Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org/ + +Copyright (c) 2000-2014 Torus Knot Software Ltd + +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 _OgreHlmsPbsTerraShadows_ +#define _OgreHlmsPbsTerraShadows_ + +#include "OgreGpuProgram.h" +#include "OgreHlmsListener.h" + +namespace Ogre +{ + class Terra; + + class HlmsPbsTerraShadows : public HlmsListener + { + protected: + Terra *mTerra; + HlmsSamplerblock const *mTerraSamplerblock; +#if OGRE_DEBUG_MODE + SceneManager *mSceneManager; +#endif + + public: + HlmsPbsTerraShadows(); + ~HlmsPbsTerraShadows(); + + void setTerra( Terra *terra ); + + virtual void shaderCacheEntryCreated( const String &shaderProfile, + const HlmsCache *hlmsCacheEntry, + const HlmsCache &passCache, + const HlmsPropertyVec &properties, + const QueuedRenderable &queuedRenderable ); + + virtual void preparePassHash( const CompositorShadowNode *shadowNode, + bool casterPass, bool dualParaboloid, + SceneManager *sceneManager, Hlms *hlms ); + + virtual uint32 getPassBufferSize( const CompositorShadowNode *shadowNode, bool casterPass, + bool dualParaboloid, SceneManager *sceneManager ) const; + + virtual float* preparePassBuffer( const CompositorShadowNode *shadowNode, bool casterPass, + bool dualParaboloid, SceneManager *sceneManager, + float *passBufferPtr ); + + virtual void hlmsTypeChanged( bool casterPass, CommandBuffer *commandBuffer, + const HlmsDatablock *datablock ); + }; + + struct PbsTerraProperty + { + static const IdString TerraEnabled; + }; +} + +#endif diff --git a/ogre2/src/terrain/Terra/include/Terra/Terra.h b/ogre2/src/terrain/Terra/include/Terra/Terra.h new file mode 100644 index 000000000..ef8d17a89 --- /dev/null +++ b/ogre2/src/terrain/Terra/include/Terra/Terra.h @@ -0,0 +1,246 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE + (Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org/ + +Copyright (c) 2000-2021 Torus Knot Software Ltd + +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 _OgreTerra_H_ +#define _OgreTerra_H_ + +#include "OgrePrerequisites.h" +#include "OgreMovableObject.h" +#include "OgreShaderParams.h" + +#include "Terra/TerrainCell.h" + +namespace Ogre +{ + struct GridPoint + { + int32 x; + int32 z; + }; + + struct GridDirection + { + int x; + int z; + }; + + class ShadowMapper; + + /** + @brief The Terra class + Internally Terra operates in Y-up space so input and outputs may + be converted to/from the correct spaces based on setting, unless + explicitly stated to be always Y-up by documentation. + */ + class Terra : public MovableObject + { + friend class TerrainCell; + + struct SavedState + { + RenderableArray m_renderables; + size_t m_currentCell; + Camera const *m_camera; + }; + + std::vector m_heightMap; + uint32 m_width; + uint32 m_depth; //PNG's Height + float m_depthWidthRatio; + float m_skirtSize; + float m_invWidth; + float m_invDepth; + + bool m_zUp; + + Vector2 m_xzDimensions; + Vector2 m_xzInvDimensions; + Vector2 m_xzRelativeSize; // m_xzDimensions / [m_width, m_height] + float m_height; + Vector3 m_terrainOrigin; + uint32 m_basePixelDimension; + + /// 0 is currently in use + /// 1 is SavedState + std::vector m_terrainCells[2]; + /// 0 & 1 are for tmp use + std::vector m_collectedCells[2]; + size_t m_currentCell; + + DescriptorSetTexture const *m_descriptorSet; + Ogre::TextureGpu* m_heightMapTex; + Ogre::TextureGpu* m_normalMapTex; + + Vector3 m_prevLightDir; + ShadowMapper *m_shadowMapper; + + /// When rendering shadows we want to override the data calculated by update + /// but only temporarily, for later restoring it. + SavedState m_savedState; + + //Ogre stuff + CompositorManager2 *m_compositorManager; + Camera const *m_camera; + + /// Converts value from Y-up to whatever the user up vector is (see m_zUp) + inline Vector3 fromYUp( Vector3 value ) const; + /// Same as fromYUp, but preserves original sign. Needed when value is a scale + inline Vector3 fromYUpSignPreserving( Vector3 value ) const; + /// Converts value from user up vector to Y-up + inline Vector3 toYUp( Vector3 value ) const; + /// Same as toYUp, but preserves original sign. Needed when value is a scale + inline Vector3 toYUpSignPreserving( Vector3 value ) const; + + public: + uint32 mHlmsTerraIndex; + + protected: + void createDescriptorSet(void); + void destroyDescriptorSet(void); + void destroyHeightmapTexture(void); + + /// Creates the Ogre texture based on the image data. + /// Called by @see createHeightmap + void createHeightmapTexture( const Image2 &image, const String &imageName ); + + /// Calls createHeightmapTexture, loads image data to our CPU-side buffers + void createHeightmap( Image2 &image, const String &imageName ); + + void createNormalTexture(void); + void destroyNormalTexture(void); + + /// Automatically calculates the optimum skirt size (no gaps with + /// lowest overdraw possible). + /// This is done by taking the heighest delta between two adjacent + /// pixels in a 4x4 block. + /// This calculation may not be perfect, as the block search should + /// get bigger for higher LODs. + void calculateOptimumSkirtSize(void); + + inline GridPoint worldToGrid( const Vector3 &vPos ) const; + inline Vector2 gridToWorld( const GridPoint &gPos ) const; + + bool isVisible( const GridPoint &gPos, const GridPoint &gSize ) const; + + void addRenderable( const GridPoint &gridPos, const GridPoint &cellSize, uint32 lodLevel ); + + void optimizeCellsAndAdd(void); + + public: + Terra( IdType id, ObjectMemoryManager *objectMemoryManager, SceneManager *sceneManager, + uint8 renderQueueId, CompositorManager2 *compositorManager, Camera *camera, bool zUp ); + ~Terra(); + + /// How low should the skirt be. Normally you should let this value untouched and let + /// calculateOptimumSkirtSize do its thing for best performance/quality ratio. + /// + /// However if your height values are unconventional (i.e. artificial, non-natural) and you + /// need to look the terrain from the "outside" (rather than being inside the terrain), + /// you may have to tweak this value manually. + /// + /// This value should be between min height and max height of the heightmap. + /// + /// A value of 0.0 will give you the biggest skirt and fix all skirt-related issues. + /// Note however, this may have a *tremendous* GPU performance impact. + void setCustomSkirtMinHeight( const float skirtMinHeight ) { m_skirtSize = skirtMinHeight; } + float getCustomSkirtMinHeight( void ) const { return m_skirtSize; } + + /** Must be called every frame so we can check the camera's position + (passed in the constructor) and update our visible batches (and LODs) + We also update the shadow map if the light direction changed. + @param lightDir + Light direction for computing the shadow map. + @param lightEpsilon + Epsilon to consider how different light must be from previous + call to recompute the shadow map. + Interesting values are in the range [0; 2], but any value is accepted. + @par + Large epsilons will reduce the frequency in which the light is updated, + improving performance (e.g. only compute the shadow map when needed) + @par + Use an epsilon of <= 0 to force recalculation every frame. This is + useful to prevent heterogeneity between frames (reduce stutter) if + you intend to update the light slightly every frame. + */ + void update( const Vector3 &lightDir, float lightEpsilon=1e-6f ); + + void load( const String &texName, const Vector3 ¢er, const Vector3 &dimensions ); + void load( Image2 &image, Vector3 center, Vector3 dimensions, + const String &imageName = BLANKSTRING ); + + /** Gets the interpolated height at the given location. + If outside the bounds, it leaves the height untouched. + @param vPos + Y-up: + [in] XZ position, Y for default height. + [out] Y height, or default Y (from input) if outside terrain bounds. + Z-up + [in] XY position, Z for default height. + [out] Z height, or default Z (from input) if outside terrain bounds. + @return + True if Y (or Z for Z-up) component was changed + */ + bool getHeightAt( Vector3 &vPos ) const; + + /// load must already have been called. + void setDatablock( HlmsDatablock *datablock ); + + //MovableObject overloads + const String& getMovableType(void) const; + + /// Swaps current state with a saved one. Useful for rendering shadow maps + void _swapSavedState( void ); + + const Camera* getCamera() const { return m_camera; } + void setCamera( const Camera *camera ) { m_camera = camera; } + + bool isZUp( void ) const { return m_zUp; } + + const ShadowMapper* getShadowMapper(void) const { return m_shadowMapper; } + + const Ogre::DescriptorSetTexture* getDescriptorSetTexture(void) const { return m_descriptorSet; } + + Ogre::TextureGpu* getHeightMapTex(void) const { return m_heightMapTex; } + Ogre::TextureGpu* getNormalMapTex(void) const { return m_normalMapTex; } + TextureGpu* _getShadowMapTex(void) const; + + // These are always in Y-up space + const Vector2& getXZDimensions(void) const { return m_xzDimensions; } + const Vector2& getXZInvDimensions(void) const { return m_xzInvDimensions; } + float getHeight(void) const { return m_height; } + const Vector3& getTerrainOriginRaw( void ) const{ return m_terrainOrigin; } + + /// Return value is in client-space (i.e. could be y- or z-up) + Vector3 getTerrainOrigin( void ) const; + + // Always in Y-up space + Vector2 getTerrainXZCenter(void) const; + }; +} + +#endif diff --git a/ogre2/src/terrain/Terra/include/Terra/TerraShadowMapper.h b/ogre2/src/terrain/Terra/include/Terra/TerraShadowMapper.h new file mode 100644 index 000000000..4954d837f --- /dev/null +++ b/ogre2/src/terrain/Terra/include/Terra/TerraShadowMapper.h @@ -0,0 +1,121 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE + (Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org/ + +Copyright (c) 2000-2021 Torus Knot Software Ltd + +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 _OgreTerraShadowMapper_H_ +#define _OgreTerraShadowMapper_H_ + +#include "OgrePrerequisites.h" +#include "OgreMovableObject.h" +#include "OgreShaderParams.h" + +#include "Terra/TerrainCell.h" + +namespace Ogre +{ + typedef TextureGpu* CompositorChannel; + + class ShadowMapper + { + Ogre::TextureGpu *m_heightMapTex; + + ConstBufferPacked *m_shadowStarts; + ConstBufferPacked *m_shadowPerGroupData; + CompositorWorkspace *m_shadowWorkspace; + TextureGpu *m_shadowMapTex; + HlmsComputeJob *m_shadowJob; + ShaderParams::Param *m_jobParamDelta; + ShaderParams::Param *m_jobParamXYStep; + ShaderParams::Param *m_jobParamIsStep; + ShaderParams::Param *m_jobParamHeightDelta; + + //Ogre stuff + SceneManager *m_sceneManager; + CompositorManager2 *m_compositorManager; + + static inline size_t getStartsPtrCount( int32 *starts, int32 *startsBase ); + + /** Gets how many steps are needed in Bresenham's algorithm to reach certain height, + given its dx / dy ratio where: + dx = abs( x1 - x0 ); + dy = abs( y1 - y0 ); + and Bresenham is drawn in ranges [x0; x1) and [y0; y1) + @param y + Height to reach + @param fStep + (dx * 0.5f) / dy; + @return + Number of X iterations needed to reach the the pixel at height 'y' + The returned value is at position (retVal; y) + which means (retVal-1; y-1) is true unless y = 0; + */ + static inline int32 getXStepsNeededToReachY( uint32 y, float fStep ); + + /** Calculates the value of the error at position x = xIterationsToSkip from + Bresenham's algorithm. + @remarks + We use this function so we can start Bresenham from '0' but resuming as + if we wouldn't be starting from 0. + @param xIterationsToSkip + The X position in which we want the error. + @param dx + delta.x + @param dy + delta.y + @return + The error at position (xIterationsToSkip; y) + */ + static inline float getErrorAfterXsteps( uint32 xIterationsToSkip, float dx, float dy ); + + static void setGaussianFilterParams( HlmsComputeJob *job, uint8 kernelRadius, + float gaussianDeviationFactor=0.5f ); + + public: + ShadowMapper( SceneManager *sceneManager, CompositorManager2 *compositorManager ); + ~ShadowMapper(); + + /** Sets the parameter of the gaussian filter we apply to the shadow map. + @param kernelRadius + Kernel radius. Must be an even number. + @param gaussianDeviationFactor + Expressed in terms of gaussianDeviation = kernelRadius * gaussianDeviationFactor + */ + void setGaussianFilterParams( uint8 kernelRadius, float gaussianDeviationFactor=0.5f ); + + void createShadowMap( IdType id, TextureGpu *heightMapTex ); + void destroyShadowMap(void); + void updateShadowMap( const Vector3 &lightDir, const Vector2 &xzDimensions, float heightScale ); + + void fillUavDataForCompositorChannel( TextureGpu **outChannel, + ResourceLayoutMap &outInitialLayouts, + ResourceAccessMap &outInitialUavAccess ) const; + + Ogre::TextureGpu* getShadowMapTex(void) const { return m_shadowMapTex; } + }; +} + +#endif diff --git a/ogre2/src/terrain/Terra/include/Terra/TerraWorkspaceListener.h b/ogre2/src/terrain/Terra/include/Terra/TerraWorkspaceListener.h new file mode 100644 index 000000000..26704836a --- /dev/null +++ b/ogre2/src/terrain/Terra/include/Terra/TerraWorkspaceListener.h @@ -0,0 +1,78 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE + (Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org/ + +Copyright (c) 2000-2021 Torus Knot Software Ltd + +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 _OgreTerraWorkspaceListener_H_ +#define _OgreTerraWorkspaceListener_H_ + +#include "Terra/Hlms/OgreHlmsTerraPrerequisites.h" + +#include "Compositor/OgreCompositorWorkspaceListener.h" + +namespace Ogre +{ + class Terra; + + /** \addtogroup Component + * @{ + */ + /** \addtogroup Effects + * @{ + */ + + /** + @brief TerraWorkspaceListener + This listener allows Terra to cast shadows in Point and Spot shadow maps + This feature is optional. + + If the listener is not registered, Terra won't cast shadows in point + and spot lights + */ + class TerraWorkspaceListener : public CompositorWorkspaceListener + { + HlmsTerra *m_hlmsTerra; + /// When true, we've modified Terra to properly perform shadow mapping + /// for non-directional lights + bool m_terraNeedsRestoring; + + public: + TerraWorkspaceListener( HlmsTerra *hlmsTerra ); + virtual ~TerraWorkspaceListener(); + + void setHlmsTerra( HlmsTerra *hlmsTerra ) { m_hlmsTerra = hlmsTerra; } + + virtual void passPreExecute( CompositorPass *pass ); + virtual void passSceneAfterShadowMaps( CompositorPassScene *pass ); + }; + + /** @} */ + /** @} */ + +} // namespace Ogre + +#include "OgreHeaderSuffix.h" + +#endif diff --git a/ogre2/src/terrain/Terra/include/Terra/TerrainCell.h b/ogre2/src/terrain/Terra/include/Terra/TerrainCell.h new file mode 100644 index 000000000..849c1b5c3 --- /dev/null +++ b/ogre2/src/terrain/Terra/include/Terra/TerrainCell.h @@ -0,0 +1,94 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE + (Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org/ + +Copyright (c) 2000-2021 Torus Knot Software Ltd + +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 _OgreTerrainCell_H_ +#define _OgreTerrainCell_H_ + +#include "OgrePrerequisites.h" +#include "OgreRenderable.h" + +namespace Ogre +{ + class Terra; + struct GridPoint; + + class TerrainCell : public Renderable + { + int32 m_gridX; + int32 m_gridZ; + uint32 m_lodLevel; + uint32 m_verticesPerLine; + + uint32 m_sizeX; + uint32 m_sizeZ; + + VaoManager *m_vaoManager; + + Terra *m_parentTerra; + + bool m_useSkirts; + + public: + TerrainCell( Terra *parentTerra ); + virtual ~TerrainCell(); + + bool getUseSkirts(void) const { return m_useSkirts; } + + bool isZUp( void ) const; + + void initialize( VaoManager *vaoManager, bool useSkirts ); + + void setOrigin( const GridPoint &gridPos, uint32 horizontalPixelDim, + uint32 verticalPixelDim, uint32 lodLevel ); + + /** Merges another TerrainCell into 'this' for reducing batch counts. + e.g. + Two 32x32 cells will merge into one 64x32 or 32x64 + Two 64x32 cells will merge into one 64x64 + A 32x64 cell cannot merge with a 32x32 one. + A 64x32 cell cannot merge with a 32x32 one. + @remarks + Merge will only happen if the cells are of the same LOD level and are contiguous. + @param next + The other TerrainCell to merge with. + @return + False if couldn't merge, true on success. + */ + bool merge( TerrainCell *next ); + + void uploadToGpu( uint32 * RESTRICT_ALIAS gpuPtr ) const; + + //Renderable overloads + virtual const LightList& getLights(void) const; + virtual void getRenderOperation( v1::RenderOperation& op, bool casterPass ); + virtual void getWorldTransforms( Matrix4* xform ) const; + virtual bool getCastsShadows(void) const; + }; +} + +#endif diff --git a/ogre2/src/terrain/Terra/src/Hlms/OgreHlmsJsonTerra.cpp b/ogre2/src/terrain/Terra/src/Hlms/OgreHlmsJsonTerra.cpp new file mode 100644 index 000000000..541807eb1 --- /dev/null +++ b/ogre2/src/terrain/Terra/src/Hlms/OgreHlmsJsonTerra.cpp @@ -0,0 +1,493 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE + (Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org/ + +Copyright (c) 2000-2014 Torus Knot Software Ltd + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +----------------------------------------------------------------------------- +*/ + +#include "OgreBuildSettings.h" + +#if !OGRE_NO_JSON && defined( IGN_TERRA_JSON_ENABLED ) + +#include "Terra/Hlms/OgreHlmsJsonTerra.h" +#include "OgreHlmsManager.h" +#include "OgreTextureGpuManager.h" + +#include "OgreLwString.h" +#include "OgreStringConverter.h" + +#include "rapidjson/document.h" + +namespace Ogre +{ + HlmsJsonTerra::HlmsJsonTerra( HlmsManager *hlmsManager, TextureGpuManager *textureManager ) : + mHlmsManager( hlmsManager ), + mTextureManager( textureManager ) + { + } + //----------------------------------------------------------------------------------- + TerraBrdf::TerraBrdf HlmsJsonTerra::parseBrdf( const char *value ) + { + if( !strcmp( value, "default" ) ) + return TerraBrdf::Default; + if( !strcmp( value, "cook_torrance" ) ) + return TerraBrdf::CookTorrance; + if( !strcmp( value, "blinn_phong" ) ) + return TerraBrdf::BlinnPhong; + if( !strcmp( value, "default_uncorrelated" ) ) + return TerraBrdf::DefaultUncorrelated; + if( !strcmp( value, "default_separate_diffuse_fresnel" ) ) + return TerraBrdf::DefaultSeparateDiffuseFresnel; + if( !strcmp( value, "cook_torrance_separate_diffuse_fresnel" ) ) + return TerraBrdf::CookTorranceSeparateDiffuseFresnel; + if( !strcmp( value, "blinn_phong_separate_diffuse_fresnel" ) ) + return TerraBrdf::BlinnPhongSeparateDiffuseFresnel; + + return TerraBrdf::Default; + } + //----------------------------------------------------------------------------------- + void HlmsJsonTerra::parseOffset( const rapidjson::Value &jsonArray, Vector4 &offsetScale ) + { + const rapidjson::SizeType arraySize = std::min( 2u, jsonArray.Size() ); + for( rapidjson::SizeType i=0; i( jsonArray[i].GetDouble() ); + } + } + //----------------------------------------------------------------------------------- + void HlmsJsonTerra::parseScale( const rapidjson::Value &jsonArray, Vector4 &offsetScale ) + { + const rapidjson::SizeType arraySize = std::min( 2u, jsonArray.Size() ); + for( rapidjson::SizeType i=0; i( jsonArray[i].GetDouble() ); + } + } + //----------------------------------------------------------------------------------- + void HlmsJsonTerra::loadTexture( const rapidjson::Value &json, const char *keyName, + TerraTextureTypes textureType, HlmsTerraDatablock *datablock, + const String &resourceGroup ) + { + assert( textureType != TERRA_REFLECTION ); + + TextureGpu *texture = 0; + + const CommonTextureTypes::CommonTextureTypes texMapTypes[NUM_TERRA_TEXTURE_TYPES] = + { + CommonTextureTypes::Diffuse, + CommonTextureTypes::NonColourData, + + CommonTextureTypes::Diffuse, + CommonTextureTypes::Diffuse, + CommonTextureTypes::Diffuse, + CommonTextureTypes::Diffuse, + CommonTextureTypes::NormalMap, + CommonTextureTypes::NormalMap, + CommonTextureTypes::NormalMap, + CommonTextureTypes::NormalMap, + + CommonTextureTypes::Monochrome, + CommonTextureTypes::Monochrome, + CommonTextureTypes::Monochrome, + CommonTextureTypes::Monochrome, + CommonTextureTypes::Monochrome, + CommonTextureTypes::Monochrome, + CommonTextureTypes::Monochrome, + CommonTextureTypes::Monochrome, + CommonTextureTypes::EnvMap + }; + + rapidjson::Value::ConstMemberIterator itor = json.FindMember( keyName ); + if( itor != json.MemberEnd() && itor->value.IsString() ) + { + const char *textureName = itor->value.GetString(); + texture = mTextureManager->createOrRetrieveTexture( textureName, + GpuPageOutStrategy::Discard, + texMapTypes[textureType], + resourceGroup ); + } + + HlmsSamplerblock samplerBlockRef; + if( textureType >= TERRA_DETAIL0 && textureType <= TERRA_DETAIL_METALNESS3 ) + { + //Detail maps default to wrap mode. + samplerBlockRef.mU = TAM_WRAP; + samplerBlockRef.mV = TAM_WRAP; + samplerBlockRef.mW = TAM_WRAP; + } + datablock->setTexture( textureType, texture, &samplerBlockRef ); + } + //----------------------------------------------------------------------------------- + void HlmsJsonTerra::loadTexture( const rapidjson::Value &json, const HlmsJson::NamedBlocks &blocks, + TerraTextureTypes textureType, HlmsTerraDatablock *datablock, + const String &resourceGroup ) + { + TextureGpu *texture = 0; + HlmsSamplerblock const *samplerblock = 0; + + const CommonTextureTypes::CommonTextureTypes texMapTypes[NUM_TERRA_TEXTURE_TYPES] = + { + CommonTextureTypes::Diffuse, + CommonTextureTypes::NonColourData, + + CommonTextureTypes::Diffuse, + CommonTextureTypes::Diffuse, + CommonTextureTypes::Diffuse, + CommonTextureTypes::Diffuse, + CommonTextureTypes::NormalMap, + CommonTextureTypes::NormalMap, + CommonTextureTypes::NormalMap, + CommonTextureTypes::NormalMap, + + CommonTextureTypes::Monochrome, + CommonTextureTypes::Monochrome, + CommonTextureTypes::Monochrome, + CommonTextureTypes::Monochrome, + CommonTextureTypes::Monochrome, + CommonTextureTypes::Monochrome, + CommonTextureTypes::Monochrome, + CommonTextureTypes::Monochrome, + CommonTextureTypes::EnvMap + }; + + rapidjson::Value::ConstMemberIterator itor = json.FindMember( "texture" ); + if( itor != json.MemberEnd() && itor->value.IsString() ) + { + const char *textureName = itor->value.GetString(); + texture = mTextureManager->createOrRetrieveTexture( textureName, + GpuPageOutStrategy::Discard, + texMapTypes[textureType], + resourceGroup ); + } + + itor = json.FindMember("sampler"); + if( itor != json.MemberEnd() && itor->value.IsString() ) + { + map::type::const_iterator it = + blocks.samplerblocks.find( LwConstString::FromUnsafeCStr(itor->value.GetString()) ); + if( it != blocks.samplerblocks.end() ) + { + samplerblock = it->second; + mHlmsManager->addReference( samplerblock ); + } + } + + if( texture ) + { + if( !samplerblock ) + samplerblock = mHlmsManager->getSamplerblock( HlmsSamplerblock() ); + datablock->_setTexture( textureType, texture, samplerblock ); + } + else if( samplerblock ) + datablock->_setSamplerblock( textureType, samplerblock ); + } + //----------------------------------------------------------------------------------- + inline Vector3 HlmsJsonTerra::parseVector3Array( const rapidjson::Value &jsonArray ) + { + Vector3 retVal( Vector3::ZERO ); + + const rapidjson::SizeType arraySize = std::min( 3u, jsonArray.Size() ); + for( rapidjson::SizeType i=0; i( jsonArray[i].GetDouble() ); + } + + return retVal; + } + //----------------------------------------------------------------------------------- + void HlmsJsonTerra::loadMaterial( const rapidjson::Value &json, const HlmsJson::NamedBlocks &blocks, + HlmsDatablock *datablock, const String &resourceGroup ) + { + assert( dynamic_cast(datablock) ); + HlmsTerraDatablock *terraDatablock = static_cast(datablock); + + rapidjson::Value::ConstMemberIterator itor = json.FindMember("brdf"); + if (itor != json.MemberEnd() && itor->value.IsString()) + terraDatablock->setBrdf(parseBrdf(itor->value.GetString())); + + itor = json.FindMember("diffuse"); + if( itor != json.MemberEnd() && itor->value.IsObject() ) + { + const rapidjson::Value &subobj = itor->value; + loadTexture( subobj, blocks, TERRA_DIFFUSE, terraDatablock, resourceGroup ); + + itor = subobj.FindMember( "value" ); + if( itor != subobj.MemberEnd() && itor->value.IsArray() ) + terraDatablock->setDiffuse( parseVector3Array( itor->value ) ); + } + + itor = json.FindMember("detail_weight"); + if( itor != json.MemberEnd() && itor->value.IsObject() ) + { + const rapidjson::Value &subobj = itor->value; + loadTexture( subobj, blocks, TERRA_DETAIL_WEIGHT, terraDatablock, resourceGroup ); + } + + for( int i=0; i<4; ++i ) + { + const String iAsStr = StringConverter::toString(i); + String texTypeName = "detail" + iAsStr; + + itor = json.FindMember(texTypeName.c_str()); + if( itor != json.MemberEnd() && itor->value.IsObject() ) + { + const rapidjson::Value &subobj = itor->value; + loadTexture( subobj, blocks, static_cast(TERRA_DETAIL0 + i), + terraDatablock, resourceGroup ); + + itor = subobj.FindMember( "roughness" ); + if( itor != subobj.MemberEnd() && itor->value.IsNumber() ) + terraDatablock->setRoughness( i, static_cast( itor->value.GetDouble() ) ); + + itor = subobj.FindMember( "metalness" ); + if( itor != subobj.MemberEnd() && itor->value.IsNumber() ) + terraDatablock->setMetalness( i, static_cast( itor->value.GetDouble() ) ); + + Vector4 offsetScale( 0, 0, 1, 1 ); + + itor = subobj.FindMember( "offset" ); + if( itor != subobj.MemberEnd() && itor->value.IsArray() ) + parseOffset( itor->value, offsetScale ); + + itor = subobj.FindMember( "scale" ); + if( itor != subobj.MemberEnd() && itor->value.IsArray() ) + parseScale( itor->value, offsetScale ); + + terraDatablock->setDetailMapOffsetScale( i, offsetScale ); + + loadTexture( subobj, "diffuse_map", + static_cast( TERRA_DETAIL0 + i ), + terraDatablock, resourceGroup ); + loadTexture( subobj, "normal_map", + static_cast( TERRA_DETAIL0_NM + i ), + terraDatablock, resourceGroup ); + loadTexture( subobj, "roughness_map", + static_cast( TERRA_DETAIL_ROUGHNESS0 + i ), + terraDatablock, resourceGroup ); + loadTexture( subobj, "metalness_map", + static_cast( TERRA_DETAIL_METALNESS0 + i ), + terraDatablock, resourceGroup ); + +// itor = subobjec.FindMember("sampler"); +// if( itor != subobjec.MemberEnd() && itor->value.IsString() ) +// { +// map::type::const_iterator it = +// blocks.samplerblocks.find( +// LwConstString::FromUnsafeCStr( itor->value.GetString()) ); +// if( it != blocks.samplerblocks.end() ) +// { +// textures[TERRA_DETAIL0 + i].samplerblock = it->second; +// textures[TERRA_DETAIL0_NM + i].samplerblock = it->second; +// textures[TERRA_DETAIL_ROUGHNESS0 + i].samplerblock = it->second; +// textures[TERRA_DETAIL_METALNESS0 + i].samplerblock = it->second; +// for( int i=0; i<4; ++i ) +// mHlmsManager->addReference( textures[TERRA_DETAIL0 + i].samplerblock ); +// } +// } + } + } + + itor = json.FindMember("reflection"); + if( itor != json.MemberEnd() && itor->value.IsObject() ) + { + const rapidjson::Value &subobj = itor->value; + loadTexture( subobj, blocks, TERRA_REFLECTION, terraDatablock, resourceGroup ); + } + } + //----------------------------------------------------------------------------------- + void HlmsJsonTerra::saveTexture( const char *blockName, + TerraTextureTypes textureType, + const HlmsTerraDatablock *datablock, String &outString, + bool writeTexture ) + { + saveTexture( Vector3( 0.0f ), blockName, textureType, + false, writeTexture, datablock, outString ); + } + //----------------------------------------------------------------------------------- + void HlmsJsonTerra::saveTexture( const Vector3 &value, const char *blockName, + TerraTextureTypes textureType, + const HlmsTerraDatablock *datablock, String &outString, + bool writeTexture ) + { + saveTexture( value, blockName, textureType, + true, writeTexture, datablock, outString ); + } + //----------------------------------------------------------------------------------- + void HlmsJsonTerra::saveTexture( const Vector3 &value, const char *blockName, + TerraTextureTypes textureType, + bool writeValue, bool writeTexture, + const HlmsTerraDatablock *datablock, String &outString ) + { + outString += ",\n\t\t\t\""; + outString += blockName; + outString += "\" :\n\t\t\t{\n"; + + const size_t currentOffset = outString.size(); + + if( writeValue ) + { + outString += "\t\t\t\t\"value\" : "; + HlmsJson::toStr( value, outString ); + } + + if( textureType >= TERRA_DETAIL0 && textureType <= TERRA_DETAIL3_NM ) + { + const Vector4 &offsetScale = + datablock->getDetailMapOffsetScale( textureType - TERRA_DETAIL0 ); + const Vector2 offset( offsetScale.x, offsetScale.y ); + const Vector2 scale( offsetScale.z, offsetScale.w ); + + if( offset != Vector2::ZERO ) + { + outString += ",\n\t\t\t\t\"offset\" : "; + HlmsJson::toStr( offset, outString ); + } + + if( scale != Vector2::UNIT_SCALE ) + { + outString += ",\n\t\t\t\t\"scale\" : "; + HlmsJson::toStr( scale, outString ); + } + } + + /*if( writeTexture ) + { + HlmsTextureManager::TextureLocation texLocation; + texLocation.texture = datablock->getTexture( textureType ); + if( !texLocation.texture.isNull() ) + { + texLocation.xIdx = datablock->_getTextureIdx( textureType ); + texLocation.yIdx = 0; + texLocation.divisor = 1; + + const String *texName = mHlmsManager->getTextureManager()->findAliasName( texLocation ); + + if( texName ) + { + outString += ",\n\t\t\t\t\"texture\" : \""; + outString += *texName; + outString += '"'; + } + } + + const HlmsSamplerblock *samplerblock = datablock->getSamplerblock( textureType ); + if( samplerblock ) + { + outString += ",\n\t\t\t\t\"sampler\" : "; + outString += HlmsJson::getName( samplerblock ); + } + }*/ + + if( !writeValue && outString.size() != currentOffset ) + { + //Remove an extra comma and newline characters. + outString.erase( currentOffset, 2 ); + } + + outString += "\n\t\t\t}"; + } + //----------------------------------------------------------------------------------- + void HlmsJsonTerra::saveMaterial( const HlmsDatablock *datablock, String &outString ) + { +// assert( dynamic_cast(datablock) ); +// const HlmsTerraDatablock *terraDatablock = static_cast(datablock); + +//// outString += ",\n\t\t\t\"workflow\" : "; +//// toQuotedStr( terraDatablock->getWorkflow(), outString ); + +// saveTexture( terraDatablock->getDiffuse(), "diffuse", TERRA_DIFFUSE, +// terraDatablock, outString ); + +// if( !terraDatablock->getTexture( TERRA_DETAIL_WEIGHT ).isNull() ) +// saveTexture( "detail_weight", TERRA_DETAIL_WEIGHT, terraDatablock, outString ); + +// for( int i=0; i<4; ++i ) +// { +// const Vector4 &offsetScale = terraDatablock->getDetailMapOffsetScale( i ); +// const Vector2 offset( offsetScale.x, offsetScale.y ); +// const Vector2 scale( offsetScale.z, offsetScale.w ); + +// const TerraTextureTypes textureType = static_cast(TERRA_DETAIL0 + i); + +// if( offset != Vector2::ZERO || +// scale != Vector2::UNIT_SCALE || terraDatablock->getDetailMapWeight( i ) != 1.0f || +// !terraDatablock->getTexture( textureType ).isNull() ) +// { +// char tmpBuffer[64]; +// LwString blockName( LwString::FromEmptyPointer( tmpBuffer, sizeof(tmpBuffer) ) ); + +// blockName.a( "detail_diffuse", i ); + +// saveTexture( terraDatablock->getDetailMapWeight( i ), blockName.c_str(), +// static_cast(TERRA_DETAIL0 + i), terraDatablock, +// outString ); +// } +// } + +// for( int i=0; i<4; ++i ) +// { +// const Vector4 &offsetScale = terraDatablock->getDetailMapOffsetScale( i + 4 ); +// const Vector2 offset( offsetScale.x, offsetScale.y ); +// const Vector2 scale( offsetScale.z, offsetScale.w ); + +// const TerraTextureTypes textureType = static_cast(TERRA_DETAIL0_NM + i); + +// if( offset != Vector2::ZERO || scale != Vector2::UNIT_SCALE || +// terraDatablock->getDetailNormalWeight( i ) != 1.0f || +// !terraDatablock->getTexture( textureType ).isNull() ) +// { +// char tmpBuffer[64]; +// LwString blockName( LwString::FromEmptyPointer( tmpBuffer, sizeof(tmpBuffer) ) ); + +// blockName.a( "detail_normal", i ); +// saveTexture( terraDatablock->getDetailNormalWeight( i ), blockName.c_str(), +// static_cast(TERRA_DETAIL0_NM + i), terraDatablock, +// outString ); +// } +// } + +// if( !terraDatablock->getTexture( TERRA_REFLECTION ).isNull() ) +// saveTexture( "reflection", TERRA_REFLECTION, terraDatablock, outString ); + } + //----------------------------------------------------------------------------------- + void HlmsJsonTerra::collectSamplerblocks( const HlmsDatablock *datablock, + set::type &outSamplerblocks ) + { + assert( dynamic_cast(datablock) ); + const HlmsTerraDatablock *terraDatablock = static_cast(datablock); + + for( int i=0; i( i ); + const HlmsSamplerblock *samplerblock = terraDatablock->getSamplerblock( textureType ); + if( samplerblock ) + outSamplerblocks.insert( samplerblock ); + } + } +} + +#endif diff --git a/ogre2/src/terrain/Terra/src/Hlms/OgreHlmsTerra.cpp b/ogre2/src/terrain/Terra/src/Hlms/OgreHlmsTerra.cpp new file mode 100644 index 000000000..949c5c604 --- /dev/null +++ b/ogre2/src/terrain/Terra/src/Hlms/OgreHlmsTerra.cpp @@ -0,0 +1,786 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE + (Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org/ + +Copyright (c) 2000-2014 Torus Knot Software Ltd + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +----------------------------------------------------------------------------- +*/ + +#include "Terra/Hlms/OgreHlmsTerra.h" +#include "Terra/Hlms/OgreHlmsTerraDatablock.h" +#include "OgreHlmsManager.h" +#include "OgreHlmsListener.h" +#include "OgreLwString.h" + +#if !OGRE_NO_JSON && defined( IGN_TERRA_JSON_ENABLED ) + #include "Terra/Hlms/OgreHlmsJsonTerra.h" +#endif + +#include "OgreViewport.h" +#include "OgreHighLevelGpuProgramManager.h" +#include "OgreHighLevelGpuProgram.h" +#include "OgreForward3D.h" +#include "Cubemaps/OgreParallaxCorrectedCubemap.h" +#include "OgreIrradianceVolume.h" +#include "OgreCamera.h" + +#include "OgreSceneManager.h" +#include "OgreRenderQueue.h" +#include "Compositor/OgreCompositorShadowNode.h" +#include "Vao/OgreVaoManager.h" +#include "Vao/OgreConstBufferPacked.h" + +#include "CommandBuffer/OgreCommandBuffer.h" +#include "CommandBuffer/OgreCbTexture.h" +#include "CommandBuffer/OgreCbShaderBuffer.h" + +#ifdef OGRE_BUILD_COMPONENT_PLANAR_REFLECTIONS + #include "OgrePlanarReflections.h" +#endif + +#include "Terra/Terra.h" + +namespace Ogre +{ + const IdString TerraProperty::UseSkirts = IdString( "use_skirts" ); + const IdString TerraProperty::ZUp = IdString( "z_up" ); + + const char *TerraProperty::DiffuseMap = "diffuse_map"; + const char *TerraProperty::EnvProbeMap = "envprobe_map"; + const char *TerraProperty::DetailWeightMap = "detail_weight_map"; + const char *TerraProperty::DetailMapN = "detail_map"; //detail_map0-4 + const char *TerraProperty::DetailMapNmN = "detail_map_nm"; //detail_map_nm0-4 + const char *TerraProperty::RoughnessMap = "roughness_map"; + const char *TerraProperty::MetalnessMap = "metalness_map"; + + HlmsTerra::HlmsTerra( Archive *dataFolder, ArchiveVec *libraryFolders ) : + HlmsPbs( dataFolder, libraryFolders ), + mLastMovableObject( 0 ), + mTerraDescSetSampler( 0 ) + { + //Override defaults + mType = HLMS_USER3; + mTypeName = "Terra"; + mTypeNameStr = "Terra"; + + mBytesPerSlot = HlmsTerraDatablock::MaterialSizeInGpuAligned; + mOptimizationStrategy = LowerGpuOverhead; + mSetupWorldMatBuf = false; + + //heightMap, terrainNormals & terrainShadows + mReservedTexSlots = 3u; + + mSkipRequestSlotInChangeRS = true; + } + //----------------------------------------------------------------------------------- + HlmsTerra::~HlmsTerra() + { + destroyAllBuffers(); + } + //----------------------------------------------------------------------------------- + void HlmsTerra::_linkTerra( Terra *terra ) + { + OGRE_ASSERT_LOW( terra->mHlmsTerraIndex == std::numeric_limits::max() && + "Terra instance must be unlinked before being linked again!" ); + + terra->mHlmsTerraIndex = static_cast( mLinkedTerras.size() ); + mLinkedTerras.push_back( terra ); + } + //----------------------------------------------------------------------------------- + void HlmsTerra::_unlinkTerra( Terra *terra ) + { + if( terra->mHlmsTerraIndex >= mLinkedTerras.size() || + terra != *( mLinkedTerras.begin() + terra->mHlmsTerraIndex ) ) + { + OGRE_EXCEPT( Exception::ERR_INTERNAL_ERROR, + "A Terra instance had it's mHlmsTerraIndex out of date!!! " + "(or the instance wasn't being tracked by this HlmsTerra)", + "HlmsTerra::_unlinkTerra" ); + } + + FastArray::iterator itor = mLinkedTerras.begin() + terra->mHlmsTerraIndex; + itor = efficientVectorRemove( mLinkedTerras, itor ); + + // The Renderable that was at the end got swapped and has now a different index + if( itor != mLinkedTerras.end() ) + ( *itor )->mHlmsTerraIndex = static_cast( itor - mLinkedTerras.begin() ); + + terra->mHlmsTerraIndex = std::numeric_limits::max(); + } + //----------------------------------------------------------------------------------- + void HlmsTerra::_changeRenderSystem( RenderSystem *newRs ) + { + HlmsPbs::_changeRenderSystem( newRs ); + + if( newRs ) + { + HlmsDatablockMap::const_iterator itor = mDatablocks.begin(); + HlmsDatablockMap::const_iterator end = mDatablocks.end(); + + while( itor != end ) + { + assert( dynamic_cast( itor->second.datablock ) ); + HlmsTerraDatablock *datablock = + static_cast( itor->second.datablock ); + + requestSlot( datablock->mTextureHash, datablock, false ); + ++itor; + } + + if( !mTerraDescSetSampler ) + { + //We need one for terrainNormals & terrainShadows. Reuse an existing samplerblock + DescriptorSetSampler baseSet; + baseSet.mSamplers.push_back( mAreaLightMasksSamplerblock ); + if( !mHasSeparateSamplers ) + baseSet.mSamplers.push_back( mAreaLightMasksSamplerblock ); + baseSet.mShaderTypeSamplerCount[PixelShader] = baseSet.mSamplers.size(); + mTerraDescSetSampler = mHlmsManager->getDescriptorSetSampler( baseSet ); + } + } + } + //----------------------------------------------------------------------------------- + void HlmsTerra::setDetailMapProperties( HlmsTerraDatablock *datablock, PiecesMap *inOutPieces ) + { + int32 minNormalMap = 4; + bool hasDiffuseMaps = false; + bool hasNormalMaps = false; + for( uint8 i=0; i<4u; ++i ) + { + setDetailTextureProperty( TerraProperty::DetailMapN, datablock, + TERRA_DETAIL0, i ); + setDetailTextureProperty( TerraProperty::DetailMapNmN, datablock, + TERRA_DETAIL0_NM, i ); + setDetailTextureProperty( TerraProperty::RoughnessMap, datablock, + TERRA_DETAIL_ROUGHNESS0, i ); + setDetailTextureProperty( TerraProperty::MetalnessMap, datablock, + TERRA_DETAIL_METALNESS0, i ); + + if( datablock->getTexture( TERRA_DETAIL0 + i ) ) + hasDiffuseMaps = true; + + if( datablock->getTexture( TERRA_DETAIL0_NM + i ) ) + { + minNormalMap = std::min( minNormalMap, i ); + hasNormalMaps = true; + } + + if( datablock->mDetailsOffsetScale[i] != Vector4( 0, 0, 1, 1 ) ) + setProperty( *PbsProperty::DetailOffsetsPtrs[i], 1 ); + } + + if( hasDiffuseMaps ) + setProperty( PbsProperty::DetailMapsDiffuse, 4 ); + + if( hasNormalMaps ) + setProperty( PbsProperty::DetailMapsNormal, 4 ); + + setProperty( PbsProperty::FirstValidDetailMapNm, minNormalMap ); + } + //----------------------------------------------------------------------------------- + void HlmsTerra::setTextureProperty( const char *propertyName, HlmsTerraDatablock *datablock, + TerraTextureTypes texType ) + { + uint8 idx = datablock->getIndexToDescriptorTexture( texType ); + if( idx != NUM_TERRA_TEXTURE_TYPES ) + { + char tmpData[64]; + LwString propName = LwString::FromEmptyPointer( tmpData, sizeof(tmpData) ); + + propName = propertyName; //diffuse_map + + const size_t basePropSize = propName.size(); // diffuse_map + + //In the template the we subtract the "+1" for the index. + //We need to increment it now otherwise @property( diffuse_map ) + //can translate to @property( 0 ) which is not what we want. + setProperty( propertyName, idx + 1 ); + + propName.resize( basePropSize ); + propName.a( "_idx" ); //diffuse_map_idx + setProperty( propName.c_str(), idx ); + + if( mHasSeparateSamplers ) + { + const uint8 samplerIdx = datablock->getIndexToDescriptorSampler( texType ); + propName.resize( basePropSize ); + propName.a( "_sampler" ); //diffuse_map_sampler + setProperty( propName.c_str(), samplerIdx ); + } + } + } + //----------------------------------------------------------------------------------- + void HlmsTerra::setDetailTextureProperty( const char *propertyName, HlmsTerraDatablock *datablock, + TerraTextureTypes baseTexType, uint8 detailIdx ) + { + const TerraTextureTypes texType = static_cast( baseTexType + detailIdx ); + const uint8 idx = datablock->getIndexToDescriptorTexture( texType ); + if( idx != NUM_TERRA_TEXTURE_TYPES ) + { + char tmpData[64]; + LwString propName = LwString::FromEmptyPointer( tmpData, sizeof(tmpData) ); + + propName.a( propertyName, detailIdx ); //detail_map0 + + const size_t basePropSize = propName.size(); // diffuse_map + + //In the template the we subtract the "+1" for the index. + //We need to increment it now otherwise @property( diffuse_map ) + //can translate to @property( 0 ) which is not what we want. + setProperty( propName.c_str(), idx + 1 ); + + propName.resize( basePropSize ); + propName.a( "_idx" ); //detail_map0_idx + setProperty( propName.c_str(), idx ); + + if( mHasSeparateSamplers ) + { + const uint8 samplerIdx = datablock->getIndexToDescriptorSampler( texType ); + propName.resize( basePropSize ); + propName.a( "_sampler" ); //detail_map0_sampler + setProperty( propName.c_str(), samplerIdx ); + } + } + } + //----------------------------------------------------------------------------------- + void HlmsTerra::calculateHashFor( Renderable *renderable, uint32 &outHash, uint32 &outCasterHash ) + { + assert( dynamic_cast( renderable->getDatablock() ) ); + HlmsTerraDatablock *datablock = static_cast( renderable->getDatablock() ); + if( datablock->getDirtyFlags() & (DirtyTextures|DirtySamplers) ) + { + //Delay hash generation for later, when we have the final (or temporary) descriptor sets. + outHash = 0; + outCasterHash = 0; + } + else + { + Hlms::calculateHashFor( renderable, outHash, outCasterHash ); + } + + datablock->loadAllTextures(); + } + //----------------------------------------------------------------------------------- + void HlmsTerra::calculateHashForPreCreate( Renderable *renderable, PiecesMap *inOutPieces ) + { + assert( dynamic_cast(renderable) && + "This Hlms can only be used on a Terra object!" ); + + TerrainCell *terrainCell = static_cast(renderable); + setProperty( TerraProperty::UseSkirts, terrainCell->getUseSkirts() ); + setProperty( TerraProperty::ZUp, terrainCell->isZUp() ); + + assert( dynamic_cast( renderable->getDatablock() ) ); + HlmsTerraDatablock *datablock = static_cast( renderable->getDatablock() ); + + setProperty( PbsProperty::FresnelScalar, 1 ); + setProperty( PbsProperty::FresnelWorkflow, 0 ); + setProperty( PbsProperty::MetallicWorkflow, 1 ); + + setProperty( PbsProperty::ReceiveShadows, 1 ); + + uint32 brdf = datablock->getBrdf(); + if( (brdf & TerraBrdf::BRDF_MASK) == TerraBrdf::Default ) + { + setProperty( PbsProperty::BrdfDefault, 1 ); + + if( !(brdf & TerraBrdf::FLAG_UNCORRELATED) ) + setProperty( PbsProperty::GgxHeightCorrelated, 1 ); + } + else if( (brdf & TerraBrdf::BRDF_MASK) == TerraBrdf::CookTorrance ) + setProperty( PbsProperty::BrdfCookTorrance, 1 ); + else if( (brdf & TerraBrdf::BRDF_MASK) == TerraBrdf::BlinnPhong ) + setProperty( PbsProperty::BrdfBlinnPhong, 1 ); + + if( brdf & TerraBrdf::FLAG_SPERATE_DIFFUSE_FRESNEL ) + setProperty( PbsProperty::FresnelSeparateDiffuse, 1 ); + + if( brdf & TerraBrdf::FLAG_LEGACY_MATH ) + setProperty( PbsProperty::LegacyMathBrdf, 1 ); + if( brdf & TerraBrdf::FLAG_FULL_LEGACY ) + setProperty( PbsProperty::RoughnessIsShininess, 1 ); + + if( datablock->mTexturesDescSet ) + setDetailMapProperties( datablock, inOutPieces ); + else + setProperty( PbsProperty::FirstValidDetailMapNm, 4 ); + + if( datablock->mSamplersDescSet ) + setProperty( PbsProperty::NumSamplers, datablock->mSamplersDescSet->mSamplers.size() ); + + if( datablock->mTexturesDescSet ) + { + bool envMap = datablock->getTexture( TERRA_REFLECTION ) != 0; + setProperty( PbsProperty::NumTextures, + datablock->mTexturesDescSet->mTextures.size() - envMap ); + + setTextureProperty( PbsProperty::DiffuseMap, datablock, TERRA_DIFFUSE ); + setTextureProperty( PbsProperty::EnvProbeMap, datablock, TERRA_REFLECTION ); + setTextureProperty( PbsProperty::DetailWeightMap,datablock, TERRA_DETAIL_WEIGHT ); + + //Save the name of the cubemap for hazard prevention + //(don't sample the cubemap and render to it at the same time). + const TextureGpu *reflectionTexture = datablock->getTexture( TERRA_REFLECTION ); + if( reflectionTexture ) + { + //Manual reflection texture +// if( datablock->getCubemapProbe() ) +// setProperty( PbsProperty::UseParallaxCorrectCubemaps, 1 ); + setProperty( PbsProperty::EnvProbeMap, + static_cast( reflectionTexture->getName().mHash ) ); + } + } + + bool usesNormalMap = false; + for( uint8 i=TERRA_DETAIL0_NM; i<=TERRA_DETAIL3_NM; ++i ) + usesNormalMap |= datablock->getTexture( i ) != 0; + setProperty( PbsProperty::NormalMap, usesNormalMap ); + + if( usesNormalMap ) + { +// TextureGpu *normalMapTex = datablock->getTexture( TERRA_DETAIL0_NM ); +// if( PixelFormatGpuUtils::isSigned( normalMapTex->getPixelFormat() ) ) + { + setProperty( PbsProperty::NormalSamplingFormat, PbsProperty::NormalRgSnorm.mHash ); + setProperty( PbsProperty::NormalRgSnorm, PbsProperty::NormalRgSnorm.mHash ); + } +// else +// { +// setProperty( PbsProperty::NormalSamplingFormat, PbsProperty::NormalRgUnorm.mHash ); +// setProperty( PbsProperty::NormalRgUnorm, PbsProperty::NormalRgUnorm.mHash ); +// } + //Reserved for supporting LA textures in GLES2. +// else +// { +// setProperty( PbsProperty::NormalSamplingFormat, PbsProperty::NormalLa.mHash ); +// setProperty( PbsProperty::NormalLa, PbsProperty::NormalLa.mHash ); +// } + } + + // IGN CUSTOMIZE BEGIN + for( size_t i = 0u; i < 4u; ++i ) + { + const IdString c_ignWeightProperties[4] = { + "ign_weight0", + "ign_weight1", + "ign_weight2", + "ign_weight3", + }; + setProperty( c_ignWeightProperties[i], + fabsf( datablock->mIgnWeightsMinHeight[i] - + datablock->mIgnWeightsMaxHeight[i] ) >= 1e-6f ); + } + // IGN CUSTOMIZE END + +#ifdef OGRE_BUILD_COMPONENT_PLANAR_REFLECTIONS + if( mPlanarReflections && mPlanarReflections->hasPlanarReflections( renderable ) ) + { + if( !mPlanarReflections->_isUpdatingRenderablesHlms() ) + mPlanarReflections->_notifyRenderableFlushedHlmsDatablock( renderable ); + else + setProperty( PbsProperty::UsePlanarReflections, 1 ); + } +#endif + } + //----------------------------------------------------------------------------------- + void HlmsTerra::calculateHashForPreCaster( Renderable *renderable, PiecesMap *inOutPieces ) + { + // Override, since shadow casting is very basic + mSetProperties.clear(); + setProperty( "hlms_no_shadowConstantBias_decl", 1 ); + + TerrainCell *terrainCell = static_cast( renderable ); + setProperty( TerraProperty::ZUp, terrainCell->isZUp() ); + } + //----------------------------------------------------------------------------------- + void HlmsTerra::notifyPropertiesMergedPreGenerationStep(void) + { + HlmsPbs::notifyPropertiesMergedPreGenerationStep(); + + setTextureReg( VertexShader, "heightMap", 0 ); + if( !getProperty( HlmsBaseProp::ShadowCaster ) ) + { + setTextureReg( PixelShader, "terrainNormals", 1 ); + setTextureReg( PixelShader, "terrainShadows", 2 ); + } + } + //----------------------------------------------------------------------------------- + uint32 HlmsTerra::fillBuffersFor( const HlmsCache *cache, const QueuedRenderable &queuedRenderable, + bool casterPass, uint32 lastCacheHash, + uint32 lastTextureHash ) + { + OGRE_EXCEPT( Exception::ERR_NOT_IMPLEMENTED, + "Trying to use slow-path on a desktop implementation. " + "Change the RenderQueue settings.", + "HlmsTerra::fillBuffersFor" ); + } + //----------------------------------------------------------------------------------- + uint32 HlmsTerra::fillBuffersForV1( const HlmsCache *cache, + const QueuedRenderable &queuedRenderable, + bool casterPass, uint32 lastCacheHash, + CommandBuffer *commandBuffer ) + { + return fillBuffersFor( cache, queuedRenderable, casterPass, + lastCacheHash, commandBuffer, true ); + } + //----------------------------------------------------------------------------------- + uint32 HlmsTerra::fillBuffersForV2( const HlmsCache *cache, + const QueuedRenderable &queuedRenderable, + bool casterPass, uint32 lastCacheHash, + CommandBuffer *commandBuffer ) + { + return fillBuffersFor( cache, queuedRenderable, casterPass, + lastCacheHash, commandBuffer, false ); + } + //----------------------------------------------------------------------------------- + uint32 HlmsTerra::fillBuffersFor( const HlmsCache *cache, const QueuedRenderable &queuedRenderable, + bool casterPass, uint32 lastCacheHash, + CommandBuffer *commandBuffer, bool isV1 ) + { + assert( dynamic_cast( queuedRenderable.renderable->getDatablock() ) ); + const HlmsTerraDatablock *datablock = static_cast( + queuedRenderable.renderable->getDatablock() ); + + if( OGRE_EXTRACT_HLMS_TYPE_FROM_CACHE_HASH( lastCacheHash ) != mType ) + { + //layout(binding = 0) uniform PassBuffer {} pass + ConstBufferPacked *passBuffer = mPassBuffers[mCurrentPassBuffer-1]; + *commandBuffer->addCommand() = CbShaderBuffer( VertexShader, + 0, passBuffer, 0, + passBuffer-> + getTotalSizeBytes() ); + *commandBuffer->addCommand() = CbShaderBuffer( PixelShader, + 0, passBuffer, 0, + passBuffer-> + getTotalSizeBytes() ); + + if( !casterPass ) + { + size_t texUnit = mReservedTexSlots; + + *commandBuffer->addCommand() = + CbSamplers( 1, mTerraDescSetSampler ); + + if( mGridBuffer ) + { + *commandBuffer->addCommand() = + CbShaderBuffer( PixelShader, texUnit++, mGridBuffer, 0, 0 ); + *commandBuffer->addCommand() = + CbShaderBuffer( PixelShader, texUnit++, mGlobalLightListBuffer, 0, 0 ); + } + + if( !mPrePassTextures->empty() ) + { + *commandBuffer->addCommand() = + CbTexture( texUnit++, (*mPrePassTextures)[0], 0 ); + *commandBuffer->addCommand() = + CbTexture( texUnit++, (*mPrePassTextures)[1], 0 ); + } + + if( mPrePassMsaaDepthTexture ) + { + *commandBuffer->addCommand() = + CbTexture( texUnit++, mPrePassMsaaDepthTexture, 0 ); + } + + if( mSsrTexture ) + { + *commandBuffer->addCommand() = + CbTexture( texUnit++, mSsrTexture, 0 ); + } + + if( mIrradianceVolume ) + { + TextureGpu *irradianceTex = mIrradianceVolume->getIrradianceVolumeTexture(); + const HlmsSamplerblock *samplerblock = mIrradianceVolume->getIrradSamplerblock(); + + *commandBuffer->addCommand() = CbTexture( texUnit, + irradianceTex, + samplerblock ); + ++texUnit; + } + + if( mUsingAreaLightMasks ) + { + *commandBuffer->addCommand() = CbTexture( texUnit, + mAreaLightMasks, + mAreaLightMasksSamplerblock ); + ++texUnit; + } + + if( mLtcMatrixTexture ) + { + *commandBuffer->addCommand() = CbTexture( texUnit, + mLtcMatrixTexture, + mAreaLightMasksSamplerblock ); + ++texUnit; + } + + for( size_t i=0; i<3u; ++i ) + { + if( mDecalsTextures[i] && + (i != 2u || !mDecalsDiffuseMergedEmissive) ) + { + *commandBuffer->addCommand() = CbTexture( texUnit, + mDecalsTextures[i], + mDecalsSamplerblock ); + ++texUnit; + } + } + + //We changed HlmsType, rebind the shared textures. + FastArray::const_iterator itor = mPreparedPass.shadowMaps.begin(); + FastArray::const_iterator end = mPreparedPass.shadowMaps.end(); + while( itor != end ) + { + *commandBuffer->addCommand() = CbTexture( texUnit, *itor, + mCurrentShadowmapSamplerblock ); + ++texUnit; + ++itor; + } + + if( mParallaxCorrectedCubemap && !mParallaxCorrectedCubemap->isRendering() ) + { + TextureGpu *pccTexture = mParallaxCorrectedCubemap->getBindTexture(); + const HlmsSamplerblock *samplerblock = + mParallaxCorrectedCubemap->getBindTrilinearSamplerblock(); + *commandBuffer->addCommand() = CbTexture( texUnit, pccTexture, + samplerblock ); + ++texUnit; + } + } + + mLastDescTexture = 0; + mLastDescSampler = 0; + mLastMovableObject = 0; + mLastBoundPool = 0; + + //layout(binding = 2) uniform InstanceBuffer {} instance + if( mCurrentConstBuffer < mConstBuffers.size() && + (size_t)((mCurrentMappedConstBuffer - mStartMappedConstBuffer) + 4) <= + mCurrentConstBufferSize ) + { + *commandBuffer->addCommand() = + CbShaderBuffer( VertexShader, 2, mConstBuffers[mCurrentConstBuffer], 0, 0 ); + *commandBuffer->addCommand() = + CbShaderBuffer( PixelShader, 2, mConstBuffers[mCurrentConstBuffer], 0, 0 ); + } + + //rebindTexBuffer( commandBuffer ); + +#ifdef OGRE_BUILD_COMPONENT_PLANAR_REFLECTIONS + mLastBoundPlanarReflection = 0u; +#endif + mListener->hlmsTypeChanged( casterPass, commandBuffer, datablock ); + } + + //Don't bind the material buffer on caster passes (important to keep + //MDI & auto-instancing running on shadow map passes) + if( mLastBoundPool != datablock->getAssignedPool() ) + { + //layout(binding = 1) uniform MaterialBuf {} materialArray + const ConstBufferPool::BufferPool *newPool = datablock->getAssignedPool(); + *commandBuffer->addCommand() = CbShaderBuffer( VertexShader, + 1, newPool->materialBuffer, 0, + newPool->materialBuffer-> + getTotalSizeBytes() ); + *commandBuffer->addCommand() = CbShaderBuffer( PixelShader, + 1, newPool->materialBuffer, 0, + newPool->materialBuffer-> + getTotalSizeBytes() ); + mLastBoundPool = newPool; + } + + if( mLastMovableObject != queuedRenderable.movableObject ) + { + //Different Terra? Must change textures then. + const Terra *terraObj = static_cast( queuedRenderable.movableObject ); + *commandBuffer->addCommand() = + CbTextures( 0, std::numeric_limits::max(), + terraObj->getDescriptorSetTexture() ); +#if OGRE_DEBUG_MODE +// Commented: Hack to get a barrier without dealing with the Compositor while debugging. +// ResourceTransition resourceTransition; +// resourceTransition.readBarrierBits = ReadBarrier::Uav; +// resourceTransition.writeBarrierBits = WriteBarrier::Uav; +// mRenderSystem->_resourceTransitionCreated( &resourceTransition ); +// mRenderSystem->_executeResourceTransition( &resourceTransition ); +// mRenderSystem->_resourceTransitionDestroyed( &resourceTransition ); + + // IGN CUSTOMIZE BEGIN + // Barriers dealt with in Ogre2Scene::UpdateAllHeightmaps + #ifdef IGN_DISABLED + TextureGpu *terraShadowText = terraObj->_getShadowMapTex(); + const CompositorTextureVec &compositorTextures = queuedRenderable.movableObject-> + _getManager()->getCompositorTextures(); + CompositorTextureVec::const_iterator itor = compositorTextures.begin(); + CompositorTextureVec::const_iterator end = compositorTextures.end(); + + while( itor != end && itor->texture != terraShadowText ) + ++itor; + + if( itor == end ) + { + assert( "Hazard Detected! You should expose this Terra's shadow map texture" + " to the compositor pass so Ogre can place the proper Barriers" && false ); + } + #endif + // IGN CUSTOMIZE END +#endif + + mLastMovableObject = queuedRenderable.movableObject; + } + + uint32 * RESTRICT_ALIAS currentMappedConstBuffer = mCurrentMappedConstBuffer; + + //--------------------------------------------------------------------------- + // ---- VERTEX SHADER ---- + //--------------------------------------------------------------------------- + //We need to correct currentMappedConstBuffer to point to the right texture buffer's + //offset, which may not be in sync if the previous draw had skeletal animation. + bool exceedsConstBuffer = (size_t)((currentMappedConstBuffer - mStartMappedConstBuffer) + 12) + > mCurrentConstBufferSize; + + if( exceedsConstBuffer ) + currentMappedConstBuffer = mapNextConstBuffer( commandBuffer ); + + const TerrainCell *terrainCell = static_cast( queuedRenderable.renderable ); + + terrainCell->uploadToGpu( currentMappedConstBuffer ); + currentMappedConstBuffer += 16u; + + //--------------------------------------------------------------------------- + // ---- PIXEL SHADER ---- + //--------------------------------------------------------------------------- + + if( !casterPass || datablock->getAlphaTest() != CMPF_ALWAYS_PASS ) + { +#ifdef OGRE_BUILD_COMPONENT_PLANAR_REFLECTIONS + if( mHasPlanarReflections && + (queuedRenderable.renderable->mCustomParameter & 0x80) && + mLastBoundPlanarReflection != queuedRenderable.renderable->mCustomParameter ) + { + const uint8 activeActorIdx = queuedRenderable.renderable->mCustomParameter & 0x7F; + TextureGpu *planarReflTex = mPlanarReflections->getTexture( activeActorIdx ); + *commandBuffer->addCommand() = + CbTexture( mTexUnitSlotStart - 1u, planarReflTex, + mPlanarReflectionsSamplerblock ); + mLastBoundPlanarReflection = queuedRenderable.renderable->mCustomParameter; + } +#endif + if( datablock->mTexturesDescSet != mLastDescTexture ) + { + if( datablock->mTexturesDescSet ) + { + //Rebind textures + size_t texUnit = mTexUnitSlotStart; + + *commandBuffer->addCommand() = + CbTextures( texUnit, std::numeric_limits::max(), + datablock->mTexturesDescSet ); + + if( !mHasSeparateSamplers ) + { + *commandBuffer->addCommand() = + CbSamplers( texUnit, datablock->mSamplersDescSet ); + } + //texUnit += datablock->mTexturesDescSet->mTextures.size(); + } + + mLastDescTexture = datablock->mTexturesDescSet; + } + + if( datablock->mSamplersDescSet != mLastDescSampler && mHasSeparateSamplers ) + { + if( datablock->mSamplersDescSet ) + { + //Bind samplers + size_t texUnit = mTexUnitSlotStart; + *commandBuffer->addCommand() = + CbSamplers( texUnit, datablock->mSamplersDescSet ); + mLastDescSampler = datablock->mSamplersDescSet; + } + } + } + + mCurrentMappedConstBuffer = currentMappedConstBuffer; + + return ((mCurrentMappedConstBuffer - mStartMappedConstBuffer) >> 4) - 1; + } + //----------------------------------------------------------------------------------- + void HlmsTerra::getDefaultPaths( String &outDataFolderPath, StringVector &outLibraryFoldersPaths ) + { + //We need to know what RenderSystem is currently in use, as the + //name of the compatible shading language is part of the path + Ogre::RenderSystem *renderSystem = Ogre::Root::getSingleton().getRenderSystem(); + Ogre::String shaderSyntax = "GLSL"; + if (renderSystem->getName() == "Direct3D11 Rendering Subsystem") + shaderSyntax = "HLSL"; + else if (renderSystem->getName() == "Metal Rendering Subsystem") + shaderSyntax = "Metal"; + + //Fill the library folder paths with the relevant folders + outLibraryFoldersPaths.clear(); + outLibraryFoldersPaths.push_back( "Hlms/Common/" + shaderSyntax ); + outLibraryFoldersPaths.push_back( "Hlms/Common/Any" ); + outLibraryFoldersPaths.push_back( "Hlms/Pbs/Any" ); + outLibraryFoldersPaths.push_back( "Hlms/Pbs/Any/Main" ); + outLibraryFoldersPaths.push_back( "Hlms/Terra/Any" ); + + //Fill the data folder path + outDataFolderPath = "Hlms/Terra/" + shaderSyntax; + } +#if !OGRE_NO_JSON && defined( IGN_TERRA_JSON_ENABLED ) + //----------------------------------------------------------------------------------- + void HlmsTerra::_loadJson( const rapidjson::Value &jsonValue, const HlmsJson::NamedBlocks &blocks, + HlmsDatablock *datablock, const String &resourceGroup, + HlmsJsonListener *listener, + const String &additionalTextureExtension ) const + { + HlmsJsonTerra jsonTerra( mHlmsManager, mRenderSystem->getTextureGpuManager() ); + jsonTerra.loadMaterial( jsonValue, blocks, datablock, resourceGroup ); + } + //----------------------------------------------------------------------------------- + void HlmsTerra::_saveJson( const HlmsDatablock *datablock, String &outString, + HlmsJsonListener *listener, + const String &additionalTextureExtension ) const + { +// HlmsJsonTerra jsonTerra( mHlmsManager ); +// jsonTerra.saveMaterial( datablock, outString ); + } + //----------------------------------------------------------------------------------- + void HlmsTerra::_collectSamplerblocks( set::type &outSamplerblocks, + const HlmsDatablock *datablock ) const + { + HlmsJsonTerra::collectSamplerblocks( datablock, outSamplerblocks ); + } +#endif + //----------------------------------------------------------------------------------- + HlmsDatablock* HlmsTerra::createDatablockImpl( IdString datablockName, + const HlmsMacroblock *macroblock, + const HlmsBlendblock *blendblock, + const HlmsParamVec ¶mVec ) + { + return OGRE_NEW HlmsTerraDatablock( datablockName, this, macroblock, blendblock, paramVec ); + } +} diff --git a/ogre2/src/terrain/Terra/src/Hlms/OgreHlmsTerraDatablock.cpp b/ogre2/src/terrain/Terra/src/Hlms/OgreHlmsTerraDatablock.cpp new file mode 100644 index 000000000..2de6429ed --- /dev/null +++ b/ogre2/src/terrain/Terra/src/Hlms/OgreHlmsTerraDatablock.cpp @@ -0,0 +1,423 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE + (Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org/ + +Copyright (c) 2000-2014 Torus Knot Software Ltd + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +----------------------------------------------------------------------------- +*/ + +#include "Terra/Hlms/OgreHlmsTerraDatablock.h" +#include "Terra/Hlms/OgreHlmsTerra.h" +#include "OgreHlmsManager.h" +#include "OgreTextureGpu.h" +#include "OgreTextureGpuManager.h" +#include "OgreRenderSystem.h" +#include "OgreTextureFilters.h" +#include "OgreLogManager.h" +#include "OgreShaderPrimitives.h" + +#define _OgreHlmsTextureBaseClassExport +#define OGRE_HLMS_TEXTURE_BASE_CLASS HlmsTerraBaseTextureDatablock +#define OGRE_HLMS_TEXTURE_BASE_MAX_TEX NUM_TERRA_TEXTURE_TYPES +#define OGRE_HLMS_CREATOR_CLASS HlmsTerra + #include "OgreHlmsTextureBaseClass.inl" +#undef _OgreHlmsTextureBaseClassExport +#undef OGRE_HLMS_TEXTURE_BASE_CLASS +#undef OGRE_HLMS_TEXTURE_BASE_MAX_TEX +#undef OGRE_HLMS_CREATOR_CLASS + +#include "OgreHlmsTerraDatablock.cpp.inc" + +namespace Ogre +{ + // IGN CUSTOMIZE BEGIN + const size_t HlmsTerraDatablock::MaterialSizeInGpu = 4 * 12 * 4; + // IGN CUSTOMIZE END + const size_t HlmsTerraDatablock::MaterialSizeInGpuAligned = alignToNextMultiple( + HlmsTerraDatablock::MaterialSizeInGpu, + 4 * 4 ); + + //----------------------------------------------------------------------------------- + HlmsTerraDatablock::HlmsTerraDatablock( IdString name, HlmsTerra *creator, + const HlmsMacroblock *macroblock, + const HlmsBlendblock *blendblock, + const HlmsParamVec ¶ms ) : + HlmsTerraBaseTextureDatablock( name, creator, macroblock, blendblock, params ), + mkDr( 0.318309886f ), mkDg( 0.318309886f ), mkDb( 0.318309886f ), //Max Diffuse = 1 / PI + mShadowConstantBiasGpu( 0.0f ), + // IGN CUSTOMIZE BEGIN + mIgnWeightsMinHeight{ 0.0f, 0.0f, 0.0f, 0.0f }, + mIgnWeightsMaxHeight{ 0.0f, 0.0f, 0.0f, 0.0f }, + // IGN CUSTOMIZE END + mBrdf( TerraBrdf::Default ) + { + mShadowConstantBiasGpu = mShadowConstantBias = 0.0f; + + mRoughness[0] = mRoughness[1] = 1.0f; + mRoughness[2] = mRoughness[3] = 1.0f; + mMetalness[0] = mMetalness[1] = 1.0f; + mMetalness[2] = mMetalness[3] = 1.0f; + + for( size_t i=0; i<4; ++i ) + mDetailsOffsetScale[i] = Vector4( 0, 0, 1, 1 ); + + creator->requestSlot( /*mTextureHash*/0, this, false ); + calculateHash(); + } + //----------------------------------------------------------------------------------- + HlmsTerraDatablock::~HlmsTerraDatablock() + { + if( mAssignedPool ) + static_cast(mCreator)->releaseSlot( this ); + + HlmsManager *hlmsManager = mCreator->getHlmsManager(); + if( hlmsManager ) + { + for( size_t i=0; idestroySamplerblock( mSamplerblocks[i] ); + } + } + } + //----------------------------------------------------------------------------------- + void HlmsTerraDatablock::calculateHash() + { + IdString hash; + + if( mTexturesDescSet ) + { + FastArray::const_iterator itor = mTexturesDescSet->mTextures.begin(); + FastArray::const_iterator end = mTexturesDescSet->mTextures.end(); + while( itor != end ) + { + hash += (*itor)->getName(); + ++itor; + } + } + if( mSamplersDescSet ) + { + FastArray::const_iterator itor= mSamplersDescSet->mSamplers.begin(); + FastArray::const_iterator end = mSamplersDescSet->mSamplers.end(); + while( itor != end ) + { + hash += IdString( (*itor)->mId ); + ++itor; + } + } + + if( static_cast(mCreator)->getOptimizationStrategy() == HlmsTerra::LowerGpuOverhead ) + { + const size_t poolIdx = static_cast(mCreator)->getPoolIndex( this ); + const uint32 finalHash = (hash.mHash & 0xFFFFFE00) | (poolIdx & 0x000001FF); + mTextureHash = finalHash; + } + else + { + const size_t poolIdx = static_cast(mCreator)->getPoolIndex( this ); + const uint32 finalHash = (hash.mHash & 0xFFFFFFF0) | (poolIdx & 0x0000000F); + mTextureHash = finalHash; + } + } + //----------------------------------------------------------------------------------- + void HlmsTerraDatablock::scheduleConstBufferUpdate(void) + { + static_cast(mCreator)->scheduleForUpdate( this ); + } + //----------------------------------------------------------------------------------- + void HlmsTerraDatablock::uploadToConstBuffer( char *dstPtr, uint8 dirtyFlags ) + { + if( dirtyFlags & (ConstBufferPool::DirtyTextures|ConstBufferPool::DirtySamplers) ) + { + //Must be called first so mTexIndices[i] gets updated before uploading to GPU. + updateDescriptorSets( (dirtyFlags & ConstBufferPool::DirtyTextures) != 0, + (dirtyFlags & ConstBufferPool::DirtySamplers) != 0 ); + } + + uint16 texIndices[OGRE_NumTexIndices]; + for( size_t i=0; i= 1e-6f; + mIgnWeightsMinHeight[i] = ignWeightsMinHeight[i]; + mIgnWeightsMaxHeight[i] = ignWeightsMaxHeight[i]; + const bool bIsDisabled = + fabsf( mIgnWeightsMinHeight[i] - mIgnWeightsMaxHeight[i] ) >= 1e-6f; + bNeedsFlushing |= bWasDisabled != bIsDisabled; + } + if( bNeedsFlushing ) + flushRenderables(); + scheduleConstBufferUpdate(); + } + //----------------------------------------------------------------------------------- + void HlmsTerraDatablock::setTexture( TerraTextureTypes texUnit, const String &name, + const HlmsSamplerblock *refParams ) + { + uint32 textureFlags = 0; + uint32 filters = TextureFilter::TypeGenerateDefaultMipmaps; + + filters |= suggestFiltersForType( texUnit ); + + if( texUnit != TERRA_REFLECTION ) + textureFlags |= TextureFlags::AutomaticBatching; + if( suggestUsingSRGB( texUnit ) ) + textureFlags |= TextureFlags::PrefersLoadingFromFileAsSRGB; + + TextureTypes::TextureTypes textureType = TextureTypes::Type2D; + if( texUnit == TERRA_REFLECTION ) + textureType = TextureTypes::TypeCube; + + TextureGpuManager *textureManager = mCreator->getRenderSystem()->getTextureGpuManager(); + TextureGpu *texture = 0; + if( !name.empty() ) + { + texture = textureManager->createOrRetrieveTexture( name, GpuPageOutStrategy::Discard, + textureFlags, textureType, + ResourceGroupManager:: + AUTODETECT_RESOURCE_GROUP_NAME, + filters ); + } + setTexture( texUnit, texture, refParams ); + } + // IGN CUSTOMIZE END + //----------------------------------------------------------------------------------- + bool HlmsTerraDatablock::suggestUsingSRGB( TerraTextureTypes type ) const + { + if( type == TERRA_DETAIL_WEIGHT || + (type >= TERRA_DETAIL_METALNESS0 && type <= TERRA_DETAIL_METALNESS3) || + (type >= TERRA_DETAIL_ROUGHNESS0 && type <= TERRA_DETAIL_ROUGHNESS3) || + (type >= TERRA_DETAIL0_NM && type <= TERRA_DETAIL3_NM) ) + { + return false; + } + + return true; + } + //----------------------------------------------------------------------------------- + uint32 HlmsTerraDatablock::suggestFiltersForType( TerraTextureTypes type ) const + { + switch( type ) + { + case TERRA_DETAIL0_NM: + case TERRA_DETAIL1_NM: + case TERRA_DETAIL2_NM: + case TERRA_DETAIL3_NM: + return TextureFilter::TypePrepareForNormalMapping; + case TERRA_DETAIL_ROUGHNESS0: + case TERRA_DETAIL_ROUGHNESS1: + case TERRA_DETAIL_ROUGHNESS2: + case TERRA_DETAIL_ROUGHNESS3: + case TERRA_DETAIL_METALNESS0: + case TERRA_DETAIL_METALNESS1: + case TERRA_DETAIL_METALNESS2: + case TERRA_DETAIL_METALNESS3: + return TextureFilter::TypeLeaveChannelR; + default: + return 0; + } + + return 0; + } + //----------------------------------------------------------------------------------- + /*HlmsTextureManager::TextureMapType HlmsTerraDatablock::suggestMapTypeBasedOnTextureType( + TerraTextureTypes type ) + { + HlmsTextureManager::TextureMapType retVal; + switch( type ) + { + default: + case TERRA_DIFFUSE: + retVal = HlmsTextureManager::TEXTURE_TYPE_DIFFUSE; + break; + case TERRA_DETAIL_WEIGHT: + retVal = HlmsTextureManager::TEXTURE_TYPE_NON_COLOR_DATA; + break; + case TERRA_DETAIL0: + case TERRA_DETAIL1: + case TERRA_DETAIL2: + case TERRA_DETAIL3: +#ifdef OGRE_TEXTURE_ATLAS + retVal = HlmsTextureManager::TEXTURE_TYPE_DETAIL; +#else + retVal = HlmsTextureManager::TEXTURE_TYPE_DIFFUSE; +#endif + break; + + case TERRA_DETAIL0_NM: + case TERRA_DETAIL1_NM: + case TERRA_DETAIL2_NM: + case TERRA_DETAIL3_NM: +#ifdef OGRE_TEXTURE_ATLAS + retVal = HlmsTextureManager::TEXTURE_TYPE_DETAIL_NORMAL_MAP; +#else + retVal = HlmsTextureManager::TEXTURE_TYPE_NORMALS; +#endif + break; + + case TERRA_DETAIL_ROUGHNESS0: + case TERRA_DETAIL_ROUGHNESS1: + case TERRA_DETAIL_ROUGHNESS2: + case TERRA_DETAIL_ROUGHNESS3: + case TERRA_DETAIL_METALNESS0: + case TERRA_DETAIL_METALNESS1: + case TERRA_DETAIL_METALNESS2: + case TERRA_DETAIL_METALNESS3: + retVal = HlmsTextureManager::TEXTURE_TYPE_MONOCHROME; + break; + + case TERRA_REFLECTION: + retVal = HlmsTextureManager::TEXTURE_TYPE_ENV_MAP; + break; + } + + return retVal; + }*/ +} diff --git a/ogre2/src/terrain/Terra/src/Hlms/OgreHlmsTerraDatablock.cpp.inc b/ogre2/src/terrain/Terra/src/Hlms/OgreHlmsTerraDatablock.cpp.inc new file mode 100644 index 000000000..d1ed12e76 --- /dev/null +++ b/ogre2/src/terrain/Terra/src/Hlms/OgreHlmsTerraDatablock.cpp.inc @@ -0,0 +1,61 @@ +// This file has been auto-generated by clone_datablock.py +// Please DO NOT manually edit this file. Any subsequent invocation of clone_datablock.py will overwrite your modifications. + +/* +----------------------------------------------------------------------------- +This source file is part of OGRE + (Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org/ + +Copyright (c) 2000-2014 Torus Knot Software Ltd + +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. +----------------------------------------------------------------------------- +*/ + +namespace Ogre +{ + //----------------------------------------------------------------------------------- + void HlmsTerraDatablock::cloneImpl( HlmsDatablock *datablock ) const + { + HlmsTerraBaseTextureDatablock::cloneImpl( datablock ); + HlmsTerraDatablock *datablockImpl = static_cast( datablock ); + + datablockImpl->mkDr = mkDr; + datablockImpl->mkDg = mkDg; + datablockImpl->mkDb = mkDb; + datablockImpl->mShadowConstantBiasGpu = mShadowConstantBiasGpu; + datablockImpl->mBrdf = mBrdf; + + for( size_t i=0; i<4; ++i ) + { + datablockImpl->mRoughness[i] = mRoughness[i]; + } + + for( size_t i=0; i<4; ++i ) + { + datablockImpl->mMetalness[i] = mMetalness[i]; + } + + for( size_t i=0; i<4; ++i ) + { + datablockImpl->mDetailsOffsetScale[i] = mDetailsOffsetScale[i]; + } + } +} diff --git a/ogre2/src/terrain/Terra/src/Hlms/PbsListener/OgreHlmsPbsTerraShadows.cpp b/ogre2/src/terrain/Terra/src/Hlms/PbsListener/OgreHlmsPbsTerraShadows.cpp new file mode 100644 index 000000000..f2a13c0de --- /dev/null +++ b/ogre2/src/terrain/Terra/src/Hlms/PbsListener/OgreHlmsPbsTerraShadows.cpp @@ -0,0 +1,186 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE + (Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org/ + +Copyright (c) 2000-2016 Torus Knot Software Ltd + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +----------------------------------------------------------------------------- +*/ + +#include "Terra/Hlms/PbsListener/OgreHlmsPbsTerraShadows.h" + +#include "Terra/Hlms/OgreHlmsTerra.h" +#include "Terra/Terra.h" + +#include "CommandBuffer/OgreCommandBuffer.h" +#include "CommandBuffer/OgreCbTexture.h" + +#include "OgreHlms.h" +#include "OgreHlmsManager.h" +#include "OgreRoot.h" + +namespace Ogre +{ + const IdString PbsTerraProperty::TerraEnabled = IdString( "terra_enabled" ); + + HlmsPbsTerraShadows::HlmsPbsTerraShadows() : + mTerra( 0 ) + , mTerraSamplerblock( 0 ) +#if OGRE_DEBUG_MODE + , mSceneManager( 0 ) +#endif + { + } + //----------------------------------------------------------------------------------- + HlmsPbsTerraShadows::~HlmsPbsTerraShadows() + { + if( mTerraSamplerblock ) + { + HlmsManager *hlmsManager = Root::getSingleton().getHlmsManager(); + hlmsManager->destroySamplerblock( mTerraSamplerblock ); + mTerraSamplerblock = 0; + } + } + //----------------------------------------------------------------------------------- + void HlmsPbsTerraShadows::setTerra( Terra *terra ) + { + mTerra = terra; + if( !mTerraSamplerblock ) + { + HlmsManager *hlmsManager = Root::getSingleton().getHlmsManager(); + mTerraSamplerblock = hlmsManager->getSamplerblock( HlmsSamplerblock() ); + } + } + //----------------------------------------------------------------------------------- + void HlmsPbsTerraShadows::shaderCacheEntryCreated( const String &shaderProfile, + const HlmsCache *hlmsCacheEntry, + const HlmsCache &passCache, + const HlmsPropertyVec &properties, + const QueuedRenderable &queuedRenderable ) + { + if( shaderProfile != "hlsl" && + shaderProfile != "metal" && + Hlms::getProperty( passCache.setProperties, HlmsBaseProp::ShadowCaster ) == 0 && + Hlms::getProperty( passCache.setProperties, PbsTerraProperty::TerraEnabled ) != 0 ) + { + if( !hlmsCacheEntry->pso.vertexShader.isNull() ) + { + GpuProgramParametersSharedPtr vsParams = hlmsCacheEntry->pso.vertexShader-> + getDefaultParameters(); + + //The slot 12 is arbitrary. Should be high enough enough to not mess + //with most materials (it could conflict if a material uses *A LOT* of + //textures and they're in different arrays). + //Note OpenGL has very low limits (usually 15-16) + vsParams->setNamedConstant( "terrainShadows", 12 ); + } + } + } + //----------------------------------------------------------------------------------- + void HlmsPbsTerraShadows::preparePassHash( const CompositorShadowNode *shadowNode, + bool casterPass, bool dualParaboloid, + SceneManager *sceneManager, Hlms *hlms ) + { + if( !casterPass ) + { +#if OGRE_DEBUG_MODE + mSceneManager = sceneManager; +#endif + + if( mTerra && hlms->_getProperty( HlmsBaseProp::LightsDirNonCaster ) > 0 ) + { + //First directional light always cast shadows thanks to our terrain shadows. + int32 shadowCasterDirectional = hlms->_getProperty( HlmsBaseProp::LightsDirectional ); + shadowCasterDirectional = std::max( shadowCasterDirectional, 1 ); + hlms->_setProperty( HlmsBaseProp::LightsDirectional, shadowCasterDirectional ); + } + + hlms->_setProperty( PbsTerraProperty::TerraEnabled, mTerra != 0 ); + hlms->_setProperty( TerraProperty::ZUp, mTerra && mTerra->isZUp() ); + } + } + //----------------------------------------------------------------------------------- + uint32 HlmsPbsTerraShadows::getPassBufferSize( const CompositorShadowNode *shadowNode, + bool casterPass, bool dualParaboloid, + SceneManager *sceneManager ) const + { + return (!casterPass && mTerra) ? 32u : 0u; + } + //----------------------------------------------------------------------------------- + float* HlmsPbsTerraShadows::preparePassBuffer( const CompositorShadowNode *shadowNode, + bool casterPass, bool dualParaboloid, + SceneManager *sceneManager, + float *passBufferPtr ) + { + if( !casterPass && mTerra ) + { + const float invHeight = 1.0f / mTerra->getHeight(); + const Vector3 &terrainOrigin = mTerra->getTerrainOriginRaw(); + const Vector2 &terrainXZInvDim = mTerra->getXZInvDimensions(); + *passBufferPtr++ = -terrainOrigin.x * terrainXZInvDim.x; + *passBufferPtr++ = -terrainOrigin.y * invHeight; + *passBufferPtr++ = -terrainOrigin.z * terrainXZInvDim.y; + *passBufferPtr++ = 1.0f; + + *passBufferPtr++ = terrainXZInvDim.x; + *passBufferPtr++ = invHeight; + *passBufferPtr++ = terrainXZInvDim.y; + *passBufferPtr++ = 1.0f; + } + + return passBufferPtr; + } + //----------------------------------------------------------------------------------- + void HlmsPbsTerraShadows::hlmsTypeChanged( bool casterPass, CommandBuffer *commandBuffer, + const HlmsDatablock *datablock ) + { + if( !casterPass && mTerra ) + { + Ogre::TextureGpu *terraShadowTex = mTerra->_getShadowMapTex(); + + //Bind the shadows' texture. Tex. slot must match with + //the one in HlmsPbsTerraShadows::shaderCacheEntryCreated + *commandBuffer->addCommand() = CbTexture( 12u, terraShadowTex, + mTerraSamplerblock ); + +#if OGRE_DEBUG_MODE + // IGN CUSTOMIZE BEGIN + // Barriers dealt with in Ogre2Scene::UpdateAllHeightmaps + #ifdef IGN_DISABLED + const CompositorTextureVec &compositorTextures = mSceneManager->getCompositorTextures(); + CompositorTextureVec::const_iterator itor = compositorTextures.begin(); + CompositorTextureVec::const_iterator end = compositorTextures.end(); + + while( itor != end && itor->texture != terraShadowTex ) + ++itor; + + if( itor == end ) + { + assert( "Hazard Detected! You should expose this Terra's shadow map texture" + " to the compositor pass so Ogre can place the proper Barriers" && false ); + } + #endif + // IGN CUSTOMIZE END +#endif + } + } +} diff --git a/ogre2/src/terrain/Terra/src/Terra.cpp b/ogre2/src/terrain/Terra/src/Terra.cpp new file mode 100644 index 000000000..eaee00af0 --- /dev/null +++ b/ogre2/src/terrain/Terra/src/Terra.cpp @@ -0,0 +1,831 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE + (Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org/ + +Copyright (c) 2000-2021 Torus Knot Software Ltd + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +----------------------------------------------------------------------------- +*/ + +#include "Terra/Terra.h" +#include "Terra/TerraShadowMapper.h" +#include "Terra/Hlms/OgreHlmsTerra.h" + +#include "OgreImage2.h" + +#include "OgreCamera.h" +#include "OgreSceneManager.h" +#include "Compositor/OgreCompositorManager2.h" +#include "Compositor/OgreCompositorWorkspace.h" +#include "Compositor/OgreCompositorChannel.h" +#include "OgreMaterialManager.h" +#include "OgreTechnique.h" +#include "OgreTextureGpuManager.h" +#include "OgreStagingTexture.h" +#include "OgrePixelFormatGpuUtils.h" +#include "OgreDescriptorSetTexture.h" +#include "OgreHlmsManager.h" +#include "OgreRoot.h" + +namespace Ogre +{ + inline Vector3 ZupToYup( Vector3 value ) + { + std::swap( value.y, value.z ); + value.z = -value.z; + return value; + } + + inline Ogre::Vector3 YupToZup( Ogre::Vector3 value ) + { + std::swap( value.y, value.z ); + value.y = -value.y; + return value; + } + + /*inline Ogre::Quaternion ZupToYup( Ogre::Quaternion value ) + { + return value * Ogre::Quaternion( Ogre::Radian( Ogre::Math::HALF_PI ), Ogre::Vector3::UNIT_X ); + }*/ + + Terra::Terra( IdType id, ObjectMemoryManager *objectMemoryManager, SceneManager *sceneManager, + uint8 renderQueueId, CompositorManager2 *compositorManager, Camera *camera, + bool zUp ) : + MovableObject( id, objectMemoryManager, sceneManager, renderQueueId ), + m_width( 0u ), + m_depth( 0u ), + m_depthWidthRatio( 1.0f ), + m_skirtSize( 10.0f ), + m_invWidth( 1.0f ), + m_invDepth( 1.0f ), + m_zUp( zUp ), + m_xzDimensions( Vector2::UNIT_SCALE ), + m_xzInvDimensions( Vector2::UNIT_SCALE ), + m_xzRelativeSize( Vector2::UNIT_SCALE ), + m_height( 1.0f ), + m_terrainOrigin( Vector3::ZERO ), + m_basePixelDimension( 256u ), + m_currentCell( 0u ), + m_descriptorSet( 0 ), + m_heightMapTex( 0 ), + m_normalMapTex( 0 ), + m_prevLightDir( Vector3::ZERO ), + m_shadowMapper( 0 ), + m_compositorManager( compositorManager ), + m_camera( camera ), + mHlmsTerraIndex( std::numeric_limits::max() ) + { + } + //----------------------------------------------------------------------------------- + Terra::~Terra() + { + if( !m_terrainCells[0].empty() && m_terrainCells[0].back().getDatablock() ) + { + HlmsDatablock *datablock = m_terrainCells[0].back().getDatablock(); + OGRE_ASSERT_HIGH( dynamic_cast( datablock->getCreator() ) ); + HlmsTerra *hlms = static_cast( datablock->getCreator() ); + hlms->_unlinkTerra( this ); + } + + destroyDescriptorSet(); + if( m_shadowMapper ) + { + m_shadowMapper->destroyShadowMap(); + delete m_shadowMapper; + m_shadowMapper = 0; + } + destroyNormalTexture(); + destroyHeightmapTexture(); + m_terrainCells[0].clear(); + m_terrainCells[1].clear(); + } + //----------------------------------------------------------------------------------- + Vector3 Terra::fromYUp( Vector3 value ) const + { + if( m_zUp ) + return YupToZup( value ); + return value; + } + //----------------------------------------------------------------------------------- + Vector3 Terra::fromYUpSignPreserving( Vector3 value ) const + { + if( m_zUp ) + std::swap( value.y, value.z ); + return value; + } + //----------------------------------------------------------------------------------- + Vector3 Terra::toYUp( Vector3 value ) const + { + if( m_zUp ) + return ZupToYup( value ); + return value; + } + //----------------------------------------------------------------------------------- + Vector3 Terra::toYUpSignPreserving( Vector3 value ) const + { + if( m_zUp ) + std::swap( value.y, value.z ); + return value; + } + //----------------------------------------------------------------------------------- + void Terra::createDescriptorSet(void) + { + destroyDescriptorSet(); + + OGRE_ASSERT_LOW( m_heightMapTex ); + OGRE_ASSERT_LOW( m_normalMapTex ); + OGRE_ASSERT_LOW( m_shadowMapper && m_shadowMapper->getShadowMapTex() ); + + DescriptorSetTexture descSet; + descSet.mTextures.push_back( m_heightMapTex ); + descSet.mTextures.push_back( m_normalMapTex ); + descSet.mTextures.push_back( m_shadowMapper->getShadowMapTex() ); + descSet.mShaderTypeTexCount[VertexShader] = 1u; + descSet.mShaderTypeTexCount[PixelShader] = 2u; + + HlmsManager *hlmsManager = Root::getSingleton().getHlmsManager(); + m_descriptorSet = hlmsManager->getDescriptorSetTexture( descSet ); + } + //----------------------------------------------------------------------------------- + void Terra::destroyDescriptorSet(void) + { + if( m_descriptorSet ) + { + HlmsManager *hlmsManager = Root::getSingleton().getHlmsManager(); + hlmsManager->destroyDescriptorSetTexture( m_descriptorSet ); + m_descriptorSet = 0; + } + } + //----------------------------------------------------------------------------------- + void Terra::destroyHeightmapTexture(void) + { + if( m_heightMapTex ) + { + destroyDescriptorSet(); + TextureGpuManager *textureManager = + mManager->getDestinationRenderSystem()->getTextureGpuManager(); + textureManager->destroyTexture( m_heightMapTex ); + m_heightMapTex = 0; + } + } + //----------------------------------------------------------------------------------- + void Terra::createHeightmapTexture( const Ogre::Image2 &image, const String &imageName ) + { + destroyHeightmapTexture(); + + if( image.getPixelFormat() != PFG_R8_UNORM && + image.getPixelFormat() != PFG_R16_UNORM && + image.getPixelFormat() != PFG_R32_FLOAT ) + { + OGRE_EXCEPT( Exception::ERR_INVALIDPARAMS, + "Texture " + imageName + "must be greyscale 8 bpp, 16 bpp, or 32-bit Float", + "Terra::createHeightmapTexture" ); + } + + //const uint8 numMipmaps = image.getNumMipmaps(); + + TextureGpuManager *textureManager = + mManager->getDestinationRenderSystem()->getTextureGpuManager(); + m_heightMapTex = textureManager->createTexture( + "HeightMapTex" + StringConverter::toString( getId() ), + GpuPageOutStrategy::SaveToSystemRam, + TextureFlags::ManualTexture, + TextureTypes::Type2D ); + m_heightMapTex->setResolution( image.getWidth(), image.getHeight() ); + m_heightMapTex->setPixelFormat( image.getPixelFormat() ); + m_heightMapTex->scheduleTransitionTo( GpuResidency::Resident ); + + StagingTexture *stagingTexture = textureManager->getStagingTexture( image.getWidth(), + image.getHeight(), + 1u, 1u, + image.getPixelFormat() ); + stagingTexture->startMapRegion(); + TextureBox texBox = stagingTexture->mapRegion( image.getWidth(), image.getHeight(), 1u, 1u, + image.getPixelFormat() ); + + //for( uint8 mip=0; mipstopMapRegion(); + stagingTexture->upload( texBox, m_heightMapTex, 0, 0, 0 ); + textureManager->removeStagingTexture( stagingTexture ); + stagingTexture = 0; + + m_heightMapTex->notifyDataIsReady(); + } + //----------------------------------------------------------------------------------- + void Terra::createHeightmap( Image2 &image, const String &imageName ) + { + m_width = image.getWidth(); + m_depth = image.getHeight(); + m_depthWidthRatio = m_depth / (float)(m_width); + m_invWidth = 1.0f / m_width; + m_invDepth = 1.0f / m_depth; + + //image.generateMipmaps( false, Image::FILTER_NEAREST ); + + createHeightmapTexture( image, imageName ); + + m_heightMap.resize( m_width * m_depth ); + + float fBpp = (float)(PixelFormatGpuUtils::getBytesPerPixel( image.getPixelFormat() ) << 3u); + const float maxValue = powf( 2.0f, fBpp ) - 1.0f; + const float invMaxValue = 1.0f / maxValue; + + if( image.getPixelFormat() == PFG_R8_UNORM ) + { + for( uint32 y=0; y( texBox.at( 0, y, 0 ) ); + for( uint32 x=0; x( texBox.at( 0, y, 0 ) ); + for( uint32 x=0; x( texBox.at( 0, y, 0 ) ); + for( uint32 x=0; x(m_width), + static_cast(m_depth) ); + + createNormalTexture(); + + m_prevLightDir = Vector3::ZERO; + + delete m_shadowMapper; + m_shadowMapper = new ShadowMapper( mManager, m_compositorManager ); + m_shadowMapper->createShadowMap( getId(), m_heightMapTex ); + + createDescriptorSet(); + + calculateOptimumSkirtSize(); + } + //----------------------------------------------------------------------------------- + void Terra::createNormalTexture(void) + { + destroyNormalTexture(); + + TextureGpuManager *textureManager = + mManager->getDestinationRenderSystem()->getTextureGpuManager(); + m_normalMapTex = textureManager->createTexture( + "NormalMapTex_" + StringConverter::toString( getId() ), + GpuPageOutStrategy::SaveToSystemRam, + TextureFlags::RenderToTexture|TextureFlags::AllowAutomipmaps, + TextureTypes::Type2D ); + m_normalMapTex->setResolution( m_heightMapTex->getWidth(), m_heightMapTex->getHeight() ); + m_normalMapTex->setNumMipmaps( + PixelFormatGpuUtils::getMaxMipmapCount( m_normalMapTex->getWidth(), + m_normalMapTex->getHeight() ) ); + m_normalMapTex->setPixelFormat( PFG_R10G10B10A2_UNORM ); + m_normalMapTex->scheduleTransitionTo( GpuResidency::Resident ); + m_normalMapTex->notifyDataIsReady(); + + MaterialPtr normalMapperMat = MaterialManager::getSingleton().load( + "Terra/GpuNormalMapper", + ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME ). + staticCast(); + Pass *pass = normalMapperMat->getTechnique(0)->getPass(0); + TextureUnitState *texUnit = pass->getTextureUnitState(0); + texUnit->setTexture( m_heightMapTex ); + + //Normalize vScale for better precision in the shader math + const Vector3 vScale = Vector3( m_xzRelativeSize.x, m_height, m_xzRelativeSize.y ).normalisedCopy(); + + GpuProgramParametersSharedPtr psParams = pass->getFragmentProgramParameters(); + psParams->setNamedConstant( "heightMapResolution", Vector4( static_cast( m_width ), + static_cast( m_depth ), + 1, 1 ) ); + psParams->setNamedConstant( "vScale", vScale ); + + CompositorChannelVec finalTargetChannels( 1, CompositorChannel() ); + finalTargetChannels[0] = m_normalMapTex; + + Camera *dummyCamera = mManager->createCamera( "TerraDummyCamera" ); + + CompositorWorkspace *workspace = + m_compositorManager->addWorkspace( mManager, finalTargetChannels, dummyCamera, + "Terra/GpuNormalMapperWorkspace", false ); + workspace->_beginUpdate( true ); + workspace->_update(); + workspace->_endUpdate( true ); + + m_compositorManager->removeWorkspace( workspace ); + mManager->destroyCamera( dummyCamera ); + } + //----------------------------------------------------------------------------------- + void Terra::destroyNormalTexture(void) + { + if( m_normalMapTex ) + { + destroyDescriptorSet(); + TextureGpuManager *textureManager = + mManager->getDestinationRenderSystem()->getTextureGpuManager(); + textureManager->destroyTexture( m_normalMapTex ); + m_normalMapTex = 0; + } + } + //----------------------------------------------------------------------------------- + void Terra::calculateOptimumSkirtSize(void) + { + m_skirtSize = std::numeric_limits::max(); + + const uint32 basePixelDimension = m_basePixelDimension; + const uint32 vertPixelDimension = static_cast(m_basePixelDimension * m_depthWidthRatio); + + for( size_t y=vertPixelDimension-1u; y( m_width ); + const float fDepth = static_cast( m_depth ); + + const float fX = floorf( ((vPos.x - m_terrainOrigin.x) * m_xzInvDimensions.x) * fWidth ); + const float fZ = floorf( ((vPos.z - m_terrainOrigin.z) * m_xzInvDimensions.y) * fDepth ); + retVal.x = fX >= 0.0f ? static_cast( fX ) : 0xffffffff; + retVal.z = fZ >= 0.0f ? static_cast( fZ ) : 0xffffffff; + + return retVal; + } + //----------------------------------------------------------------------------------- + inline Vector2 Terra::gridToWorld( const GridPoint &gPos ) const + { + Vector2 retVal; + const float fWidth = static_cast( m_width ); + const float fDepth = static_cast( m_depth ); + + retVal.x = (gPos.x / fWidth) * m_xzDimensions.x + m_terrainOrigin.x; + retVal.y = (gPos.z / fDepth) * m_xzDimensions.y + m_terrainOrigin.z; + + return retVal; + } + //----------------------------------------------------------------------------------- + bool Terra::isVisible( const GridPoint &gPos, const GridPoint &gSize ) const + { + if( gPos.x >= static_cast( m_width ) || + gPos.z >= static_cast( m_depth ) || + gPos.x + gSize.x <= 0 || + gPos.z + gSize.z <= 0 ) + { + //Outside terrain bounds. + return false; + } + +// return true; + + const Vector2 cellPos = gridToWorld( gPos ); + const Vector2 cellSize( (gSize.x + 1u) * m_xzRelativeSize.x, + (gSize.z + 1u) * m_xzRelativeSize.y ); + + const Vector3 vHalfSizeYUp = Vector3( cellSize.x, m_height, cellSize.y ) * 0.5f; + const Vector3 vCenter = + fromYUp( Vector3( cellPos.x, m_terrainOrigin.y, cellPos.y ) + vHalfSizeYUp ); + const Vector3 vHalfSize = fromYUpSignPreserving( vHalfSizeYUp ); + + for( unsigned i = 0; i < 6u; ++i ) + { + //Skip far plane if view frustum is infinite + if( i == FRUSTUM_PLANE_FAR && m_camera->getFarClipDistance() == 0 ) + continue; + + Plane::Side side = m_camera->getFrustumPlane(i).getSide( vCenter, vHalfSize ); + + //We only need one negative match to know the obj is outside the frustum + if( side == Plane::NEGATIVE_SIDE ) + return false; + } + + return true; + } + //----------------------------------------------------------------------------------- + void Terra::addRenderable( const GridPoint &gridPos, const GridPoint &cellSize, uint32 lodLevel ) + { + TerrainCell *cell = &m_terrainCells[0][m_currentCell++]; + cell->setOrigin( gridPos, cellSize.x, cellSize.z, lodLevel ); + m_collectedCells[0].push_back( cell ); + } + //----------------------------------------------------------------------------------- + void Terra::optimizeCellsAndAdd(void) + { + //Keep iterating until m_collectedCells[0] stops shrinking + size_t numCollectedCells = std::numeric_limits::max(); + while( numCollectedCells != m_collectedCells[0].size() ) + { + numCollectedCells = m_collectedCells[0].size(); + + if( m_collectedCells[0].size() > 1 ) + { + m_collectedCells[1].clear(); + + std::vector::const_iterator itor = m_collectedCells[0].begin(); + std::vector::const_iterator end = m_collectedCells[0].end(); + + while( end - itor >= 2u ) + { + TerrainCell *currCell = *itor; + TerrainCell *nextCell = *(itor+1); + + m_collectedCells[1].push_back( currCell ); + if( currCell->merge( nextCell ) ) + itor += 2; + else + ++itor; + } + + while( itor != end ) + m_collectedCells[1].push_back( *itor++ ); + + m_collectedCells[1].swap( m_collectedCells[0] ); + } + } + + std::vector::const_iterator itor = m_collectedCells[0].begin(); + std::vector::const_iterator end = m_collectedCells[0].end(); + while( itor != end ) + mRenderables.push_back( *itor++ ); + + m_collectedCells[0].clear(); + } + //----------------------------------------------------------------------------------- + void Terra::update( const Vector3 &lightDir, float lightEpsilon ) + { + const float lightCosAngleChange = Math::Clamp( + (float)m_prevLightDir.dotProduct( lightDir.normalisedCopy() ), -1.0f, 1.0f ); + if( lightCosAngleChange <= (1.0f - lightEpsilon) ) + { + m_shadowMapper->updateShadowMap( toYUp( lightDir ), m_xzDimensions, m_height ); + m_prevLightDir = lightDir.normalisedCopy(); + } + //m_shadowMapper->updateShadowMap( Vector3::UNIT_X, m_xzDimensions, m_height ); + //m_shadowMapper->updateShadowMap( Vector3(2048,0,1024), m_xzDimensions, m_height ); + //m_shadowMapper->updateShadowMap( Vector3(1,0,0.1), m_xzDimensions, m_height ); + //m_shadowMapper->updateShadowMap( Vector3::UNIT_Y, m_xzDimensions, m_height ); //Check! Does NAN + + mRenderables.clear(); + m_currentCell = 0; + + const Vector3 camPos = toYUp( m_camera->getDerivedPosition() ); + + const uint32 basePixelDimension = m_basePixelDimension; + const uint32 vertPixelDimension = static_cast(m_basePixelDimension * m_depthWidthRatio); + + GridPoint cellSize; + cellSize.x = basePixelDimension; + cellSize.z = vertPixelDimension; + + //Quantize the camera position to basePixelDimension steps + GridPoint camCenter = worldToGrid( camPos ); + camCenter.x = (camCenter.x / basePixelDimension) * basePixelDimension; + camCenter.z = (camCenter.z / vertPixelDimension) * vertPixelDimension; + + uint32 currentLod = 0; + +// camCenter.x = 64; +// camCenter.z = 64; + + //LOD 0: Add full 4x4 grid + for( int32 z=-2; z<2; ++z ) + { + for( int32 x=-2; x<2; ++x ) + { + GridPoint pos = camCenter; + pos.x += x * cellSize.x; + pos.z += z * cellSize.z; + + if( isVisible( pos, cellSize ) ) + addRenderable( pos, cellSize, currentLod ); + } + } + + optimizeCellsAndAdd(); + + m_currentCell = 16u; //The first 16 cells don't use skirts. + + const uint32 maxRes = std::max( m_width, m_depth ); + //TODO: When we're too far (outside the terrain), just display a 4x4 grid or something like that. + + size_t numObjectsAdded = std::numeric_limits::max(); + //LOD n: Add 4x4 grid, ignore 2x2 center (which + //is the same as saying the borders of the grid) + while( numObjectsAdded != m_currentCell || + (mRenderables.empty() && (1u << currentLod) <= maxRes) ) + { + numObjectsAdded = m_currentCell; + + cellSize.x <<= 1u; + cellSize.z <<= 1u; + ++currentLod; + + //Row 0 + { + const int32 z = 1; + for( int32 x=-2; x<2; ++x ) + { + GridPoint pos = camCenter; + pos.x += x * cellSize.x; + pos.z += z * cellSize.z; + + if( isVisible( pos, cellSize ) ) + addRenderable( pos, cellSize, currentLod ); + } + } + //Row 3 + { + const int32 z = -2; + for( int32 x=-2; x<2; ++x ) + { + GridPoint pos = camCenter; + pos.x += x * cellSize.x; + pos.z += z * cellSize.z; + + if( isVisible( pos, cellSize ) ) + addRenderable( pos, cellSize, currentLod ); + } + } + //Cells [0, 1] & [0, 2]; + { + const int32 x = -2; + for( int32 z=-1; z<1; ++z ) + { + GridPoint pos = camCenter; + pos.x += x * cellSize.x; + pos.z += z * cellSize.z; + + if( isVisible( pos, cellSize ) ) + addRenderable( pos, cellSize, currentLod ); + } + } + //Cells [3, 1] & [3, 2]; + { + const int32 x = 1; + for( int32 z=-1; z<1; ++z ) + { + GridPoint pos = camCenter; + pos.x += x * cellSize.x; + pos.z += z * cellSize.z; + + if( isVisible( pos, cellSize ) ) + addRenderable( pos, cellSize, currentLod ); + } + } + + optimizeCellsAndAdd(); + } + } + //----------------------------------------------------------------------------------- + void Terra::load( const String &texName, const Vector3 ¢er, const Vector3 &dimensions ) + { + Ogre::Image2 image; + image.load( texName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME ); + + load( image, center, dimensions, texName ); + } + //----------------------------------------------------------------------------------- + void Terra::load( Image2 &image, Vector3 center, + Vector3 dimensions, const String &imageName ) + { + // Use sign-preserving because origin in XZ plane is always from + // bottom-left to top-right. + // If we use toYUp, we'll start from top-right and go up and right + m_terrainOrigin = toYUpSignPreserving( center - dimensions * 0.5f ); + center = toYUp( center ); + dimensions = toYUpSignPreserving( dimensions ); + m_xzDimensions = Vector2( dimensions.x, dimensions.z ); + m_xzInvDimensions = 1.0f / m_xzDimensions; + m_height = dimensions.y; + m_basePixelDimension = 64u; + createHeightmap( image, imageName ); + + { + //Find out how many TerrainCells we need. I think this might be + //solved analitically with a power series. But my math is rusty. + const uint32 basePixelDimension = m_basePixelDimension; + const uint32 vertPixelDimension = static_cast( m_basePixelDimension * + m_depthWidthRatio ); + const uint32 maxPixelDimension = std::max( basePixelDimension, vertPixelDimension ); + const uint32 maxRes = std::max( m_width, m_depth ); + + uint32 numCells = 16u; //4x4 + uint32 accumDim = 0u; + uint32 iteration = 1u; + while( accumDim < maxRes ) + { + numCells += 12u; //4x4 - 2x2 + accumDim += maxPixelDimension * (1u << iteration); + ++iteration; + } + + numCells += 12u; + accumDim += maxPixelDimension * (1u << iteration); + ++iteration; + + for( size_t i = 0u; i < 2u; ++i ) + { + m_terrainCells[i].clear(); + m_terrainCells[i].resize( numCells, TerrainCell( this ) ); + } + } + + VaoManager *vaoManager = mManager->getDestinationRenderSystem()->getVaoManager(); + + for( size_t i = 0u; i < 2u; ++i ) + { + std::vector::iterator itor = m_terrainCells[i].begin(); + std::vector::iterator endt = m_terrainCells[i].end(); + + const std::vector::iterator begin = itor; + + while( itor != endt ) + { + itor->initialize( vaoManager, ( itor - begin ) >= 16u ); + ++itor; + } + } + } + //----------------------------------------------------------------------------------- + bool Terra::getHeightAt( Vector3 &vPosArg ) const + { + bool retVal = false; + + Vector3 vPos = toYUp( vPosArg ); + + GridPoint pos2D = worldToGrid( vPos ); + + if( pos2D.x < m_width-1 && pos2D.z < m_depth-1 ) + { + const Vector2 vPos2D = gridToWorld( pos2D ); + + const float dx = (vPos.x - vPos2D.x) * m_width * m_xzInvDimensions.x; + const float dz = (vPos.z - vPos2D.y) * m_depth * m_xzInvDimensions.y; + + float a, b, c; + const float h00 = m_heightMap[ pos2D.z * m_width + pos2D.x ]; + const float h11 = m_heightMap[ (pos2D.z+1) * m_width + pos2D.x + 1 ]; + + c = h00; + if( dx < dz ) + { + //Plane eq: y = ax + bz + c + //x=0 z=0 -> c = h00 + //x=0 z=1 -> b + c = h01 -> b = h01 - c + //x=1 z=1 -> a + b + c = h11 -> a = h11 - b - c + const float h01 = m_heightMap[ (pos2D.z+1) * m_width + pos2D.x ]; + + b = h01 - c; + a = h11 - b - c; + } + else + { + //Plane eq: y = ax + bz + c + //x=0 z=0 -> c = h00 + //x=1 z=0 -> a + c = h10 -> a = h10 - c + //x=1 z=1 -> a + b + c = h11 -> b = h11 - a - c + const float h10 = m_heightMap[ pos2D.z * m_width + pos2D.x + 1 ]; + + a = h10 - c; + b = h11 - a - c; + } + + vPos.y = a * dx + b * dz + c + m_terrainOrigin.y; + retVal = true; + } + + vPosArg = fromYUp( vPos ); + + return retVal; + } + //----------------------------------------------------------------------------------- + void Terra::setDatablock( HlmsDatablock *datablock ) + { + if( !datablock && !m_terrainCells[0].empty() && m_terrainCells[0].back().getDatablock() ) + { + // Unsetting the datablock. We have no way of unlinking later on. Do it now + HlmsDatablock *oldDatablock = m_terrainCells[0].back().getDatablock(); + OGRE_ASSERT_HIGH( dynamic_cast( oldDatablock->getCreator() ) ); + HlmsTerra *hlms = static_cast( oldDatablock->getCreator() ); + hlms->_unlinkTerra( this ); + } + + for( size_t i = 0u; i < 2u; ++i ) + { + std::vector::iterator itor = m_terrainCells[i].begin(); + std::vector::iterator end = m_terrainCells[i].end(); + + while( itor != end ) + { + itor->setDatablock( datablock ); + ++itor; + } + } + + if( mHlmsTerraIndex == std::numeric_limits::max() ) + { + OGRE_ASSERT_HIGH( dynamic_cast( datablock->getCreator() ) ); + HlmsTerra *hlms = static_cast( datablock->getCreator() ); + hlms->_linkTerra( this ); + } + } + //----------------------------------------------------------------------------------- + Ogre::TextureGpu* Terra::_getShadowMapTex(void) const + { + return m_shadowMapper->getShadowMapTex(); + } + //----------------------------------------------------------------------------------- + Vector3 Terra::getTerrainOrigin( void ) const { return fromYUpSignPreserving( m_terrainOrigin ); } + //----------------------------------------------------------------------------------- + Vector2 Terra::getTerrainXZCenter(void) const + { + return Vector2( m_terrainOrigin.x + m_xzDimensions.x * 0.5f, + m_terrainOrigin.z + m_xzDimensions.y * 0.5f ); + } + //----------------------------------------------------------------------------------- + const String& Terra::getMovableType(void) const + { + static const String movType = "Terra"; + return movType; + } + //----------------------------------------------------------------------------------- + void Terra::_swapSavedState( void ) + { + m_terrainCells[0].swap( m_terrainCells[1] ); + m_savedState.m_renderables.swap( mRenderables ); + std::swap( m_savedState.m_currentCell, m_currentCell ); + std::swap( m_savedState.m_camera, m_camera ); + } +} diff --git a/ogre2/src/terrain/Terra/src/TerraShadowMapper.cpp b/ogre2/src/terrain/Terra/src/TerraShadowMapper.cpp new file mode 100644 index 000000000..31964adf4 --- /dev/null +++ b/ogre2/src/terrain/Terra/src/TerraShadowMapper.cpp @@ -0,0 +1,471 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE + (Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org/ + +Copyright (c) 2000-2021 Torus Knot Software Ltd + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +----------------------------------------------------------------------------- +*/ + +#include "Terra/TerraShadowMapper.h" + +#include "OgreTextureGpuManager.h" + +#include "OgreSceneManager.h" +#include "Compositor/OgreCompositorManager2.h" +#include "Compositor/OgreCompositorWorkspace.h" +#include "Compositor/OgreCompositorChannel.h" + +#include "OgreHlmsManager.h" +#include "OgreHlmsCompute.h" +#include "OgreHlmsComputeJob.h" +#include "Vao/OgreConstBufferPacked.h" +#include "Vao/OgreVaoManager.h" +#include "OgreRoot.h" + +#include "OgreLwString.h" + +namespace Ogre +{ + ShadowMapper::ShadowMapper( SceneManager *sceneManager, CompositorManager2 *compositorManager ) : + m_shadowStarts( 0 ), + m_shadowPerGroupData( 0 ), + m_shadowWorkspace( 0 ), + m_shadowMapTex( 0 ), + m_shadowJob( 0 ), + m_jobParamDelta( 0 ), + m_jobParamXYStep( 0 ), + m_jobParamIsStep( 0 ), + m_jobParamHeightDelta( 0 ), + m_sceneManager( sceneManager ), + m_compositorManager( compositorManager ) + { + } + //----------------------------------------------------------------------------------- + ShadowMapper::~ShadowMapper() + { + destroyShadowMap(); + } + //----------------------------------------------------------------------------------- + void ShadowMapper::createShadowMap( IdType id, TextureGpu *heightMapTex ) + { + destroyShadowMap(); + + m_heightMapTex = heightMapTex; + + VaoManager *vaoManager = m_sceneManager->getDestinationRenderSystem()->getVaoManager(); + + if( !m_shadowStarts ) + { + m_shadowStarts = vaoManager->createConstBuffer( 4096u * 16u, + BT_DYNAMIC_PERSISTENT, 0, false ); + } + if( !m_shadowPerGroupData ) + { + m_shadowPerGroupData = vaoManager->createConstBuffer( 4096u * 16u, + BT_DYNAMIC_PERSISTENT, 0, false ); + } + + HlmsManager *hlmsManager = Root::getSingleton().getHlmsManager(); + HlmsCompute *hlmsCompute = hlmsManager->getComputeHlms(); + m_shadowJob = hlmsCompute->findComputeJob( "Terra/ShadowGenerator" ); + + //TODO: Mipmaps + TextureGpuManager *textureManager = + m_sceneManager->getDestinationRenderSystem()->getTextureGpuManager(); + m_shadowMapTex = textureManager->createTexture( + "ShadowMap" + StringConverter::toString( id ), + GpuPageOutStrategy::SaveToSystemRam, + TextureFlags::Uav, + TextureTypes::Type2D ); + m_shadowMapTex->setResolution( m_heightMapTex->getWidth(), m_heightMapTex->getHeight() ); + m_shadowMapTex->setPixelFormat( PFG_R10G10B10A2_UNORM ); + m_shadowMapTex->scheduleTransitionTo( GpuResidency::Resident ); + + CompositorChannelVec finalTarget( 1, CompositorChannel() ); + finalTarget[0] = m_shadowMapTex; + m_shadowWorkspace = m_compositorManager->addWorkspace( m_sceneManager, finalTarget, 0, + "Terra/ShadowGeneratorWorkspace", false ); + + ShaderParams &shaderParams = m_shadowJob->getShaderParams( "default" ); + m_jobParamDelta = shaderParams.findParameter( "delta" ); + m_jobParamXYStep = shaderParams.findParameter( "xyStep" ); + m_jobParamIsStep = shaderParams.findParameter( "isSteep" ); + m_jobParamHeightDelta = shaderParams.findParameter( "heightDelta" ); + + setGaussianFilterParams( 8, 0.5f ); + } + //----------------------------------------------------------------------------------- + void ShadowMapper::destroyShadowMap(void) + { + m_heightMapTex = 0; + + VaoManager *vaoManager = m_sceneManager->getDestinationRenderSystem()->getVaoManager(); + + if( m_shadowStarts ) + { + if( m_shadowStarts->getMappingState() != MS_UNMAPPED ) + m_shadowStarts->unmap( UO_UNMAP_ALL ); + vaoManager->destroyConstBuffer( m_shadowStarts ); + m_shadowStarts = 0; + } + + if( m_shadowPerGroupData ) + { + if( m_shadowPerGroupData->getMappingState() != MS_UNMAPPED ) + m_shadowPerGroupData->unmap( UO_UNMAP_ALL ); + vaoManager->destroyConstBuffer( m_shadowPerGroupData ); + m_shadowPerGroupData = 0; + } + + if( m_shadowWorkspace ) + { + m_compositorManager->removeWorkspace( m_shadowWorkspace ); + m_shadowWorkspace = 0; + } + + if( m_shadowMapTex ) + { + TextureGpuManager *textureManager = + m_sceneManager->getDestinationRenderSystem()->getTextureGpuManager(); + textureManager->destroyTexture( m_shadowMapTex ); + m_shadowMapTex = 0; + } + } + //----------------------------------------------------------------------------------- + inline size_t ShadowMapper::getStartsPtrCount( int32 *starts, int32 *startsBase ) + { + const size_t offset = starts - startsBase; + if( (offset & 0x11) == 0 ) + return offset >> 2u; + else + return ( (offset - 2u) >> 2u ) + 4096u; + } + //----------------------------------------------------------------------------------- + inline int32 ShadowMapper::getXStepsNeededToReachY( uint32 y, float fStep ) + { + return static_cast( ceilf( Ogre::max( ( (y << 1u) - 1u ) * fStep, 0.0f ) ) ); + } + //----------------------------------------------------------------------------------- + inline float ShadowMapper::getErrorAfterXsteps( uint32 xIterationsToSkip, float dx, float dy ) + { + //Round accumulatedError to next multiple of dx, then subtract accumulatedError. + //That's the error at position (x; y). *MUST* be done in double precision, otherwise + //we get artifacts with certain light angles. + const double accumulatedError = dx * 0.5 + dy * (double)(xIterationsToSkip); + const double newErrorAtX = ceil(accumulatedError / dx) * dx - accumulatedError; + return static_cast( newErrorAtX ); + } + //----------------------------------------------------------------------------------- + void ShadowMapper::updateShadowMap( const Vector3 &lightDir, const Vector2 &xzDimensions, + float heightScale ) + { + struct PerGroupData + { + int32 iterations; + float deltaErrorStart; + float padding0; + float padding1; + }; + + Vector2 lightDir2d( Vector2(lightDir.x, lightDir.z).normalisedCopy() ); + float heightDelta = lightDir.y; + + if( lightDir2d.squaredLength() < 1e-6f ) + { + //lightDir = Vector3::UNIT_Y. Fix NaNs. + lightDir2d.x = 1.0f; + lightDir2d.y = 0.0f; + } + + assert( m_shadowStarts->getNumElements() >= (m_heightMapTex->getHeight() << 4u) ); + + int32 *startsBase = reinterpret_cast( + m_shadowStarts->map( 0, m_shadowStarts->getNumElements() ) ); + PerGroupData *perGroupData = reinterpret_cast( + m_shadowPerGroupData->map( 0, m_shadowPerGroupData->getNumElements() ) ); + + uint32 width = m_heightMapTex->getWidth(); + uint32 height = m_heightMapTex->getHeight(); + + //Bresenham's line algorithm. + float x0 = 0; + float y0 = 0; + float x1 = static_cast( width - 1u ); + float y1 = static_cast( height - 1u ); + + uint32 heightOrWidth; + uint32 widthOrHeight; + + if( fabsf( lightDir2d.x ) > fabsf( lightDir2d.y ) ) + { + y1 *= fabsf( lightDir2d.y ) / fabsf( lightDir2d.x ); + heightOrWidth = height; + widthOrHeight = width; + + heightDelta *= 1.0f / fabsf( lightDir.x ); + } + else + { + x1 *= fabsf( lightDir2d.x ) / fabsf( lightDir2d.y ); + heightOrWidth = width; + widthOrHeight = height; + + heightDelta *= 1.0f / fabsf( lightDir.z ); + } + + if( lightDir2d.x < 0 ) + std::swap( x0, x1 ); + if( lightDir2d.y < 0 ) + std::swap( y0, y1 ); + + const bool steep = fabsf(y1 - y0) > fabsf(x1 - x0); + if( steep ) + { + std::swap( x0, y0 ); + std::swap( x1, y1 ); + } + + m_jobParamIsStep->setManualValue( (int32)steep ); + + float dx; + float dy; + { + float _x0 = x0; + float _y0 = y0; + float _x1 = x1; + float _y1 = y1; + if( _x0 > _x1 ) + { + std::swap(_x0, _x1); + std::swap(_y0, _y1); + } + dx = _x1 - _x0 + 1.0f; + dy = fabs(_y1 - _y0); + if( fabsf( lightDir2d.x ) > fabsf( lightDir2d.y ) ) + dy += 1.0f * fabsf( lightDir2d.y ) / fabsf( lightDir2d.x ); + else + dy += 1.0f * fabsf( lightDir2d.x ) / fabsf( lightDir2d.y ); + m_jobParamDelta->setManualValue( Vector2( dx, dy ) ); + } + + const int32 xyStep[2] = + { + (x0 < x1) ? 1 : -1, + (y0 < y1) ? 1 : -1 + }; + m_jobParamXYStep->setManualValue( xyStep, 2u ); + + heightDelta = ( -heightDelta * (xzDimensions.x / width) ) / heightScale; + //Avoid sending +/- inf (which causes NaNs inside the shader). + //Values greater than 1.0 (or less than -1.0) are pointless anyway. + heightDelta = Ogre::max( -1.0f, Ogre::min( 1.0f, heightDelta ) ); + m_jobParamHeightDelta->setManualValue( heightDelta ); + + //y0 is not needed anymore, and we need it to be either 0 or heightOrWidth for the + //algorithm to work correctly (depending on the sign of xyStep[1]). So do this now. + if( y0 >= y1 ) + y0 = heightOrWidth; + + int32 *starts = startsBase; + + const float fStep = (dx * 0.5f) / dy; + //TODO numExtraIterations correct? -1? +1? + uint32 numExtraIterations = static_cast( + Ogre::min( ceilf( dy ), ceilf( ((heightOrWidth - 1u) / fStep - 1u) * 0.5f ) ) ); + + const uint32 threadsPerGroup = m_shadowJob->getThreadsPerGroupX(); + const uint32 firstThreadGroups = alignToNextMultiple( heightOrWidth, + threadsPerGroup ) / threadsPerGroup; + const uint32 lastThreadGroups = alignToNextMultiple( numExtraIterations, + threadsPerGroup ) / threadsPerGroup; + const uint32 totalThreadGroups = firstThreadGroups + lastThreadGroups; + + const int32 idy = static_cast( floorf( dy ) ); + + //"First" series of threadgroups + for( uint32 h=0; h( x0 ); + *starts++ = static_cast( y0 ) + static_cast( startY + i ) * xyStep[1]; + ++starts; + ++starts; + + if( starts - startsBase >= (4096u << 2u) ) + starts -= (4096u << 2u) - 2u; + } + + perGroupData->iterations = widthOrHeight - std::max( 0, idy - (heightOrWidth - startY) ); + perGroupData->deltaErrorStart = 0; + perGroupData->padding0 = 0; + perGroupData->padding1 = 0; + ++perGroupData; + } + + //"Last" series of threadgroups + for( uint32 h=0; h( x0 ) + xN * xyStep[0]; + *starts++ = static_cast( y0 ) - static_cast( i ) * xyStep[1]; + ++starts; + ++starts; + + if( starts - startsBase >= (4096u << 2u) ) + starts -= (4096u << 2u) - 2u; + } + + perGroupData->iterations = widthOrHeight - xN; + perGroupData->deltaErrorStart = getErrorAfterXsteps( xN, dx, dy ) - dx * 0.5f; + ++perGroupData; + } + + m_shadowPerGroupData->unmap( UO_KEEP_PERSISTENT ); + m_shadowStarts->unmap( UO_KEEP_PERSISTENT ); + + //Re-Set them every frame (they may have changed if we have multiple Terra instances) + m_shadowJob->setConstBuffer( 0, m_shadowStarts ); + m_shadowJob->setConstBuffer( 1, m_shadowPerGroupData ); + DescriptorSetTexture2::TextureSlot texSlot( DescriptorSetTexture2::TextureSlot::makeEmpty() ); + texSlot.texture = m_heightMapTex; + m_shadowJob->setTexture( 0, texSlot ); + + m_shadowJob->setNumThreadGroups( totalThreadGroups, 1u, 1u ); + + ShaderParams &shaderParams = m_shadowJob->getShaderParams( "default" ); + shaderParams.setDirty(); + + m_shadowWorkspace->_update(); + } + //----------------------------------------------------------------------------------- + void ShadowMapper::fillUavDataForCompositorChannel( TextureGpu **outChannel, + ResourceLayoutMap &outInitialLayouts, + ResourceAccessMap &outInitialUavAccess ) const + { + *outChannel = m_shadowMapTex; + outInitialLayouts.insert( m_shadowWorkspace->getResourcesLayout().begin(), + m_shadowWorkspace->getResourcesLayout().end() ); + outInitialUavAccess.insert( m_shadowWorkspace->getUavsAccess().begin(), + m_shadowWorkspace->getUavsAccess().end() ); + } + //----------------------------------------------------------------------------------- + void ShadowMapper::setGaussianFilterParams( uint8 kernelRadius, float gaussianDeviationFactor ) + { + HlmsManager *hlmsManager = Root::getSingleton().getHlmsManager(); + HlmsCompute *hlmsCompute = hlmsManager->getComputeHlms(); + + HlmsComputeJob *job = 0; + job = hlmsCompute->findComputeJob( "Terra/GaussianBlurH" ); + setGaussianFilterParams( job, kernelRadius, gaussianDeviationFactor ); + job = hlmsCompute->findComputeJob( "Terra/GaussianBlurV" ); + setGaussianFilterParams( job, kernelRadius, gaussianDeviationFactor ); + } + //----------------------------------------------------------------------------------- + void ShadowMapper::setGaussianFilterParams( HlmsComputeJob *job, uint8 kernelRadius, + float gaussianDeviationFactor ) + { + assert( !(kernelRadius & 0x01) && "kernelRadius must be even!" ); + + if( job->getProperty( "kernel_radius" ) != kernelRadius ) + job->setProperty( "kernel_radius", kernelRadius ); + ShaderParams &shaderParams = job->getShaderParams( "default" ); + + std::vector weights( kernelRadius + 1u ); + + const float fKernelRadius = kernelRadius; + const float gaussianDeviation = fKernelRadius * gaussianDeviationFactor; + + //It's 2.0f if using the approximate filter (sampling between two pixels to + //get the bilinear interpolated result and cut the number of samples in half) + const float stepSize = 1.0f; + + //Calculate the weights + float fWeightSum = 0; + for( uint32 i=0; iname.find( "c_weights[" ); + + if( pos != String::npos ) + { + itor = shaderParams.mParams.erase( itor ); + end = shaderParams.mParams.end(); + } + else + { + ++itor; + } + } + + const bool bIsHlsl = job->getCreator()->getShaderProfile() == "hlsl"; + + //Set the shader constants, 16 at a time (since that's the limit of what ManualParam can hold) + char tmp[32]; + LwString weightsString( LwString::FromEmptyPointer( tmp, sizeof(tmp) ) ); + const uint32 floatsPerParam = sizeof( ShaderParams::ManualParam().dataBytes ) / sizeof(float); + for( uint32 i=0; i> 2u ), "]" ); + + ShaderParams::Param p; + p.isAutomatic = false; + p.isDirty = true; + p.name = weightsString.c_str(); + shaderParams.mParams.push_back( p ); + ShaderParams::Param *param = &shaderParams.mParams.back(); + + param->setManualValue( &weights[i], std::min( floatsPerParam, weights.size() - i ) ); + } + + shaderParams.setDirty(); + } +} diff --git a/ogre2/src/terrain/Terra/src/TerraWorkspaceListener.cpp b/ogre2/src/terrain/Terra/src/TerraWorkspaceListener.cpp new file mode 100644 index 000000000..c9139099c --- /dev/null +++ b/ogre2/src/terrain/Terra/src/TerraWorkspaceListener.cpp @@ -0,0 +1,147 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE + (Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org/ + +Copyright (c) 2000-2021 Torus Knot Software Ltd + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +----------------------------------------------------------------------------- +*/ + +#include "Terra/TerraWorkspaceListener.h" + +#include "Terra/Hlms/OgreHlmsTerra.h" +#include "Terra/Terra.h" + +#include "Compositor/OgreCompositorShadowNode.h" +#include "Compositor/Pass/OgreCompositorPass.h" +#include "Compositor/Pass/PassScene/OgreCompositorPassScene.h" +#include "Compositor/Pass/PassScene/OgreCompositorPassSceneDef.h" +#include "OgreCamera.h" + +namespace Ogre +{ + TerraWorkspaceListener::TerraWorkspaceListener( HlmsTerra *hlmsTerra ) : + m_hlmsTerra( hlmsTerra ), + m_terraNeedsRestoring( false ) + { + } + //------------------------------------------------------------------------- + TerraWorkspaceListener::~TerraWorkspaceListener() {} + //------------------------------------------------------------------------- + void TerraWorkspaceListener::passPreExecute( CompositorPass *pass ) + { + const CompositorPassDef *definition = pass->getDefinition(); + if( definition->getType() != PASS_SCENE ) + return; // We don't care + + OGRE_ASSERT_HIGH( dynamic_cast( definition ) ); + const CompositorPassSceneDef *sceneDef = + static_cast( definition ); + + if( sceneDef->mShadowNodeRecalculation != SHADOW_NODE_CASTER_PASS ) + return; + + OGRE_ASSERT_HIGH( dynamic_cast( pass ) ); + const CompositorPassScene *passScene = static_cast( pass ); + + const Camera *renderingCamera = passScene->getCamera(); + const SceneManager *sceneManager = renderingCamera->getSceneManager(); + + const CompositorShadowNode *shadowNode = sceneManager->getCurrentShadowNode(); + + const Light *light = shadowNode->getLightAssociatedWith( definition->mShadowMapIdx ); + + if( light->getType() != Light::LT_DIRECTIONAL ) + { + const bool needsRestoring = m_terraNeedsRestoring; + + const FastArray &linkedTerras = m_hlmsTerra->getLinkedTerras(); + + FastArray::const_iterator itor = linkedTerras.begin(); + FastArray::const_iterator endt = linkedTerras.end(); + + while( itor != endt ) + { + Terra *terra = *itor; + if( !needsRestoring ) + { + // This is our first time rendering non-directional shadows + // We have to save the state + terra->_swapSavedState(); + } + terra->setCastShadows( true ); + terra->setCamera( renderingCamera ); + + // Use 5.0f as epsilon because that guarantees us + // it will never trigger a shadow recalculation + terra->update( Ogre::Vector3::ZERO, 5.0f ); + ++itor; + } + + m_terraNeedsRestoring = true; + } + else + { + const FastArray &linkedTerras = m_hlmsTerra->getLinkedTerras(); + + FastArray::const_iterator itor = linkedTerras.begin(); + FastArray::const_iterator endt = linkedTerras.end(); + + while( itor != endt ) + { + ( *itor )->setCastShadows( false ); + ++itor; + } + } + } + //------------------------------------------------------------------------- + void TerraWorkspaceListener::passSceneAfterShadowMaps( CompositorPassScene *pass ) + { + if( !m_terraNeedsRestoring ) + return; // Nothing to restore + + const CompositorPassDef *definition = pass->getDefinition(); + if( definition->getType() != PASS_SCENE ) + return; // We don't care + + OGRE_ASSERT_HIGH( dynamic_cast( definition ) ); + const CompositorPassSceneDef *sceneDef = + static_cast( definition ); + + if( sceneDef->mShadowNodeRecalculation == SHADOW_NODE_CASTER_PASS ) + return; // passEarlyPreExecute handled this + + const FastArray &linkedTerras = m_hlmsTerra->getLinkedTerras(); + + FastArray::const_iterator itor = linkedTerras.begin(); + FastArray::const_iterator endt = linkedTerras.end(); + + while( itor != endt ) + { + ( *itor )->setCamera( 0 ); + ( *itor )->_swapSavedState(); + ++itor; + } + + m_terraNeedsRestoring = false; + } +} // namespace Ogre diff --git a/ogre2/src/terrain/Terra/src/TerrainCell.cpp b/ogre2/src/terrain/Terra/src/TerrainCell.cpp new file mode 100644 index 000000000..a12f86f5e --- /dev/null +++ b/ogre2/src/terrain/Terra/src/TerrainCell.cpp @@ -0,0 +1,215 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE + (Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org/ + +Copyright (c) 2000-2021 Torus Knot Software Ltd + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +----------------------------------------------------------------------------- +*/ + +#include "Terra/TerrainCell.h" +#include "Terra/Terra.h" + +#include "Vao/OgreVaoManager.h" +#include "Vao/OgreVertexArrayObject.h" + +namespace Ogre +{ + TerrainCell::TerrainCell( Terra *parentTerra ) : + m_gridX( 0 ), + m_gridZ( 0 ), + m_lodLevel( 0 ), + m_verticesPerLine( 1 ), + m_sizeX( 0 ), + m_sizeZ( 0 ), + m_vaoManager( 0 ), + m_parentTerra( parentTerra ), + m_useSkirts( false ) + { + } + //----------------------------------------------------------------------------------- + TerrainCell::~TerrainCell() + { + VertexArrayObjectArray::const_iterator itor = mVaoPerLod[VpNormal].begin(); + VertexArrayObjectArray::const_iterator end = mVaoPerLod[VpNormal].end(); + + while( itor != end ) + m_vaoManager->destroyVertexArrayObject( *itor++ ); + + mVaoPerLod[VpNormal].clear(); + mVaoPerLod[VpShadow].clear(); + } + //----------------------------------------------------------------------------------- + bool TerrainCell::isZUp( void ) const { return m_parentTerra->m_zUp; } + //----------------------------------------------------------------------------------- + void TerrainCell::initialize( VaoManager *vaoManager, bool useSkirts ) + { + assert( mVaoPerLod[VpNormal].empty() && "Already initialized!" ); + m_vaoManager = vaoManager; + m_useSkirts = useSkirts; + + //Setup bufferless vao + VertexBufferPackedVec vertexBuffers; + VertexArrayObject *vao = vaoManager->createVertexArrayObject( + vertexBuffers, 0, OT_TRIANGLE_STRIP ); + mVaoPerLod[VpNormal].push_back( vao ); + mVaoPerLod[VpShadow].push_back( vao ); + } + //----------------------------------------------------------------------------------- + void TerrainCell::setOrigin( const GridPoint &gridPos, uint32 horizontalPixelDim, + uint32 verticalPixelDim, uint32 lodLevel ) + { + m_gridX = gridPos.x; + m_gridZ = gridPos.z; + m_lodLevel = lodLevel; + + horizontalPixelDim = std::min( horizontalPixelDim, m_parentTerra->m_width - m_gridX ); + verticalPixelDim = std::min( verticalPixelDim, m_parentTerra->m_depth - m_gridZ ); + + m_sizeX = horizontalPixelDim; + m_sizeZ = verticalPixelDim; + + //Divide by 2^lodLevel and round up + horizontalPixelDim = (horizontalPixelDim + (1u << lodLevel) - 1u) >> lodLevel; + verticalPixelDim = (verticalPixelDim + (1u << lodLevel) - 1u) >> lodLevel; + + //Add an extra vertex to fill the gaps to the next TerrainCell. + horizontalPixelDim += 1u; + verticalPixelDim += 1u; + + horizontalPixelDim = std::max( horizontalPixelDim, 2u ); + verticalPixelDim = std::max( verticalPixelDim, 2u ); + + if( m_useSkirts ) + { + //Add two extra vertices & two extra rows for the skirts. + horizontalPixelDim += 2u; + verticalPixelDim += 2u; + } + + m_verticesPerLine = horizontalPixelDim * 2u + 2u; + + assert( m_verticesPerLine * (verticalPixelDim - 1u) > 0u ); + + VertexArrayObject *vao = mVaoPerLod[VpNormal][0]; + vao->setPrimitiveRange( 0, m_verticesPerLine * (verticalPixelDim - 1u) ); + } + //----------------------------------------------------------------------- + bool TerrainCell::merge( TerrainCell *next ) + { + bool merged = false; + + if( m_lodLevel == next->m_lodLevel ) + { + GridPoint pos; + pos.x = m_gridX; + pos.z = m_gridZ; + uint32 horizontalPixelDim = m_sizeX; + uint32 verticalPixelDim = m_sizeZ; + + if( (this->m_gridX + this->m_sizeX == next->m_gridX || + next->m_gridX + next->m_sizeX == this->m_gridX) && + m_gridZ == next->m_gridZ && m_sizeZ == next->m_sizeZ ) + { + //Merge horizontally + pos.x = std::min( m_gridX, next->m_gridX ); + horizontalPixelDim += next->m_sizeX; + + this->setOrigin( pos, horizontalPixelDim, verticalPixelDim, m_lodLevel ); + merged = true; + } + else if( (this->m_gridZ + this->m_sizeZ == next->m_gridZ || + next->m_gridZ + next->m_sizeZ == this->m_gridZ) && + m_gridX == next->m_gridX && m_sizeX == next->m_sizeX ) + { + //Merge vertically + pos.z = std::min( m_gridZ, next->m_gridZ ); + verticalPixelDim += next->m_sizeZ; + + this->setOrigin( pos, horizontalPixelDim, verticalPixelDim, m_lodLevel ); + merged = true; + } + } + + return merged; + } + //----------------------------------------------------------------------- + void TerrainCell::uploadToGpu( uint32 * RESTRICT_ALIAS gpuPtr ) const + { + //uint32 rows = (m_sizeZ + (1u << m_lodLevel) - 1u) >> m_lodLevel; + VertexArrayObject *vao = mVaoPerLod[VpNormal][0]; + + //uvec4 numVertsPerLine + gpuPtr[0] = m_verticesPerLine; + gpuPtr[1] = m_lodLevel; + gpuPtr[2] = vao->getPrimitiveCount() / m_verticesPerLine - 2u; + gpuPtr[3] = *reinterpret_cast( &m_parentTerra->m_skirtSize ); + + //ivec4 xzTexPosBounds + ((int32*RESTRICT_ALIAS)gpuPtr)[4] = m_gridX; + ((int32*RESTRICT_ALIAS)gpuPtr)[5] = m_gridZ; + ((int32*RESTRICT_ALIAS)gpuPtr)[6] = m_parentTerra->m_width - 1u; + ((int32*RESTRICT_ALIAS)gpuPtr)[7] = m_parentTerra->m_depth - 1u; + + ((float*RESTRICT_ALIAS)gpuPtr)[8] = m_parentTerra->m_terrainOrigin.x; + ((float*RESTRICT_ALIAS)gpuPtr)[9] = m_parentTerra->m_terrainOrigin.y; + ((float*RESTRICT_ALIAS)gpuPtr)[10] = m_parentTerra->m_terrainOrigin.z; + ((float*RESTRICT_ALIAS)gpuPtr)[11] = m_parentTerra->m_invWidth; + + ((float*RESTRICT_ALIAS)gpuPtr)[12] = m_parentTerra->m_xzRelativeSize.x; + ((float*RESTRICT_ALIAS)gpuPtr)[13] = m_parentTerra->m_height; + ((float*RESTRICT_ALIAS)gpuPtr)[14] = m_parentTerra->m_xzRelativeSize.y; + ((float*RESTRICT_ALIAS)gpuPtr)[15] = m_parentTerra->m_invDepth; + } + //----------------------------------------------------------------------- + const LightList& TerrainCell::getLights(void) const + { + return m_parentTerra->queryLights(); + } + //----------------------------------------------------------------------------- + void TerrainCell::getRenderOperation( v1::RenderOperation& op, bool casterPass ) + { + OGRE_EXCEPT( Exception::ERR_NOT_IMPLEMENTED, + "Items do not implement getRenderOperation. You've put an Item in " + "the wrong RenderQueue ID (which is set to be compatible with " + "v1::Entity). Do not mix Items and Entities", + "TerrainCell::getRenderOperation" ); + } + //----------------------------------------------------------------------------- + void TerrainCell::getWorldTransforms(Matrix4* xform) const + { + OGRE_EXCEPT( Exception::ERR_NOT_IMPLEMENTED, + "Items do not implement getWorldTransforms. You've put an Item in " + "the wrong RenderQueue ID (which is set to be compatible with " + "v1::Entity). Do not mix Items and Entities", + "TerrainCell::getWorldTransforms" ); + } + //----------------------------------------------------------------------------- + bool TerrainCell::getCastsShadows(void) const + { + OGRE_EXCEPT( Exception::ERR_NOT_IMPLEMENTED, + "Items do not implement getCastsShadows. You've put an Item in " + "the wrong RenderQueue ID (which is set to be compatible with " + "v1::Entity). Do not mix Items and Entities", + "TerrainCell::getCastsShadows" ); + } +}