text: /*
   * Copyright (c) Contributors to the Open 3D Engine Project.
   * For complete copyright and license terms please see the LICENSE at the root of this distribution.
   *
   * SPDX-License-Identifier: Apache-2.0 OR MIT
   *
   */
  
  #pragma once
  
  #include <Atom/RPI.Reflect/Asset/AssetHandler.h>
  #include <Atom/RPI.Reflect/Model/ModelLodAsset.h>
  
  #include <AzCore/Asset/AssetCommon.h>
  #include <AzCore/Math/Aabb.h>
  #include <AzCore/Name/Name.h>
  
  namespace AZ
  {
      class ReflectContext;
  
      namespace RPI
      {
          class ModelKdTree;
  
          //! Contains a set of RPI::ModelLodAsset objects.
          //! Serialized to a .azmodel file.
          //! Actual model data is stored in the BufferAssets referenced by ModelLodAssets.
          class ModelAsset
              : public AZ::Data::AssetData
          {
              friend class ModelAssetCreator;
  
          public:
              static const char* DisplayName;
              static const char* Extension;
              static const char* Group;
  
              AZ_RTTI(ModelAsset, "{2C7477B6-69C5-45BE-8163-BCD6A275B6D8}", AZ::Data::AssetData);
              AZ_CLASS_ALLOCATOR(ModelAsset, AZ::SystemAllocator, 0);
  
              static void Reflect(AZ::ReflectContext* context);
  
              ModelAsset();
              virtual ~ModelAsset();
  
              //! Returns the human readable name of the model
              const Name& GetName() const;
  
              //! Returns the model-space axis aligned bounding box
              const AZ::Aabb& GetAabb() const;
  
              //! Returns the number of Lods in the model
              size_t GetLodCount() const;
  
              AZStd::array_view<Data::Asset<ModelLodAsset>> GetLodAssets() const;
  
              //! Checks a ray for intersection against this model. The ray must be in the same coordinate space as the model.
              //! Important: only to be used in the Editor, it may kick off a job to calculate spatial information.
              //! [GFX TODO][ATOM-4343 Bake mesh spatial information during AP processing]
              //!
              //! @param rayStart  The starting point of the ray.
              //! @param rayDir  The direction and length of the ray (magnitude is encoded in the direction).
              //! @param allowBruteForce  Allow for brute force queries while the mesh is baking (remove when ATOM-4343 is complete)
              //! @param[out] distanceNormalized  If an intersection is found, will be set to the normalized distance of the intersection
              //! (in the range 0.0-1.0) - to calculate the actual distance, multiply distanceNormalized by the magnitude of rayDir.
              //! @param[out] normal If an intersection is found, will be set to the normal at the point of collision.
              //! @return  True if the ray intersects the mesh.
              virtual bool LocalRayIntersectionAgainstModel(
                  const AZ::Vector3& rayStart, const AZ::Vector3& rayDir, bool allowBruteForce,
                  float& distanceNormalized, AZ::Vector3& normal) const;
  
          private:
              void SetReady();
  
              AZ::Name m_name;
              AZ::Aabb m_aabb = AZ::Aabb::CreateNull();
              AZStd::fixed_vector<Data::Asset<ModelLodAsset>, ModelLodAsset::LodCountMax> m_lodAssets;
  
              // mutable method
              void BuildKdTree() const;
              bool BruteForceRayIntersect(
                  const AZ::Vector3& rayStart, const AZ::Vector3& rayDir, float& distanceNormalized, AZ::Vector3& normal) const;
  
              bool LocalRayIntersectionAgainstMesh(
                  const ModelLodAsset::Mesh& mesh,
                  const AZ::Vector3& rayStart,
                  const AZ::Vector3& rayDir,
                  float& distanceNormalized,
                  AZ::Vector3& normal) const;
  
              // Various model information used in raycasting
              AZ::Name m_positionName{ "POSITION" };
              // there is a tradeoff between memory use and performance but anywhere under a few thousand triangles or so remains under a few milliseconds per ray cast
              static const AZ::u32 s_minimumModelTriangleCountToOptimize = 100;
              mutable AZStd::unique_ptr<ModelKdTree> m_kdTree;
              volatile mutable bool m_isKdTreeCalculationRunning = false;
              mutable AZStd::mutex m_kdTreeLock;
              mutable AZStd::optional<AZStd::size_t> m_modelTriangleCount;
  
              AZStd::size_t CalculateTriangleCount() const;
          };
  
          class ModelAssetHandler
              : public AssetHandler<ModelAsset>
          {
          public:
              AZ_RTTI(ModelAssetHandler, "{993B8CE3-1BBF-4712-84A0-285DB9AE808F}", AssetHandler<ModelAsset>);
  
              // AZ::AssetTypeInfoBus::Handler overrides
              bool HasConflictingProducts(const AZStd::vector<AZ::Data::AssetType>& productAssetTypes) const override;
          };
      } //namespace RPI
  } // namespace AZ
  