//+------------------------------------------------------------------+
//|                                                     Canvas3D.mqh |
//|                             Copyright 2000-2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
//#include "Canvas.mqh"
#include <Canvas\Canvas.mqh>
#include "DX\DXObjectBase.mqh"
#include "DX\DXHandle.mqh"
#include "DX\DXMath.mqh"
#include "DX\DXObject.mqh"
#include "DX\DXTexture.mqh"
#include "DX\DXShader.mqh"
#include "DX\DXUtils.mqh"
#include "DX\DXDispatcher.mqh"
//+------------------------------------------------------------------+
//| Render flags                                                     |
//+------------------------------------------------------------------+
enum ENUM_DX_RENDER_FLAGS
  {
   DX_CLEAR_COLOR = 1,                       // clear colors buffer
   DX_CLEAR_DEPTH = 2                        // clear z-buffer
  };
//+------------------------------------------------------------------+
//| Class CCanvas3D                                                  |
//| Usage: class for render 3D graphics through DirectX              |
//+------------------------------------------------------------------+
class CCanvas3D : public CCanvas
  {
protected:
   int               m_dx_context;           // DX Context Handle
   CDXDispatcher     m_dx_dispatcher;        // DX resource dispatcher
   CDXObjectBase     m_dx_objects;           // DX scene objects array
   CDXInput*         m_input_scene;          // Scene input of DXInputScene type
   DXMatrix          m_view_matrix;          // View matrix
   DXVector3         m_view_position;        // Point of view
   DXVector3         m_view_target;          // View target point
   DXVector3         m_view_up_dir;          // View up direction
   DXMatrix          m_projection_matrix;    // Projection matrix
   DXVector4         m_light_directon;       // Light direction
   DXColor           m_light_color;          // Directional light color
   DXColor           m_ambient_color;        // Ambient light color

public:
                     CCanvas3D(void);
                    ~CCanvas3D(void);
   //--- get DX Context Handle
   int               DXContext(void) const { return(m_dx_context); }
   CDXDispatcher*    DXDispatcher(void)    { return(&m_dx_dispatcher); }
   CDXInput*         InputScene(void)      { return(m_input_scene); }
   //--- create/attach/destroy
   virtual bool      Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA) override;
   virtual bool      Attach(const long chart_id,const string objname,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA) override;
   virtual bool      Attach(const long chart_id,const string objname,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA) override;
   virtual void      Destroy(void) override;
   //--- create/delete 3D Objects
   bool              ObjectAdd(CDXObject *object);
   //--- render functions
   bool              Render(uint flags,uint background_color=0);
   virtual bool      RenderBegin(uint flags,uint background_color=0);
   virtual bool      RenderEnd(bool redraw=false);
   //--- view control function
   void              ViewMatrixGet(DXMatrix &view_matrix);
   void              ViewMatrixSet(const DXMatrix &view_matrix);
   void              ViewPositionSet(const DXVector3 &posiiton);
   void              ViewRotationSet(const DXVector3 &rotation);
   void              ViewTargetSet(const DXVector3 &target);
   void              ViewUpDirectionSet(const DXVector3 &up_direction);
   //--- projection control functions
   void              ProjectionMatrixGet(DXMatrix &projection_matrix);
   void              ProjectionMatrixSet(float fov,float aspect_ratio,float z_near,float z_far);
   //--- light control functions
   void              LightDirectionSet(const DXVector3 &light_direction);
   void              LightDirectionGet(DXVector3 &light_direction);
   void              LightColorSet(const DXColor &light_color);
   void              LightColorGet(DXColor &light_color);
   void              AmbientColorSet(const DXColor &ambient_color);
   void              AmbientColorGet(DXColor &ambient_color);
  };

//+------------------------------------------------------------------+
//| Set view matrix                                                  |
//+------------------------------------------------------------------+
void CCanvas3D::ViewMatrixGet(DXMatrix &view_matrix)
  {
   view_matrix=m_view_matrix;
  }
//+------------------------------------------------------------------+
//| Get view matrix                                                  |
//+------------------------------------------------------------------+
void CCanvas3D::ViewMatrixSet(const DXMatrix &view_matrix)
  {
   m_view_matrix=view_matrix;
  }
//+------------------------------------------------------------------+
//| Set point of view position                                       |
//+------------------------------------------------------------------+
void CCanvas3D::ViewPositionSet(const DXVector3 &position)
  {
//--- set position
   m_view_position=position;
//--- update view matrix
   DXMatrixLookAtLH(m_view_matrix,m_view_position,m_view_target,m_view_up_dir);
  }
//+------------------------------------------------------------------+
//| Set point of view position                                       |
//+------------------------------------------------------------------+
void CCanvas3D::ViewRotationSet(const DXVector3 &rotation)
  {
//--- get view rotation matrix
   DXMatrix rot;
   DXMatrixRotationYawPitchRoll(rot,rotation.y,rotation.x,rotation.z);
//--- get direction and up vectors for this matrix
   m_view_target.x=0.0;
   m_view_target.x=0.0;
   m_view_target.x=1.0;
   DXVec3TransformCoord(m_view_target,m_view_target,rot);
//--- get up vector for this matrix
   m_view_up_dir.x=0.0;
   m_view_up_dir.y=1.0;
   m_view_up_dir.y=0.0;
   DXVec3TransformCoord(m_view_up_dir,m_view_up_dir,rot);
//--- calculate far target position
   DXVec3Scale(m_view_target,m_view_target,1000.0);
   DXVec3Add(m_view_target,m_view_position,m_view_target);
//--- update view matrix
   DXMatrixLookAtLH(m_view_matrix,m_view_position,m_view_target,m_view_up_dir);
  }
//+------------------------------------------------------------------+
//| Set view target point                                            |
//+------------------------------------------------------------------+
void CCanvas3D::ViewTargetSet(const DXVector3 &target)
  {
//--- set target
   m_view_target=target;
//--- update view matrix
   DXMatrixLookAtLH(m_view_matrix,m_view_position,m_view_target,m_view_up_dir);
  }
//+------------------------------------------------------------------+
//| Set view up direction                                            |
//+------------------------------------------------------------------+
void CCanvas3D::ViewUpDirectionSet(const DXVector3 &up_direction)
  {
//--- set up direction
   m_view_up_dir=up_direction;
//--- update view matrix
   DXMatrixLookAtLH(m_view_matrix,m_view_position,m_view_target,m_view_up_dir);
  }
//+------------------------------------------------------------------+
//| Get projection matrix                                            |
//+------------------------------------------------------------------+
void CCanvas3D::ProjectionMatrixGet(DXMatrix &projection_matrix)
  {
   projection_matrix=m_projection_matrix;
  }
//+------------------------------------------------------------------+
//| Set projection matrix                                            |
//+------------------------------------------------------------------+
void CCanvas3D::ProjectionMatrixSet(float fov,float aspect_ratio,float z_near,float z_far)
  {
   DXMatrixPerspectiveFovLH(m_projection_matrix,fov,aspect_ratio,z_near,z_far);
  }
//+------------------------------------------------------------------+
//| Set directional light direction                                  |
//+------------------------------------------------------------------+
void CCanvas3D::LightDirectionSet(const DXVector3 &light_direction)
  {
   float len=DXVec3Length(light_direction);
   m_light_directon.x=light_direction.x/len;
   m_light_directon.y=light_direction.y/len;
   m_light_directon.z=light_direction.z/len;
   //--- don't translate the vetor
   m_light_directon.w=0.0;
  }
//+------------------------------------------------------------------+
//| Get directional light direction                                  |
//+------------------------------------------------------------------+
void CCanvas3D::LightDirectionGet(DXVector3 &light_direction)
  {
   light_direction=DXVector3(m_light_directon);
  }
//+------------------------------------------------------------------+
//| Set directional light color                                      |
//+------------------------------------------------------------------+
void CCanvas3D::LightColorSet(const DXColor &light_color)
  {
   m_light_color=light_color;
  }
//+------------------------------------------------------------------+
//| Get directional light color                                      |
//+------------------------------------------------------------------+
void CCanvas3D::LightColorGet(DXColor &light_color)
  {
   light_color=m_light_color;
  }
//+------------------------------------------------------------------+
//| Set ambient light color                                          |
//+------------------------------------------------------------------+  
void CCanvas3D::AmbientColorSet(const DXColor &ambient_color)
  {
   m_ambient_color=ambient_color;
  }
//+------------------------------------------------------------------+
//| Get ambient light color                                          |
//+------------------------------------------------------------------+
void CCanvas3D::AmbientColorGet(DXColor &ambient_color)
  {
   ambient_color=m_ambient_color;
  }
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CCanvas3D::CCanvas3D(void) : m_dx_context(-1)
  {
//--- set identity matrices
   DXMatrixIdentity(m_view_matrix);
   DXMatrixIdentity(m_projection_matrix);
//--- set zero directional light
   m_light_directon=DXVector4(0.0);   
   m_light_color   =DXColor(0.0,0.0,0.0,0.0);
//--- set full white ambient light   
   m_ambient_color =DXColor(1.0,1.0,1.0,1.0);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CCanvas3D::~CCanvas3D(void)
  {
   Destroy();
  }
//+------------------------------------------------------------------+
//| Create dynamic resource with 3D context                          |
//+------------------------------------------------------------------+
bool CCanvas3D::Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt)
  {
//--- create 2D canvas
   if(!CCanvas::Create(name,width,height,clrfmt))
      return(false);
//--- create 3D context
   m_dx_context=DXContextCreate(width,height);
   if(m_dx_context!=INVALID_HANDLE)
      if(m_dx_dispatcher.Create(m_dx_context))
        {
         m_input_scene=m_dx_dispatcher.InputCreate<DXInputScene>();
         if(m_input_scene!=NULL)
           {
            m_input_scene.AddRef();
            return(true);
           }
        }
//---
   Destroy();
   return(false);
  }
//+------------------------------------------------------------------+
//| Remove object from chart and release DX Context                  |
//+------------------------------------------------------------------+
void CCanvas3D::Destroy(void)
  {
//--- destroy 2D canvas
   CCanvas::Destroy();
//--- delete all scene objects
   while(m_dx_objects.Next())
      delete m_dx_objects.Next();
//--- release dx resources
   if(CheckPointer(m_input_scene)!=POINTER_INVALID)
     {
      m_input_scene.Release();
      m_input_scene=NULL;
     }
//--- destroy DX dispatcher
   m_dx_dispatcher.Destroy();
//--- release context
   if(m_dx_context!=-1)
     {
      DXRelease(m_dx_context);
      m_dx_context=-1;
     }
  }
//+------------------------------------------------------------------+
//| Attach new object with bitmap resource                           |
//+------------------------------------------------------------------+
bool CCanvas3D::Attach(const long chart_id,const string objname,ENUM_COLOR_FORMAT clrfmt)
  {
//--- attach to the chart object
   if(!CCanvas::Attach(chart_id,objname,clrfmt))
      return(false);
//--- create new DX Context if it does not exists
   if(m_dx_context==-1)
     {
      m_dx_context=DXContextCreate(m_width,m_height);
      return(m_dx_context!=-1);
     }
//--- DX Context exists, resize it to the new size
   return(DXContextSetSize(m_dx_context,m_width,m_height));
  }
//+------------------------------------------------------------------+
//| Attach new object without bitmap resource                        |
//+------------------------------------------------------------------+
bool CCanvas3D::Attach(const long chart_id,const string objname,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA)
  {
//--- attach to the chart object
   if(!CCanvas::Attach(chart_id,objname,width,height,clrfmt))
      return(false);
//--- create new DX Context if it does not exists
   if(m_dx_context==-1)
     {
      m_dx_context=DXContextCreate(m_width,m_height);
      return(m_dx_context!=-1);
     }
//--- DX Context exists, resize it to the new size
   return(DXContextSetSize(m_dx_context,m_width,m_height));
  }
//+------------------------------------------------------------------+
//| Rendering 3D scene                                               |
//+------------------------------------------------------------------+
bool CCanvas3D::Render(uint flags,uint background_color)
  {
//--- start rendering
   if(!RenderBegin(flags,background_color))
      return(false);
//--- pass all non-skipped objects to render
   for(CDXObject *object=m_dx_objects.Next(); CheckPointer(object)!=POINTER_INVALID; object=object.Next())
      object.Render();
//--- finish the render
   return(RenderEnd(false));
  }
//+------------------------------------------------------------------+
//| Start 3D render                                                  |
//+------------------------------------------------------------------+
bool CCanvas3D::RenderBegin(uint flags,uint background_color)
  {
//--- check resource dispatcher  
   m_dx_dispatcher.Check();
//--- clear color buffer
   if((flags&DX_CLEAR_COLOR)!=0)
     {
      DXVector v;
      v.x=GETRGBR(background_color)/255.0f;
      v.y=GETRGBG(background_color)/255.0f;
      v.z=GETRGBB(background_color)/255.0f;
      v.w=GETRGBA(background_color)/255.0f;
      if(!DXContextClearColors(m_dx_context,v))
         return(false);
     }
//--- clear depth buffer
   if((flags&DX_CLEAR_DEPTH)!=0)
      if(!DXContextClearDepth(m_dx_context))
         return(false);
//--- update scene input
   DXInputScene input_data;
   DXMatrixTranspose(input_data.view,      m_view_matrix);
   DXMatrixTranspose(input_data.projection,m_projection_matrix);
   DXVec4Transform(input_data.light_direction,m_light_directon,m_view_matrix);
   input_data.light_color  =m_light_color;
   input_data.ambient_color=m_ambient_color;
//--- set input   
   if(!m_input_scene.InputSet(input_data))
      return(false);
//--- succes
   return(true);
  }
//+------------------------------------------------------------------+
//| Finish 3D render                                                 |
//+------------------------------------------------------------------+
bool CCanvas3D::RenderEnd(bool redraw)
  {
//--- finish render
   if(!DXContextGetColors(m_dx_context,m_pixels))
      return(false);
//--- update canvas 2D if needed
   if(redraw)
      CCanvas::Update(redraw);
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Add user 3D object                                               |
//+------------------------------------------------------------------+
bool CCanvas3D::ObjectAdd(CDXObject *object)
  {
//--- add user object
   if(!CheckPointer(object))
      return(false);
//---
   CDXObjectBase *last=&m_dx_objects;

   while(CheckPointer(last.Next())!=POINTER_INVALID)
     {
      if(last==object)
         return(false);
      //---
      last=last.Next();
     }

   object.Next(NULL);
   object.Prev(last);
   last.Next(object);
   return(true);
  }
//+------------------------------------------------------------------+
