#include "gfx.h"
#include "vector.h"
#include "sokol_gfx.h"
#if INTERFACE
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include "cute_png.h"
#include "sokol_gfx.h"
#include "gb_math.h"

typedef uint32_t u32;
typedef uint16_t u16;
typedef uint8_t u8;
typedef uintptr_t umm;

typedef gbVec2 vec2;
typedef gbVec2 Vector2;
typedef gbVec3 vec3;
typedef gbVec3 Vector3;
typedef gbVec4 vec4;
typedef gbVec4 Vector4;
typedef gbVec4 Quaternion;
typedef gbMat4 mat4;

typedef enum{
  FLOAT,
  BYTE,
  HALF
}vertexFormatTypes;

typedef enum{
  ZFORMAT,
  VFORMAT
}vertexFormat;

typedef struct vertexComponent_t{
  sg_vertex_format format;
  vertexFormatTypes type;
  int count;
}vertexComponent_t;

typedef struct vertexFormat_t {
  struct vertexComponent_t components[8];
  int comp;
  int stride;
  bool split;
}vertexFormat_t;

typedef struct { 
  float x,  y,  z, w;
  float u,  v;
  float r,  g,  b, a;
  float nx, ny, nz;
} m_vert;

typedef struct { 
  float x,  y,  z, w;
  float u,  v;
  float r,  g,  b, a;
  float nx, ny, nz;
  u8 bi1,bi2,bi3,bi4;
  u8 bw1,bw2,bw3,bw4;
} a_vert;

typedef struct Image {
    void *data;             // Image raw data
    int width;              // Image base width
    int height;             // Image base height
    int mipmaps;            // Mipmap levels, 1 by default
    int format;             // Data format (PixelFormat type)
} Image;

typedef struct Texture {
    sg_image img;
    unsigned int id;        // OpenGL texture id
    int width;              // Texture base width
    int height;             // Texture base height
    int mipmaps;            // Mipmap levels, 1 by default
    int format;             // Data format (PixelFormat type)
} Texture;
typedef Texture Texture2D;

typedef struct RenderTexture {
    unsigned int id;        // OpenGL framebuffer object id
    Texture color;        // Color buffer attachment texture
    Texture depth;          // Depth buffer attachment texture
} RenderTexture;
typedef RenderTexture RenderTexture2D;

typedef struct Color {
    unsigned char r;
    unsigned char g;
    unsigned char b;
    unsigned char a;
} Color;

typedef struct MaterialMap {
  Texture2D texture;
  Color color;
  float value;
} MaterialMap;

typedef struct Shader {
  sg_shader_desc desc;
  sg_shader program;
  const char* name;
  bool dirty;
  Texture samplers;
  sg_range vs_uniforms;
  sg_range fs_uniforms;
} Shader;

typedef struct Material {
  Shader shader;
  MaterialMap *maps;
  float params[4];
} Material;

typedef struct Mesh {
  int vertexCount;
  int triangleCount;
  int vertexStart;
  int vertexRange;
  int components;
  bool indexed;
  vertexFormat format;
  sg_bindings bind;
  uint32_t* map;
  float *vertices;
  uint8_t *colors;
  int8_t *uvs;
} Mesh;

typedef struct BoneInfo {
    char name[32];          // Bone name
    int parent;             // Bone parent
} BoneInfo;

typedef struct Transform {
    Vector3 translation;   
    Quaternion rotation;   
    Vector3 scale;         
} Transform;

typedef struct Model {
  mat4 transform;
  int meshCount;
  int materialCount;
  Mesh *meshes;
  Material *materials;
  int *meshMaterial;
  int boneCount;
  BoneInfo *bones;
  Transform *bindPose;
} Model;

typedef struct gfx_pipe_t{
  int id;
  sg_pipeline pipe;
  sg_pipeline_desc desc;
}gfx_pipe_t;

typedef struct gfx_stack_t{
  int mode;
  bool wireframe;
  vec4 color;
  int blendMode;
  int canvas;
  int shader;
  mat4 transform;
}gfx_stack_t;

typedef struct gfx_state_t{
  Shader* shader;

  gfx_pipe_t pipes[255];
  sg_pipeline_desc pipeNext; 
  int pipeCurrent; 

  sg_filter defaultFilter;
  sg_cull_mode meshCullMode;
  sg_compare_func depthMode;
  int blendMode;
  int alphaMode;
  
  bool depthWrite;
  bool wireframe;
  bool inPass;

  int currentVertexFormat;
  int passesPassed;
  int textureMemoryUsage;
  int meshMemoryUsage;
  int meshCount;
  int renderTextureCount;
  int textureCount;
  int frameBufferCounter;
  int pushed;
  int currentFontSize;
  int lineWidth;
  int pipelineCount;
  int pipelineSwitches;
  
  mat4 transform;
  vec4 scissor;

  gfx_stack_t stack[64];
  // int textureSizes[1024];
  // int meshSizes[1024];
  // tempDepthTextures = {}
  // renderTextures = {}
}gfx_state_t;

typedef struct Ray {
  Vector3 position;
  Vector3 direction;
} Ray;

typedef struct RayHitInfo {
  bool hit;
  float distance;
  Vector3 position;
  Vector3 normal;
  Vector3 triangle[3];
} RayHitInfo;

typedef struct BoundingBox {
  Vector3 min;
  Vector3 max;
} BoundingBox;



typedef enum {
    PIXELFORMAT_UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha)
    PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA,    // 8*2 bpp (2 channels)
    PIXELFORMAT_UNCOMPRESSED_R5G6B5,        // 16 bpp
    PIXELFORMAT_UNCOMPRESSED_R8G8B8,        // 24 bpp
    PIXELFORMAT_UNCOMPRESSED_R5G5B5A1,      // 16 bpp (1 bit alpha)
    PIXELFORMAT_UNCOMPRESSED_R4G4B4A4,      // 16 bpp (4 bit alpha)
    PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,      // 32 bpp
    PIXELFORMAT_UNCOMPRESSED_R32,           // 32 bpp (1 channel - float)
    PIXELFORMAT_UNCOMPRESSED_R32G32B32,     // 32*3 bpp (3 channels - float)
    PIXELFORMAT_UNCOMPRESSED_R32G32B32A32,  // 32*4 bpp (4 channels - float)
    PIXELFORMAT_COMPRESSED_DXT1_RGB,        // 4 bpp (no alpha)
    PIXELFORMAT_COMPRESSED_DXT1_RGBA,       // 4 bpp (1 bit alpha)
    PIXELFORMAT_COMPRESSED_DXT3_RGBA,       // 8 bpp
    PIXELFORMAT_COMPRESSED_DXT5_RGBA,       // 8 bpp
    PIXELFORMAT_COMPRESSED_ETC1_RGB,        // 4 bpp
    PIXELFORMAT_COMPRESSED_ETC2_RGB,        // 4 bpp
    PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA,   // 8 bpp
    PIXELFORMAT_COMPRESSED_PVRT_RGB,        // 4 bpp
    PIXELFORMAT_COMPRESSED_PVRT_RGBA,       // 4 bpp
    PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA,   // 8 bpp
    PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA    // 2 bpp
} PixelFormat;

#endif
static unsigned char images_missing[] = {
  0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 
  0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x64, 
  0x08, 0x06, 0x00, 0x00, 0x00, 0x70, 0xe2, 0x95, 0x54, 0x00, 0x00, 0x02, 
  0x5e, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0xed, 0xdb, 0xc1, 0x6e, 0xd4, 
  0x40, 0x0c, 0x80, 0x61, 0x1b, 0x55, 0x02, 0xd1, 0x4a, 0x45, 0x82, 0x23, 
  0xef, 0xff, 0x66, 0x85, 0xaa, 0xb4, 0xac, 0x54, 0xa0, 0xb4, 0x27, 0x73, 
  0x49, 0xe8, 0x22, 0x84, 0xca, 0x66, 0x3c, 0xb6, 0x27, 0xf3, 0x7f, 0xd7, 
  0x76, 0x33, 0x4e, 0x3c, 0xf6, 0xcc, 0x26, 0x59, 0x11, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x46, 0x67, 0x8b, 0xec, 0x38, 0x3c, 0x68, 0x76, 0x00, 0xa7, 
  0x30, 0xb3, 0x1f, 0x22, 0xf2, 0xf6, 0x84, 0x8f, 0xfc, 0x54, 0xd5, 0x53, 
  0xfe, 0x3f, 0x5d, 0xe9, 0x84, 0x74, 0x98, 0xf5, 0xf7, 0xaa, 0x7a, 0xe9, 
  0x7c, 0x4c, 0x57, 0xe5, 0x12, 0x62, 0x66, 0x8f, 0x22, 0xf2, 0xba, 0xf7, 
  0x38, 0xaa, 0x5a, 0xee, 0xdc, 0x45, 0x0a, 0x25, 0x24, 0x6b, 0x0d, 0xa8, 
  0x96, 0x98, 0x57, 0xd9, 0x01, 0x98, 0xd9, 0x21, 0x73, 0x41, 0xae, 0xb6, 
  0x19, 0x48, 0x4d, 0xc8, 0x72, 0x31, 0xde, 0x65, 0xc6, 0xb0, 0xc6, 0x61, 
  0x66, 0x37, 0xd9, 0x71, 0x88, 0x24, 0xb6, 0xac, 0x6a, 0x33, 0x73, 0x71, 
  0xa7, 0xaa, 0x1f, 0x32, 0x03, 0x48, 0xa9, 0x90, 0xa2, 0xc9, 0x10, 0x11, 
  0x79, 0x6f, 0x66, 0x57, 0x99, 0x01, 0x84, 0x57, 0x48, 0xe1, 0x64, 0xfc, 
  0x96, 0xb9, 0xd0, 0x87, 0x0e, 0x3c, 0x42, 0x32, 0x56, 0x59, 0x49, 0x09, 
  0x6b, 0x59, 0x66, 0xf6, 0x25, 0x6a, 0x2c, 0x0f, 0x59, 0xf1, 0x86, 0xcd, 
  0x82, 0x0e, 0xd5, 0xf1, 0x20, 0x22, 0x07, 0x55, 0xfd, 0x78, 0x34, 0xc6, 
  0x77, 0x11, 0x39, 0xf7, 0x1a, 0x20, 0xa3, 0x4a, 0x42, 0x2a, 0xc4, 0x71, 
  0x4b, 0x79, 0xa5, 0xcf, 0xce, 0x8f, 0x93, 0x21, 0x22, 0xa2, 0xaa, 0x17, 
  0xeb, 0x1f, 0x45, 0xe4, 0xd0, 0x3a, 0x98, 0x99, 0xdd, 0xb6, 0x1e, 0xe3, 
  0x54, 0x21, 0x33, 0xc0, 0xa3, 0x3a, 0xb6, 0xce, 0xd6, 0xd6, 0xb1, 0xa3, 
  0xab, 0x24, 0xfd, 0x9b, 0xfa, 0xff, 0x68, 0xb9, 0x28, 0xd5, 0x6e, 0x8d, 
  0xbc, 0xa4, 0x7c, 0x42, 0x3c, 0x2e, 0x68, 0xcb, 0x31, 0x96, 0x5b, 0xfe, 
  0x61, 0xba, 0x27, 0xa4, 0xb1, 0x65, 0x3c, 0xb8, 0x05, 0x22, 0xf2, 0xb8, 
  0xf1, 0x73, 0xa1, 0xcf, 0x53, 0xba, 0x97, 0x73, 0x4b, 0x42, 0xbc, 0xdb, 
  0xcd, 0xd6, 0x58, 0x22, 0xdb, 0x5e, 0xf9, 0x96, 0x35, 0x9b, 0xca, 0x09, 
  0xb9, 0xf7, 0x3e, 0x60, 0xc3, 0x4e, 0xed, 0x93, 0x77, 0x2c, 0xff, 0xd2, 
  0xb5, 0x14, 0x5b, 0x9e, 0xfe, 0xf5, 0x6a, 0x13, 0xd5, 0xdb, 0x56, 0xef, 
  0x0a, 0xe9, 0xfe, 0x28, 0x76, 0x6f, 0x2a, 0xb7, 0xac, 0x29, 0x9d, 0xf5, 
  0x3c, 0xb8, 0xaa, 0x6a, 0xc1, 0x3b, 0xbc, 0xb7, 0xd2, 0xf9, 0xbc, 0x5b, 
  0x54, 0xde, 0xf6, 0xa6, 0x3f, 0xbd, 0xcb, 0x50, 0x39, 0x21, 0xc3, 0xdd, 
  0xf6, 0xf0, 0x10, 0xb1, 0x86, 0x7c, 0x0b, 0x18, 0x63, 0x37, 0xaa, 0xdf, 
  0xed, 0x7d, 0x52, 0xd5, 0x37, 0xae, 0xc1, 0x14, 0x57, 0x7d, 0x97, 0x35, 
  0xdd, 0xb6, 0x39, 0x2a, 0x21, 0x9b, 0xdb, 0x56, 0xc1, 0x5d, 0x5a, 0x57, 
  0xc3, 0x3c, 0xc2, 0x9d, 0x65, 0x81, 0x8f, 0x6c, 0x59, 0x77, 0x2d, 0x1f, 
  0x9e, 0xa5, 0x52, 0x86, 0x7b, 0x0d, 0x68, 0xef, 0x95, 0x12, 0xba, 0xa8, 
  0x7b, 0x5c, 0xcc, 0xe5, 0x3d, 0xdc, 0xaf, 0x1e, 0xf1, 0x54, 0x94, 0x32, 
  0xdb, 0xbc, 0xda, 0xcf, 0x1e, 0xab, 0x65, 0xf8, 0x97, 0xad, 0xf7, 0x96, 
  0x94, 0xd4, 0x93, 0x21, 0x29, 0x7f, 0x4b, 0x3f, 0x11, 0x92, 0xf2, 0xa7, 
  0x12, 0x27, 0x41, 0x52, 0x9e, 0x95, 0xb8, 0x75, 0xb2, 0x5c, 0xc8, 0xeb, 
  0xd6, 0xe3, 0xec, 0xe1, 0xbb, 0x4a, 0xb9, 0x19, 0x35, 0xfb, 0x77, 0x95, 
  0x12, 0x15, 0x72, 0x6c, 0xe4, 0x8b, 0xe9, 0xa1, 0xec, 0xc9, 0xcf, 0x7a, 
  0xef, 0xab, 0x5c, 0x85, 0xac, 0x96, 0x0b, 0xba, 0xf5, 0xf5, 0xcf, 0x61, 
  0x95, 0x9f, 0x45, 0xd5, 0xdf, 0xa3, 0xf2, 0x56, 0xb6, 0x42, 0x56, 0x0d, 
  0x6f, 0x1b, 0x7e, 0xf6, 0x8e, 0x25, 0xc2, 0x10, 0xb3, 0x68, 0xeb, 0x4f, 
  0xd5, 0x46, 0xac, 0x92, 0x61, 0x02, 0xde, 0xd2, 0xba, 0x46, 0x4c, 0x48, 
  0xf9, 0x96, 0x35, 0x1b, 0x12, 0x52, 0xcc, 0x48, 0x09, 0x09, 0xfd, 0x69, 
  0x59, 0x96, 0x61, 0x7a, 0x2c, 0x6b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x30, 0xab, 0x5f, 0xd2, 0xd3, 0xee, 0x16, 0x22, 
  0x9e, 0xed, 0xeb, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 
  0x42, 0x60, 0x82
};
static unsigned int images_missing_len = 663;

static gfx_state_t gfx = {
.defaultFilter = SG_FILTER_LINEAR,
.meshCullMode = SG_CULLMODE_NONE,
.depthMode = SG_COMPAREFUNC_LESS,
.depthWrite = false,
.currentVertexFormat = -1,
.currentFontSize=12,
.lineWidth = 1
};

static vertexFormat_t vertexFormats[16] = {
  {
    .components[0] = {.format=SG_VERTEXFORMAT_FLOAT3, .count = 3, .type=FLOAT},
    .components[1] = {.format=SG_VERTEXFORMAT_FLOAT4, .count = 4, .type=FLOAT},
    .components[2] = {.format=SG_VERTEXFORMAT_FLOAT4, .count = 4, .type=FLOAT},
    .components[3] = {.format=SG_VERTEXFORMAT_FLOAT2, .count = 2, .type=FLOAT}
  },{
    .components[0] = {.format=SG_VERTEXFORMAT_FLOAT3, .count = 3, .type=FLOAT},
    .components[1] = {.format=SG_VERTEXFORMAT_UBYTE4, .count = 4, .type=BYTE},
    .components[2] = {.format=SG_VERTEXFORMAT_BYTE4,  .count = 4, .type=BYTE},
    .components[3] = {.format=SG_VERTEXFORMAT_BYTE4,  .count = 4, .type=BYTE}
  }
};

// Get color from a pixel from certain format
Color GetPixelColor(void *srcPtr, int format)
{
    Color color = { 0 };

    switch (format)
    {
        case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: color = (Color){ ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[0], 255 }; break;
        case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: color = (Color){ ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[1] }; break;
        case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
        {
            color.r = (unsigned char)((((unsigned short *)srcPtr)[0] >> 11)*255/31);
            color.g = (unsigned char)(((((unsigned short *)srcPtr)[0] >> 5) & 0b0000000000111111)*255/63);
            color.b = (unsigned char)((((unsigned short *)srcPtr)[0] & 0b0000000000011111)*255/31);
            color.a = 255;

        } break;
        case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
        {
            color.r = (unsigned char)((((unsigned short *)srcPtr)[0] >> 11)*255/31);
            color.g = (unsigned char)(((((unsigned short *)srcPtr)[0] >> 6) & 0b0000000000011111)*255/31);
            color.b = (unsigned char)((((unsigned short *)srcPtr)[0] & 0b0000000000011111)*255/31);
            color.a = (((unsigned short *)srcPtr)[0] & 0b0000000000000001)? 255 : 0;

        } break;
        case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
        {
            color.r = (unsigned char)((((unsigned short *)srcPtr)[0] >> 12)*255/15);
            color.g = (unsigned char)(((((unsigned short *)srcPtr)[0] >> 8) & 0b0000000000001111)*255/15);
            color.b = (unsigned char)(((((unsigned short *)srcPtr)[0] >> 4) & 0b0000000000001111)*255/15);
            color.a = (unsigned char)((((unsigned short *)srcPtr)[0] & 0b0000000000001111)*255/15);

        } break;
        case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: color = (Color){ ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[1], ((unsigned char *)srcPtr)[2], ((unsigned char *)srcPtr)[3] }; break;
        case PIXELFORMAT_UNCOMPRESSED_R8G8B8: color = (Color){ ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[1], ((unsigned char *)srcPtr)[2], 255 }; break;
        case PIXELFORMAT_UNCOMPRESSED_R32:
        {
            // NOTE: Pixel normalized float value is converted to [0..255]
            color.r = (unsigned char)(((float *)srcPtr)[0]*255.0f);
            color.g = (unsigned char)(((float *)srcPtr)[0]*255.0f);
            color.b = (unsigned char)(((float *)srcPtr)[0]*255.0f);
            color.a = 255;

        } break;
        case PIXELFORMAT_UNCOMPRESSED_R32G32B32:
        {
            // NOTE: Pixel normalized float value is converted to [0..255]
            color.r = (unsigned char)(((float *)srcPtr)[0]*255.0f);
            color.g = (unsigned char)(((float *)srcPtr)[1]*255.0f);
            color.b = (unsigned char)(((float *)srcPtr)[2]*255.0f);
            color.a = 255;

        } break;
        case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
        {
            // NOTE: Pixel normalized float value is converted to [0..255]
            color.r = (unsigned char)(((float *)srcPtr)[0]*255.0f);
            color.g = (unsigned char)(((float *)srcPtr)[1]*255.0f);
            color.b = (unsigned char)(((float *)srcPtr)[2]*255.0f);
            color.a = (unsigned char)(((float *)srcPtr)[3]*255.0f);

        } break;
        default: break;
    }

    return color;
}

// Set pixel color formatted into destination pointer
void SetPixelColor(void *dstPtr, Color color, int format)
{
    switch (format)
    {
        case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
        {
            // NOTE: Calculate grayscale equivalent color
            Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
            unsigned char gray = (unsigned char)((coln.x*0.299f + coln.y*0.587f + coln.z*0.114f)*255.0f);

            ((unsigned char *)dstPtr)[0] = gray;

        } break;
        case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
        {
            // NOTE: Calculate grayscale equivalent color
            Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
            unsigned char gray = (unsigned char)((coln.x*0.299f + coln.y*0.587f + coln.z*0.114f)*255.0f);

            ((unsigned char *)dstPtr)[0] = gray;
            ((unsigned char *)dstPtr)[1] = color.a;

        } break;
        case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
        {
            // NOTE: Calculate R5G6B5 equivalent color
            Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };

            unsigned char r = (unsigned char)(round(coln.x*31.0f));
            unsigned char g = (unsigned char)(round(coln.y*63.0f));
            unsigned char b = (unsigned char)(round(coln.z*31.0f));

            ((unsigned short *)dstPtr)[0] = (unsigned short)r << 11 | (unsigned short)g << 5 | (unsigned short)b;

        } break;
        case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
        {
            // NOTE: Calculate R5G5B5A1 equivalent color
            Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f };

            unsigned char r = (unsigned char)(round(coln.x*31.0f));
            unsigned char g = (unsigned char)(round(coln.y*31.0f));
            unsigned char b = (unsigned char)(round(coln.z*31.0f));
            unsigned char a = (coln.w > ((float)50/255.0f))? 1 : 0;

            ((unsigned short *)dstPtr)[0] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1 | (unsigned short)a;

        } break;
        case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
        {
            // NOTE: Calculate R5G5B5A1 equivalent color
            Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f };

            unsigned char r = (unsigned char)(round(coln.x*15.0f));
            unsigned char g = (unsigned char)(round(coln.y*15.0f));
            unsigned char b = (unsigned char)(round(coln.z*15.0f));
            unsigned char a = (unsigned char)(round(coln.w*15.0f));

            ((unsigned short *)dstPtr)[0] = (unsigned short)r << 12 | (unsigned short)g << 8 | (unsigned short)b << 4 | (unsigned short)a;

        } break;
        case PIXELFORMAT_UNCOMPRESSED_R8G8B8:
        {
            ((unsigned char *)dstPtr)[0] = color.r;
            ((unsigned char *)dstPtr)[1] = color.g;
            ((unsigned char *)dstPtr)[2] = color.b;

        } break;
        case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
        {
            ((unsigned char *)dstPtr)[0] = color.r;
            ((unsigned char *)dstPtr)[1] = color.g;
            ((unsigned char *)dstPtr)[2] = color.b;
            ((unsigned char *)dstPtr)[3] = color.a;

        } break;
        default: break;
    }
}

// Get pixel data size in bytes for certain format
// NOTE: Size can be requested for Image or Texture data
int GetPixelDataSize(int width, int height, int format)
{
    int dataSize = 0;       // Size in bytes
    int bpp = 0;            // Bits per pixel

    switch (format)
    {
        case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break;
        case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
        case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
        case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
        case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break;
        case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break;
        case PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break;
        case PIXELFORMAT_UNCOMPRESSED_R32: bpp = 32; break;
        case PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 32*3; break;
        case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: bpp = 32*4; break;
        case PIXELFORMAT_COMPRESSED_DXT1_RGB:
        case PIXELFORMAT_COMPRESSED_DXT1_RGBA:
        case PIXELFORMAT_COMPRESSED_ETC1_RGB:
        case PIXELFORMAT_COMPRESSED_ETC2_RGB:
        case PIXELFORMAT_COMPRESSED_PVRT_RGB:
        case PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break;
        case PIXELFORMAT_COMPRESSED_DXT3_RGBA:
        case PIXELFORMAT_COMPRESSED_DXT5_RGBA:
        case PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA:
        case PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: bpp = 8; break;
        case PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: bpp = 2; break;
        default: break;
    }

    dataSize = width*height*bpp/8;  // Total data size in bytes

    // Most compressed formats works on 4x4 blocks,
    // if texture is smaller, minimum dataSize is 8 or 16
    if ((width < 4) && (height < 4))
    {
        if ((format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) && (format < PIXELFORMAT_COMPRESSED_DXT3_RGBA)) dataSize = 8;
        else if ((format >= PIXELFORMAT_COMPRESSED_DXT3_RGBA) && (format < PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA)) dataSize = 16;
    }

    return dataSize;
}

uint32_t createRGBA(unsigned char* buffer)
{
    return ((uint32_t)buffer[3] << 24) | ((uint32_t)buffer[2] << 16) | ((uint32_t)buffer[1] << 8) | (uint32_t)buffer[0];
}

void update_image(sg_image image,Image data){
  uint32_t pixels[data.height][data.width];
  unsigned char* source = data.data;
  for (int y = 0; y < data.height; ++y) {
    for (int x = 0; x < data.width; ++x) {
      // printf("offset %i\n",source);
      pixels[y][x]=createRGBA(source);
      source+=4;
    }
  }
  sg_update_image(image,&(sg_image_data){
    .subimage[0][0] = SG_RANGE(pixels)
  });
}

void setCompressionPNG(int n){
  // stbi_write_png_compression_level = n;
}

Image imgFromMem(void* ptr, size_t size){
  Image image = { 0 };

  cp_image_t loaded = cp_load_png_mem(ptr,size);
  image.data = loaded.pix;
  image.width = loaded.w;
  image.height = loaded.h;
  image.mipmaps = 1;
  image.format = SG_PIXELFORMAT_RGBA8;

  if (image.data == NULL) 
    printf("IMAGE: Failed to load data");
    // printf("IMAGE:Data loaded successfully (%ix%i)", image.width, image.height);
  // else 

  return image;
}

sg_image texFromMem(void* ptr, size_t size){
  int png_width, png_height, num_channels;
  const int desired_channels = 4;

  cp_image_t loaded = cp_load_png_mem(ptr,size);

  if (loaded.pix) {
    /* ok, time to actually initialize the sokol-gfx texture */
    sg_image tex = sg_alloc_image();
    sg_init_image(tex, &(sg_image_desc){
      .width = loaded.w,
      .height = loaded.h,
      .pixel_format = SG_PIXELFORMAT_RGBA8,
      .data.subimage[0][0] = {
        .ptr = loaded.pix,
        .size = (size_t)(loaded.w * loaded.h * 4),
      }
    });
    free(loaded.pix);
    return tex;
  }
}

Texture2D texFromData(Image image){
  Texture2D texture = { 0 };

  if ((image.data != NULL) && (image.width != 0) && (image.height != 0)) {
    sg_image tex = sg_alloc_image();
    sg_init_image(tex, &(sg_image_desc){
      .width = image.width,
      .height = image.height,
      .pixel_format = image.format,
      .data.subimage[0][0] = {
        .ptr = image.data,
        .size = (size_t)(image.width * image.height * 4),
      }
    });
    texture.img     = tex;
    texture.id      = tex.id;
    texture.width   = image.width;
    texture.height  = image.height;
    texture.mipmaps = image.mipmaps;
    texture.format  = image.format;
    
    // puts("texture loaded"); 
  }else{ 
    puts("Image data is not valid to load texture"); 
  }

  return texture;
}

sg_image deftex;
sg_sampler defsmp;
gfx_state_t* gfx_setupState(){

  deftex = texFromData(imgFromMem(images_missing,images_missing_len)).img;
  defsmp = sg_make_sampler(&(sg_sampler_desc){0});
  // printf("%i %i",deftex.id,defsmp.id);
  // exit(0);
   for (int f = 0; vertexFormats[f].components[0].count != NULL; ++f) {
        vertexFormat_t* format = &vertexFormats[f];
        format->stride = 0;
        format->comp = 0;
        format->split = false;

        for (int a = 0; format->components[a].count != NULL; ++a) {
          vertexComponent_t component = format->components[a];
            if (a > 0 && (component.type!=format->components[a - 1].type)) {
                format->split = true;
            }

            format->stride += component.count*(component.type==FLOAT ? 4 : 1);
            format->comp += component.count;
        }
    }
    return &gfx;
}


void gfx_applyPipeline(){
  sg_apply_pipeline(gfx.pipes[gfx.pipeCurrent].pipe);
}

int gfx_updatePipeline(Mesh* mesh){

  sg_pipeline_desc* curr = &gfx.pipeNext;


  if (gfx.currentVertexFormat != mesh->format) { 
    vertexFormat_t format = vertexFormats[mesh->format];
    for (int a=0; a<7; a++) {
      // printf("%i,%i,%i\n", a,format.comp, format.split);
      if ((format.split && a>=format.comp) || (!format.split && a>0)){ 
        curr->layout.attrs[a].buffer_index = 0;
      }else{
        curr->layout.attrs[a].buffer_index = a;
      }
      if (format.components[a].count>0) { 
        curr->layout.attrs[a].format = format.components[a].format;
      }
    }
    gfx.currentVertexFormat = mesh->format;
  }
  
  bool new_pipe = true;
  int id = 0;

  curr->index_type = (mesh->indexed==true) ? 3 : 1;
  curr->face_winding = 1;

  curr->colors[0].blend.enabled = true;
  curr->colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA;
  curr->colors[0].blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
  curr->colors[0].blend.src_factor_alpha = SG_BLENDFACTOR_ONE;
  curr->colors[0].blend.dst_factor_alpha = SG_BLENDFACTOR_ONE;
  curr->colors[0].blend.op_alpha= 1;
  


  if (gfx.pipelineCount>0) {
    for (int i = 0; i < 16; ++i){
      sg_pipeline_desc* comp = &(gfx.pipes[i].desc);
      if (
           (curr->shader.id != comp->shader.id)
        || (curr->layout.buffers[0].stride != comp->layout.buffers[0].stride)
        || (curr->cull_mode != comp->cull_mode)
        || (curr->color_count != comp->color_count)
        || (curr->sample_count != comp->sample_count)
        || (curr->index_type != comp->index_type)
        || (curr->primitive_type != comp->primitive_type)
        || (curr->depth.write_enabled != comp->depth.write_enabled)
        || (curr->depth.compare != comp->depth.compare)
        || (curr->colors[0].pixel_format != comp->colors[0].pixel_format)
        || (curr->depth.pixel_format != comp->depth.pixel_format)
        || !((curr->layout.attrs[0].format == comp->layout.attrs[0].format) && (curr->layout.attrs[0].buffer_index == comp->layout.attrs[0].buffer_index))
        || !((curr->layout.attrs[1].format == comp->layout.attrs[1].format) && (curr->layout.attrs[1].buffer_index == comp->layout.attrs[1].buffer_index))
        || !((curr->layout.attrs[2].format == comp->layout.attrs[2].format) && (curr->layout.attrs[2].buffer_index == comp->layout.attrs[2].buffer_index))
        || !((curr->layout.attrs[3].format == comp->layout.attrs[3].format) && (curr->layout.attrs[3].buffer_index == comp->layout.attrs[3].buffer_index))
        )
      {
      }else{
        new_pipe = false;
        id = i;
        break;
      }
    }
  }

  if (new_pipe) {
    gfx.pipelineCount = gfx.pipelineCount + 1;
    id = gfx.pipelineCount;
      gfx.pipes[id].id = id;
      memcpy(&gfx.pipes[id].desc,curr,sizeof(sg_pipeline_desc));
      gfx.pipes[id].pipe = sg_make_pipeline(curr);
    gfx.pipeCurrent = id;
    // printf("\n new pipe %i",id);
    sg_apply_pipeline(gfx.pipes[id].pipe);
    
  }else{
    if (gfx.pipeCurrent != id) {
      gfx.pipeCurrent = id;
      // printf("\n switch pipe %i",id);
      sg_apply_pipeline(gfx.pipes[id].pipe);
    }
  }

  return gfx.pipelineCount;
}

void gfx_setWireframe(bool set){
  gfx.wireframe = true;
  gfx.pipeNext.primitive_type = set ? SG_PRIMITIVETYPE_LINES : SG_PRIMITIVETYPE_TRIANGLES;
}
void gfx_setMeshCullMode(sg_cull_mode mode){
  gfx.pipeNext.cull_mode = mode;
}

bool gfx_drawMesh(Mesh* mesh, mat4* mvp) {
    if (gfx.shader==NULL) return false;
    // Update shader samplers
    // for (int s = 0; s < sizeof(gfx.shader.samplers) / sizeof(gfx.shader.samplers[0]); s++) {
    //     if (gfx.shader.samplers[s]) {
    //         struct sg_image tex = gfx.shader.samplers[s].isCanvas ? gfx.shader.samplers[s].texture.color : gfx.shader.samplers[s].texture;
    //         mesh->bind.fs.images[s].id = tex.img.id;
    //     }
    // }

    // Update shader images
    for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) {
        if (gfx.shader->desc.fs.images[i].used) {
            if (mesh->bind.fs.images[i].id == 0) {
                mesh->bind.fs.images[i].id = deftex.id;
            }
        } else {
            mesh->bind.fs.images[i].id = 0;
            break;
        }
    }

    // // Update shader samplers
    for (int i = 0; i < SG_MAX_SHADERSTAGE_SAMPLERS; i++) {
        if (gfx.shader->desc.fs.samplers[i].used) {
            if (mesh->bind.fs.samplers[i].id == 0) {
                mesh->bind.fs.samplers[i].id = defsmp.id;
            }
        } else {
            mesh->bind.fs.samplers[i].id = 0;
            break;
        }
    }

    // Check if in pass
    if (!gfx.inPass) {
        printf("can't apply pipeline when not in pass\n");
        return false;
    }

    gfx_applyPipeline();

    if (gfx.shader->vs_uniforms.size!=0)
        sg_apply_uniforms(0, 0, &gfx.shader->vs_uniforms);
    if (gfx.shader->fs_uniforms.size!=0)
        sg_apply_uniforms(1, 0, &gfx.shader->fs_uniforms);

    sg_apply_bindings(&mesh->bind);

    sg_draw(mesh->vertexStart, mesh->vertexRange, 1);
    return true;
}

bool gfx_setVertices(
  Mesh* mesh, void* vertices, 
  int start, int last, 
  bool separate, bool append, bool mapped, bool updated)
{ 

  if (separate){
    strkvec* verts = (strkvec*)vertices;
    int v = 3; int c=4; int u=4;
    for (int i = start; i<=last; i++) {
      int j = i + 1;
      mesh->vertices[i * v]     = verts[j].x;
      mesh->vertices[i * v + 1] = verts[j].y;
      mesh->vertices[i * v + 2] = verts[j].z;

      mesh->colors[i*c+0]  = verts[j].r;
      mesh->colors[i*c+1]  = verts[j].g;
      mesh->colors[i*c+2]  = verts[j].b;
      mesh->colors[i*c+3]  = verts[j].a;

      // mesh->uvs[i*u+0] = 0//verts[j].nx
      // mesh->uvs[i*u+1] = 0//verts[j].ny
      // mesh->uvs[i*u+2] = 0//verts[j].u-127
      // mesh->uvs[i*u+3] = 0//verts[j].v-127
    }
  }else{
    m_vert* verts = (m_vert*)vertices;
    int a = 13;
    for (int i = start; i<=last; i++) {
      int j = i + 1;
      mesh->vertices[i * a] = verts[j].x;
      
      mesh->vertices[i * a + 1] = verts[j].y;
      mesh->vertices[i * a + 2] = verts[j].z;

      mesh->vertices[i * a + 3] = verts[j].r;
      mesh->vertices[i * a + 4] = verts[j].g;
      mesh->vertices[i * a + 5] = verts[j].b;
      mesh->vertices[i * a + 6] = verts[j].a;

      //   mesh->vertices[i*a+7]  = verts[j].nx
      //   mesh->vertices[i*a+8] = verts[j].ny
      //   mesh->vertices[i*a+9] = verts[j].nz
      //   mesh->vertices[i*a+10] = verts[j].nz

      mesh->vertices[i * a + 11] = verts[j].u;
      mesh->vertices[i * a + 12] = verts[j].v;
    } 
  }
  // if (append) {
  //   int offset = sg_append_buffer(mesh->bind.vertex_buffers[0], &SG_RANGE(mesh->vertices));
  //   mesh->bind.vertex_buffer_offsets[0] = offset;
  //   if (mesh->map!=NULL) {
  //       unsigned int ioffset = sg_append_buffer(mesh->bind.index_buffer, &SG_RANGE(mesh->map));
  //       mesh->bind.index_buffer_offset = ioffset;
  //   }
  // } else {
  //   if (updated == true) {
  //       return true;
  //   }
  //   if (separate) {
  //       sg_update_buffer(mesh->bind.vertex_buffers[0], &SG_RANGE(mesh->vertices));
  //       sg_update_buffer(mesh->bind.vertex_buffers[1], &SG_RANGE(mesh->colors));
  //       sg_update_buffer(mesh->bind.vertex_buffers[2], &SG_RANGE(mesh->uvs));
  //   } else {
  //       sg_update_buffer(mesh->bind.vertex_buffers[0], &SG_RANGE(mesh->vertices));
  //   }
  //   if (mesh->map!=NULL) {
  //       sg_update_buffer(mesh->bind.index_buffer, &SG_RANGE(mesh->map));
  //   }
  // }
  return false;
}

// void gfx_draw3D(Mesh mesh, mat4 transform){

//   gfx_drawBatch()

//   updatePipeline(mesh);

//   // gfx_send("has_tex", mesh->texture and 1 or 0)
//   // gfx_send("mainColor", gfx.color)

//   gfx_drawMesh(mesh, HMM_MultiplyMat4(gfx.camera.vp, transform or gfx.identityMatrix))

// }


// vec3
// Raycasting::GetCameraRayDirectionFromScreenspacePosition(vec2 screenspace_position)
// {
//     RenderContext *rcx = &game_state->rcx;

//     mat4 view = rcx->GetViewMatrix();
//     mat4 proj = rcx->GetProjectionMatrix();

//     vec3 camera_x = Vec3(view.Elements[0][0],
//                          view.Elements[1][0],
//                          view.Elements[2][0]);

//     vec3 camera_y = Vec3(view.Elements[0][1],
//                          view.Elements[1][1],
//                          view.Elements[2][1]);

//     vec3 camera_z = Vec3(view.Elements[0][2],
//                          view.Elements[1][2],
//                          view.Elements[2][2]);

//     float film_half_w = 1.0f / proj.Elements[0][0];
//     float film_half_h = 1.0f / proj.Elements[1][1];

//     vec3 ray_d = Normalize(screenspace_position.X*camera_x*film_half_w +
//                            screenspace_position.Y*camera_y*film_half_h +
//                            -camera_z);

//     return ray_d;
// }


bool CheckCollisionRayBox(Ray ray, BoundingBox box)
{
    bool collision = false;

    float t[8];
    t[0] = (box.min.x - ray.position.x)/ray.direction.x;
    t[1] = (box.max.x - ray.position.x)/ray.direction.x;
    t[2] = (box.min.y - ray.position.y)/ray.direction.y;
    t[3] = (box.max.y - ray.position.y)/ray.direction.y;
    t[4] = (box.min.z - ray.position.z)/ray.direction.z;
    t[5] = (box.max.z - ray.position.z)/ray.direction.z;
    t[6] = (float)fmax(fmax(fmin(t[0], t[1]), fmin(t[2], t[3])), fmin(t[4], t[5]));
    t[7] = (float)fmin(fmin(fmax(t[0], t[1]), fmax(t[2], t[3])), fmax(t[4], t[5]));

    collision = !(t[7] < 0 || t[6] > t[7]);

    return collision;
}

// Add two vectors
Vector3 Vector3Add(Vector3 v1, Vector3 v2)
{
    Vector3 result = { v1.x + v2.x, v1.y + v2.y, v1.z + v2.z };
    return result;
}
// Subtract two vectors
Vector3 Vector3Subtract(Vector3 v1, Vector3 v2)
{
    Vector3 result = { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z };
    return result;
}

// Calculate two vectors cross product
Vector3 Vector3CrossProduct(Vector3 v1, Vector3 v2)
{
    Vector3 result = { v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x };
    return result;
}
// Calculate two vectors dot product
float Vector3DotProduct(Vector3 v1, Vector3 v2)
{
    float result = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z);
    return result;
}
// Normalize provided vector
Vector3 Vector3Normalize(Vector3 v)
{
    Vector3 result = v;

    float length, ilength;
    length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z);
    if (length == 0.0f) length = 1.0f;
    ilength = 1.0f/length;

    result.x *= ilength;
    result.y *= ilength;
    result.z *= ilength;

    return result;
}
Vector3 Vector3Scale(Vector3 v, float scalar)
{
    Vector3 result = { v.x*scalar, v.y*scalar, v.z*scalar };
    return result;
}
// Get collision info between ray and triangle
// NOTE: Based on https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm
RayHitInfo GetCollisionRayTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3)
{
    #define EPSILON 0.000001        // A small number

    Vector3 edge1, edge2;
    Vector3 p, q, tv;
    float det, invDet, u, v, t;
    RayHitInfo result = {0};

    // Find vectors for two edges sharing V1
    edge1 = Vector3Subtract(p2, p1);
    edge2 = Vector3Subtract(p3, p1);

    // Begin calculating determinant - also used to calculate u parameter
    p = Vector3CrossProduct(ray.direction, edge2);

    // If determinant is near zero, ray lies in plane of triangle or ray is parallel to plane of triangle
    det = Vector3DotProduct(edge1, p);

    // Avoid culling!
    if ((det > -EPSILON) && (det < EPSILON)) return result;

    invDet = 1.0f/det;

    // Calculate distance from V1 to ray origin
    tv = Vector3Subtract(ray.position, p1);

    // Calculate u parameter and test bound
    u = Vector3DotProduct(tv, p)*invDet;

    // The intersection lies outside of the triangle
    if ((u < 0.0f) || (u > 1.0f)) return result;

    // Prepare to test v parameter
    q = Vector3CrossProduct(tv, edge1);

    // Calculate V parameter and test bound
    v = Vector3DotProduct(ray.direction, q)*invDet;

    // The intersection lies outside of the triangle
    if ((v < 0.0f) || ((u + v) > 1.0f)) return result;

    t = Vector3DotProduct(edge2, q)*invDet;

    if (t > EPSILON)
    {
        // Ray hit, get hit point and normal
        result.hit = true;
        result.distance = t;
        result.hit = true;
        result.normal = Vector3Normalize(Vector3CrossProduct(edge1, edge2));
        result.position = Vector3Add(ray.position, Vector3Scale(ray.direction, t));
    }

    return result;
}


RayHitInfo GetCollisionRayMesh(Ray ray, Mesh* mesh)
{
    RayHitInfo result = { 0 };

    // Check if mesh vertex data on CPU for testing
    if (mesh->vertices != NULL)
    {

        // printf("tricount %i\n",mesh->triangleCount);
        float* vertdata = (float*)mesh->vertices;
        // Test against all triangles in mesh
        // for (int i = 0; i < mesh->vertexCount; i++) {
        //   int v = i*mesh->components;
        //   printf("%03.0f %03.0f %03.0f %03.0f %03.0f %03.0f %03.0f %03.0f %03.0f %03.0f %03.0f %03.0f %03.0f  \n",vertdata[v],
        //     vertdata[v+1],
        //     vertdata[v+2],
        //     vertdata[v+3],
        //     vertdata[v+4],
        //     vertdata[v+5],
        //     vertdata[v+6],
        //     vertdata[v+7],
        //     vertdata[v+8],
        //     vertdata[v+9],
        //     vertdata[v+10],
        //     vertdata[v+11],
        //     vertdata[v+12]

        //     );
        // }
        for (int i = 0; i < mesh->triangleCount; i++)
        { 
            vec4 aa, bb, cc;
            vec3 a, b, c;
            int v1, v2, v3;

            if (mesh->indexed) {
              v1 = mesh->map[i*3]*mesh->components;
              v2 = mesh->map[i*3+1]*mesh->components;
              v3 = mesh->map[i*3+2]*mesh->components;
            } else {
              v1 = i*3*mesh->components;
              v2 = (i*3+1)*mesh->components;
              v3 = (i*3+2)*mesh->components;
            }
            a = (vec3){.x=vertdata[v1 + 0], .y=vertdata[v1 + 1], .z=vertdata[v1 + 2]};
            b = (vec3){.x=vertdata[v2 + 0], .y=vertdata[v2 + 1], .z=vertdata[v2 + 2]};
            c = (vec3){.x=vertdata[v3 + 0], .y=vertdata[v3 + 1], .z=vertdata[v3 + 2]};
            // printf("%0.2f %0.2f %0.2f\n",a.x,a.y,a.z);
            // printf(" indexed:%i %i,%i,%i (%03.0f %03.0f %03.0f) (%03.0f %03.0f %03.0f) (%03.0f %03.0f %03.0f)\n",mesh->indexed ? 1 : 0 ,v1,v2,v3, a.x,a.y,a.z, b.x,b.y,b.z,  c.x,c.y,c.z); 

            RayHitInfo triHitInfo = GetCollisionRayTriangle(ray, a, b, c);

            if (triHitInfo.hit)
            {
                // Save the closest hit triangle
                if ((!result.hit) || (result.distance > triHitInfo.distance)) result = triHitInfo;
            }
        }
    }
    return result;
}

// Get collision info between ray and model
RayHitInfo GetCollisionRayModel(Ray ray, Model model)
{
    RayHitInfo result = { 0 };

    for (int m = 0; m < model.meshCount; m++)
    { 
      // printf("test model\n");
        RayHitInfo meshHitInfo = GetCollisionRayMesh(ray, &model.meshes[m]);

        if (meshHitInfo.hit)
        {
            // Save the closest hit mesh
            if ((!result.hit) || (result.distance > meshHitInfo.distance)) result = meshHitInfo;
        }
    }

    return result;
}