//#####################################################################
// Copyright 2020, Craig Schroeder.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################

#include <Core/Grids/GRID.h>
#include <Core/Log/LOG.h>
#include <Core/Matrices/DIAGONAL_MATRIX.h>
#include <Core/Matrices/MATRIX.h>
#include <Core/Meshes/MESH_OBJECT.h>
#include <Core/Vectors/VECTOR.h>
#include "QT_CAMERA.h"
#include "QT_MATH.h"
#include "QT_PICK.h"
#include "QT_RENDER_UTILS.h"
#include "QT_SHADER.h"
#include "QT_SHADER_FLAGS.h"
#include "QT_WINDOW.h"
#include <QOpenGLBuffer>
#include <QOpenGLContext>
#include <QOpenGLDebugMessage>
#include <QOpenGLFunctions>
#include <QOpenGLPaintDevice>
#include <QOpenGLShader>
#include <QOpenGLVertexArrayObject>
#include <QPainter>
#include <QScreen>

namespace PhysBAM{
//#####################################################################
// Constructor
//#####################################################################
QT_RENDER_UTILS::
QT_RENDER_UTILS(QT_WINDOW* window,QOpenGLFunctions& gl,const QT_CAMERA& camera)
    :window(window),camera(camera),gl(gl)
{
    logger = new QOpenGLDebugLogger(window);
    logger->initialize();
    logger->disableMessages(QOpenGLDebugMessage::AnySource,
        QOpenGLDebugMessage::AnyType,
        QOpenGLDebugMessage::NotificationSeverity);
    camera_light_pos[0]={0.3f,0.3f,1};
    camera_light_pos[1]={-0.3f,0.3f,1};
}
//#####################################################################
// Destructor
//#####################################################################
QT_RENDER_UTILS::
~QT_RENDER_UTILS()
{
    for(auto p:shaders) delete p.second;
}
//#####################################################################
// Function Dump_GL_Errors
//#####################################################################
void QT_RENDER_UTILS::
Dump_GL_Errors(const char* file,int line) const
{
    const QList<QOpenGLDebugMessage> messages = logger->loggedMessages();
    if(messages.empty()) return;
    LOG::printf("file: %s;   line %i;  error: ",file,line);
    for (const QOpenGLDebugMessage &message : messages)
        qDebug() << message;
}
//#####################################################################
// Function Render_Triangle
//#####################################################################
template<class TV> void QT_RENDER_UTILS::
Render_Triangle(const TV& A,const TV& B,const TV& C,
    const RU_COLOR& color,OPT_TRI<typename TV::SCALAR> opt)
{
    Render_Triangles<TV>(VECTOR<TV,3>(A,B,C),color,opt);
}
//#####################################################################
// Function Render_Triangles
//#####################################################################
// type=0 -> 3 pts per triangle (GL_TRIANGLES)
// type=1 -> fan (GL_TRIANGLE_FAN)
// type=2 -> strip (GL_TRIANGLE_STRIP)
template<class TV> void QT_RENDER_UTILS::
Render_Triangles(ARRAY_VIEW<const TV> pts,
    const RU_COLOR& color,OPT_TRI<typename TV::SCALAR> opt)
{
    if(opt.line_width<0) opt.line_width=line_width;
    SHADER_FLAGS flags{2,2-(wireframe||!opt.fill),color.Flags(),
        opt.bgcolor.Flags(),select,TV::m==2,TV::m};
    SHADER::SH sh(Get_Shader(flags),*this,flags);
    SHADER::ATTR pos(sh,"pos",pts);
    SHADER::ATTR col(sh,"col",color);
    SHADER::ATTR bgcol(sh,"bgcol",opt.bgcolor);

    glLineWidth(opt.line_width*(1+select));
    auto tri_type=opt.type==0?GL_TRIANGLES:(opt.type==1?GL_TRIANGLE_FAN:GL_TRIANGLE_STRIP);
    glDrawArrays(tri_type,0,pts.m);
    if(select) pick.Commit_Names(opt.type==0?pts.m/3:pts.m-2);
    CHECK_ERRORS(this);
}
//#####################################################################
// Function Render_Mesh
//#####################################################################
template<class TV,int d> void QT_RENDER_UTILS::
Render_Mesh(const MESH_OBJECT<TV,d>& obj,
    const RU_COLOR& color,OPT_TRI<typename TV::SCALAR> opt)
{
    if(opt.line_width<0) opt.line_width=line_width;
    if constexpr(d==3)
        Render_Mesh(*obj.triangulated_surface,color,opt);
    else
    {
        SHADER_FLAGS flags{d,2-(wireframe||!opt.fill||d==1),color.Flags(),
            opt.bgcolor.Flags(),select,TV::m==2||d==1,TV::m};
        SHADER::SH sh(Get_Shader(flags),*this,flags);
        SHADER::ATTR pos(sh,"pos",obj.particles.X);
        SHADER::ATTR col(sh,"col",color);
        SHADER::ATTR bgcol(sh,"bgcol",opt.bgcolor);
        glLineWidth(opt.line_width*(1+select));

        QOpenGLBuffer ib(QOpenGLBuffer::IndexBuffer);
        PHYSBAM_ASSERT(ib.create());
        PHYSBAM_ASSERT(ib.bind());
        ib.setUsagePattern(QOpenGLBuffer::StaticDraw);
        auto e=obj.mesh.elements.Flattened();
        ib.allocate(e.Get_Array_Pointer(),e.m*sizeof(int));
        glDrawElements(d==2?GL_TRIANGLES:GL_LINES,e.m,GL_UNSIGNED_INT,0);
        if(select) pick.Commit_Names(obj.mesh.elements.m);
    }
    CHECK_ERRORS(this);
}
//#####################################################################
// Function Render_Line
//#####################################################################
template<class TV> void QT_RENDER_UTILS::
Render_Line(const TV& A,const TV& B,
    const RU_COLOR& color,OPT_LINE<typename TV::SCALAR> opt)
{
    Render_Lines<TV>(VECTOR<TV,2>(A,B),color,opt);
}
//#####################################################################
// Function Render_Lines
//#####################################################################
// type=0 -> 2 pts per line (GL_LINES)
// type=1 -> connected lines (GL_LINE_STRIP)
// type=2 -> line loop (GL_LINE_LOOP)
template<class TV> void QT_RENDER_UTILS::
Render_Lines(ARRAY_VIEW<const TV> pts,
    const RU_COLOR& color,OPT_LINE<typename TV::SCALAR> opt)
{
    if(opt.line_width<0) opt.line_width=line_width;
    SHADER_FLAGS flags{1,1,color.Flags(),0,select,true,TV::m};
    SHADER::SH sh(Get_Shader(flags),*this,flags);
    SHADER::ATTR pos(sh,"pos",pts);
    SHADER::ATTR col(sh,"col",color);

    glLineWidth(opt.line_width*(1+select));
    auto line_type=opt.type==0?GL_LINES:(opt.type==1?GL_LINE_STRIP:GL_LINE_LOOP);
    glDrawArrays(line_type,0,pts.m);
    if(select) pick.Commit_Names(opt.type==0?pts.m/2:(opt.type==1?pts.m-1:pts.m));
    CHECK_ERRORS(this);
}
//#####################################################################
// Function Render_Point
//#####################################################################
template<class TV> void QT_RENDER_UTILS::
Render_Point(const TV& pt,const RU_COLOR& color,
    OPT_POINT<typename TV::SCALAR> opt)
{
    Render_Points<TV>(VECTOR<TV,1>(pt),color,opt);
}
//#####################################################################
// Function Render_Points
//#####################################################################
template<class TV> void QT_RENDER_UTILS::
Render_Points(ARRAY_VIEW<const TV> pts,const RU_COLOR& color,
    OPT_POINT<typename TV::SCALAR> opt)
{
    if(opt.point_size<0) opt.point_size=point_size;
    if(select) opt.point_size=max(opt.point_size,(typename TV::SCALAR)(point_size*1.5));
    SHADER_FLAGS flags{0,0,color.Flags(),0,select,true,TV::m};
    SHADER::SH sh(Get_Shader(flags),*this,flags);
    SHADER::ATTR pos(sh,"pos",pts);
    SHADER::ATTR col(sh,"col",color);

    glPointSize(opt.point_size);
    glDrawArrays(GL_POINTS,0,pts.m);
    if(select) pick.Commit_Names(pts.m);
    CHECK_ERRORS(this);
}
//#####################################################################
// Function Render_Text
//#####################################################################
template<class TV> void QT_RENDER_UTILS::
Render_Text(const TV& pt,const std::string& text,const RU_COLOR& color)
{
    typedef typename TV::SCALAR T;
    VECTOR<float,4> X(0,0,0,1);
    for(int i=0;i<TV::m;i++) X(i)=pt(i);
    auto Y=world_to_gl*(obj_to_world*X);
    int w=window->width();
    int h=window->height();
    int x=(1+Y(0)/Y(3))/2*w;
    int y=(1-Y(1)/Y(3))/2*h;
    
    QPainter painter(window);
    painter.setPen(to_qt_color(color.color));
    painter.setFont(QFont("Helvetica", 12));
    painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
    painter.drawText(x+5,y-5,to_qt(text)); // z = pointT4.z + distOverOp / 4
    painter.end();
}
//#####################################################################
// Function Render_Arrow
//#####################################################################
template<class TV> void QT_RENDER_UTILS::
Render_Arrow(const TV& X,const TV& dX,const RU_COLOR& color,
    OPT_ARROW<typename TV::SCALAR> opt)
{
    Render_Arrows<TV>(VECTOR<TV,1>(X),VECTOR<TV,1>(dX),color,opt);
}
//#####################################################################
// Function Render_Arrows
//#####################################################################
template<class TV> void QT_RENDER_UTILS::
Render_Arrows(ARRAY_VIEW<const TV> X,ARRAY_VIEW<const TV> dX,
    const RU_COLOR& color,OPT_ARROW<typename TV::SCALAR> opt)
{
    if(opt.line_width<0) opt.line_width=line_width;
    if(opt.scale<0) opt.scale=scale;
    SHADER_FLAGS flags{0,1,color.Flags(),0,select,true,TV::m,opt.use_arrowhead?SHADER_TYPE::arrow:SHADER_TYPE::segment};
    SHADER::SH sh(Get_Shader(flags),*this,flags);
    SHADER::ATTR pos(sh,"pos",X);
    SHADER::ATTR dpos(sh,"dpos",dX);
    SHADER::ATTR col(sh,"col",color);
    sh.Set_Uniform("scale",(float)opt.scale);

    glLineWidth(opt.line_width*(1+select));
    glDrawArrays(GL_POINTS,0,X.m);
    if(select) pick.Commit_Names(X.m);
    CHECK_ERRORS(this);
}
//#####################################################################
// Function Render_Sphere
//#####################################################################
template<class TV> void QT_RENDER_UTILS::
Render_Sphere(const TV& X,typename TV::SCALAR radius,const RU_COLOR& color,
    OPT_SPHERE<typename TV::SCALAR> opt)
{
    Render_Spheres<TV>(VECTOR<TV,1>(X),VECTOR<typename TV::SCALAR,1>(radius),color,opt);
}
//#####################################################################
// Function Render_Spheres
//#####################################################################
template<class TV> void QT_RENDER_UTILS::
Render_Spheres(ARRAY_VIEW<const TV> X,ARRAY_VIEW<const typename TV::SCALAR> radius,
    const RU_COLOR& color,OPT_SPHERE<typename TV::SCALAR> opt)
{
    if(opt.line_width<0) opt.line_width=line_width;
    if(opt.scale<0) opt.scale=scale;
    SHADER_FLAGS flags{0,2-(wireframe||!opt.fill),color.Flags(),
        opt.bgcolor.Flags(),select,TV::m==2,TV::m,SHADER_TYPE::sphere};
    SHADER::SH sh(Get_Shader(flags),*this,flags);
    SHADER::ATTR pos(sh,"pos",X);
    SHADER::ATTR col(sh,"col",color);
    SHADER::ATTR rad(sh,"radius",radius);
    sh.Set_Uniform("scale",(float)opt.scale);

    glLineWidth(opt.line_width*(1+select));
    glDrawArrays(GL_POINTS,0,X.m);
    if(select) pick.Commit_Names(X.m);
    CHECK_ERRORS(this);
}
//#####################################################################
// Function Render_Box
//#####################################################################
template<class TV> void QT_RENDER_UTILS::
Render_Box(const TV& X,const TV& size,const RU_COLOR& color,OPT_TRI<typename TV::SCALAR> opt)
{
    Render_Boxes<TV>(VECTOR<TV,1>(X),size,color,opt);
}
//#####################################################################
// Function Render_Box
//#####################################################################
template<class TV> void QT_RENDER_UTILS::
Render_Box(const RANGE<TV>& range,const RU_COLOR& color,
    OPT_TRI<typename TV::SCALAR> opt)
{
    Render_Boxes<TV>(VECTOR<TV,1>(range.Center()),range.Edge_Lengths(),color,opt);
}
//#####################################################################
// Function Render_Boxes
//#####################################################################
template<class TV> void QT_RENDER_UTILS::
Render_Boxes(ARRAY_VIEW<const TV> X,const TV& size,const RU_COLOR& color,
    OPT_TRI<typename TV::SCALAR> opt)
{
    typedef typename TV::SCALAR T;
    if(opt.line_width<0) opt.line_width=line_width;
    SHADER_FLAGS flags{0,2-(wireframe||!opt.fill),color.Flags(),
        opt.bgcolor.Flags(),select,TV::m==2,TV::m,
        opt.type==1?SHADER_TYPE::diamond:SHADER_TYPE::box};
    SHADER::SH sh(Get_Shader(flags),*this,flags);
    SHADER::ATTR pos(sh,"pos",X);
    SHADER::ATTR col(sh,"col",color);
    MATRIX<float,TV::m> SR;
    obj_to_world.Get_Submatrix(0,0,SR);
    auto box_edges=MATRIX<T,TV::m>(SR)*DIAGONAL_MATRIX<T,TV::m>(size);
    sh.Set_Uniform("box_edges",to_qt(box_edges));

    glLineWidth(opt.line_width*(1+select));
    glDrawArrays(GL_POINTS,0,X.m);
    if(select) pick.Commit_Names(X.m);
    CHECK_ERRORS(this);
}
//#####################################################################
// Function Render_Diamonds
//#####################################################################
template<class TV> void QT_RENDER_UTILS::
Render_Diamonds(ARRAY_VIEW<const TV> X,const TV& size,const RU_COLOR& color,
    OPT_TRI<typename TV::SCALAR> opt)
{
    opt.type=1;
    Render_Boxes(X,size,color,opt);
}
//#####################################################################
// Function Render_Regular_Poly
//#####################################################################
template<class TV> void QT_RENDER_UTILS::
Render_Regular_Poly(const TV& X,typename TV::SCALAR radius,int sides,
    const RU_COLOR& color,OPT_REG_POLY<typename TV::SCALAR> opt)
{
    Render_Regular_Polys<TV>(VECTOR<TV,1>(X),
        VECTOR<typename TV::SCALAR,1>(radius),sides,color,opt);
}
//#####################################################################
// Function Render_Regular_Poly
//#####################################################################
template<class TV> void QT_RENDER_UTILS::
Render_Regular_Polys(ARRAY_VIEW<const TV> X,ARRAY_VIEW<const typename TV::SCALAR> radius,
    int sides,const RU_COLOR& color,OPT_REG_POLY<typename TV::SCALAR> opt)
{
    if constexpr(TV::m==3)
        Render_Spheres(X,radius,color,{opt.bgcolor,opt.line_width,opt.scale,opt.fill});
    else
    {
        if(opt.line_width<0) opt.line_width=line_width;
        if(opt.scale<0) opt.scale=scale;
        SHADER_FLAGS flags{0,2-(wireframe||!opt.fill),color.Flags(),
            0,select,TV::m==2,TV::m,SHADER_TYPE::regular_poly};
        SHADER::SH sh(Get_Shader(flags),*this,flags);
        SHADER::ATTR pos(sh,"pos",X);
        SHADER::ATTR col(sh,"col",color);
        SHADER::ATTR rad(sh,"radius",radius);
        sh.Set_Uniform("scale",(float)opt.scale);
        sh.Set_Uniform("num_sides",sides);
        sh.Set_Uniform("initial_rotate",opt.angle);

        glLineWidth(opt.line_width*(1+select));
        glDrawArrays(GL_POINTS,0,X.m);
        if(select) pick.Commit_Names(X.m);
        CHECK_ERRORS(this);
    }
}
//#####################################################################
// Function Update_Transform
//#####################################################################
void QT_RENDER_UTILS::
Update_Transform()
{
    world_to_gl=camera.Transform();
    obj_to_world=MAT()+1;
    obj_to_world_scale=1;
    for(int i=0;i<2;i++)
        world_light_pos[i]=camera.Camera_Frame_To_World(camera_light_pos[i]);
}
//#####################################################################
// Function Reset
//#####################################################################
void QT_RENDER_UTILS::
Reset()
{
    pick.Reset();
    info_text.clear();
    info_text.str(std::string());
    select=false;
}
//#####################################################################
// Function Set_Clipping_Planes
//#####################################################################
void QT_RENDER_UTILS::
Set_Clipping_Planes()
{
    if(!slice_grid || slice_axis<0 || slice_axis>=3) return;

    FV n=FV::Axis_Vector(slice_axis);
    FV x=slice_grid->Node_X(IV().Add_Axis(slice_axis,slice_index));
    FV y=slice_grid->Node_X(IV().Add_Axis(slice_axis,slice_index+1));
    // (p-x).Dot(n)=0;
    // p.Dot(n)-x.Dot(n)=0;
    clip_plane0=n.Append(-x.Dot(n));
    clip_plane1=n.Append(-y.Dot(n));
    

// GRID<FV>* slice_grid=nullptr;
    // int slice_axis=0;
    // int slice_index=0;
}
//#####################################################################
// Function Get_Shader
//#####################################################################
const SHADER& QT_RENDER_UTILS::
Get_Shader(SHADER_FLAGS flags)
{
    flags.Simplify();
    SHADER*& s=shaders[flags];
    if(!s) s=new SHADER(window,flags);
    return *s;
}
//#####################################################################
// Function Render_Matrices
//#####################################################################
template<class TV> void QT_RENDER_UTILS::
Render_Matrices(ARRAY_VIEW<const TV> X,
    ARRAY_VIEW<const MATRIX<typename TV::SCALAR,TV::m>> M,
    OPT_MATRIX<typename TV::SCALAR> opt)
{
    if(opt.line_width<0) opt.line_width=line_width;
    if(opt.scale<0) opt.scale=scale;
    SHADER_FLAGS flags{0,1,2,0,select,true,TV::m,SHADER_TYPE::matrix};
    SHADER::SH sh(Get_Shader(flags),*this,flags);
    SHADER::ATTR pos(sh,"pos",X);
    SHADER::ATTR mat0(sh,TV::m>0?"mat0":nullptr,M,0);
    SHADER::ATTR mat1(sh,TV::m>1?"mat1":nullptr,M,1);
    SHADER::ATTR mat2(sh,TV::m>2?"mat2":nullptr,M,2);
    SHADER::ATTR mat3(sh,TV::m>3?"mat3":nullptr,M,3);
    sh.Set_Uniform("scale",(float)opt.scale);

    glLineWidth(opt.line_width*(1+select));
    glDrawArrays(GL_POINTS,0,X.m);
    if(select) pick.Commit_Names(X.m);
    CHECK_ERRORS(this);
}
template void QT_RENDER_UTILS::Render_Arrow<VECTOR<double,2> >(VECTOR<double,2> const&,VECTOR<double,2> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_ARROW<VECTOR<double,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Arrow<VECTOR<double,3> >(VECTOR<double,3> const&,VECTOR<double,3> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_ARROW<VECTOR<double,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Arrow<VECTOR<float,2> >(VECTOR<float,2> const&,VECTOR<float,2> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_ARROW<VECTOR<float,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Arrow<VECTOR<float,3> >(VECTOR<float,3> const&,VECTOR<float,3> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_ARROW<VECTOR<float,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Arrows<VECTOR<double,2> >(ARRAY_VIEW<VECTOR<double,2> const,int>,ARRAY_VIEW<VECTOR<double,2> const,int>,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_ARROW<VECTOR<double,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Arrows<VECTOR<double,3> >(ARRAY_VIEW<VECTOR<double,3> const,int>,ARRAY_VIEW<VECTOR<double,3> const,int>,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_ARROW<VECTOR<double,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Arrows<VECTOR<float,2> >(ARRAY_VIEW<VECTOR<float,2> const,int>,ARRAY_VIEW<VECTOR<float,2> const,int>,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_ARROW<VECTOR<float,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Arrows<VECTOR<float,3> >(ARRAY_VIEW<VECTOR<float,3> const,int>,ARRAY_VIEW<VECTOR<float,3> const,int>,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_ARROW<VECTOR<float,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Box<VECTOR<double,2> >(RANGE<VECTOR<double,2> > const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<double,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Box<VECTOR<double,3> >(RANGE<VECTOR<double,3> > const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<double,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Box<VECTOR<float,2> >(RANGE<VECTOR<float,2> > const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<float,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Box<VECTOR<float,3> >(RANGE<VECTOR<float,3> > const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<float,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Line<VECTOR<double,2> >(VECTOR<double,2> const&,VECTOR<double,2> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_LINE<VECTOR<double,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Line<VECTOR<double,3> >(VECTOR<double,3> const&,VECTOR<double,3> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_LINE<VECTOR<double,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Line<VECTOR<float,2> >(VECTOR<float,2> const&,VECTOR<float,2> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_LINE<VECTOR<float,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Line<VECTOR<float,3> >(VECTOR<float,3> const&,VECTOR<float,3> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_LINE<VECTOR<float,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Lines<VECTOR<double,2> >(ARRAY_VIEW<VECTOR<double,2> const,int>,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_LINE<VECTOR<double,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Lines<VECTOR<double,3> >(ARRAY_VIEW<VECTOR<double,3> const,int>,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_LINE<VECTOR<double,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Lines<VECTOR<float,2> >(ARRAY_VIEW<VECTOR<float,2> const,int>,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_LINE<VECTOR<float,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Lines<VECTOR<float,3> >(ARRAY_VIEW<VECTOR<float,3> const,int>,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_LINE<VECTOR<float,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Mesh<VECTOR<double,2>,1>(MESH_OBJECT<VECTOR<double,2>,1> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<double,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Mesh<VECTOR<double,2>,2>(MESH_OBJECT<VECTOR<double,2>,2> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<double,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Mesh<VECTOR<double,3>,1>(MESH_OBJECT<VECTOR<double,3>,1> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<double,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Mesh<VECTOR<double,3>,2>(MESH_OBJECT<VECTOR<double,3>,2> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<double,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Mesh<VECTOR<float,2>,1>(MESH_OBJECT<VECTOR<float,2>,1> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<float,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Mesh<VECTOR<float,2>,2>(MESH_OBJECT<VECTOR<float,2>,2> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<float,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Mesh<VECTOR<float,3>,1>(MESH_OBJECT<VECTOR<float,3>,1> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<float,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Mesh<VECTOR<float,3>,2>(MESH_OBJECT<VECTOR<float,3>,2> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<float,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Point<VECTOR<double,2> >(VECTOR<double,2> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_POINT<VECTOR<double,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Point<VECTOR<double,3> >(VECTOR<double,3> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_POINT<VECTOR<double,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Point<VECTOR<float,2> >(VECTOR<float,2> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_POINT<VECTOR<float,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Point<VECTOR<float,3> >(VECTOR<float,3> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_POINT<VECTOR<float,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Points<VECTOR<double,2> >(ARRAY_VIEW<VECTOR<double,2> const,int>,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_POINT<VECTOR<double,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Points<VECTOR<double,3> >(ARRAY_VIEW<VECTOR<double,3> const,int>,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_POINT<VECTOR<double,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Points<VECTOR<float,2> >(ARRAY_VIEW<VECTOR<float,2> const,int>,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_POINT<VECTOR<float,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Points<VECTOR<float,3> >(ARRAY_VIEW<VECTOR<float,3> const,int>,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_POINT<VECTOR<float,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Regular_Poly<VECTOR<double,2> >(VECTOR<double,2> const&,VECTOR<double,2>::SCALAR,int,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_REG_POLY<VECTOR<double,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Regular_Poly<VECTOR<double,3> >(VECTOR<double,3> const&,VECTOR<double,3>::SCALAR,int,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_REG_POLY<VECTOR<double,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Regular_Poly<VECTOR<float,2> >(VECTOR<float,2> const&,VECTOR<float,2>::SCALAR,int,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_REG_POLY<VECTOR<float,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Regular_Poly<VECTOR<float,3> >(VECTOR<float,3> const&,VECTOR<float,3>::SCALAR,int,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_REG_POLY<VECTOR<float,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Sphere<VECTOR<double,2> >(VECTOR<double,2> const&,VECTOR<double,2>::SCALAR,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_SPHERE<VECTOR<double,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Sphere<VECTOR<double,3> >(VECTOR<double,3> const&,VECTOR<double,3>::SCALAR,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_SPHERE<VECTOR<double,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Sphere<VECTOR<float,2> >(VECTOR<float,2> const&,VECTOR<float,2>::SCALAR,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_SPHERE<VECTOR<float,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Sphere<VECTOR<float,3> >(VECTOR<float,3> const&,VECTOR<float,3>::SCALAR,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_SPHERE<VECTOR<float,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Spheres<VECTOR<double,2> >(ARRAY_VIEW<VECTOR<double,2> const,int>,ARRAY_VIEW<VECTOR<double,2>::SCALAR const,int>,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_SPHERE<VECTOR<double,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Spheres<VECTOR<double,3> >(ARRAY_VIEW<VECTOR<double,3> const,int>,ARRAY_VIEW<VECTOR<double,3>::SCALAR const,int>,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_SPHERE<VECTOR<double,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Spheres<VECTOR<float,2> >(ARRAY_VIEW<VECTOR<float,2> const,int>,ARRAY_VIEW<VECTOR<float,2>::SCALAR const,int>,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_SPHERE<VECTOR<float,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Spheres<VECTOR<float,3> >(ARRAY_VIEW<VECTOR<float,3> const,int>,ARRAY_VIEW<VECTOR<float,3>::SCALAR const,int>,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_SPHERE<VECTOR<float,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Text<VECTOR<double,2> >(VECTOR<double,2> const&,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char> > const&,QT_RENDER_UTILS::RU_COLOR const&);
template void QT_RENDER_UTILS::Render_Text<VECTOR<double,3> >(VECTOR<double,3> const&,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char> > const&,QT_RENDER_UTILS::RU_COLOR const&);
template void QT_RENDER_UTILS::Render_Text<VECTOR<float,2> >(VECTOR<float,2> const&,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char> > const&,QT_RENDER_UTILS::RU_COLOR const&);
template void QT_RENDER_UTILS::Render_Text<VECTOR<float,3> >(VECTOR<float,3> const&,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char> > const&,QT_RENDER_UTILS::RU_COLOR const&);
template void QT_RENDER_UTILS::Render_Triangle<VECTOR<double,2> >(VECTOR<double,2> const&,VECTOR<double,2> const&,VECTOR<double,2> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<double,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Triangle<VECTOR<double,3> >(VECTOR<double,3> const&,VECTOR<double,3> const&,VECTOR<double,3> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<double,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Triangle<VECTOR<float,2> >(VECTOR<float,2> const&,VECTOR<float,2> const&,VECTOR<float,2> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<float,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Triangle<VECTOR<float,3> >(VECTOR<float,3> const&,VECTOR<float,3> const&,VECTOR<float,3> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<float,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Triangles<VECTOR<double,2> >(ARRAY_VIEW<VECTOR<double,2> const,int>,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<double,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Triangles<VECTOR<double,3> >(ARRAY_VIEW<VECTOR<double,3> const,int>,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<double,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Triangles<VECTOR<float,2> >(ARRAY_VIEW<VECTOR<float,2> const,int>,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<float,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Triangles<VECTOR<float,3> >(ARRAY_VIEW<VECTOR<float,3> const,int>,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<float,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Boxes<VECTOR<double,2> >(ARRAY_VIEW<VECTOR<double,2> const,int>,VECTOR<double,2> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<double,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Boxes<VECTOR<double,3> >(ARRAY_VIEW<VECTOR<double,3> const,int>,VECTOR<double,3> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<double,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Boxes<VECTOR<float,2> >(ARRAY_VIEW<VECTOR<float,2> const,int>,VECTOR<float,2> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<float,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Boxes<VECTOR<float,3> >(ARRAY_VIEW<VECTOR<float,3> const,int>,VECTOR<float,3> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<float,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Matrices<VECTOR<double,2> >(ARRAY_VIEW<VECTOR<double,2> const,int>,ARRAY_VIEW<MATRIX<VECTOR<double,2>::SCALAR,VECTOR<double,2>::m,VECTOR<double,2>::m> const,int>,QT_RENDER_UTILS::OPT_MATRIX<VECTOR<double,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Matrices<VECTOR<double,3> >(ARRAY_VIEW<VECTOR<double,3> const,int>,ARRAY_VIEW<MATRIX<VECTOR<double,3>::SCALAR,VECTOR<double,3>::m,VECTOR<double,3>::m> const,int>,QT_RENDER_UTILS::OPT_MATRIX<VECTOR<double,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Matrices<VECTOR<float,2> >(ARRAY_VIEW<VECTOR<float,2> const,int>,ARRAY_VIEW<MATRIX<VECTOR<float,2>::SCALAR,VECTOR<float,2>::m,VECTOR<float,2>::m> const,int>,QT_RENDER_UTILS::OPT_MATRIX<VECTOR<float,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Matrices<VECTOR<float,3> >(ARRAY_VIEW<VECTOR<float,3> const,int>,ARRAY_VIEW<MATRIX<VECTOR<float,3>::SCALAR,VECTOR<float,3>::m,VECTOR<float,3>::m> const,int>,QT_RENDER_UTILS::OPT_MATRIX<VECTOR<float,3>::SCALAR>);
template void QT_RENDER_UTILS::Render_Diamonds<VECTOR<float,2> >(ARRAY_VIEW<VECTOR<float,2> const,int>,VECTOR<float,2> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<float,2>::SCALAR>);
template void QT_RENDER_UTILS::Render_Diamonds<VECTOR<float,3> >(ARRAY_VIEW<VECTOR<float,3> const,int>,VECTOR<float,3> const&,QT_RENDER_UTILS::RU_COLOR const&,QT_RENDER_UTILS::OPT_TRI<VECTOR<float,3>::SCALAR>);
}
