//#####################################################################
// Copyright 2021, 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/Log/DEBUG_UTILITIES.h>
#include "QT_GEOMETRY_SHADER.h"
#include "QT_SHADER_FLAGS.h"
#include <string>
namespace PhysBAM{

static std::string Generate_Geometry_Shader_Arrows(SHADER_FLAGS flags)
{
    bool c=flags.col==2;
    bool a=flags.special==SHADER_TYPE::arrow;
    bool C=a&&c;
    std::string x;

    if(flags.dim==3 && a) PHYSBAM_WARNING("Arrows are not properly implemented in 3D");
    
    if(1) x+="#version 420                                              \n";
    if(1) x+="layout(points) in;                                        \n";
    if(1) x+="layout(line_strip,max_vertices=5) out;                    \n";
    if(1) x+="                                                          \n";
    if(1) x+="in vec4 gs_world_pos[];                                   \n";
    if(1) x+="in vec3 gs_dpos[];                                        \n";
    if(c) x+="in vec3 gs_col[];                                         \n";
    if(1) x+="                                                          \n";
    if(1) x+="out vec4 world_pos;                                       \n";
    if(c) x+="out vec3 col;                                             \n";
    if(1) x+="                                                          \n";
    if(1) x+="uniform mat4 obj_to_world;                                \n";
    if(1) x+="uniform mat4 world_to_gl;                                 \n";
    if(1) x+="uniform float scale;                                      \n";
    if(1) x+="uniform vec4 slice_plane_0;                               \n";
    if(1) x+="uniform vec4 slice_plane_1;                               \n";
    if(a) x+="float arrowhead_size=0.3;                                 \n";
    if(a) x+="float arrowhead_angle=0.3;                                \n";
    if(a) x+="float cs=cos(arrowhead_angle)*arrowhead_size;             \n";
    if(a) x+="float sn=sin(arrowhead_angle)*arrowhead_size;             \n";
    if(1) x+="                                                          \n";
    if(1) x+="void main(void)                                           \n";
    if(1) x+="{                                                         \n";
    if(a) x+="    mat4 rot=mat4(1.0);                                   \n";
    if(a) x+="    rot[0][0]=1-cs;                                       \n";
    if(a) x+="    rot[0][1]=  sn;                                       \n";
    if(a) x+="    rot[1][0]= -sn;                                       \n";
    if(a) x+="    rot[1][1]=1-cs;                                       \n";
    if(a) x+="                                                          \n";
    if(1) x+="    vec4 A = gs_world_pos[0];                             \n";
    if(1) x+="    vec4 dA = obj_to_world*vec4(gs_dpos[0]*scale,0);      \n";
    if(1) x+="    vec4 B = A+dA;                                        \n";
    if(a) x+="    vec4 C = A+rot*dA;                                    \n";
    if(a) x+="    vec4 D = A+transpose(rot)*dA;                         \n";
    if(1) x+="    vec4 AG = gl_in[0].gl_Position;                       \n";
    if(1) x+="    vec4 BG = world_to_gl * B;                            \n";
    if(a) x+="    vec4 CG = world_to_gl * C;                            \n";
    if(a) x+="    vec4 DG = world_to_gl * D;                            \n";
    if(1) x+="                                                          \n";
    if(1) x+="    world_pos = A;                                        \n";
    if(1) x+="    gl_Position = AG;                                     \n";
    if(1) x+="    gl_ClipDistance[0] = dot(world_pos,slice_plane_0);    \n";
    if(1) x+="    gl_ClipDistance[1] = -dot(world_pos,slice_plane_1);   \n";
    if(1) x+="    gl_PrimitiveID = gl_PrimitiveIDIn;                    \n";
    if(c) x+="    col = gs_col[0];                                      \n";
    if(1) x+="    EmitVertex();                                         \n";
    if(1) x+="                                                          \n";
    if(1) x+="    world_pos = B;                                        \n";
    if(1) x+="    gl_Position = BG;                                     \n";
    if(1) x+="    gl_ClipDistance[0] = dot(world_pos,slice_plane_0);    \n";
    if(1) x+="    gl_ClipDistance[1] = -dot(world_pos,slice_plane_1);   \n";
    if(1) x+="    gl_PrimitiveID = gl_PrimitiveIDIn;                    \n";
    if(c) x+="    col = gs_col[0];                                      \n";
    if(1) x+="    EmitVertex();                                         \n";
    if(1) x+="    EndPrimitive();                                       \n";
    if(a) x+="                                                          \n";
    if(a) x+="    world_pos = C;                                        \n";
    if(a) x+="    gl_Position = CG;                                     \n";
    if(a) x+="    gl_ClipDistance[0] = dot(world_pos,slice_plane_0);    \n";
    if(a) x+="    gl_ClipDistance[1] = -dot(world_pos,slice_plane_1);   \n";
    if(a) x+="    gl_PrimitiveID = gl_PrimitiveIDIn;                    \n";
    if(C) x+="    col = gs_col[0];                                      \n";
    if(a) x+="    EmitVertex();                                         \n";
    if(a) x+="                                                          \n";
    if(a) x+="    world_pos = B;                                        \n";
    if(a) x+="    gl_Position = BG;                                     \n";
    if(a) x+="    gl_ClipDistance[0] = dot(world_pos,slice_plane_0);    \n";
    if(a) x+="    gl_ClipDistance[1] = -dot(world_pos,slice_plane_1);   \n";
    if(a) x+="    gl_PrimitiveID = gl_PrimitiveIDIn;                    \n";
    if(C) x+="    col = gs_col[0];                                      \n";
    if(a) x+="    EmitVertex();                                         \n";
    if(a) x+="                                                          \n";
    if(a) x+="    world_pos = D;                                        \n";
    if(a) x+="    gl_Position = DG;                                     \n";
    if(a) x+="    gl_ClipDistance[0] = dot(world_pos,slice_plane_0);    \n";
    if(a) x+="    gl_ClipDistance[1] = -dot(world_pos,slice_plane_1);   \n";
    if(a) x+="    gl_PrimitiveID = gl_PrimitiveIDIn;                    \n";
    if(C) x+="    col = gs_col[0];                                      \n";
    if(a) x+="    EmitVertex();                                         \n";
    if(a) x+="    EndPrimitive();                                       \n";
    if(1) x+="}                                                         \n";

    return x;
}

static std::string Generate_Geometry_Shader_Matrix(SHADER_FLAGS flags)
{
    bool a=flags.dim==2;
    bool b=flags.dim==3;
    std::string x;
    
    if(1) x+="#version 420                                              \n";
    if(1) x+="layout(points) in;                                        \n";
    if(1) x+="layout(line_strip,max_vertices=6) out;                    \n";
    if(1) x+="                                                          \n";
    if(1) x+="in vec4 gs_world_pos[];                                   \n";
    if(a) x+="in vec2 gs_mat0[];                                        \n";
    if(a) x+="in vec2 gs_mat1[];                                        \n";
    if(b) x+="in vec3 gs_mat0[];                                        \n";
    if(b) x+="in vec3 gs_mat1[];                                        \n";
    if(b) x+="in vec3 gs_mat2[];                                        \n";
    if(1) x+="                                                          \n";
    if(1) x+="out vec4 world_pos;                                       \n";
    if(1) x+="out vec3 col;                                             \n";
    if(1) x+="                                                          \n";
    if(1) x+="uniform mat4 obj_to_world;                                \n";
    if(1) x+="uniform mat4 world_to_gl;                                 \n";
    if(1) x+="uniform float scale;                                      \n";
    if(1) x+="uniform vec4 slice_plane_0;                               \n";
    if(1) x+="uniform vec4 slice_plane_1;                               \n";
    if(1) x+="                                                          \n";
    if(1) x+="void pt(vec4 A,vec3 c)                                    \n";
    if(1) x+="{                                                         \n";
    if(1) x+="    world_pos = A;                                        \n";
    if(1) x+="    gl_ClipDistance[0] = dot(world_pos,slice_plane_0);    \n";
    if(1) x+="    gl_ClipDistance[1] = -dot(world_pos,slice_plane_1);   \n";
    if(1) x+="    gl_PrimitiveID = gl_PrimitiveIDIn;                    \n";
    if(1) x+="    gl_Position = world_to_gl * world_pos;                \n";
    if(1) x+="    col = c;                                              \n";
    if(1) x+="    EmitVertex();                                         \n";
    if(1) x+="}                                                         \n";
    if(1) x+="                                                          \n";
    if(1) x+="mat4 Q=obj_to_world*scale;                                \n";
    if(a) x+="vec4 u(vec2 r){return Q*vec4(r.x,r.y,0,0);}               \n";
    if(b) x+="vec4 u(vec3 r){return Q*vec4(r.x,r.y,r.z,0);}             \n";
    if(1) x+="                                                          \n";
    if(1) x+="void main(void)                                           \n";
    if(1) x+="{                                                         \n";
    if(1) x+="    vec4 A = gs_world_pos[0];                             \n";
    if(1) x+="    pt(A,vec3(1,0,0));                                    \n";
    if(1) x+="    pt(A+u(gs_mat0[0]),vec3(1,0,0));                    \n";
    if(1) x+="    EndPrimitive();                                       \n";
    if(1) x+="    pt(A,vec3(0,1,0));                                    \n";
    if(1) x+="    pt(A+u(gs_mat1[0]),vec3(0,1,0));                    \n";
    if(1) x+="    EndPrimitive();                                       \n";
    if(b) x+="    pt(A,vec3(0,0,1));                                    \n";
    if(b) x+="    pt(A+u(gs_mat2[0]),vec3(0,0,1));                    \n";
    if(b) x+="    EndPrimitive();                                       \n";
    if(1) x+="}                                                         \n";

    return x;
}

std::string Generate_Geometry_Shader_Regular_Poly_2D(SHADER_FLAGS flags)
{
    bool L=flags.out_dim==1;
    bool T=flags.out_dim==2;
    bool c=flags.col==2;
    bool b=flags.bg_col==2;
    bool g=flags.special==SHADER_TYPE::regular_poly;
    bool s=!g;
    std::string x;

    if(1) x+="#version 420                                              \n";
    if(1) x+="layout(points) in;                                        \n";
    if(L) x+="layout(line_strip,max_vertices=65) out;                   \n";
    if(T) x+="layout(triangle_strip,max_vertices=65) out;               \n";
    if(1) x+="                                                          \n";
    if(1) x+="in vec4 gs_world_pos[];                                   \n";
    if(c) x+="in vec3 gs_col[];                                         \n";
    if(b) x+="in vec3 gs_bgcol[];                                       \n";
    if(1) x+="in float gs_radius[];                                     \n";
    if(1) x+="                                                          \n";
    if(1) x+="out vec4 world_pos;                                       \n";
    if(c) x+="out vec3 col;                                             \n";
    if(b) x+="out vec3 bgcol;                                           \n";
    if(1) x+="                                                          \n";
    if(1) x+="uniform mat4 obj_to_world;                                \n";
    if(1) x+="uniform float obj_to_world_scale;                         \n";
    if(1) x+="uniform mat4 world_to_gl;                                 \n";
    if(1) x+="uniform float scale;                                      \n";
    if(1) x+="uniform vec4 slice_plane_0;                               \n";
    if(1) x+="uniform vec4 slice_plane_1;                               \n";
    if(g) x+="uniform int num_sides;                                    \n";
    if(g) x+="uniform int initial_rotate;                               \n";
    if(s) x+="int num_sides=64;                                         \n";
    if(1) x+="                                                          \n";
    if(1) x+="vec2 x0=gs_world_pos[0].xy;                               \n";
    if(s) x+="vec2 dx=vec2(1,0);                                        \n";
    if(g) x+="float Q=sqrt(0.5);                                        \n";
    if(g) x+="vec2 dxl[8]={vec2(1,0),vec2(Q,Q),vec2(0,1),vec2(-Q,Q),    \n";
    if(g) x+="    vec2(-1,0),vec2(-Q,-Q),vec2(0,-1),vec2(Q,-Q)};        \n";
    if(g) x+="vec2 dx=dxl[initial_rotate];                              \n";
    if(1) x+="                                                          \n";
    if(1) x+="void dump(vec2 dy)                                        \n";
    if(1) x+="{                                                         \n";
    if(1) x+="    vec4 A = vec4(dy,gs_world_pos[0].z,1.0);              \n";
    if(1) x+="    world_pos = A;                                        \n";
    if(1) x+="    gl_Position = world_to_gl * A;                        \n";
    if(1) x+="    gl_ClipDistance[0] = dot(world_pos,slice_plane_0);    \n";
    if(1) x+="    gl_ClipDistance[1] = -dot(world_pos,slice_plane_1);   \n";
    if(1) x+="    gl_PrimitiveID = gl_PrimitiveIDIn;                    \n";
    if(c) x+="    col = gs_col[0];                                      \n";
    if(b) x+="    bgcol = gs_bgcol[0];                                  \n";
    if(1) x+="    EmitVertex();                                         \n";
    if(1) x+="}                                                         \n";
    if(1) x+="                                                          \n";
    if(1) x+="void main(void)                                           \n";
    if(1) x+="{                                                         \n";
    if(1) x+="    dx*=gs_radius[0]*scale*obj_to_world_scale;            \n";
    if(1) x+="    float pi=3.141592653589793;                           \n";
    if(1) x+="    float a=2*pi/num_sides,c=cos(a),s=sin(a);             \n";
    if(1) x+="    mat2 M=mat2(c,-s,s,c);                                \n";
    if(1) x+="    vec2 dy=dx;                                           \n";
    if(1) x+="    for(int i=0; i<num_sides-1; ++i)                      \n";
    if(1) x+="    {                                                     \n";
    if(1) x+="        dump(x0+dy);                                      \n";
    if(1) x+="        dy=M*dy;                                          \n";
    if(T) x+="        dump(x0+dy);                                      \n";
    if(T) x+="        dump(x0);                                         \n";
    if(T) x+="        EndPrimitive();                                   \n";
    if(1) x+="    }                                                     \n";
    if(1) x+="    dump(x0+dy);                                          \n";
    if(1) x+="    dump(x0+dx);                                          \n";
    if(T) x+="    dump(x0);                                             \n";
    if(1) x+="    EndPrimitive();                                       \n";
    if(1) x+="}                                                         \n";

    return x;
}

std::string Generate_Geometry_Shader_Sphere_3D(SHADER_FLAGS flags)
{
    bool p=flags.in_dim==0;
    bool L=flags.out_dim==1;
    bool T=flags.out_dim==2;
    bool c=flags.col==2;
    bool b=flags.bg_col==2;
    bool n=!flags.flat_col;
    std::string x;

    if(1) x+="#version 420                                              \n";
    if(p) x+="layout(points) in;                                        \n";
    if(L) x+="layout(line_strip,max_vertices=4) out;                    \n";
    if(T) x+="layout(triangle_strip,max_vertices=3) out;                \n";
    if(1) x+="                                                          \n";
    if(1) x+="in vec4 gs_world_pos[];                                   \n";
    if(c) x+="in vec3 gs_col[];                                         \n";
    if(b) x+="in vec3 gs_bgcol[];                                       \n";
    if(1) x+="in float gs_radius[];                                     \n";
    if(1) x+="                                                          \n";
    if(1) x+="out vec4 world_pos;                                       \n";
    if(n) x+="out vec3 normal;                                          \n";
    if(c) x+="out vec3 col;                                             \n";
    if(b) x+="out vec3 bgcol;                                           \n";
    if(1) x+="                                                          \n";
    if(1) x+="uniform mat4 obj_to_world;                                \n";
    if(1) x+="uniform mat4 world_to_gl;                                 \n";
    if(1) x+="uniform float scale;                                      \n";
    if(1) x+="uniform vec4 slice_plane_0;                               \n";
    if(1) x+="uniform vec4 slice_plane_1;                               \n";
    if(1) x+="uniform float obj_to_world_scale;                         \n";
    if(1) x+="                                                          \n";
    if(1) x+="void do_vert(vec3 X,vec3 N)                               \n";
    if(1) x+="{                                                         \n";
    if(1) x+="    vec4 A = vec4(X,1);                                   \n";
    if(1) x+="    world_pos = A;                                        \n";
    if(1) x+="    gl_Position = world_to_gl * A;                        \n";
    if(1) x+="    gl_ClipDistance[0] = dot(world_pos,slice_plane_0);    \n";
    if(1) x+="    gl_ClipDistance[1] = -dot(world_pos,slice_plane_1);   \n";
    if(1) x+="    gl_PrimitiveID = gl_PrimitiveIDIn;                    \n";
    if(c) x+="    col = gs_col[0];                                      \n";
    if(b) x+="    bgcol = gs_bgcol[0];                                  \n";
    if(n) x+="    normal = N;                                           \n";
    if(1) x+="    EmitVertex();                                         \n";
    if(1) x+="}                                                         \n";
    if(1) x+="                                                          \n";
    if(1) x+="void do_tri(vec3 A,vec3 B,vec3 C);                        \n";
    if(1) x+="{                                                         \n";
    if(1) x+="    vec3 N;                                               \n";
    if(1) x+="    float f = 0.41946952412160632330;                     \n";
    if(n) x+="    N=(A+B+C)*f;                                          \n";
    if(1) x+="    do_vert(A,N);                                         \n";
    if(1) x+="    do_vert(B,N);                                         \n";
    if(1) x+="    do_vert(C,N);                                         \n";
    if(L) x+="    do_vert(A,N);                                         \n";
    if(1) x+="    EndPrimitive();                                       \n";
    if(1) x+="}                                                         \n";
    if(1) x+="                                                          \n";
    if(1) x+="void main(void)                                           \n";
    if(1) x+="{                                                         \n";
    if(1) x+="    float r = gs_radius[0]*scale*obj_to_world_scale;      \n";
    if(1) x+="    float x0 = 0.89442719099991587856*r;                  \n";
    if(1) x+="    float x1 = 0.27639320225002103036*r;                  \n";
    if(1) x+="    float x2 = -0.72360679774997896964*r;                 \n";
    if(1) x+="    float y1 = 0.85065080835203993218*r;                  \n";
    if(1) x+="    float y2 = 0.52573111211913360601*r;                  \n";
    if(1) x+="    float z = 0.44721359549995793928*r;                   \n";
    if(1) x+="    vec4 C=gs_world_pos[0];                               \n";
    if(1) x+="    vec4 A=vec4(0,0,r,0);                                 \n";
    if(1) x+="    vec4 B0=vec4(x0,0,z,0);                               \n";
    if(1) x+="    vec4 B1=vec4(x1,y1,z,0);                              \n";
    if(1) x+="    vec4 B2=vec4(x2,y2,z,0);                              \n";
    if(1) x+="    vec4 B3=vec4(x2,-y2,z,0);                             \n";
    if(1) x+="    vec4 B4=vec4(x1,-y1,z,0);                             \n";
    if(1) x+="    do_tri(C+A,C+B0,C+B1);                                \n";
    if(1) x+="    do_tri(C+A,C+B1,C+B2);                                \n";
    if(1) x+="    do_tri(C+A,C+B2,C+B3);                                \n";
    if(1) x+="    do_tri(C+A,C+B3,C+B4);                                \n";
    if(1) x+="    do_tri(C+A,C+B4,C+B0);                                \n";
    if(1) x+="    do_tri(C-B3,C+B1,C+B0);                               \n";
    if(1) x+="    do_tri(C-B4,C+B2,C+B1);                               \n";
    if(1) x+="    do_tri(C-B0,C+B3,C+B2);                               \n";
    if(1) x+="    do_tri(C-B1,C+B4,C+B3);                               \n";
    if(1) x+="    do_tri(C-B2,C+B0,C+B4);                               \n";
    if(1) x+="    do_tri(C+B3,C-B0,C-B1);                               \n";
    if(1) x+="    do_tri(C+B4,C-B1,C-B2);                               \n";
    if(1) x+="    do_tri(C+B0,C-B2,C-B3);                               \n";
    if(1) x+="    do_tri(C+B1,C-B3,C-B4);                               \n";
    if(1) x+="    do_tri(C+B2,C-B4,C-B0);                               \n";
    if(1) x+="    do_tri(C-A,C-B1,C-B0);                                \n";
    if(1) x+="    do_tri(C-A,C-B2,C-B1);                                \n";
    if(1) x+="    do_tri(C-A,C-B3,C-B2);                                \n";
    if(1) x+="    do_tri(C-A,C-B4,C-B3);                                \n";
    if(1) x+="    do_tri(C-A,C-B0,C-B4);                                \n";
    if(1) x+="}                                                         \n";

    return x;
}

std::string Generate_Geometry_Shader_Box_2D(SHADER_FLAGS flags)
{
    bool p=flags.in_dim==0;
    bool L=flags.out_dim==1;
    bool T=flags.out_dim==2;
    bool c=flags.col==2;
    bool b=flags.bg_col==2;
    std::string x;
    
    if(1) x+="#version 420                                              \n";
    if(p) x+="layout(points) in;                                        \n";
    if(L) x+="layout(line_strip,max_vertices=5) out;                    \n";
    if(T) x+="layout(triangle_strip,max_vertices=4) out;                \n";
    if(1) x+="                                                          \n";
    if(1) x+="in vec4 gs_world_pos[];                                   \n";
    if(c) x+="in vec3 gs_col[];                                         \n";
    if(b) x+="in vec3 gs_bgcol[];                                       \n";
    if(1) x+="                                                          \n";
    if(1) x+="out vec4 world_pos;                                       \n";
    if(c) x+="out vec3 col;                                             \n";
    if(b) x+="out vec3 bgcol;                                           \n";
    if(1) x+="                                                          \n";
    if(1) x+="uniform mat4 obj_to_world;                                \n";
    if(1) x+="uniform mat4 world_to_gl;                                 \n";
    if(1) x+="uniform vec4 slice_plane_0;                               \n";
    if(1) x+="uniform vec4 slice_plane_1;                               \n";
    if(1) x+="uniform mat2 box_edges;                                   \n";
    if(1) x+="                                                          \n";
    if(1) x+="vec2 X00=gs_world_pos[0].xy-box_edges*vec2(0.5,0.5);      \n";
    if(1) x+="vec2 X10=X00+box_edges[0];                                \n";
    if(1) x+="vec2 X01=X00+box_edges[1];                                \n";
    if(1) x+="vec2 X11=X10+box_edges[1];                                \n";
    if(1) x+="                                                          \n";
    if(1) x+="void do_vert(vec2 X)                                      \n";
    if(1) x+="{                                                         \n";
    if(1) x+="    vec4 A = vec4(X,gs_world_pos[0].zw);                  \n";
    if(1) x+="    world_pos = A;                                        \n";
    if(1) x+="    gl_Position = world_to_gl * A;                        \n";
    if(1) x+="    gl_ClipDistance[0] = dot(world_pos,slice_plane_0);    \n";
    if(1) x+="    gl_ClipDistance[1] = -dot(world_pos,slice_plane_1);   \n";
    if(1) x+="    gl_PrimitiveID = gl_PrimitiveIDIn;                    \n";
    if(c) x+="    col = gs_col[0];                                      \n";
    if(b) x+="    bgcol = gs_bgcol[0];                                  \n";
    if(1) x+="    EmitVertex();                                         \n";
    if(1) x+="}                                                         \n";
    if(1) x+="                                                          \n";
    if(1) x+="void main(void)                                           \n";
    if(1) x+="{                                                         \n";
    if(1) x+="    do_vert(X00);                                         \n";
    if(1) x+="    do_vert(X10);                                         \n";
    if(L) x+="    do_vert(X11);                                         \n";
    if(1) x+="    do_vert(X01);                                         \n";
    if(T) x+="    do_vert(X11);                                         \n";
    if(L) x+="    do_vert(X00);                                         \n";
    if(1) x+="    EndPrimitive();                                       \n";
    if(1) x+="}                                                         \n";

    return x;
}

std::string Generate_Geometry_Shader_Box_3D(SHADER_FLAGS flags)
{
    bool p=flags.in_dim==0;
    bool L=flags.out_dim==1;
    bool T=flags.out_dim==2;
    bool c=flags.col==2;
    bool b=flags.bg_col==2;
    bool n=!flags.flat_col,N=!n;
    std::string x;

    if(1) x+="#version 420                                              \n";
    if(p) x+="layout(points) in;                                        \n";
    if(L) x+="layout(line_strip,max_vertices=30) out;                   \n";
    if(T) x+="layout(triangle_strip,max_vertices=24) out;               \n";
    if(1) x+="                                                          \n";
    if(1) x+="in vec4 gs_world_pos[];                                   \n";
    if(c) x+="in vec3 gs_col[];                                         \n";
    if(b) x+="in vec3 gs_bgcol[];                                       \n";
    if(1) x+="                                                          \n";
    if(1) x+="out vec4 world_pos;                                       \n";
    if(n) x+="out vec3 normal;                                          \n";
    if(c) x+="out vec3 col;                                             \n";
    if(b) x+="out vec3 bgcol;                                           \n";
    if(1) x+="                                                          \n";
    if(1) x+="uniform mat4 obj_to_world;                                \n";
    if(1) x+="uniform mat4 world_to_gl;                                 \n";
    if(1) x+="uniform float scale;                                      \n";
    if(1) x+="uniform vec4 slice_plane_0;                               \n";
    if(1) x+="uniform vec4 slice_plane_1;                               \n";
    if(1) x+="uniform mat3 box_edges;                                   \n";
    if(1) x+="                                                          \n";
    if(1) x+="vec3 X000=gs_world_pos[0].xyz-box_edges*vec3(0.5,0.5,0.5);\n";
    if(1) x+="vec3 X100=X000+box_edges[0];                              \n";
    if(1) x+="vec3 X010=X000+box_edges[1];                              \n";
    if(1) x+="vec3 X110=X100+box_edges[1];                              \n";
    if(1) x+="vec3 X001=X000+box_edges[2];                              \n";
    if(1) x+="vec3 X101=X100+box_edges[2];                              \n";
    if(1) x+="vec3 X011=X010+box_edges[2];                              \n";
    if(1) x+="vec3 X111=X110+box_edges[2];                              \n";
    if(1) x+="                                                          \n";
    if(n) x+="vec3 N0=normalize(box_edges[0]);                          \n";
    if(n) x+="vec3 N1=normalize(box_edges[1]);                          \n";
    if(n) x+="vec3 N2=normalize(box_edges[2]);                          \n";
    if(N) x+="vec3 N0=vec3(0,0,0);                                      \n";
    if(N) x+="vec3 N1=vec3(0,0,0);                                      \n";
    if(N) x+="vec3 N2=vec3(0,0,0);                                      \n";
    if(1) x+="                                                          \n";
    if(1) x+="void do_vert(vec3 X,vec3 N)                               \n";
    if(1) x+="{                                                         \n";
    if(1) x+="    vec4 A = vec4(X,1                 );                  \n";
    if(1) x+="    world_pos = A;                                        \n";
    if(1) x+="    gl_Position = world_to_gl * A;                        \n";
    if(1) x+="    gl_ClipDistance[0] = dot(world_pos,slice_plane_0);    \n";
    if(1) x+="    gl_ClipDistance[1] = -dot(world_pos,slice_plane_1);   \n";
    if(1) x+="    gl_PrimitiveID = gl_PrimitiveIDIn;                    \n";
    if(c) x+="    col = gs_col[0];                                      \n";
    if(b) x+="    bgcol = gs_bgcol[0];                                  \n";
    if(n) x+="    normal = N;                                           \n";
    if(1) x+="    EmitVertex();                                         \n";
    if(1) x+="}                                                         \n";
    if(1) x+="                                                          \n";
    if(1) x+="void do_face(vec3 A,vec3 B,vec3 C,vec3 D,vec3 N)          \n";
    if(1) x+="{                                                         \n";
    if(1) x+="    do_vert(A,N);                                         \n";
    if(1) x+="    do_vert(B,N);                                         \n";
    if(T) x+="    do_vert(D,N);                                         \n";
    if(1) x+="    do_vert(C,N);                                         \n";
    if(L) x+="    do_vert(D,N);                                         \n";
    if(L) x+="    do_vert(A,N);                                         \n";
    if(1) x+="    EndPrimitive();                                       \n";
    if(1) x+="}                                                         \n";
    if(1) x+="                                                          \n";
    if(1) x+="void main(void)                                           \n";
    if(1) x+="{                                                         \n";
    if(1) x+="    do_face(X000,X001,X011,X010,-N0);                     \n";
    if(1) x+="    do_face(X100,X101,X111,X110,N0);                      \n";
    if(1) x+="    do_face(X000,X100,X101,X001,-N1);                     \n";
    if(1) x+="    do_face(X010,X110,X111,X011,N1);                      \n";
    if(1) x+="    do_face(X000,X010,X110,X100,-N2);                     \n";
    if(1) x+="    do_face(X001,X011,X111,X101,N2);                      \n";
    if(1) x+="}                                                         \n";

    return x;
}

std::string Generate_Geometry_Shader_Diamond_2D(SHADER_FLAGS flags)
{
    bool p=flags.in_dim==0;
    bool L=flags.out_dim==1;
    bool T=flags.out_dim==2;
    bool c=flags.col==2;
    bool b=flags.bg_col==2;
    std::string x;
    
    if(1) x+="#version 420                                              \n";
    if(p) x+="layout(points) in;                                        \n";
    if(L) x+="layout(line_strip,max_vertices=5) out;                    \n";
    if(T) x+="layout(triangle_strip,max_vertices=4) out;                \n";
    if(1) x+="                                                          \n";
    if(1) x+="in vec4 gs_world_pos[];                                   \n";
    if(c) x+="in vec3 gs_col[];                                         \n";
    if(b) x+="in vec3 gs_bgcol[];                                       \n";
    if(1) x+="                                                          \n";
    if(1) x+="out vec4 world_pos;                                       \n";
    if(c) x+="out vec3 col;                                             \n";
    if(b) x+="out vec3 bgcol;                                           \n";
    if(1) x+="                                                          \n";
    if(1) x+="uniform mat4 obj_to_world;                                \n";
    if(1) x+="uniform mat4 world_to_gl;                                 \n";
    if(1) x+="uniform vec4 slice_plane_0;                               \n";
    if(1) x+="uniform vec4 slice_plane_1;                               \n";
    if(1) x+="uniform mat2 box_edges;                                   \n";
    if(1) x+="                                                          \n";
    if(1) x+="vec2 a=0.5*box_edges[0];                                  \n";
    if(1) x+="vec2 b=0.5*box_edges[1];                                  \n";
    if(1) x+="vec2 p=gs_world_pos[0].xy;                                \n";
    if(1) x+="vec2 X00=p+a;                                             \n";
    if(1) x+="vec2 X10=p+b;                                             \n";
    if(1) x+="vec2 X11=p-a;                                             \n";
    if(1) x+="vec2 X01=p-b;                                             \n";
    if(1) x+="                                                          \n";
    if(1) x+="void do_vert(vec2 X)                                      \n";
    if(1) x+="{                                                         \n";
    if(1) x+="    vec4 A = vec4(X,gs_world_pos[0].zw);                  \n";
    if(1) x+="    world_pos = A;                                        \n";
    if(1) x+="    gl_Position = world_to_gl * A;                        \n";
    if(1) x+="    gl_ClipDistance[0] = dot(world_pos,slice_plane_0);    \n";
    if(1) x+="    gl_ClipDistance[1] = -dot(world_pos,slice_plane_1);   \n";
    if(1) x+="    gl_PrimitiveID = gl_PrimitiveIDIn;                    \n";
    if(c) x+="    col = gs_col[0];                                      \n";
    if(b) x+="    bgcol = gs_bgcol[0];                                  \n";
    if(1) x+="    EmitVertex();                                         \n";
    if(1) x+="}                                                         \n";
    if(1) x+="                                                          \n";
    if(1) x+="void main(void)                                           \n";
    if(1) x+="{                                                         \n";
    if(1) x+="    do_vert(X00);                                         \n";
    if(1) x+="    do_vert(X10);                                         \n";
    if(L) x+="    do_vert(X11);                                         \n";
    if(1) x+="    do_vert(X01);                                         \n";
    if(T) x+="    do_vert(X11);                                         \n";
    if(L) x+="    do_vert(X00);                                         \n";
    if(1) x+="    EndPrimitive();                                       \n";
    if(1) x+="}                                                         \n";

    return x;
}

std::string Generate_Geometry_Shader(SHADER_FLAGS flags)
{
    if(flags.special==SHADER_TYPE::segment || flags.special==SHADER_TYPE::arrow)
        return Generate_Geometry_Shader_Arrows(flags);
    if(flags.special==SHADER_TYPE::sphere || flags.special==SHADER_TYPE::regular_poly)
    {
        if(flags.dim==2)
            return Generate_Geometry_Shader_Regular_Poly_2D(flags);
        PHYSBAM_ASSERT(flags.special==SHADER_TYPE::sphere);
        return Generate_Geometry_Shader_Sphere_3D(flags);
    }
    if(flags.special==SHADER_TYPE::box)
    {
        if(flags.dim==2)
            return Generate_Geometry_Shader_Box_2D(flags);
        return Generate_Geometry_Shader_Box_3D(flags);
    }
    if(flags.special==SHADER_TYPE::matrix)
        return Generate_Geometry_Shader_Matrix(flags);
    if(flags.special==SHADER_TYPE::diamond)
    {
        if(flags.dim==2)
            return Generate_Geometry_Shader_Diamond_2D(flags);
        return Generate_Geometry_Shader_Box_3D(flags);
    }

    bool p=flags.in_dim==0;
    bool l=flags.in_dim==1;
    bool t=flags.in_dim==2;
    bool P=flags.out_dim==0;
    bool L=flags.out_dim==1;
    bool T=flags.out_dim==2;
    bool c=flags.col==2;
    bool b=flags.bg_col==2;
    bool n=!flags.flat_col && t;
    bool w=t && L;
    std::string x;

    PHYSBAM_ASSERT(p||l||t||P||L||T);
    
    if(1) x+="#version 420                                              \n";
    if(p) x+="layout(points) in;                                        \n";
    if(l) x+="layout(lines) in;                                         \n";
    if(t) x+="layout(triangles) in;                                     \n";
    if(P) x+="layout(points,max_vertices=1) out;                        \n";
    if(L) x+="layout(line_strip,max_vertices=4) out;                    \n";
    if(T) x+="layout(triangle_strip,max_vertices=3) out;                \n";
    if(1) x+="                                                          \n";
    if(1) x+="in vec4 gs_world_pos[];                                   \n";
    if(c) x+="in vec3 gs_col[];                                         \n";
    if(b) x+="in vec3 gs_bgcol[];                                       \n";
    if(1) x+="                                                          \n";
    if(1) x+="out vec4 world_pos;                                       \n";
    if(n) x+="out vec3 normal;                                          \n";
    if(c) x+="out vec3 col;                                             \n";
    if(b) x+="out vec3 bgcol;                                           \n";
    if(1) x+="                                                          \n";
    if(1) x+="uniform mat4 obj_to_world;                                \n";
    if(1) x+="uniform mat4 world_to_gl;                                 \n";
    if(1) x+="uniform vec4 slice_plane_0;                               \n";
    if(1) x+="uniform vec4 slice_plane_1;                               \n";
    if(1) x+="                                                          \n";
    if(n) x+="vec3 N;                                                   \n";
    if(1) x+="                                                          \n";
    if(1) x+="void do_vert(int i)                                       \n";
    if(1) x+="{                                                         \n";
    if(1) x+="    gl_Position = gl_in[i].gl_Position;                   \n";
    if(1) x+="    gl_PrimitiveID = gl_PrimitiveIDIn;                    \n";
    if(1) x+="    world_pos = gs_world_pos[i];                          \n";
    if(1) x+="    gl_ClipDistance[0] = dot(world_pos,slice_plane_0);    \n";
    if(1) x+="    gl_ClipDistance[1] = -dot(world_pos,slice_plane_1);   \n";
    if(c) x+="    col = gs_col[i];                                      \n";
    if(b) x+="    bgcol = gs_bgcol[i];                                  \n";
    if(n) x+="    normal = N;                                           \n";
    if(1) x+="    EmitVertex();                                         \n";
    if(1) x+="}                                                         \n";
    if(1) x+="                                                          \n";
    if(1) x+="void main(void)                                           \n";
    if(1) x+="{                                                         \n";
    if(n) x+="    vec3 a = (gs_world_pos[1] - gs_world_pos[0]).xyz;     \n";
    if(n) x+="    vec3 b = (gs_world_pos[2] - gs_world_pos[0]).xyz;     \n";
    if(n) x+="    N = normalize(cross(a,b));                            \n";
    if(1) x+="    for(int i=0; i<gl_in.length(); ++i) do_vert(i);       \n";
    if(w) x+="    do_vert(0);                                           \n";
    if(1) x+="    EndPrimitive();                                       \n";
    if(1) x+="}                                                         \n";

    return x;
}
}
