text: /*
   * Copyright (C) 2016 The Android Open Source Project
   *
   * 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 TNT_FILAMENT_DETAILS_SHADOWMAP_H
  #define TNT_FILAMENT_DETAILS_SHADOWMAP_H
  
  #include "components/LightManager.h"
  
  #include "details/Camera.h"
  #include "details/Scene.h"
  
  #include "private/backend/DriverApiForward.h"
  #include "private/backend/SamplerGroup.h"
  
  #include <filament/Viewport.h>
  
  #include <math/mat4.h>
  #include <math/vec4.h>
  
  namespace filament {
  
  class FView;
  class RenderPass;
  
  class ShadowMap {
  public:
      explicit ShadowMap(FEngine& engine) noexcept;
      ~ShadowMap();
  
      struct ShadowMapLayout {
          // the smallest increment in depth precision
          // e.g., for 16 bit depth textures, is this 1 / (2^16)
          float zResolution = 0.0f;
  
          // the dimension of the encompassing texture atlas
          size_t atlasDimension = 0;
  
          // the dimension of a single shadow map texture within the atlas
          // e.g., for at atlas size of 1024 split into 4 quadrants, textureDimension would be 512
          size_t textureDimension = 0;
  
          // the dimension of the actual shadow map, taking into account the 1 texel border
          // e.g., for a texture dimension of 512, shadowDimension would be 510
          size_t shadowDimension = 0;
      };
  
      struct CascadeParameters {
          // The near and far planes, in clip space, to use for this shadow map
          math::float2 csNearFar = { -1.0f, 1.0f };
  
          // The following fields are set by computeSceneCascadeParams.
  
          // Light-space near/far planes for the scene.
          math::float2 lsNearFar;
  
          // View-space near/far planes for the scene.
          math::float2 vsNearFar;
  
          Aabb wsShadowCastersVolume;
          Aabb wsShadowReceiversVolume;
  
          // Position of the directional light in world space.
          math::float3 wsLightPosition;
      };
  
      // Call once per frame to populate the CascadeParameters struct, then pass to update().
      // This computes values constant across all cascades.
      static void computeSceneCascadeParams(const FScene::LightSoa& lightData, size_t index,
              FView const& view, filament::CameraInfo const& camera, uint8_t visibleLayers,
              CascadeParameters& cascadeParams);
  
      // Call once per frame if the light, scene (or visible layers) or camera changes.
      // This computes the light's camera.
      void update(const FScene::LightSoa& lightData, size_t index, FScene const* scene,
              filament::CameraInfo const& camera, uint8_t visibleLayers,
              ShadowMapLayout layout, const CascadeParameters& cascadeParams) noexcept;
  
      void render(backend::DriverApi& driver, utils::Range<uint32_t> const& range, RenderPass& pass,
              FView& view) noexcept;
  
      // Do we have visible shadows. Valid after calling update().
      bool hasVisibleShadows() const noexcept { return mHasVisibleShadows; }
  
      // Computes the transform to use in the shader to access the shadow map.
      // Valid after calling update().
      math::mat4f const& getLightSpaceMatrix() const noexcept { return mLightSpace; }
  
      // Computes the transform to use in the shader to access the shadow map for VSM.
      // Valid after calling update().
      math::mat4f const& getLightSpaceMatrixVsm() const noexcept { return mLightSpaceVsm; }
  
      // return the size of a texel in world space (pre-warping)
      float getTexelSizeWorldSpace() const noexcept { return mTexelSizeWs; }
  
      // Returns the light's projection. Valid after calling update().
      FCamera const& getCamera() const noexcept { return *mCamera; }
  
      // use only for debugging
      FCamera const& getDebugCamera() const noexcept { return *mDebugCamera; }
  
      backend::PolygonOffset getPolygonOffset() const noexcept { return mPolygonOffset; }
  
  private:
      struct CameraInfo {
          math::mat4f projection;
          math::mat4f model;
          math::mat4f view;
          math::mat4f worldOrigin;
          float zn = 0;
          float zf = 0;
          Frustum frustum;
          float getNear() const noexcept { return zn; }
          float getFar() const noexcept { return zf; }
          math::float3 const& getPosition() const noexcept { return model[3].xyz; }
          math::float3 getForwardVector() const noexcept {
              return -normalize(model[2].xyz);   // the camera looks towards -z
          }
      };
  
      struct Segment {
          uint8_t v0, v1;
      };
  
      struct Quad {
          uint8_t v0, v1, v2, v3;
      };
  
      // 8 corners, 12 segments w/ 2 intersection max -- all of this twice (8 + 12 * 2) * 2 (768 bytes)
      using FrustumBoxIntersection = std::array<math::float3, 64>;
  
      void computeShadowCameraDirectional(
              math::float3 const& direction, FScene const* scene,
              CameraInfo const& camera, FLightManager::ShadowParams const& params,
              uint8_t visibleLayers, CascadeParameters cascadeParams) noexcept;
      void computeShadowCameraSpot(math::float3 const& position, math::float3 const& dir,
              float outerConeAngle, float radius, CameraInfo const& camera,
              FLightManager::ShadowParams const& params) noexcept;
  
      static math::mat4f applyLISPSM(math::mat4f& Wp,
              CameraInfo const& camera, FLightManager::ShadowParams const& params,
              const math::mat4f& LMpMv,
              FrustumBoxIntersection const& wsShadowReceiverVolume, size_t vertexCount,
              const math::float3& dir);
  
      static inline void snapLightFrustum(math::float2& s, math::float2& o,
              math::mat4f const& Mv, math::float3 worldOrigin, math::float2 shadowMapResolution) noexcept;
  
      static inline void computeFrustumCorners(math::float3* out,
              const math::mat4f& projectionViewInverse, math::float2 csNearFar = { -1.0f, 1.0f }) noexcept;
  
      static inline math::float2 computeNearFar(math::mat4f const& view,
              Aabb const& wsShadowCastersVolume) noexcept;
  
      static inline math::float2 computeNearFar(math::mat4f const& view,
              math::float3 const* wsVertices, size_t count) noexcept;
  
      static inline math::float4 computeBoundingSphere(
              math::float3 const* vertices, size_t count) noexcept;
  
      template<typename Casters, typename Receivers>
      static void visitScene(FScene const& scene, uint32_t visibleLayers,
              Casters casters, Receivers receivers) noexcept;
  
      static inline Aabb compute2DBounds(const math::mat4f& lightView,
              math::float3 const* wsVertices, size_t count) noexcept;
  
      static inline Aabb compute2DBounds(const math::mat4f& lightView,
              math::float4 const& sphere) noexcept;
  
      static inline void intersectWithShadowCasters(Aabb& lightFrustum, const math::mat4f& lightView,
              Aabb const& wsShadowCastersVolume) noexcept;
  
      static inline math::float2 computeNearFarOfWarpSpace(math::mat4f const& lightView,
              math::float3 const* wsVertices, size_t count) noexcept;
  
      static inline bool intersectSegmentWithPlanarQuad(math::float3& p,
              math::float3 s0, math::float3 s1,
              math::float3 t0, math::float3 t1,
              math::float3 t2, math::float3 t3) noexcept;
  
      static inline bool intersectSegmentWithTriangle(math::float3& UTILS_RESTRICT p,
              math::float3 s0, math::float3 s1,
              math::float3 t0, math::float3 t1, math::float3 t2) noexcept;
  
      static size_t intersectFrustum(math::float3* out, size_t vertexCount,
              math::float3 const* segmentsVertices, math::float3 const* quadsVertices) noexcept;
  
      static size_t intersectFrustumWithBox(
              FrustumBoxIntersection& outVertices,
              const math::float3* wsFrustumCorners,
              Aabb const& wsBox);
  
      static math::mat4f warpFrustum(float n, float f) noexcept;
  
      static math::mat4f directionalLightFrustum(float n, float f) noexcept;
  
      math::mat4f getTextureCoordsMapping() const noexcept;
  
      static math::mat4f computeVsmLightSpaceMatrix(const math::mat4f& lightSpace,
              const math::mat4f& Mv, float zfar) noexcept;
  
      float texelSizeWorldSpace(const math::mat3f& worldToShadowTexture) const noexcept;
      float texelSizeWorldSpace(const math::mat4f& W, const math::mat4f& MbMtF) const noexcept;
  
      static constexpr const Segment sBoxSegments[12] = {
              { 0, 1 }, { 1, 3 }, { 3, 2 }, { 2, 0 },
              { 4, 5 }, { 5, 7 }, { 7, 6 }, { 6, 4 },
              { 0, 4 }, { 1, 5 }, { 3, 7 }, { 2, 6 },
      };
      static constexpr const Quad sBoxQuads[6] = {
              { 2, 0, 1, 3 },  // far
              { 6, 4, 5, 7 },  // near
              { 2, 0, 4, 6 },  // left
              { 3, 1, 5, 7 },  // right
              { 0, 4, 5, 1 },  // bottom
              { 2, 6, 7, 3 },  // top
      };
  
      FCamera* mCamera = nullptr;
      FCamera* mDebugCamera = nullptr;
      math::mat4f mLightSpace;
      math::mat4f mLightSpaceVsm;
      float mTexelSizeWs = 0.0f;
  
      // set-up in update()
      ShadowMapLayout mShadowMapLayout;
      bool mHasVisibleShadows = false;
      backend::PolygonOffset mPolygonOffset{};
  
      // use a member here (instead of stack) because we don't want to pay the
      // initialization of the float3 each time
      FrustumBoxIntersection mWsClippedShadowReceiverVolume;
  
      FEngine& mEngine;
      const bool mClipSpaceFlipped;
      const bool mTextureSpaceFlipped;
  };
  
  } // namespace filament
  
  #endif // TNT_FILAMENT_DETAILS_SHADOWMAP_H
  