//+------------------------------------------------------------------+
//|                                                       DXMesh.mqh |
//|                             Copyright 2000-2026, MetaQuotes Ltd. |
//|                                                     www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2000-2026, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#include "DXObject.mqh"
#include "DXDispatcher.mqh"
#include "DXData.mqh"
#include "DXUtils.mqh"
//+------------------------------------------------------------------+
//| DX simple mesh object with default shader                        |
//+------------------------------------------------------------------+
class CDXMesh : public CDXObject
  {
protected:
   //--- DX resources
   CDXVertexBuffer*  m_buffer_vertex;
   CDXIndexBuffer*   m_buffer_index;
   CDXInput*         m_buffer_object;
   CDXInput*         m_buffer_scene;
   CDXShader*        m_shader_vertex;
   CDXShader*        m_shader_pixel;
   CDXTexture*       m_texture;
   //--- object data
   DXMatrix          m_transform_matrix;
   DXColor           m_diffuse_color;
   DXColor           m_emission_color;
   DXColor           m_specular_color;
   float             m_specular_power;

   ENUM_DX_PRIMITIVE_TOPOLOGY m_topology;

public:
   //+------------------------------------------------------------------+
   //| Constructor                                                      |
   //+------------------------------------------------------------------+
                     CDXMesh(void):m_buffer_vertex(NULL),m_buffer_index(NULL),m_buffer_object(NULL),m_buffer_scene(NULL),m_shader_vertex(NULL),m_shader_pixel(NULL),m_topology(WRONG_VALUE)
     {
     }
   //+------------------------------------------------------------------+
   //| Destructor                                                       |
   //+------------------------------------------------------------------+
                    ~CDXMesh(void)
     {
      Shutdown();
     }
   //+------------------------------------------------------------------+
   //| Create mesh object from vertices and indices list                |
   //+------------------------------------------------------------------+
   bool              Create(CDXDispatcher &dispatcher,CDXInput* buffer_scene,const DXVertex &vertices[],const uint &indices[],ENUM_DX_PRIMITIVE_TOPOLOGY topology=DX_PRIMITIVE_TOPOLOGY_TRIANGLELIST)
     {
      Shutdown();
      //--- check data
      if(buffer_scene==NULL || buffer_scene.Handle()==INVALID_HANDLE ||
         ArraySize(vertices)<1 || ArraySize(indices)<1)
         return(false);
      //--- update context
      m_context=dispatcher.DXContext();
      //--- initialize default parameters
      DXMatrixIdentity(m_transform_matrix);
      m_diffuse_color =DXColor(1.0,1.0,1.0,1.0);
      m_emission_color=DXColor(0.0,0.0,0.0,0.0);
      m_specular_color=DXColor(0.0,0.0,0.0,0.0);
      m_specular_power=16.0f;
      //--- create object input
      m_buffer_object=dispatcher.InputCreate<DXInputObject>();
      if(m_buffer_object!=NULL)
        {
         m_buffer_object.AddRef();
         //--- use default vertex shader
         m_shader_vertex=dispatcher.ShaderCreateDefault(DX_SHADER_VERTEX);
         if(m_shader_vertex!=NULL)
           {
            m_shader_vertex.AddRef();
            //--- use default pixel shader
            m_shader_pixel=dispatcher.ShaderCreateDefault(DX_SHADER_PIXEL);
            if(m_shader_pixel!=NULL)
              {
               m_shader_pixel.AddRef();
               //--- create vertex buffer
               m_buffer_vertex=dispatcher.VertexBufferCreate(vertices);
               if(m_buffer_vertex!=NULL)
                 {
                  m_buffer_vertex.AddRef();
                  //--- create index buffer
                  m_buffer_index=dispatcher.IndexBufferCreate(indices);
                  if(m_buffer_index!=NULL)
                     m_buffer_index.AddRef();
                 }
              }
           }
        }
      //--- set object buffer to shaders
      if(m_buffer_object==NULL || m_shader_vertex==NULL || m_shader_pixel==NULL || m_buffer_vertex==NULL || m_buffer_index==NULL)
        {
         Shutdown();
         return(false);
        }
      //--- save scene buffer
      m_buffer_scene=buffer_scene;
      m_buffer_scene.AddRef();
      //--- save topology
      m_topology=topology;
      //---
      return(true);
     }
   //+------------------------------------------------------------------+
   //| Create mesh object from OBJ mesh file                            |
   //+------------------------------------------------------------------+
   bool              Create(CDXDispatcher &dispatcher,CDXInput* buffer_scene,string obj_path,float scale=1.0f,bool inverse_winding=false)
     {
      DXVertex vertices[];
      uint     indices[];
      //--- load model
      if(!DXLoadObjModel(obj_path,vertices,indices,scale))
         return(false);
      //--- set white vertices color
      int count=ArraySize(vertices);
      for(int i=0; i<count; i++)
         vertices[i].vcolor=DXColor(1.0f,1.0f,1.0f,1.0f);
      //--- inverse winding
      if(inverse_winding)
         DXInverseWinding(vertices,indices);
      //--- create mesh
      return(Create(dispatcher,buffer_scene,vertices,indices));
     }
   //+------------------------------------------------------------------+
   //| Update vertices                                                  |
   //+------------------------------------------------------------------+
   bool              VerticesSet(const DXVertex &vertices[])
     {
      //--- check size
      if(ArraySize(vertices)<1)
         return(false);
      //--- create new vertex buffer
      if(!m_buffer_vertex.Create(m_context,vertices))
        {
         Shutdown();
         return(false);
        }
      //---
      return(true);
     }
   //+------------------------------------------------------------------+
   //| Update indices                                                   |
   //+------------------------------------------------------------------+
   bool              IndicesSet(const uint &indices[])
     {
      //--- check size
      if(ArraySize(indices)<1)
         return(false);
      //--- create new index buffer
      if(!m_buffer_index.Create(m_context,indices))
        {
         Shutdown();
         return(false);
        }
      //---
      return(true);
     }
   //+------------------------------------------------------------------+
   //| Update indices                                                   |
   //+------------------------------------------------------------------+
   bool              TopologySet(ENUM_DX_PRIMITIVE_TOPOLOGY topology)
     {
      m_topology=topology;
      //---
      return(true);
     }
   //+------------------------------------------------------------------+
   //| Get transform matrix                                             |
   //+------------------------------------------------------------------+
   void              TransformMatrixGet(DXMatrix &mat) const
     {
      mat=m_transform_matrix;
     }
   //+------------------------------------------------------------------+
   //| Set transform matrix                                             |
   //+------------------------------------------------------------------+
   void              TransformMatrixSet(const DXMatrix &mat)
     {
      m_transform_matrix=mat;
     }
   //+------------------------------------------------------------------+
   //| Get diffuse color                                                |
   //+------------------------------------------------------------------+
   void              DiffuseColorGet(DXColor &clr) const
     {
      clr=m_diffuse_color;
     }
   //+------------------------------------------------------------------+
   //| Set diffuse color                                                |
   //+------------------------------------------------------------------+
   void              DiffuseColorSet(const DXColor &clr)
     {
      m_diffuse_color=clr;
     }
   //+------------------------------------------------------------------+
   //| Get specular color                                               |
   //+------------------------------------------------------------------+
   void              SpecularColorGet(DXColor &clr) const
     {
      clr=m_specular_color;
     }
   //+------------------------------------------------------------------+
   //| Set specular color                                               |
   //+------------------------------------------------------------------+
   void              SpecularColorSet(const DXColor &clr)
     {
      m_specular_color=clr;
     }
   //+------------------------------------------------------------------+
   //| Get specular power                                               |
   //+------------------------------------------------------------------+
   void              SpecularPowerGet(float &power) const
     {
      power=m_specular_power;
     }
   //+------------------------------------------------------------------+
   //| Set specular power                                               |
   //+------------------------------------------------------------------+
   void              SpecularPowerSet(float power)
     {
      m_specular_power=power;
     }
   //+------------------------------------------------------------------+
   //| Get emission color                                               |
   //+------------------------------------------------------------------+
   void              EmissionColorGet(DXColor &clr) const
     {
      clr=m_emission_color;
     }
   //+------------------------------------------------------------------+
   //| Set emission color                                               |
   //+------------------------------------------------------------------+
   void              EmissionColorSet(const DXColor &clr)
     {
      m_emission_color=clr;
     }
   //+------------------------------------------------------------------+
   //| Render mesh                                                      |
   //+------------------------------------------------------------------+
   virtual bool      Render(void) override
     {
      //--- update object buffer
      DXInputObject input_data;
      DXMatrixTranspose(input_data.transform,m_transform_matrix);
      input_data.diffuse_color =m_diffuse_color;
      input_data.emission_color=m_emission_color;
      input_data.specular_color=m_specular_color;
      input_data.specular_power=m_specular_power;
      //--- clear shader textures
      m_shader_pixel.TexturesClear();
      //--- set buffers, shaders, topology and render
      if(m_buffer_object.InputSet(input_data))
         if(m_buffer_vertex.Render())
            if(m_buffer_index.Render())
               if(m_shader_vertex.InputSet(0,m_buffer_scene) && m_shader_vertex.InputSet(1,m_buffer_object))
                  if(m_shader_pixel.InputSet(0,m_buffer_scene) && m_shader_pixel.InputSet(1,m_buffer_object))
                     if(CheckPointer(m_texture)==POINTER_INVALID || m_shader_pixel.TextureSet(0,m_texture))
                        if(m_shader_vertex.Render())
                           if(m_shader_pixel.Render())
                              if(DXPrimiveTopologySet(m_context,m_topology))
                                 return(DXDrawIndexed(m_context));
      //---
      return(false);
     }
   //+------------------------------------------------------------------+
   //| Set texture from image file                                      |
   //+------------------------------------------------------------------+
   bool              TextureSet(CDXDispatcher &dispatcher,string path,uint data_x=0,uint data_y=0,uint data_width=0,uint data_height=0)
     {
      //--- check if texture already exist
      if(CheckPointer(m_texture)==POINTER_INVALID)
        {
         m_texture=dispatcher.TextureCreateFromFile(path,data_x,data_y,data_width,data_height);
         if(m_texture==NULL)
            return(false);
         m_texture.AddRef();
         return(true);
        }
      //--- update texture data
      if(!m_texture.Create(m_context,path,data_x,data_y,data_width,data_height))
        {
         m_texture.Release();
         m_texture=NULL;
         return(false);
        }
      //---
      return(true);
     }
   //+------------------------------------------------------------------+
   //| Set texture from image data                                      |
   //+------------------------------------------------------------------+
   bool              TextureSet(CDXDispatcher &dispatcher,ENUM_DX_FORMAT format,uint width,uint height,const uint &data[],uint data_x=0,uint data_y=0,uint data_width=0,uint data_height=0)
     {
      //--- check if texture already exist
      if(CheckPointer(m_texture)==POINTER_INVALID)
        {
         m_texture=dispatcher.TextureCreateFromData(format,width,height,data,data_x,data_y,data_width,data_height);
         if(m_texture==NULL)
            return(false);
         m_texture.AddRef();
         return(true);
        }
      //--- update texture data
      if(!m_texture.Create(m_context,format,width,height,data,data_x,data_y,data_width,data_height))
        {
         m_texture.Release();
         m_texture=NULL;
         return(false);
        }
      //---
      return(true);
     }
   //+------------------------------------------------------------------+
   //| Delete used texture                                              |
   //+------------------------------------------------------------------+
   void              TextureDelete()
     {
      //--- release texture
      if(CheckPointer(m_texture)!=POINTER_INVALID)
        {
         m_texture.Release();
         m_texture=NULL;
        }
     }
   //+------------------------------------------------------------------+
   //| Shutdown                                                         |
   //+------------------------------------------------------------------+
   virtual void      Shutdown(void)
     {
      //--- release all dx resources
      if(CheckPointer(m_buffer_vertex)!=POINTER_INVALID)
        {
         m_buffer_vertex.Release();
         m_buffer_vertex=NULL;
        }
      if(CheckPointer(m_buffer_index)!=POINTER_INVALID)
        {
         m_buffer_index.Release();
         m_buffer_index=NULL;
        }
      if(CheckPointer(m_buffer_object)!=POINTER_INVALID)
        {
         m_buffer_object.Release();
         m_buffer_object=NULL;
        }
      if(CheckPointer(m_texture)!=POINTER_INVALID)
        {
         m_texture.Release();
         m_texture=NULL;
        }
      if(CheckPointer(m_buffer_scene)!=POINTER_INVALID)
        {
         m_buffer_scene.Release();
         m_buffer_scene=NULL;
        }
      if(CheckPointer(m_shader_pixel)!=POINTER_INVALID)
        {
         m_shader_pixel.Release();
         m_shader_pixel=NULL;
        }
      if(CheckPointer(m_shader_vertex)!=POINTER_INVALID)
        {
         m_shader_vertex.Release();
         m_shader_vertex=NULL;
        }
      m_topology =WRONG_VALUE;
     }
  };
//+------------------------------------------------------------------+
