#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
enum {

  GL_FALSE = 0,
  GL_TRUE = 1,

  GL_BYTE = 0x1400,
  GL_UNSIGNED_BYTE = 0x1401,
  GL_SHORT = 0x1402,
  GL_UNSIGNED_SHORT = 0x1403,
  GL_INT = 0x1404,
  GL_UNSIGNED_INT = 0x1405,
  GL_FLOAT = 0x1406,
  GL_DOUBLE = 0x140A,

  GL_LINES = 0x0001,
  GL_POINTS = 0x0000,
  GL_LINE_STRIP = 0x0003,
  GL_LINE_LOOP = 0x0002,
  GL_TRIANGLES = 0x0004,
  GL_TRIANGLE_STRIP = 0x0005,
  GL_TRIANGLE_FAN = 0x0006,
  GL_QUADS = 0x0007,
  GL_QUAD_STRIP = 0x0008,
  GL_POLYGON = 0x0009,

  GL_VERTEX_ARRAY = 0x8074,
  GL_NORMAL_ARRAY = 0x8075,
  GL_COLOR_ARRAY = 0x8076,
  GL_TEXTURE_COORD_ARRAY = 0x8078,
  GL_EDGE_FLAG_ARRAY = 0x8079,

  GL_MATRIX_MODE = 0x0BA0,
  GL_MODELVIEW = 0x1700,
  GL_PROJECTION = 0x1701,
  GL_TEXTURE = 0x1702,

  GL_POINT_SIZE = 0x0B11,
  GL_POINT_SIZE_RANGE = 0x0B12,

  GL_LINE_WIDTH = 0x0B21,
  GL_LINE_WIDTH_RANGE = 0x0B22,

  GL_POINT = 0x1B00,
  GL_LINE = 0x1B01,
  GL_FILL = 0x1B02,
  GL_CCW = 0x0901,
  GL_CW = 0x0900,
  GL_FRONT = 0x0404,
  GL_BACK = 0x0405,
  GL_CULL_FACE = 0x0B44,

  GL_COMPILE = 0x1300,
  GL_COMPILE_AND_EXECUTE = 0x1301,

  GL_DEPTH_TEST = 0x0B71,
  GL_BLEND = 0x0BE2,

  GL_LIGHTING = 0x0B50,
  GL_LIGHT0 = 0x4000,
  GL_LIGHT1 = 0x4001,
  GL_LIGHT2 = 0x4002,
  GL_LIGHT3 = 0x4003,
  GL_LIGHT4 = 0x4004,
  GL_LIGHT5 = 0x4005,
  GL_LIGHT6 = 0x4006,
  GL_LIGHT7 = 0x4007,

  GL_SPOT_EXPONENT = 0x1205,
  GL_SPOT_CUTOFF = 0x1206,

  GL_CONSTANT_ATTENUATION = 0x1207,
  GL_LINEAR_ATTENUATION = 0x1208,
  GL_QUADRATIC_ATTENUATION = 0x1209,
  GL_AMBIENT = 0x1200,
  GL_DIFFUSE = 0x1201,
  GL_SPECULAR = 0x1202,
  GL_SHININESS = 0x1601,
  GL_EMISSION = 0x1600,
  GL_POSITION = 0x1203,
  GL_SPOT_DIRECTION = 0x1204,
  GL_AMBIENT_AND_DIFFUSE = 0x1602,
  GL_LIGHT_MODEL_TWO_SIDE = 0x0B52,
  GL_LIGHT_MODEL_LOCAL_VIEWER = 0x0B51,
  GL_LIGHT_MODEL_AMBIENT = 0x0B53,
  GL_FRONT_AND_BACK = 0x0408,

  GL_SHADE_MODEL = 0x0B54,
  GL_FLAT = 0x1D00,
  GL_SMOOTH = 0x1D01,
  GL_COLOR_MATERIAL = 0x0B57,
  GL_NORMALIZE = 0x0BA1,

  GL_RENDER = 0x1C00,
  GL_SELECT = 0x1C02,

  GL_RGB = 0x1907,

  GL_MAX_MODELVIEW_STACK_DEPTH = 0x0D36,
  GL_MAX_PROJECTION_STACK_DEPTH = 0x0D38,
  GL_MAX_TEXTURE_STACK_DEPTH = 0x0D39,
  GL_MAX_LIGHTS = 0x0D31,
  GL_MAX_TEXTURE_SIZE = 0x0D33,

  GL_MODELVIEW_MATRIX = 0x0BA6,
  GL_PROJECTION_MATRIX = 0x0BA7,
  GL_TEXTURE_MATRIX = 0x0BA8,
  GL_VIEWPORT = 0x0BA2,

  GL_PERSPECTIVE_CORRECTION_HINT = 0x0C50,
  GL_DONT_CARE = 0x1100,
  GL_FASTEST = 0x1101,
  GL_NICEST = 0x1102,

  GL_TEXTURE_2D = 0x0DE1,

  GL_VENDOR = 0x1F00,
  GL_RENDERER = 0x1F01,
  GL_VERSION = 0x1F02,

  GL_NO_ERROR = 0,
  GL_INVALID_VALUE = 0x0501,
  GL_INVALID_ENUM = 0x0500,
  GL_INVALID_OPERATION = 0x0502,
  GL_STACK_OVERFLOW = 0x0503,
  GL_STACK_UNDERFLOW = 0x0504,
  GL_OUT_OF_MEMORY = 0x0505,

  GL_DEPTH_BUFFER_BIT = 0x0100,
  GL_COLOR_BUFFER_BIT = 0x4000,
};

void glEnable(int code);
void glDisable(int code);

void glShadeModel(int mode);
void glCullFace(int mode);
void glPolygonMode(int face,int mode);

void glBegin(int type);
void glEnd(void);

void glVertex2f(float x,float y);
void glVertex2fv(float *v);
void glVertex4f(float x,float y,float z,float w);
void glVertex4fv(float *v);
void glVertex3f(float x,float y,float z);
void glVertex3fv(float *v);

void glNormal3f(float x,float y,float z);
void glNormal3fv(float *v);

void glColor3f(float x,float y,float z);
void glColor3fv(float *v);
void glColor4f(float r,float g,float b,float a);
void glColor4fv(float *v);

void glTexCoord2f(float s,float t);
void glTexCoord2fv(float *v);
void glTexCoord4f(float s,float t,float r,float q);
void glTexCoord4fv(float *v);

void glEdgeFlag(int flag);

void glMatrixMode(int mode);
void glLoadMatrixf(const float *m);
void glLoadIdentity(void);
void glMultMatrixf(const float *m);
void glPushMatrix(void);
void glPopMatrix(void);
void glRotatef(float angle,float x,float y,float z);
void glTranslatef(float x,float y,float z);
void glScalef(float x,float y,float z);

void glViewport(int x,int y,int width,int height);
void glFrustum(double left,double right,double bottom,double top, double near,double far);

unsigned int glGenLists(int range);
int glIsList(unsigned int list);
void glNewList(unsigned int list,int mode);
void glEndList(void);
void glCallList(unsigned int list);

void glClear(int mask);
void glClearColor(float r,float g,float b,float a);

int glRenderMode(int mode);
void glSelectBuffer(int size,unsigned int *buf);

void glInitNames(void);
void glPushName(unsigned int name);
void glPopName(void);
void glLoadName(unsigned int name);


void glGenTextures(int n, unsigned int *textures);
void glDeleteTextures(int n, const unsigned int *textures);
void glBindTexture(int target,int texture);
void glTexImage2D( int target, int level, int components,
                   int width, int height, int border,
                   int format, int type, void *pixels);

void glMaterialfv(int mode,int type,float *v);
void glMaterialf(int mode,int type,float v);
void glColorMaterial(int mode,int type);

void glLightfv(int light,int type,float *v);
void glLightf(int light,int type,float v);
void glLightModeli(int pname,int param);
void glLightModelfv(int pname,float *param);

void glHint(int target,int mode);
void glGetIntegerv(int pname,int *params);
void glGetFloatv(int pname, float *v);
const char * glGetString(int name);

void glFrontFace(int mode);

void glEnableClientState(int iarray);
void glDisableClientState(int iarray);
void glArrayElement(int i);
void glVertexPointer (int isize, int type, int stride, const void *pointer);
void glColorPointer (int isize, int type, int stride, const void *pointer);
void glNormalPointer ( int type, int stride, const void *pointer);
void glTexCoordPointer(int isize, int type, int stride, const void *pointer);
void glEdgeFlagPointer( int stride, const void *pointer);

void glDrawArrays(int mode, int first, int count);

void glFlush(void);
void glDepthFunc(int func);

typedef unsigned short PIXEL;

typedef struct {
  float m[4][4];
} M4;

typedef struct {
  float m[3][3];
} M3;

typedef struct {
  float v[3];
} V3;

typedef struct {
  float v[4];
} V4;

typedef struct {
  int iWidth,iHeight;
  int iPitchInBytes;
  int iPitchInPixels;
  int iMode;

  unsigned short* zbuf;
  PIXEL * current_texture;
  PIXEL * pbuf;
  int frame_buffer_allocated;

  int nb_colors;
  unsigned char * dctable;
  int * ctable;
} ZBuffer;

typedef struct TGLSpecBuf {
  int shininess_i;
  int last_used;
  float buf[1024 +1];
  struct TGLSpecBuf *next;
} TGLSpecBuf;

typedef struct TGLLight {
  V4 ambient;
  V4 diffuse;
  V4 specular;
  V4 position;
  V3 spot_direction;
  float spot_exponent;
  float spot_cutoff;
  float attenuation[3];

  float cos_spot_cutoff;
  V3 norm_spot_direction;
  V3 norm_position;

  int enabled;
  struct TGLLight *next,*prev;
} TGLLight;

typedef struct TGLMaterial {
  V4 emission;
  V4 ambient;
  V4 diffuse;
  V4 specular;
  float shininess;

  int shininess_i;
  int do_specular;
} TGLMaterial;

typedef struct TGLViewport {
  int iX,iY,iWidth,iHeight;
  V3 scale;
  V3 trans;
  int iNotUpToDate;
} TGLViewport;

typedef union {
  int o;
  float f;
  int i;
  unsigned int c;
  void * p;
} TGLParam;

typedef struct TGLParamBuffer {
  TGLParam ops[512];
  struct TGLParamBuffer *next;
} TGLParamBuffer;

typedef struct TGLList {
  TGLParamBuffer *first_op_buffer;
} TGLList;

typedef struct {
  int ix,iy,iz;
  int is,it;
  int ir,ig,ib;
  float fs,ft;
} ZBufferPoint;

typedef struct TGLVertex {
  int edge_flag;
  V3 normal;
  V4 coord;
  V4 tex_coord;
  V4 color;

  V4 ec;
  V4 pc;
  int clip_code;
  ZBufferPoint zp;
} TGLVertex;

typedef struct TGLImage {
  void *pixmap;
  int xsize,ysize,components;
} TGLImage;

typedef struct TGLTexture {
  TGLImage images[11];
  int handle;
  struct TGLTexture *next,*prev;
} TGLTexture;

typedef struct TGLSharedState {
  TGLList **lists;
  TGLTexture **texture_hash_table;
} TGLSharedState;

struct TGLContext;

typedef void (*tgl_draw_triangle_func)(struct TGLContext *c,
                                       TGLVertex *p0,
                                       TGLVertex *p1,
                                       TGLVertex *p2);


typedef struct TGLContext {

  ZBuffer *zb;

  TGLLight lights[8];
  TGLLight *first_light;
  V4 ambient_light_model;
  int local_light_model;
  int lighting_enabled;
  int light_model_two_side;

  TGLMaterial materials[2];
  int color_material_enabled;
  int current_color_material_mode;
  int current_color_material_type;

  TGLTexture *current_texture;
  int texture_2d_enabled;
  int blend_enabled;

  TGLSharedState shared_state;

  TGLParamBuffer *current_op_buffer;
  int current_op_buffer_index;
  int exec_flag;
  int compile_flag;

  int matrix_mode;
  M4 *matrix_stack[3];
  M4 *matrix_stack_ptr[3];
  int matrix_stack_depth_max[3];

  M4 matrix_model_view_inv;
  M4 matrix_model_projection;
  int iModelProjectionNotUpToDate;
  int matrix_model_projection_no_w_transform;
  int apply_texture_matrix;

  TGLViewport viewport;

  int perspective_hint;

  int polygon_mode_back;
  int polygon_mode_front;
  int current_front_face;
  int current_shade_model;
  int current_cull_face;

  int cull_face_enabled;
  int normalize_enabled;
  tgl_draw_triangle_func draw_triangle_back;
  tgl_draw_triangle_func draw_triangle_front;

  int render_mode;
  unsigned int *select_buffer;
  int select_size;
  unsigned int *select_ptr,*select_hit;
  int select_overflow;
  int select_hits;

  unsigned int name_stack[64];
  int name_stack_size;

  V4 clear_color;

  V4 current_color;

  unsigned int current_ui_color[3];
  V4 current_normal;
  V4 current_tex_coord;
  int current_edge_flag;

  int in_begin;
  int begin_type;
  int vertex_n,vertex_cnt;
  int vertex_max;
  TGLVertex *vertex;

  int ignore_in_begin;
  int client_states;

  void * vertex_array;
  int vertex_array_size;
  int vertex_array_type;
  int vertex_array_stride;

  void * normal_array;
  int normal_array_type;
  int normal_array_stride;

  void * color_array;
  int color_array_size;
  int color_array_type;
  int color_array_stride;

  void * texcoord_array;
  int texcoord_array_size;
  int texcoord_array_type;
  int texcoord_array_stride;

  int * edgeflag_array;
  int edgeflag_array_stride;

  TGLSpecBuf *specbuf_first;
  int specbuf_used_counter;
  int specbuf_num_buffers;

  int depth_test;
} TGLContext;

void tgl_Init(void * zbuffer1);
void tgl_Close(void);

void tgl_add_op(TGLParam *p);

void tgl_transform_to_viewport(TGLContext *c,TGLVertex *v);

void tgl_add_select(TGLContext *c,unsigned int zmin,unsigned int zmax);
void tgl_enable_disable_light(TGLContext *c,int light,int v);
void tgl_shade_vertex(TGLContext *c,TGLVertex *v);

void tgl_InitTextures(TGLContext *c);
void tgl_EndTextures(TGLContext *c);
TGLTexture *tgl_alloc_texture(TGLContext *c,int h);

void tgl_convertRGB_to_5R6G5B(unsigned short *pixmap,unsigned char *rgb, int xsize,int ysize);
void tgl_resizeImageNoInterpolateRGB_RGB(unsigned char *dest,int xsize_dest,int ysize_dest, unsigned char *src,int xsize_src,int ysize_src);
void tgl_resizeImageNoInterpolateRGBA_RGB(unsigned char *dest,int xsize_dest,int ysize_dest, unsigned char *src,int xsize_src,int ysize_src);

TGLSpecBuf * tgl_specbuf_get_buffer(TGLContext *c, const int shininess_i, const float shininess);
void tgl_specbuf_cleanup(TGLContext *c);

ZBuffer * ZB_open(int xsize,int ysize,int mode, int nb_colors, unsigned char *color_indexes, int *color_table, void *frame_buffer);
void ZB_close(ZBuffer *zb);

void ZB_resize(ZBuffer *zb,void *frame_buffer,int xsize,int ysize);
void ZB_clear(ZBuffer *zb,int clear_z,int z, int clear_color,int r,int g,int b);

void ZB_copyFrameBuffer(ZBuffer *zb,void *buf,int iPitch);

void ZB_initDither(ZBuffer *zb,int nb_colors, unsigned char *color_indexes,int *color_table);
void ZB_closeDither(ZBuffer *zb);
void ZB_ditherFrameBuffer(ZBuffer *zb,unsigned char *dest, int iPitch);

void ZB_DrawPoint (ZBuffer *zb,ZBufferPoint *p);
void ZB_DrawLine (ZBuffer *zb, ZBufferPoint *p0, ZBufferPoint *p1);

void ZB_DrawPoint_z(ZBuffer *zb,ZBufferPoint *p);
void ZB_DrawLine_z(ZBuffer *zb, ZBufferPoint *p0, ZBufferPoint *p1);

void ZB_DrawTriangleFlat (ZBuffer *zb, ZBufferPoint *p0,ZBufferPoint *p1,ZBufferPoint *p2);
void ZB_DrawTriangleSmooth (ZBuffer *zb, ZBufferPoint *p0,ZBufferPoint *p1,ZBufferPoint *p2);

void ZB_DrawTriangleFlat_z (ZBuffer *zb, ZBufferPoint *p0,ZBufferPoint *p1,ZBufferPoint *p2);
void ZB_DrawTriangleSmooth_z (ZBuffer *zb, ZBufferPoint *p0,ZBufferPoint *p1,ZBufferPoint *p2);

void ZB_setTexture(ZBuffer *zb, PIXEL *texture);

void ZB_DrawTriangleTextured (ZBuffer *zb, ZBufferPoint *p0,ZBufferPoint *p1,ZBufferPoint *p2);
void ZB_DrawTriangleTexturedPerspective (ZBuffer *zb, ZBufferPoint *p0,ZBufferPoint *p1,ZBufferPoint *p2);
void ZB_DrawTriangleTexturedPerspectiveNice(ZBuffer *zb, ZBufferPoint *p0,ZBufferPoint *p1,ZBufferPoint *p2);

void ZB_DrawTriangleTextured_b (ZBuffer *zb, ZBufferPoint *p0,ZBufferPoint *p1,ZBufferPoint *p2);
void ZB_DrawTriangleTexturedPerspective_b (ZBuffer *zb, ZBufferPoint *p0,ZBufferPoint *p1,ZBufferPoint *p2);
void ZB_DrawTriangleTexturedPerspectiveNice_b(ZBuffer *zb, ZBufferPoint *p0,ZBufferPoint *p1,ZBufferPoint *p2);

void ZB_DrawTriangleTextured_z (ZBuffer *zb, ZBufferPoint *p0,ZBufferPoint *p1,ZBufferPoint *p2);
void ZB_DrawTriangleTexturedPerspective_z (ZBuffer *zb, ZBufferPoint *p0,ZBufferPoint *p1,ZBufferPoint *p2);
void ZB_DrawTriangleTexturedPerspectiveNice_z(ZBuffer *zb, ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2);

void ZB_DrawTriangleTextured_zb (ZBuffer *zb, ZBufferPoint *p0,ZBufferPoint *p1,ZBufferPoint *p2);
void ZB_DrawTriangleTexturedPerspective_zb (ZBuffer *zb, ZBufferPoint *p0,ZBufferPoint *p1,ZBufferPoint *p2);
void ZB_DrawTriangleTexturedPerspectiveNice_zb(ZBuffer *zb, ZBufferPoint *p0,ZBufferPoint *p1,ZBufferPoint *p2);

enum {
OP_Color ,
OP_TexCoord ,
OP_EdgeFlag ,
OP_Normal ,
OP_Begin ,
OP_Vertex ,
OP_End ,
OP_EnableDisable ,
OP_MatrixMode ,
OP_LoadMatrix ,
OP_LoadIdentity ,
OP_MultMatrix ,
OP_PushMatrix ,
OP_PopMatrix ,
OP_Rotate ,
OP_Translate ,
OP_Scale ,
OP_Viewport ,
OP_Frustum ,
OP_Material ,
OP_ColorMaterial ,
OP_Light ,
OP_LightModel ,
OP_Clear ,
OP_ClearColor ,
OP_InitNames ,
OP_PushName ,
OP_PopName ,
OP_LoadName ,
OP_TexImage2D ,
OP_BindTexture ,
OP_ShadeModel ,
OP_CullFace ,
OP_FrontFace ,
OP_PolygonMode ,
OP_CallList ,
OP_Hint ,
OP_EndList ,
OP_NextBuffer ,
OP_ArrayElement ,
OP_EnableClientState ,
OP_DisableClientState ,
OP_VertexPointer ,
OP_ColorPointer ,
OP_NormalPointer ,
OP_TexCoordPointer ,
OP_EdgeFlagPointer ,
OP_DrawArrays ,
};


void tglopColor (TGLContext *,TGLParam *);
void tglopTexCoord (TGLContext *,TGLParam *);
void tglopEdgeFlag (TGLContext *,TGLParam *);
void tglopNormal (TGLContext *,TGLParam *);

void tglopBegin (TGLContext *,TGLParam *);
void tglopVertex (TGLContext *,TGLParam *);
void tglopEnd (TGLContext *,TGLParam *);

void tglopEnableDisable (TGLContext *,TGLParam *);

void tglopMatrixMode (TGLContext *,TGLParam *);
void tglopLoadMatrix (TGLContext *,TGLParam *);
void tglopLoadIdentity (TGLContext *,TGLParam *);
void tglopMultMatrix (TGLContext *,TGLParam *);
void tglopPushMatrix (TGLContext *,TGLParam *);
void tglopPopMatrix (TGLContext *,TGLParam *);
void tglopRotate (TGLContext *,TGLParam *);
void tglopTranslate (TGLContext *,TGLParam *);
void tglopScale (TGLContext *,TGLParam *);

void tglopViewport (TGLContext *,TGLParam *);
void tglopFrustum (TGLContext *,TGLParam *);

void tglopMaterial (TGLContext *,TGLParam *);
void tglopColorMaterial (TGLContext *,TGLParam *);
void tglopLight (TGLContext *,TGLParam *);
void tglopLightModel (TGLContext *,TGLParam *);

void tglopClear (TGLContext *,TGLParam *);
void tglopClearColor (TGLContext *,TGLParam *);

void tglopInitNames (TGLContext *,TGLParam *);
void tglopPushName (TGLContext *,TGLParam *);
void tglopPopName (TGLContext *,TGLParam *);
void tglopLoadName (TGLContext *,TGLParam *);

void tglopTexImage2D (TGLContext *,TGLParam *);
void tglopBindTexture (TGLContext *,TGLParam *);

void tglopShadeModel (TGLContext *,TGLParam *);
void tglopCullFace (TGLContext *,TGLParam *);
void tglopFrontFace (TGLContext *,TGLParam *);
void tglopPolygonMode (TGLContext *,TGLParam *);

void tglopCallList (TGLContext *,TGLParam *);
void tglopHint (TGLContext *,TGLParam *);


void tglopEndList (TGLContext *,TGLParam *);
void tglopNextBuffer (TGLContext *,TGLParam *);


void tglopArrayElement (TGLContext *,TGLParam *);
void tglopEnableClientState (TGLContext *,TGLParam *);
void tglopDisableClientState (TGLContext *,TGLParam *);
void tglopVertexPointer (TGLContext *,TGLParam *);
void tglopColorPointer (TGLContext *,TGLParam *);
void tglopNormalPointer (TGLContext *,TGLParam *);
void tglopTexCoordPointer (TGLContext *,TGLParam *);
void tglopEdgeFlagPointer (TGLContext *,TGLParam *);

void tglopDrawArrays (TGLContext *,TGLParam *);

static void (*op_table_func[])(TGLContext *,TGLParam *)= {
tglopColor ,
tglopTexCoord ,
tglopEdgeFlag ,
tglopNormal ,
tglopBegin ,
tglopVertex ,
tglopEnd ,
tglopEnableDisable ,
tglopMatrixMode ,
tglopLoadMatrix ,
tglopLoadIdentity ,
tglopMultMatrix ,
tglopPushMatrix ,
tglopPopMatrix ,
tglopRotate ,
tglopTranslate ,
tglopScale ,
tglopViewport ,
tglopFrustum ,
tglopMaterial ,
tglopColorMaterial ,
tglopLight ,
tglopLightModel ,
tglopClear ,
tglopClearColor ,
tglopInitNames ,
tglopPushName ,
tglopPopName ,
tglopLoadName ,
tglopTexImage2D ,
tglopBindTexture ,
tglopShadeModel ,
tglopCullFace ,
tglopFrontFace ,
tglopPolygonMode ,
tglopCallList ,
tglopHint ,
tglopEndList ,
tglopNextBuffer ,
tglopArrayElement ,
tglopEnableClientState ,
tglopDisableClientState ,
tglopVertexPointer ,
tglopColorPointer ,
tglopNormalPointer ,
tglopTexCoordPointer ,
tglopEdgeFlagPointer ,
tglopDrawArrays ,
};

static int op_table_size[]= {
7 + 1 ,
4 + 1 ,
1 + 1 ,
3 + 1 ,
1 + 1 ,
4 + 1 ,
0 + 1 ,
2 + 1 ,
1 + 1 ,
16 + 1 ,
0 + 1 ,
16 + 1 ,
0 + 1 ,
0 + 1 ,
4 + 1 ,
3 + 1 ,
3 + 1 ,
4 + 1 ,
6 + 1 ,
6 + 1 ,
2 + 1 ,
6 + 1 ,
5 + 1 ,
1 + 1 ,
4 + 1 ,
0 + 1 ,
1 + 1 ,
0 + 1 ,
1 + 1 ,
9 + 1 ,
2 + 1 ,
1 + 1 ,
1 + 1 ,
1 + 1 ,
2 + 1 ,
1 + 1 ,
2 + 1 ,
0 + 1 ,
1 + 1 ,
1 + 1 ,
1 + 1 ,
1 + 1 ,
4 + 1 ,
4 + 1 ,
3 + 1 ,
4 + 1 ,
2 + 1 ,
3 + 1 ,
};


TGLParam tgl_p[64];
TGLContext *tgl_ctx = ((void *)0);

int glError = GL_NO_ERROR;

void tgl_free(void *p) {
  free(p);
}
void *tgl_malloc(size_t uSize) {
  return malloc(uSize);
}
void *tgl_zalloc(size_t uSize) {
  return calloc(1, uSize);
}
void *tgl_realloc(void * p, size_t uSize) {
  return realloc(p, uSize);
}
void tgl_copy(void * d, void * s, size_t uSize) {
  memcpy(d, s, uSize);
}

void memset_s(void *adr, int val, int icount){
  int i;
  unsigned int *p = adr;
  unsigned short *q;
  int v = val | (val << 16);
  int n = icount >> 3;

  for (i = 0; i < n; i++) {
    p[0] = v;
    p[1] = v;
    p[2] = v;
    p[3] = v;
    p += 4;
  }

  q = (unsigned short *) p;
  n = icount & 7;
  for (i = 0; i < n; i++)
    *q++ = val;
}

void memset_l(void *adr, int val, int icount) {
  int i;
  unsigned int *p = adr;
  int v = val;
  int n = icount >> 2;
  for (i = 0; i < n; i++) {
    p[0] = v;
    p[1] = v;
    p[2] = v;
    p[3] = v;
    p += 4;
  }

  n = icount & 3;
  for (i = 0; i < n; i++)
    *p++ = val;
}

void memset_RGB24(void *adr,int r, int g, int b,long icount){
  long i, n;
  long v1,v2,v3,*pt=(long *)(adr);
  unsigned char *p, R=(unsigned char)r, G=(unsigned char)g, B=(unsigned char)b;

  p=(unsigned char *)adr;
  *p++=R; *p++=G; *p++=B;
  *p++=R; *p++=G; *p++=B;
  *p++=R; *p++=G; *p++=B;
  *p++=R; *p++=G; *p++=B;
  v1=*pt++; v2=*pt++; v3=*pt++;
  n = icount >> 2;
  for(i=1;i<n;i++) {
    *pt++=v1;
    *pt++=v2;
    *pt++=v3;
  }
}


void tgl_M4_Identity(M4 *a){
  int i,j;
  for(i=0;i<4;i++){
    for(j=0;j<4;j++){
      if (i==j)
        a->m[i][j]=1.0;
      else
        a->m[i][j]=0.0;
    }
  }
}

int tgl_M4_IsIdentity(M4 *a){
  int i,j;
  for(i=0;i<4;i++) {
    for(j=0;j<4;j++) {
      if (i==j) {
        if (a->m[i][j] != 1.0)
          return 0;
      }
      else if (a->m[i][j] != 0.0)
        return 0;
    }
  }
  return 1;
}

void tgl_M4_Mul(M4 *c,M4 *a,M4 *b) {
  int i,j,k;
  float s;
  for(i=0;i<4;i++) {
    for(j=0;j<4;j++) {
      s=0.0;
      for(k=0;k<4;k++) {
        s+=a->m[i][k]*b->m[k][j];
      }
      c->m[i][j]=s;
    }
  }
}

void tgl_M4_MulLeft(M4 *c,M4 *b) {
  int i,j,k;
  float s;
  M4 a;
  a=*c;
  for(i=0;i<4;i++) {
    for(j=0;j<4;j++) {
      s=0.0;
      for(k=0;k<4;k++) {
        s+=a.m[i][k]*b->m[k][j];
      }
      c->m[i][j]=s;
    }
  }
}

void tgl_M4_Move(M4 *a,M4 *b) {
  memcpy(a,b,sizeof(M4));
}

void tgl_MoveV3(V3 *a,V3 *b) {
  memcpy(a,b,sizeof(V3));
}

void tgl_MulM3V3(V3 *a,M4 *b,V3 *c) {
   a->v[0]=b->m[0][0]*c->v[0] + b->m[0][1]*c->v[1] + b->m[0][2]*c->v[2];
   a->v[1]=b->m[1][0]*c->v[0] + b->m[1][1]*c->v[1] + b->m[1][2]*c->v[2];
   a->v[2]=b->m[2][0]*c->v[0] + b->m[2][1]*c->v[1] + b->m[2][2]*c->v[2];
}

void tgl_MulM4V3(V3 *a,M4 *b,V3 *c) {
   a->v[0]=b->m[0][0]*c->v[0] + b->m[0][1]*c->v[1] + b->m[0][2]*c->v[2] + b->m[0][3];
   a->v[1]=b->m[1][0]*c->v[0] + b->m[1][1]*c->v[1] + b->m[1][2]*c->v[2] + b->m[1][3];
   a->v[2]=b->m[2][0]*c->v[0] + b->m[2][1]*c->v[1] + b->m[2][2]*c->v[2] + b->m[2][3];
}

void tgl_M4_MulV4(V4 *a,M4 *b,V4 *c) {
   a->v[0]=b->m[0][0]*c->v[0] + b->m[0][1]*c->v[1] + b->m[0][2]*c->v[2] + b->m[0][3]*c->v[3];
   a->v[1]=b->m[1][0]*c->v[0] + b->m[1][1]*c->v[1] + b->m[1][2]*c->v[2] + b->m[1][3]*c->v[3];
   a->v[2]=b->m[2][0]*c->v[0] + b->m[2][1]*c->v[1] + b->m[2][2]*c->v[2] + b->m[2][3]*c->v[3];
   a->v[3]=b->m[3][0]*c->v[0] + b->m[3][1]*c->v[1] + b->m[3][2]*c->v[2] + b->m[3][3]*c->v[3];
}


int Matrix_Inv(float *r, float *m, int n) {
  int i,j,k,l;
  float max,tmp,t;


  for(i=0;i<n*n;i++)
    r[i]=0;
  for(i=0;i<n;i++)
    r[i*n+i]=1;

  for(j=0;j<n;j++) {
    max=m[j*n+j];
    k=j;
    for(i=j+1;i<n;i++) {
      if (fabs(m[i*n+j])>fabs(max)) {
        k=i;
        max=m[i*n+j];
      }
    }
    if (max==0) return 1;

    if (k!=j) {
      for(i=0;i<n;i++) {
        tmp=m[j*n+i];
        m[j*n+i]=m[k*n+i];
        m[k*n+i]=tmp;

        tmp=r[j*n+i];
        r[j*n+i]=r[k*n+i];
        r[k*n+i]=tmp;
      }
    }

    max=1/max;
    for(i=0;i<n;i++) {
      m[j*n+i]*=max;
      r[j*n+i]*=max;
    }

    for(l=0;l<n;l++) if (l!=j) {
      t=m[l*n+j];
      for(i=0;i<n;i++) {
        m[l*n+i]-=m[j*n+i]*t;
        r[l*n+i]-=r[j*n+i]*t;
      }
    }
  }
  return 0;
}

void tgl_M4_Transpose(M4 *a,M4 *b) {
  a->m[0][0]=b->m[0][0];
  a->m[0][1]=b->m[1][0];
  a->m[0][2]=b->m[2][0];
  a->m[0][3]=b->m[3][0];

  a->m[1][0]=b->m[0][1];
  a->m[1][1]=b->m[1][1];
  a->m[1][2]=b->m[2][1];
  a->m[1][3]=b->m[3][1];

  a->m[2][0]=b->m[0][2];
  a->m[2][1]=b->m[1][2];
  a->m[2][2]=b->m[2][2];
  a->m[2][3]=b->m[3][2];

  a->m[3][0]=b->m[0][3];
  a->m[3][1]=b->m[1][3];
  a->m[3][2]=b->m[2][3];
  a->m[3][3]=b->m[3][3];
}


int tgl_M4_Inverse(M4 *a, M4 *b){
  M4 tmp_b;
  memcpy(&tmp_b, b, 16*sizeof(float));
  return Matrix_Inv(&a->m[0][0], &tmp_b.m[0][0], 4);
}

void tgl_M4_Rotate(M4 *a, float t, int u) {
  int v=u+1;
  if (v>2)
    v=0;

  int w=v+1;
  if (w>2)
    w=0;

  float s = sinf(t);
  float c = cosf(t);
  tgl_M4_Identity(a);
  a->m[v][v] = c;
  a->m[v][w] =-s;
  a->m[w][v] = s;
  a->m[w][w] = c;
}


int tgl_M3_Inverse(M3 *a, M3 *m) {
   float det = m->m[0][0]*m->m[1][1]*m->m[2][2]-m->m[0][0]*m->m[1][2]*m->m[2][1]
             - m->m[1][0]*m->m[0][1]*m->m[2][2]+m->m[1][0]*m->m[0][2]*m->m[2][1]
             + m->m[2][0]*m->m[0][1]*m->m[1][2]-m->m[2][0]*m->m[0][2]*m->m[1][1];
   if (det==0.0f)
      return 1;
   a->m[0][0] = ( m->m[1][1]*m->m[2][2]-m->m[1][2]*m->m[2][1])/det;
   a->m[0][1] = -( m->m[0][1]*m->m[2][2]-m->m[0][2]*m->m[2][1])/det;
   a->m[0][2] = -(-m->m[0][1]*m->m[1][2]+m->m[0][2]*m->m[1][1])/det;

   a->m[1][0] = -(m->m[1][0]*m->m[2][2]-m->m[1][2]*m->m[2][0])/det;
   a->m[1][1] = (m->m[0][0]*m->m[2][2]-m->m[0][2]*m->m[2][0])/det;
   a->m[1][2] = -(m->m[0][0]*m->m[1][2]-m->m[0][2]*m->m[1][0])/det;

   a->m[2][0] = (m->m[1][0]*m->m[2][1]-m->m[1][1]*m->m[2][0])/det;
   a->m[2][1] = -(m->m[0][0]*m->m[2][1]-m->m[0][1]*m->m[2][0])/det;
   a->m[2][2] = (m->m[0][0]*m->m[1][1]-m->m[0][1]*m->m[1][0])/det;
   return 0;
}

int tgl_V3_Norm(V3 *a) {
  float n = a->v[0]*a->v[0] + a->v[1]*a->v[1] + a->v[2]*a->v[2];
  if (n==0.0)
    return 1;
  n=1.0f/sqrtf(n);
  a->v[0] *= n;
  a->v[1] *= n;
  a->v[2] *= n;
  return 0;
}

float tgl_V3_LengthSquared(V3 *a) {
  float n = a->v[0]*a->v[0] + a->v[1]*a->v[1] + a->v[2]*a->v[2];
  return n;
}

float tgl_V3_Length(V3 *a) {
  float n = a->v[0]*a->v[0] + a->v[1]*a->v[1] + a->v[2]*a->v[2];
  if (n!=0.0f)
    n=sqrtf(n);
  return n;
}

float tgl_V3_Dot(V3 *a, V3 *b) {
  float n = a->v[0]*b->v[0] + a->v[1]*b->v[1] + a->v[2]*b->v[2];
  return n;
}

void tgl_V3_Cross(V3 * c, V3 *a, V3 *b) {
  c->v[0] = a->v[1]*b->v[2] - a->v[2]*b->v[1];
  c->v[1] = a->v[2]*b->v[0] - a->v[0]*b->v[2];
  c->v[2] = a->v[0]*b->v[1] - a->v[1]*b->v[0];
}


V3 tgl_V3_New(float x,float y,float z) {
  V3 a;
  a.v[0] = x;
  a.v[1] = y;
  a.v[2] = z;
  return a;
}

V4 tgl_V4_New(float x,float y,float z,float w) {
  V4 a;
  a.v[0] = x;
  a.v[1] = y;
  a.v[2] = z;
  a.v[3] = w;
  return a;
}

void tgl_convertRGB_to_5R6G5B(unsigned short *pixmap,unsigned char *rgb, int xsize,int ysize){
  unsigned char *p=rgb;
  int i,n = xsize*ysize;
  for (i=0;i<n;i++) {
    pixmap[i]=((p[0]&0xF8)<<8) | ((p[1]&0xFC)<<3) | ((p[2]&0xF8)>>3);
    p+=3;
  }
}

void tgl_resizeImageNoInterpolateRGB_RGB(unsigned char *dest,int xsize_dest,int ysize_dest,
                                         unsigned char *src ,int xsize_src ,int ysize_src){
  unsigned char *pix,*pix_src,*pix1;
  int x1,y1,x1inc,y1inc;
  int xi,yi,x,y;

  pix=dest;
  pix_src=src;

  x1inc=(int)((float) ((xsize_src)<<16) / (float) (xsize_dest));
  y1inc=(int)((float) ((ysize_src)<<16) / (float) (ysize_dest));

  y1=0;
  for(y=0;y<ysize_dest;y++) {
    x1=0;
    for(x=0;x<xsize_dest;x++) {
      xi=x1 >> 16;
      yi=y1 >> 16;
      pix1=pix_src+(yi*xsize_src+xi)*3;

      pix[0]=pix1[0];
      pix[1]=pix1[1];
      pix[2]=pix1[2];

      pix+=3;
      x1+=x1inc;
    }
    y1+=y1inc;
  }
}

void tgl_resizeImageNoInterpolateRGBA_RGB(unsigned char *dest,int xsize_dest,int ysize_dest,
                                          unsigned char *src ,int xsize_src ,int ysize_src){
  unsigned char *pix,*pix_src,*pix1;
  int x1,y1,x1inc,y1inc;
  int xi,yi,x,y;

  pix=dest;
  pix_src=src;

  x1inc=(int)((float) ((xsize_src)<<16) / (float) (xsize_dest));
  y1inc=(int)((float) ((ysize_src)<<16) / (float) (ysize_dest));

  y1=0;
  for(y=0;y<ysize_dest;y++) {
    x1=0;
    for(x=0;x<xsize_dest;x++) {
      xi=x1 >> 16;
      yi=y1 >> 16;
      pix1=pix_src+(yi*xsize_src+xi)*4;

      pix[0]=pix1[2];
      pix[1]=pix1[1];
      pix[2]=pix1[0];

      pix+=3;
      x1+=x1inc;
    }
    y1+=y1inc;
  }
}

inline int tgl_clipcode(float x, float y, float z, float w) {
  float p=w * (1.0 + (1E-5));
  float m=-p;
  return (x<m) | ((x>p)<<1) | ((y<m)<<2) | ((y>p)<<3) | ((z<m)<<4) | ((z>p)<<5) ;
}

void tgl_transform_to_viewport(TGLContext *c, TGLVertex *v) {
  float winv;
  winv=1.0/v->pc.v[3];
  v->zp.ix= (int) ( v->pc.v[0]*winv*c->viewport.scale.v[0] + c->viewport.trans.v[0] );
  v->zp.iy= (int) ( v->pc.v[1]*winv*c->viewport.scale.v[1] + c->viewport.trans.v[1] );
  v->zp.iz= (int) ( v->pc.v[2]*winv*c->viewport.scale.v[2] + c->viewport.trans.v[2] );

  if (c->lighting_enabled) {
    v->zp.ir=(int)(v->color.v[0] * (( (1<<16)-(1<<10) ) - ( (1<<10) )) + ( (1<<10) ));
    v->zp.ig=(int)(v->color.v[1] * (( (1<<16)-(1<<9) ) - ( (1<<9) )) + ( (1<<9) ));
    v->zp.ib=(int)(v->color.v[2] * (( (1<<16)-(1<<10) ) - ( (1<<10) )) + ( (1<<10) ));
  }
  else
  {
    v->zp.ir = c->current_ui_color[0];
    v->zp.ig = c->current_ui_color[1];
    v->zp.ib = c->current_ui_color[2];
  }

  if (c->texture_2d_enabled) {
    v->zp.is=(int)(v->tex_coord.v[0] * (( (1<<22)-(1<<13) ) - ( (1<<13) )) + ( (1<<13) ));
    v->zp.it=(int)(v->tex_coord.v[1] * (( (1<<30)-(1<<21) ) - ( (1<<21) )) + ( (1<<21) ));
  }
}

void tgl_add_select(TGLContext *c,unsigned int zmin,unsigned int zmax){
  unsigned int *ptr;
  int n,i;

  if (!c->select_overflow) {
    if (c->select_hit==((void *)0) ) {
      n=c->name_stack_size;
      if ((c->select_ptr-c->select_buffer+3+n) > c->select_size) {
        c->select_overflow=1;
      } else {
        ptr = c->select_ptr;
        c->select_hit=ptr;
        *ptr++ = c->name_stack_size;
        *ptr++ = zmin;
        *ptr++ = zmax;
        for(i=0;i<n;i++)
          *ptr++=c->name_stack[i];
        c->select_ptr=ptr;
        c->select_hits++;
      }
    } else {
      if (zmin<c->select_hit[1])
        c->select_hit[1]=zmin;
      if (zmax>c->select_hit[2])
        c->select_hit[2]=zmax;
    }
  }
}

static void tgl_add_select1(TGLContext *c,int z1,int z2,int z3) {
  unsigned int uimin,uimax;
  uimin=uimax=z1;
  if (z2<uimin) uimin=z2;
  if (z3<uimin) uimin=z3;
  if (z2>uimax) uimax=z2;
  if (z3>uimax) uimax=z3;
  tgl_add_select(c,0xffffffff-uimin,0xffffffff-uimax);
}

void tgl_draw_point(TGLContext *c, TGLVertex *p0){
  if (p0->clip_code == 0) {
    if (c->render_mode == GL_SELECT) {
      tgl_add_select(c,p0->zp.iz,p0->zp.iz);
    }
    else
    {
      if (c->depth_test)
        ZB_DrawPoint_z(c->zb,&p0->zp);
      else
        ZB_DrawPoint(c->zb,&p0->zp);
    }
  }
}


static inline void interpolate(TGLVertex *q, TGLVertex *p0, TGLVertex *p1, float t){
  q->pc.v[0]=p0->pc.v[0]+(p1->pc.v[0] - p0->pc.v[0])*t;
  q->pc.v[1]=p0->pc.v[1]+(p1->pc.v[1] - p0->pc.v[1])*t;
  q->pc.v[2]=p0->pc.v[2]+(p1->pc.v[2] - p0->pc.v[2])*t;
  q->pc.v[3]=p0->pc.v[3]+(p1->pc.v[3] - p0->pc.v[3])*t;

  q->color.v[0]=p0->color.v[0] + (p1->color.v[0]-p0->color.v[0])*t;
  q->color.v[1]=p0->color.v[1] + (p1->color.v[1]-p0->color.v[1])*t;
  q->color.v[2]=p0->color.v[2] + (p1->color.v[2]-p0->color.v[2])*t;
}


static inline int tgl_ClipLine(float denom, float num, float *tmin, float *tmax){
  float t;

  if (denom>0) {
    t=num/denom;
    if (t>*tmax) return 0;
    if (t>*tmin) *tmin=t;
  }
  else if (denom<0) {
    t=num/denom;
    if (t<*tmin) return 0;
    if (t<*tmax) *tmax=t;
  }
  else if (num>0)
    return 0;

  return 1;
}

void tgl_draw_line(TGLContext *c, TGLVertex *p1, TGLVertex *p2){
  float dx,dy,dz,dw,x1,y1,z1,w1;
  float tmin,tmax;
  TGLVertex q1,q2;
  int cc1,cc2;
  cc1=p1->clip_code;
  cc2=p2->clip_code;

  if ( (cc1 | cc2) == 0) {

    if (c->render_mode == GL_SELECT) {
      tgl_add_select1(c,p1->zp.iz,p2->zp.iz,p2->zp.iz);
    }
    else
    {
      if (c->depth_test)
        ZB_DrawLine_z(c->zb,&p1->zp,&p2->zp);
      else
        ZB_DrawLine(c->zb,&p1->zp,&p2->zp);
    }
  }
  else if ( (cc1 & cc2) != 0 ) {
    return;
  }
  else
  {
    dx=p2->pc.v[0]-p1->pc.v[0];
    dy=p2->pc.v[1]-p1->pc.v[1];
    dz=p2->pc.v[2]-p1->pc.v[2];
    dw=p2->pc.v[3]-p1->pc.v[3];

    x1=p1->pc.v[0];
    y1=p1->pc.v[1];
    z1=p1->pc.v[2];
    w1=p1->pc.v[3];

    tmin=0;
    tmax=1;
    if (tgl_ClipLine( dx+dw,-x1-w1,&tmin,&tmax) &&
        tgl_ClipLine(-dx+dw, x1-w1,&tmin,&tmax) &&
        tgl_ClipLine( dy+dw,-y1-w1,&tmin,&tmax) &&
        tgl_ClipLine(-dy+dw, y1-w1,&tmin,&tmax) &&
        tgl_ClipLine( dz+dw,-z1-w1,&tmin,&tmax) &&
        tgl_ClipLine(-dz+dw, z1-w1,&tmin,&tmax)) {

      interpolate(&q1,p1,p2,tmin);
      interpolate(&q2,p1,p2,tmax);
      tgl_transform_to_viewport(c,&q1);
      tgl_transform_to_viewport(c,&q2);

      if (c->depth_test)
        ZB_DrawLine_z(c->zb,&q1.zp,&q2.zp);
      else
        ZB_DrawLine(c->zb,&q1.zp,&q2.zp);
    }
  }
}

static float clip_xmin(V4 *c,V4 *a,V4 *b) {
  float t;
  float dX = (b->v[0] - a->v[0]);
  float dY = (b->v[1] - a->v[1]);
  float dZ = (b->v[2] - a->v[2]);
  float dW = (b->v[3] - a->v[3]);
  float den = -(- dX) + dW;
  if (den == 0)
    t=0;
  else
    t = ( - a->v[0] - a->v[3]) / den;
  c->v[1] = a->v[1] + t * dY;
  c->v[2] = a->v[2] + t * dZ;
  c->v[3] = a->v[3] + t * dW;
  c->v[0] = - c->v[3];
  return t;
}
static float clip_xmax(V4 *c,V4 *a,V4 *b) {
  float t;
  float dX = (b->v[0] - a->v[0]);
  float dY = (b->v[1] - a->v[1]);
  float dZ = (b->v[2] - a->v[2]);
  float dW = (b->v[3] - a->v[3]);
  float den = -(+ dX) + dW;
  if (den == 0)
    t=0;
  else
    t = ( + a->v[0] - a->v[3]) / den;
  c->v[1] = a->v[1] + t * dY;
  c->v[2] = a->v[2] + t * dZ;
  c->v[3] = a->v[3] + t * dW;
  c->v[0] = + c->v[3];
  return t;
}
static float clip_ymin(V4 *c,V4 *a,V4 *b) {
  float t;
  float dX = (b->v[0] - a->v[0]);
  float dY = (b->v[1] - a->v[1]);
  float dZ = (b->v[2] - a->v[2]);
  float dW = (b->v[3] - a->v[3]);
  float den = -(- dY) + dW;
  if (den == 0)
    t=0;
  else
    t = ( - a->v[1] - a->v[3]) / den;
  c->v[0] = a->v[0] + t * dX;
  c->v[2] = a->v[2] + t * dZ;
  c->v[3] = a->v[3] + t * dW;
  c->v[1] = - c->v[3];
  return t;
}
static float clip_ymax(V4 *c,V4 *a,V4 *b) {
  float t;
  float dX = (b->v[0] - a->v[0]);
  float dY = (b->v[1] - a->v[1]);
  float dZ = (b->v[2] - a->v[2]);
  float dW = (b->v[3] - a->v[3]);
  float den = -(+ dY) + dW;
  if (den == 0)
    t=0;
  else
    t = ( + a->v[1] - a->v[3]) / den;
  c->v[0] = a->v[0] + t * dX;
  c->v[2] = a->v[2] + t * dZ;
  c->v[3] = a->v[3] + t * dW;
  c->v[1] = + c->v[3];
  return t;
}
static float clip_zmin(V4 *c,V4 *a,V4 *b) {
  float t;
  float dX = (b->v[0] - a->v[0]);
  float dY = (b->v[1] - a->v[1]);
  float dZ = (b->v[2] - a->v[2]);
  float dW = (b->v[3] - a->v[3]);
  float den = -(- dZ) + dW;
  if (den == 0)
    t=0;
  else
    t = ( - a->v[2] - a->v[3]) / den;
  c->v[0] = a->v[0] + t * dX;
  c->v[1] = a->v[1] + t * dY;
  c->v[3] = a->v[3] + t * dW;
  c->v[2] = - c->v[3];
  return t;
}
static float clip_zmax(V4 *c,V4 *a,V4 *b) {
  float t;
  float dX = (b->v[0] - a->v[0]);
  float dY = (b->v[1] - a->v[1]);
  float dZ = (b->v[2] - a->v[2]);
  float dW = (b->v[3] - a->v[3]);
  float den = -(+ dZ) + dW;
  if (den == 0)
    t=0;
  else
    t = ( + a->v[2] - a->v[3]) / den;
  c->v[0] = a->v[0] + t * dX;
  c->v[1] = a->v[1] + t * dY;
  c->v[3] = a->v[3] + t * dW;
  c->v[2] = + c->v[3];
  return t;
}

float (*clip_proc[6])(V4 *,V4 *,V4 *) = {
  clip_xmin,clip_xmax,
  clip_ymin,clip_ymax,
  clip_zmin,clip_zmax
};

inline void updateTmp(TGLContext *c, TGLVertex *q, TGLVertex *p0, TGLVertex *p1, float t) {
  if (c->current_shade_model == GL_SMOOTH) {
    q->color.v[0]=p0->color.v[0] + (p1->color.v[0]-p0->color.v[0])*t;
    q->color.v[1]=p0->color.v[1] + (p1->color.v[1]-p0->color.v[1])*t;
    q->color.v[2]=p0->color.v[2] + (p1->color.v[2]-p0->color.v[2])*t;
  }
  else
  {
    q->color.v[0]=p0->color.v[0];
    q->color.v[1]=p0->color.v[1];
    q->color.v[2]=p0->color.v[2];
  }

  if (c->texture_2d_enabled) {
    q->tex_coord.v[0]=p0->tex_coord.v[0] + (p1->tex_coord.v[0]-p0->tex_coord.v[0])*t;
    q->tex_coord.v[1]=p0->tex_coord.v[1] + (p1->tex_coord.v[1]-p0->tex_coord.v[1])*t;
  }

  q->clip_code=tgl_clipcode(q->pc.v[0], q->pc.v[1], q->pc.v[2], q->pc.v[3]);
  if (q->clip_code==0)
    tgl_transform_to_viewport(c,q);
}

static void tgl_draw_triangle_clip(TGLContext *c, TGLVertex *p0, TGLVertex *p1, TGLVertex *p2, int clip_bit);

void tgl_draw_triangle(TGLContext *c,
                       TGLVertex *p0,
                       TGLVertex *p1,
                       TGLVertex *p2) {
  int c_or = p0->clip_code | p1->clip_code | p2->clip_code;
  if (c_or==0) {
    float norm=(float)(p1->zp.ix - p0->zp.ix)*(float)(p2->zp.iy - p0->zp.iy)
              -(float)(p2->zp.ix - p0->zp.ix)*(float)(p1->zp.iy - p0->zp.iy);
    if (norm == 0.0f)
      return;

    int is_front = norm < 0.0f;
    is_front = is_front ^ c->current_front_face;

    if (c->cull_face_enabled) {

      if (c->current_cull_face == GL_BACK) {
        if (is_front == 0)
          return;
        c->draw_triangle_front(c,p0,p1,p2);
      }
      else if (c->current_cull_face == GL_FRONT) {
        if (is_front != 0)
          return;
        c->draw_triangle_back(c,p0,p1,p2);
      }
      else
      {
        return;
      }
    }
    else
    {

      if (is_front) {
        c->draw_triangle_front(c,p0,p1,p2);
      }
      else
      {
        c->draw_triangle_back(c,p0,p1,p2);
      }
    }
  }
  else
  {
    int c_and=p0->clip_code & p1->clip_code & p2->clip_code;
    if (c_and==0) {
      tgl_draw_triangle_clip(c,p0,p1,p2,0);
    }
  }
}

static void tgl_draw_triangle_clip(TGLContext *c, TGLVertex *p0, TGLVertex *p1, TGLVertex *p2, int clip_bit) {
  TGLVertex tmp1,tmp2,*q[3];

  int c_or = p0->clip_code | p1->clip_code | p2->clip_code;
  if (c_or == 0) {
    tgl_draw_triangle(c,p0,p1,p2);
  }
  else
  {
    int c_and=p0->clip_code & p1->clip_code & p2->clip_code;
    if (c_and != 0)
      return;

    while (clip_bit < 6 && (c_or & (1 << clip_bit)) == 0) {
      clip_bit++;
    }

    if (clip_bit == 6) {
      return;
    }

    int clip_mask = 1 << clip_bit;
    int c_xor=(p0->clip_code ^ p1->clip_code ^ p2->clip_code) & clip_mask;
    if (c_xor) {
      if (p0->clip_code & clip_mask) {
        q[0]=p0; q[1]=p1; q[2]=p2;
      }
      else if (p1->clip_code & clip_mask) {
        q[0]=p1; q[1]=p2; q[2]=p0;
      }
      else {
        q[0]=p2; q[1]=p0; q[2]=p1;
      }

      updateTmp(c,&tmp1,q[0],q[1],clip_proc[clip_bit](&tmp1.pc,&q[0]->pc,&q[1]->pc));
      updateTmp(c,&tmp2,q[0],q[2],clip_proc[clip_bit](&tmp2.pc,&q[0]->pc,&q[2]->pc));

      tmp1.edge_flag = q[0]->edge_flag;
      int edge_flag = q[2]->edge_flag;
      q[2]->edge_flag = 0;
      tgl_draw_triangle_clip(c,&tmp1,q[1],q[2],clip_bit+1);

      tmp2.edge_flag = 1;
      tmp1.edge_flag = 0;
      q[2]->edge_flag = edge_flag;
      tgl_draw_triangle_clip(c,&tmp2,&tmp1,q[2],clip_bit+1);
    }
    else
    {

      if ((p0->clip_code & clip_mask)==0) {
        q[0]=p0; q[1]=p1; q[2]=p2;
      }
      else if ((p1->clip_code & clip_mask)==0) {
        q[0]=p1; q[1]=p2; q[2]=p0;
      }
      else {
        q[0]=p2; q[1]=p0; q[2]=p1;
      }
      updateTmp(c,&tmp1,q[0],q[1],clip_proc[clip_bit](&tmp1.pc,&q[0]->pc,&q[1]->pc));
      updateTmp(c,&tmp2,q[0],q[2],clip_proc[clip_bit](&tmp2.pc,&q[0]->pc,&q[2]->pc));
      tmp1.edge_flag=1;
      tmp2.edge_flag=q[2]->edge_flag;
      tgl_draw_triangle_clip(c,q[0],&tmp1,&tmp2,clip_bit+1);
    }
  }
}

void tgl_draw_triangle_select(TGLContext *c, TGLVertex *p0, TGLVertex *p1, TGLVertex *p2) {
  tgl_add_select1(c, p0->zp.iz, p1->zp.iz, p2->zp.iz);
}

void ZB_DrawPoint(ZBuffer * zb, ZBufferPoint * p) {
  PIXEL *pp = zb->pbuf + zb->iPitchInPixels * p->iy + p->ix;
 *pp = (((p->ir) & 0xF800) | (((p->ig) >> 5) & 0x07E0) | ((p->ib) >> 11));
}

void ZB_DrawPoint_z(ZBuffer * zb, ZBufferPoint * p) {
  unsigned short * pz = zb->zbuf + (p->iy * zb->iWidth + p->ix);
  PIXEL * pp = zb->pbuf + zb->iPitchInPixels * p->iy + p->ix;
  int zz = p->iz >> 14;
  if (zz >= *pz) {
    *pp = (((p->ir) & 0xF800) | (((p->ig) >> 5) & 0x07E0) | ((p->ib) >> 11));
    *pz = zz;
  }
}



static void ZB_line_flat(ZBuffer * zb, ZBufferPoint * p1, ZBufferPoint * p2, int color) {
  if (p1->iy > p2->iy || (p1->iy == p2->iy && p1->ix > p2->ix)) {
   ZBufferPoint * tmp = p1;
   p1 = p2;
   p2 = tmp;
  }

  PIXEL * pp = zb->pbuf + zb->iPitchInPixels * p1->iy + p1->ix;

  int sx = zb->iWidth;
  int dx = p2->ix - p1->ix;
  int dy = p2->iy - p1->iy;

  if (dx == 0 && dy == 0) {
   *pp = color;
  }
  else if (dx > 0) {
    if (dx >= dy) {
      int n = dx;
      int a = 2*dy - dx;
      dy <<= 1;
      dx <<= 1;
      dx -= dy;
      int pp_inc_1 = (sx + 1);
      int pp_inc_2 = (1);
      do {
        *pp = color;
        if (a>0) {
          pp += pp_inc_1;
          a-=dx;
        } else {
          pp += pp_inc_2;
          a += dy;
        }
      } while (--n >= 0);
    }
    else
    {
      int n = dy;
      int a = 2*dx - dy;
      dx <<= 1;
      dy <<= 1;
      dy -= dx;
      int pp_inc_1 = (sx + 1);
      int pp_inc_2 = (sx);
      do {
        *pp = color;
        if (a>0) {
           pp += pp_inc_1;
           a-=dy;
         } else {
           pp += pp_inc_2;
           a += dx;
         }
      } while (--n >= 0);
    }
  }
  else
  {
    dx = -dx;
    if (dx >= dy) {
      int n = dx;
      int a = 2*dy - dx;
      dy <<= 1;
      dx <<= 1;
      dx -= dy;
      int pp_inc_1 = (sx - 1);
      int pp_inc_2 = (-1);
      do {
        *pp = color;
        if (a>0) {
          pp += pp_inc_1;
          a-=dx;
        } else {
          pp += pp_inc_2;
          a += dy;
        }
      } while (--n >= 0);
    }
    else
    {
      int n = dy;
      int a = 2*dx - dy;
      dx <<= 1;
      dy <<= 1;
      dy -= dx;
      int pp_inc_1 = (sx - 1);
      int pp_inc_2 = (sx);
      do {
        *pp = color;
        if (a>0) {
          pp += pp_inc_1;
          a-=dy;
        } else {
          pp += pp_inc_2;
          a += dx;
        }
      } while (--n >= 0);
    }
  }
}

static void ZB_line_interp(ZBuffer * zb, ZBufferPoint * p1, ZBufferPoint * p2) {
  if (p1->iy > p2->iy || (p1->iy == p2->iy && p1->ix > p2->ix)) {
    ZBufferPoint * tmp = p1;
    p1 = p2;
    p2 = tmp;
  }

  PIXEL * pp = zb->pbuf + zb->iPitchInPixels * p1->iy + p1->ix;

  int sx = zb->iWidth;
  int dx = p2->ix - p1->ix;
  int dy = p2->iy - p1->iy;

  unsigned int rinc, ginc, binc;
  unsigned int r = p2->ir << 8;
  unsigned int g = p2->ig << 8;
  unsigned int b = p2->ib << 8;

  if (dx == 0 && dy == 0) {
    *pp = (((r >> 8) & 0xF800) | (((g >> 8) >> 5) & 0x07E0) | ((b >> 8) >> 11));
  }
  else if (dx > 0) {
    if (dx >= dy) {
      int n = dx; 
      rinc=((p2->ir-p1->ir) << 8)/n; 
      ginc=((p2->ig-p1->ig) << 8)/n; 
      binc=((p2->ib-p1->ib) << 8)/n; 
      int a = 2*dy - dx; 
      dy <<= 1; 
      dx <<= 1; 
      dx -= dy; 
      int pp_inc_1 = (sx + 1); 
      int pp_inc_2 = (1); 
      do { 
        *pp = (((r >> 8) & 0xF800) | (((g >> 8) >> 5) & 0x07E0) | ((b >> 8) >> 11)); 
        r+=rinc;
        g+=ginc;
        b+=binc; 
        if (a>0) { 
          pp += pp_inc_1; 
          a-=dx; 
        } else { 
          pp += pp_inc_2; 
          a += dy; 
        }
      } while (--n >= 0);;
    }
    else
    {
      int n = dy; 
      rinc=((p2->ir-p1->ir) << 8)/n; 
      ginc=((p2->ig-p1->ig) << 8)/n; 
      binc=((p2->ib-p1->ib) << 8)/n; 
      int a = 2*dx - dy; 
      dx <<= 1; 
      dy <<= 1; 
      dy -= dx; 
      int pp_inc_1 = (sx + 1); 
      int pp_inc_2 = (sx); 
      do { 
        *pp = (((r >> 8) & 0xF800) | (((g >> 8) >> 5) & 0x07E0) | ((b >> 8) >> 11)); 
        r+=rinc;
        g+=ginc;
        b+=binc; 
        if (a>0) { 
          pp += pp_inc_1; 
          a-=dy; 
        } else { 
          pp += pp_inc_2; 
          a += dx; 
        } 
      } while (--n >= 0);;
    }
  }
  else
  {
    dx = -dx;
    if (dx >= dy) {
      int n = dx;
      rinc=((p2->ir-p1->ir) << 8)/n; 
      ginc=((p2->ig-p1->ig) << 8)/n; 
      binc=((p2->ib-p1->ib) << 8)/n; 
      int a = 2*dy - dx; 
      dy <<= 1; 
      dx <<= 1; 
      dx -= dy; 
      int pp_inc_1 = (sx - 1); 
      int pp_inc_2 = (-1); 
      do { 
        *pp = (((r >> 8) & 0xF800) | (((g >> 8) >> 5) & 0x07E0) | ((b >> 8) >> 11)); 
        r+=rinc;
        g+=ginc;
        b+=binc; 
        if (a>0) { 
          pp += pp_inc_1; 
          a-=dx; 
        } else { 
          pp += pp_inc_2; 
          a += dy; 
        }
      } while (--n >= 0);;
    }
    else
    {
      int n = dy; 
      rinc=((p2->ir-p1->ir) << 8)/n; 
      ginc=((p2->ig-p1->ig) << 8)/n; 
      binc=((p2->ib-p1->ib) << 8)/n; 
      int a = 2*dx - dy; 
      dx <<= 1; 
      dy <<= 1; 
      dy -= dx; 
      int pp_inc_1 = (sx - 1); 
      int pp_inc_2 = (sx); 
      do { 
        *pp = (((r >> 8) & 0xF800) | (((g >> 8) >> 5) & 0x07E0) | ((b >> 8) >> 11)); 
        r+=rinc;
        g+=ginc;
        b+=binc; 
        if (a>0) { 
          pp += pp_inc_1; 
          a-=dy; 
        } else { 
          pp += pp_inc_2; 
          a += dx; 
        }
      } while (--n >= 0);;
    }
  }
}

static void ZB_line_flat_z(ZBuffer * zb, ZBufferPoint * p1, ZBufferPoint * p2, int color) {
  if (p1->iy > p2->iy || (p1->iy == p2->iy && p1->ix > p2->ix)) {
    ZBufferPoint * tmp = p1;
    p1 = p2;
    p2 = tmp;
  }

  PIXEL * pp = zb->pbuf + zb->iPitchInPixels * p1->iy + p1->ix;

  int sx = zb->iWidth;
  int dx = p2->ix - p1->ix;
  int dy = p2->iy - p1->iy;

  unsigned short * pz = zb->zbuf + p1->iy * zb->iWidth + p1->ix;
  int zz, zinc;
  int z = p1->iz;

  if (dx == 0 && dy == 0) {
    zz=z >> 14;
    if (zz >= *pz) {
      *pp = color;
      *pz=zz;
    }
  }
  else if (dx > 0) {
    if (dx >= dy) {
      int n = dx;
      zinc=(p2->iz-p1->iz)/n;
      int a = 2*dy - dx;
      dy <<= 1;
      dx <<= 1;
      dx -= dy;
      int pp_inc_1 = (sx + 1);
      int pp_inc_2 = (1);
      do {
        zz=z >> 14;
        if (zz >= *pz) {
          *pp = color;
          *pz=zz;
        }
        z+=zinc;
        if (a>0) {
          pp += pp_inc_1;
          pz+=(sx + 1);
          a-=dx;
        } else {
          pp += pp_inc_2;
          pz+=(1);
          a += dy;
        }
      } while (--n >= 0);;
    }
    else
    {
      int n = dy; 
      zinc=(p2->iz-p1->iz)/n; 
      int a = 2*dx - dy; 
      dx <<= 1; 
      dy <<= 1; 
      dy -= dx; 
      int pp_inc_1 = (sx + 1); 
      int pp_inc_2 = (sx); 
      do { 
        zz=z >> 14; 
        if (zz >= *pz) { 
          *pp = color; 
          *pz=zz; 
        } 
        
        z+=zinc;
        if (a>0) { 
          pp += pp_inc_1; 
          pz+=(sx + 1); 
          a-=dy; 
        } else { 
          pp += pp_inc_2; 
          pz+=(sx); 
          a += dx; 
        } 
      } while (--n >= 0);;
    }
  }
  else
  {
    dx = -dx;
    if (dx >= dy) {
      int n = dx; 
      zinc=(p2->iz-p1->iz)/n; 
      int a = 2*dy - dx; 
      dy <<= 1; 
      dx <<= 1; 
      dx -= dy; 
      int pp_inc_1 = (sx - 1); 
      int pp_inc_2 = (-1); 
      do { 
        zz=z >> 14; 
        if (zz >= *pz) { 
          *pp = color; 
          *pz=zz; 
        } 
        z+=zinc; 
        if (a>0) { 
          pp += pp_inc_1; 
          pz+=(sx - 1); 
          a-=dx; 
        } else { 
          pp += pp_inc_2; 
          pz+=(-1); 
          a += dy; 
        } 
      } while (--n >= 0);;
    }
    else
    {
      int n = dy; 
      zinc=(p2->iz-p1->iz)/n; 
      int a = 2*dx - dy; 
      dx <<= 1; 
      dy <<= 1; 
      dy -= dx; 
      int pp_inc_1 = (sx - 1); 
      int pp_inc_2 = (sx); 
      do { 
        zz=z >> 14; 
        if (zz >= *pz) { 
          *pp = color; 
          *pz=zz; 
        } 
        z+=zinc; 
        if (a>0) { 
          pp += pp_inc_1; 
          pz+=(sx - 1); 
          a-=dy; 
        } else { 
          pp += pp_inc_2; 
          pz+=(sx); 
          a += dx; 
        } 
      } while (--n >= 0);;
    }
  }
}


static void ZB_line_interp_z(ZBuffer * zb, ZBufferPoint * p1, ZBufferPoint * p2) {
  if (p1->iy > p2->iy || (p1->iy == p2->iy && p1->ix > p2->ix)) {
   ZBufferPoint * tmp = p1;
   p1 = p2;
   p2 = tmp;
  }

  PIXEL * pp = zb->pbuf + zb->iPitchInPixels * p1->iy + p1->ix;

  int sx = zb->iWidth;
  int dx = p2->ix - p1->ix;
  int dy = p2->iy - p1->iy;


  unsigned short * pz = zb->zbuf + p1->iy * zb->iWidth + p1->ix;
  int zz, zinc;
  int z = p1->iz;



  unsigned int rinc, ginc, binc;
  unsigned int r = p2->ir << 8;
  unsigned int g = p2->ig << 8;
  unsigned int b = p2->ib << 8;

  if (dx == 0 && dy == 0) {
    zz=z >> 14;
    if (zz >= *pz) {
      *pp = (((r >> 8) & 0xF800) | (((g >> 8) >> 5) & 0x07E0) | ((b >> 8) >> 11));
      *pz=zz;
    }
  }
  else if (dx > 0) {
    if (dx >= dy) {
      int n = dx; 
      zinc=(p2->iz-p1->iz)/n; 
      rinc=((p2->ir-p1->ir) << 8)/n; 
      ginc=((p2->ig-p1->ig) << 8)/n; 
      binc=((p2->ib-p1->ib) << 8)/n; 
      int a = 2*dy - dx; 
      dy <<= 1; 
      dx <<= 1; 
      dx -= dy; 
      int pp_inc_1 = (sx + 1); 
      int pp_inc_2 = (1); 
      do { 
        zz=z >> 14; 
        if (zz >= *pz) { 
          *pp = (((r >> 8) & 0xF800) | (((g >> 8) >> 5) & 0x07E0) | ((b >> 8) >> 11)); 
          *pz=zz; 
        }
        z+=zinc; 
        r+=rinc;
        g+=ginc;
        b+=binc; 
        if (a>0) { 
          pp += pp_inc_1; 
          pz+=(sx + 1); 
          a-=dx; 
        } else { 
          pp += pp_inc_2; 
          pz+=1; 
          a += dy; 
        } 
      } while (--n >= 0);;
    }
    else
    {
      int n = dy; 
      zinc=(p2->iz-p1->iz)/n; 
      rinc=((p2->ir-p1->ir) << 8)/n; 
      ginc=((p2->ig-p1->ig) << 8)/n; 
      binc=((p2->ib-p1->ib) << 8)/n; 
      int a = 2*dx - dy; 
      dx <<= 1; 
      dy <<= 1; 
      dy -= dx; 
      int pp_inc_1 = (sx + 1); 
      int pp_inc_2 = (sx); 
      do { 
        zz=z >> 14; 
        if (zz >= *pz) { 
          *pp = (((r >> 8) & 0xF800) | (((g >> 8) >> 5) & 0x07E0) | ((b >> 8) >> 11)); 
          *pz=zz; 
        }
        z+=zinc; 
        r+=rinc;
        g+=ginc;
        b+=binc; 
        if (a>0) { 
          pp += pp_inc_1; 
          pz+=(sx + 1); 
          a-=dy; 
        } else { 
          pp += pp_inc_2; 
          pz+=(sx); 
          a += dx; 
        } 
      } while (--n >= 0);;
    }
  }
  else
  {
    dx = -dx;
    if (dx >= dy) {
      int n = dx; 
      zinc=(p2->iz-p1->iz)/n; 
      rinc=((p2->ir-p1->ir) << 8)/n; 
      ginc=((p2->ig-p1->ig) << 8)/n; 
      binc=((p2->ib-p1->ib) << 8)/n; 
      int a = 2*dy - dx; 
      dy <<= 1; 
      dx <<= 1; 
      dx -= dy; 
      int pp_inc_1 = (sx - 1); 
      int pp_inc_2 = -1; 
      do { 
        zz=z >> 14; 
        if (zz >= *pz) { 
          *pp = (((r >> 8) & 0xF800) | (((g >> 8) >> 5) & 0x07E0) | ((b >> 8) >> 11)); 
          *pz=zz; 
        } 
        z+=zinc; 
        r+=rinc;
        g+=ginc;
        b+=binc; 
        if (a>0) { 
          pp += pp_inc_1; 
          pz+=(sx - 1); 
          a-=dx; 
        } else { 
          pp += pp_inc_2; 
          pz+=-1; 
          a += dy; 
        } 
      } while (--n >= 0);
    }
    else
    {
      int n = dy; 
      zinc=(p2->iz-p1->iz)/n; 
      rinc=((p2->ir-p1->ir) << 8)/n; 
      ginc=((p2->ig-p1->ig) << 8)/n; 
      binc=((p2->ib-p1->ib) << 8)/n; 
      int a = 2*dx - dy; 
      dx <<= 1; 
      dy <<= 1; 
      dy -= dx; 
      int pp_inc_1 = (sx - 1); 
      int pp_inc_2 = sx; 
      do { 
        zz=z >> 14; 
        if (zz >= *pz) { 
          *pp = (((r >> 8) & 0xF800) | (((g >> 8) >> 5) & 0x07E0) | ((b >> 8) >> 11)); 
          *pz=zz; 
        } 
        z+=zinc; 
        r+=rinc;
        g+=ginc;
        b+=binc; 
        if (a>0) { 
          pp += pp_inc_1; 
          pz+=(sx - 1); 
          a-=dy; 
        } else { 
          pp += pp_inc_2; 
          pz+=sx; 
          a += dx; 
        } 
      } while (--n >= 0);
    }
  }
}

void ZB_DrawLine_z(ZBuffer * zb, ZBufferPoint * p1, ZBufferPoint * p2){
  int color1, color2;
  color1 = (((p1->ir) & 0xF800) | (((p1->ig) >> 5) & 0x07E0) | ((p1->ib) >> 11));
  color2 = (((p2->ir) & 0xF800) | (((p2->ig) >> 5) & 0x07E0) | ((p2->ib) >> 11));
  if (color1 == color2) {
    ZB_line_flat_z(zb, p1, p2, color1);
  } else {
    ZB_line_interp_z(zb, p1, p2);
  }
}

void ZB_DrawLine(ZBuffer * zb, ZBufferPoint * p1, ZBufferPoint * p2){
  int color1, color2;
  color1 = (((p1->ir) & 0xF800) | (((p1->ig) >> 5) & 0x07E0) | ((p1->ib) >> 11));
  color2 = (((p2->ir) & 0xF800) | (((p2->ig) >> 5) & 0x07E0) | ((p2->ib) >> 11));
  if (color1 == color2) {
    ZB_line_flat(zb, p1, p2, color1);
  } else {
    ZB_line_interp(zb, p1, p2);
  }
}


void ZB_DrawTriangleFlat(ZBuffer *zb,
                         ZBufferPoint *p0,
                         ZBufferPoint *p1,
                         ZBufferPoint *p2) {
  int color;
  ZBufferPoint *t,*pr1,*pr2,*pLeft,*pRight;
  float fdx1, fdx2, fdy1, fdy2, fz;
  PIXEL * pPixelRow;
  int iPart,iUpdateLeft,iUpdateRight;
  int tmp,iScanLines,iDX1,iDY1,iDX2,iDY2;
  int iError,iDerror;
  int iX1,iXRightStep,iXLeftStep;

  int iX2=0,iDXY2=0;

  if (p1->iy < p0->iy) {
    t = p0;
    p0 = p1;
    p1 = t;
  }
  if (p2->iy < p0->iy) {
    t = p2;
    p2 = p1;
    p1 = p0;
    p0 = t;
  }
  else if (p2->iy < p1->iy) {
    t = p1;
    p1 = p2;
    p2 = t;
  }

  fdx1 = p1->ix - p0->ix;
  fdy1 = p1->iy - p0->iy;

  fdx2 = p2->ix - p0->ix;
  fdy2 = p2->iy - p0->iy;

  fz = fdx1 * fdy2 - fdx2 * fdy1;
  if (fz == 0)
    return;

  fz = 1.0 / fz;
  fdx1 *= fz;
  fdy1 *= fz;
  fdx2 *= fz;
  fdy2 *= fz;

  pPixelRow = zb->pbuf + zb->iPitchInPixels * p0->iy;
  color=(((p2->ir) & 0xF800) | (((p2->ig) >> 5) & 0x07E0) | ((p2->ib) >> 11));;

  for(iPart=0; iPart<2; iPart++) {
    if (iPart == 0) {
      iUpdateLeft = 1;
      iUpdateRight = 1;
      pLeft = p0;
      pr1 = p0;
      if (fz > 0) {
        pRight = p2;
        pr2 = p1;
      }
      else
      {
        pRight = p1;
        pr2 = p2;
      }
      iScanLines = p1->iy - p0->iy;
    }
    else
    {
      if (fz > 0) {
        iUpdateLeft = 0;
        iUpdateRight = 1;
        pr1 = p1;
        pr2 = p2;
      }
      else
      {
        iUpdateLeft = 1;
        iUpdateRight = 0;
        pLeft = p1;
        pRight = p2;
      }
      iScanLines = p2->iy - p1->iy + 1;
    }

    if (iUpdateLeft) {
      iDY1 = pRight->iy - pLeft->iy;
      iDX1 = pRight->ix - pLeft->ix;
      if (iDY1 > 0)
        tmp = (iDX1 << 16) / iDY1;
      else
        tmp = 0;
      iX1 = pLeft->ix;
      iError = 0;
      iDerror = tmp & 0x0000ffff;
      iXRightStep = tmp >> 16;
      iXLeftStep = iXRightStep + 1;
    }

    if (iUpdateRight) {
      iDX2 = (pr2->ix - pr1->ix);
      iDY2 = (pr2->iy - pr1->iy);
      if (iDY2>0)
        iDXY2 = ( iDX2 << 16) / iDY2;
      else
        iDXY2 = 0;
      iX2 = pr1->ix << 16;
    }

    while (iScanLines>0) {
      iScanLines--;
      PIXEL *pp;
      int n;
      pp = pPixelRow + iX1;
      n = (iX2 >> 16) - iX1;
      while (n>=3) {
        pp[0]=color;
        pp[1]=color;
        pp[2]=color;
        pp[3]=color;
        pp += 4;
        n-=4;
      }
      while (n>=0) {
        pp[0]=color;
        pp += 1;
        n-=1;
      }
      

      iError+=iDerror;
      if (iError > 0) {
        iError-=0x10000;
        iX1+=iXLeftStep;
      }
      else
      {
        iX1+=iXRightStep;
      }
      iX2 += iDXY2;
      pPixelRow += zb->iPitchInPixels;
    }
  }
}

void ZB_DrawTriangleFlat_z(ZBuffer *zb,
                           ZBufferPoint *p0,
                           ZBufferPoint *p1,
                           ZBufferPoint *p2) {
  int color;
  ZBufferPoint *t,*pr1,*pr2,*pLeft,*pRight;
  float fdx1, fdx2, fdy1, fdy2, fz;
  PIXEL * pPixelRow;
  int iPart,iUpdateLeft,iUpdateRight;
  int tmp,iScanLines,iDX1,iDY1,iDX2,iDY2;
  int iError,iDerror;
  int iX1,iXRightStep,iXLeftStep;

  int iX2=0,iDXY2=0;

  unsigned short *pz1;
  float fDZ1, fDZ2;
  int iZ,iDZX,iDZY,iZRightStep,iZLeftStep;

  if (p1->iy < p0->iy) {
    t = p0;
    p0 = p1;
    p1 = t;
  }
  if (p2->iy < p0->iy) {
    t = p2;
    p2 = p1;
    p1 = p0;
    p0 = t;
  }
  else if (p2->iy < p1->iy) {
    t = p1;
    p1 = p2;
    p2 = t;
  }


  fdx1 = p1->ix - p0->ix;
  fdy1 = p1->iy - p0->iy;

  fdx2 = p2->ix - p0->ix;
  fdy2 = p2->iy - p0->iy;

  fz = fdx1 * fdy2 - fdx2 * fdy1;
  if (fz == 0)
    return;

  fz = 1.0 / fz;
  fdx1 *= fz;
  fdy1 *= fz;
  fdx2 *= fz;
  fdy2 *= fz;

  fDZ1 = p1->iz - p0->iz;
  fDZ2 = p2->iz - p0->iz;
  iDZX = (int) (fdy2 * fDZ1 - fdy1 * fDZ2);
  iDZY = (int) (fdx1 * fDZ2 - fdx2 * fDZ1);

  pPixelRow = zb->pbuf + zb->iPitchInPixels * p0->iy;
  pz1 = zb->zbuf + p0->iy * zb->iWidth;
  color=(((p2->ir) & 0xF800) | (((p2->ig) >> 5) & 0x07E0) | ((p2->ib) >> 11));

  for(iPart=0; iPart<2; iPart++) {
    if (iPart == 0) {
      iUpdateLeft = 1;
      iUpdateRight = 1;
      pLeft = p0;
      pr1 = p0;
      if (fz > 0) {
        pRight = p2;
        pr2 = p1;
      }
      else
      {
        pRight = p1;
        pr2 = p2;
      }
      iScanLines = p1->iy - p0->iy;
    }
    else
    {
      if (fz > 0) {
        iUpdateLeft = 0;
        iUpdateRight = 1;
        pr1 = p1;
        pr2 = p2;
      }
      else
      {
        iUpdateLeft = 1;
        iUpdateRight = 0;
        pLeft = p1;
        pRight = p2;
      }
      iScanLines = p2->iy - p1->iy + 1;
    }

    if (iUpdateLeft) {
      iDY1 = pRight->iy - pLeft->iy;
      iDX1 = pRight->ix - pLeft->ix;
      if (iDY1 > 0)
        tmp = (iDX1 << 16) / iDY1;
      else
        tmp = 0;

      iX1 = pLeft->ix;
      iError = 0;
      iDerror = tmp & 0x0000ffff;
      iXRightStep = tmp >> 16;
      iXLeftStep = iXRightStep + 1;
      iZ = pLeft->iz;
      iZRightStep=(iDZY + iDZX * iXRightStep);
      iZLeftStep=iZRightStep + iDZX;
    }


    if (iUpdateRight) {
      iDX2 = (pr2->ix - pr1->ix);
      iDY2 = (pr2->iy - pr1->iy);
      if (iDY2>0)
        iDXY2 = ( iDX2 << 16) / iDY2;
      else
        iDXY2 = 0;
      iX2 = pr1->ix << 16;
    }

    while (iScanLines>0) {
      iScanLines--;
      PIXEL *pp;
      int n;
      unsigned short * pz = pz1+iX1;
      unsigned int zz;
      unsigned int z = iZ;
      pp = pPixelRow + iX1;
      n = (iX2 >> 16) - iX1;
      while (n>=3) {
        zz=z >> 14;
        if (zz >= pz[0]) {
          pp[0]=color;
          pz[0]=zz;
        }
        z+=iDZX;
        zz=z >> 14;
        if (zz >= pz[1]) {
          pp[1]=color;
          pz[1]=zz;
        }
        z+=iDZX;
        zz=z >> 14;
        if (zz >= pz[2]) {
          pp[2]=color;
          pz[2]=zz;
        }
        z+=iDZX;
        zz=z >> 14;
        if (zz >= pz[3]) {
          pp[3]=color; 
          pz[3]=zz;
        }
        z+=iDZX;
        pz+=4;
        pp += 4;
        n-=4;
      }
      while (n>=0) {
        zz=z >> 14;
        if (zz >= pz[0]) {
          pp[0]=color;
          pz[0]=zz;
        }
        z+=iDZX;
        pz+=1;
        pp += 1;
        n-=1;
      }
      
      iError+=iDerror;
      if (iError > 0) {
        iError-=0x10000;
        iX1+=iXLeftStep;
        iZ+=iZLeftStep;
      }
      else
      {
        iX1+=iXRightStep;
        iZ+=iZRightStep;
      }
      iX2 += iDXY2;
      pPixelRow += zb->iPitchInPixels;
      pz1 += zb->iWidth;
    }
  }
}

void ZB_DrawTriangleSmooth(ZBuffer *zb,
                           ZBufferPoint *p0,
                           ZBufferPoint *p1,
                           ZBufferPoint *p2){
  int _drgbdx;

  ZBufferPoint *t,*pr1,*pr2,*pLeft,*pRight;
  float fdx1, fdx2, fdy1, fdy2, fz;
  PIXEL * pPixelRow;
  int iPart,iUpdateLeft,iUpdateRight;
  int tmp,iScanLines,iDX1,iDY1,iDX2,iDY2;
  int iError,iDerror;
  int iX1,iXRightStep,iXLeftStep;

  int iX2=0,iDXY2=0;

  float fDC1, fDC2;
  int iR,iDRX,iDRY,iRRightStep,iRLeftStep;
  int iG,iDGX,iDGY,iGRightStep,iGLeftStep;
  int iB,iDBX,iDBY,iBRightStep,iBLeftStep;

  if (p1->iy < p0->iy) {
    t = p0;
    p0 = p1;
    p1 = t;
  }
  if (p2->iy < p0->iy) {
    t = p2;
    p2 = p1;
    p1 = p0;
    p0 = t;
  }
  else if (p2->iy < p1->iy) {
    t = p1;
    p1 = p2;
    p2 = t;
  }


  fdx1 = p1->ix - p0->ix;
  fdy1 = p1->iy - p0->iy;

  fdx2 = p2->ix - p0->ix;
  fdy2 = p2->iy - p0->iy;

  fz = fdx1 * fdy2 - fdx2 * fdy1;
  if (fz == 0)
    return;

  fz = 1.0 / fz;
  fdx1 *= fz;
  fdy1 *= fz;
  fdx2 *= fz;
  fdy2 *= fz;

  fDC1 = p1->ir - p0->ir;
  fDC2 = p2->ir - p0->ir;
  iDRX = (int) (fdy2 * fDC1 - fdy1 * fDC2);
  iDRY = (int) (fdx1 * fDC2 - fdx2 * fDC1);

  fDC1 = p1->ig - p0->ig;
  fDC2 = p2->ig - p0->ig;
  iDGX = (int) (fdy2 * fDC1 - fdy1 * fDC2);
  iDGY = (int) (fdx1 * fDC2 - fdx2 * fDC1);

  fDC1 = p1->ib - p0->ib;
  fDC2 = p2->ib - p0->ib;
  iDBX = (int) (fdy2 * fDC1 - fdy1 * fDC2);
  iDBY = (int) (fdx1 * fDC2 - fdx2 * fDC1);

  pPixelRow = zb->pbuf + zb->iPitchInPixels * p0->iy;

  _drgbdx = ((iDRX / (1<<6)) << 22) & 0xFFC00000;
  _drgbdx |= (iDGX / (1<<5)) & 0x000007FF;
  _drgbdx |= ((iDBX / (1<<7)) << 12) & 0x001FF000;

  for(iPart=0; iPart<2; iPart++) {
    if (iPart == 0) {
      iUpdateLeft = 1;
      iUpdateRight = 1;
      pLeft = p0;
      pr1 = p0;
      if (fz > 0) {
        pRight = p2;
        pr2 = p1;
      }
      else
      {
        pRight = p1;
        pr2 = p2;
      }
      iScanLines = p1->iy - p0->iy;
    }
    else
    {
      if (fz > 0) {
        iUpdateLeft = 0;
        iUpdateRight = 1;
        pr1 = p1;
        pr2 = p2;
      }
      else
      {
        iUpdateLeft = 1;
        iUpdateRight = 0;
        pLeft = p1;
        pRight = p2;
      }
      iScanLines = p2->iy - p1->iy + 1;
    }

    if (iUpdateLeft) {
      iDY1 = pRight->iy - pLeft->iy;
      iDX1 = pRight->ix - pLeft->ix;
      if (iDY1 > 0)
        tmp = (iDX1 << 16) / iDY1;
      else
        tmp = 0;

      iX1 = pLeft->ix;
      iError = 0;
      iDerror = tmp & 0x0000ffff;
      iXRightStep = tmp >> 16;
      iXLeftStep = iXRightStep + 1;

      iR = pLeft->ir;
      iRRightStep = (iDRY + iDRX * iXRightStep);
      iRLeftStep = iRRightStep + iDRX;

      iG = pLeft->ig;
      iGRightStep = (iDGY + iDGX * iXRightStep);
      iGLeftStep = iGRightStep + iDGX;

      iB = pLeft->ib;
      iBRightStep = (iDBY + iDBX * iXRightStep);
      iBLeftStep = iBRightStep + iDBX;
    }

    if (iUpdateRight) {
      iDX2 = (pr2->ix - pr1->ix);
      iDY2 = (pr2->iy - pr1->iy);
      if (iDY2>0)
        iDXY2 = ( iDX2 << 16) / iDY2;
      else
        iDXY2 = 0;
      iX2 = pr1->ix << 16;
    }

    while (iScanLines>0) {
      iScanLines--;
      unsigned int tmp;
      unsigned int drgbdx =_drgbdx;
      int n = (iX2 >> 16) - iX1;
      PIXEL * pp = pPixelRow + iX1;
      unsigned int rgb = (iR << 16) & 0xFFC00000;
      rgb |= (iG >> 5) & 0x000007FF;
      rgb |= (iB << 5) & 0x001FF000;
      while (n>=3) {
        tmp=rgb & 0xF81F07E0; pp[0]=tmp | (tmp >> 16); rgb=(rgb+drgbdx) & ( ~ 0x00200800);
        tmp=rgb & 0xF81F07E0; pp[1]=tmp | (tmp >> 16); rgb=(rgb+drgbdx) & ( ~ 0x00200800);
        tmp=rgb & 0xF81F07E0; pp[2]=tmp | (tmp >> 16); rgb=(rgb+drgbdx) & ( ~ 0x00200800);
        tmp=rgb & 0xF81F07E0; pp[3]=tmp | (tmp >> 16); rgb=(rgb+drgbdx) & ( ~ 0x00200800);
        pp += 4;
        n -= 4;
      }
      while (n>=0) {
        tmp=rgb & 0xF81F07E0; pp[0]=tmp | (tmp >> 16); rgb=(rgb+drgbdx) & ( ~ 0x00200800);
        pp += 1; n -= 1;
      }
      
      iError+=iDerror;
      if (iError > 0) {
        iError-=0x10000;
        iX1+=iXLeftStep;
        iR+=iRLeftStep;
        iG+=iGLeftStep;
        iB+=iBLeftStep;
      }
      else
      {
        iX1+=iXRightStep;
        iR+=iRRightStep;
        iG+=iGRightStep;
        iB+=iBRightStep;
      }
      iX2 += iDXY2;
      pPixelRow += zb->iPitchInPixels;
    }
  }
}

void ZB_DrawTriangleSmooth_z(ZBuffer *zb,
                             ZBufferPoint *p0,
                             ZBufferPoint *p1,
                             ZBufferPoint *p2){
  int _drgbdx;
  ZBufferPoint *t,*pr1,*pr2,*pLeft,*pRight;
  float fdx1, fdx2, fdy1, fdy2, fz;
  PIXEL * pPixelRow;
  int iPart,iUpdateLeft,iUpdateRight;
  int tmp,iScanLines,iDX1,iDY1,iDX2,iDY2;
  int iError,iDerror;
  int iX1,iXRightStep,iXLeftStep;
  int iX2=0,iDXY2=0;
  unsigned short *pz1;

  float fDZ1, fDZ2;
  int iZ,iDZX,iDZY,iZRightStep,iZLeftStep;

  float fDC1, fDC2;
  int iR,iDRX,iDRY,iRRightStep,iRLeftStep;
  int iG,iDGX,iDGY,iGRightStep,iGLeftStep;
  int iB,iDBX,iDBY,iBRightStep,iBLeftStep;

  if (p1->iy < p0->iy) {
    t = p0;
    p0 = p1;
    p1 = t;
  }
  if (p2->iy < p0->iy) {
    t = p2;
    p2 = p1;
    p1 = p0;
    p0 = t;
  }
  else if (p2->iy < p1->iy) {
    t = p1;
    p1 = p2;
    p2 = t;
  }

  fdx1 = p1->ix - p0->ix;
  fdy1 = p1->iy - p0->iy;

  fdx2 = p2->ix - p0->ix;
  fdy2 = p2->iy - p0->iy;

  fz = fdx1 * fdy2 - fdx2 * fdy1;
  if (fz == 0)
    return;

  fz = 1.0 / fz;
  fdx1 *= fz;
  fdy1 *= fz;
  fdx2 *= fz;
  fdy2 *= fz;

  fDZ1 = p1->iz - p0->iz;
  fDZ2 = p2->iz - p0->iz;
  iDZX = (int) (fdy2 * fDZ1 - fdy1 * fDZ2);
  iDZY = (int) (fdx1 * fDZ2 - fdx2 * fDZ1);

  fDC1 = p1->ir - p0->ir;
  fDC2 = p2->ir - p0->ir;
  iDRX = (int) (fdy2 * fDC1 - fdy1 * fDC2);
  iDRY = (int) (fdx1 * fDC2 - fdx2 * fDC1);

  fDC1 = p1->ig - p0->ig;
  fDC2 = p2->ig - p0->ig;
  iDGX = (int) (fdy2 * fDC1 - fdy1 * fDC2);
  iDGY = (int) (fdx1 * fDC2 - fdx2 * fDC1);

  fDC1 = p1->ib - p0->ib;
  fDC2 = p2->ib - p0->ib;
  iDBX = (int) (fdy2 * fDC1 - fdy1 * fDC2);
  iDBY = (int) (fdx1 * fDC2 - fdx2 * fDC1);

  pPixelRow = zb->pbuf + zb->iPitchInPixels * p0->iy;
  pz1 = zb->zbuf + p0->iy * zb->iWidth;

  _drgbdx = ((iDRX / (1<<6)) << 22) & 0xFFC00000;
  _drgbdx |= (iDGX / (1<<5)) & 0x000007FF;
  _drgbdx |= ((iDBX / (1<<7)) << 12) & 0x001FF000;

  for(iPart=0; iPart<2; iPart++) {
    if (iPart == 0) {
      iUpdateLeft = 1;
      iUpdateRight = 1;
      pLeft = p0;
      pr1 = p0;
      if (fz > 0) {
        pRight = p2;
        pr2 = p1;
      }
      else
      {
        pRight = p1;
        pr2 = p2;
      }
      iScanLines = p1->iy - p0->iy;
    }
    else
    {
      if (fz > 0) {
        iUpdateLeft = 0;
        iUpdateRight = 1;
        pr1 = p1;
        pr2 = p2;
      }
      else
      {
        iUpdateLeft = 1;
        iUpdateRight = 0;
        pLeft = p1;
        pRight = p2;
      }
      iScanLines = p2->iy - p1->iy + 1;
    }

    if (iUpdateLeft) {
      iDY1 = pRight->iy - pLeft->iy;
      iDX1 = pRight->ix - pLeft->ix;
      if (iDY1 > 0)
        tmp = (iDX1 << 16) / iDY1;
      else
        tmp = 0;

      iX1 = pLeft->ix;
      iError = 0;
      iDerror = tmp & 0x0000ffff;
      iXRightStep = tmp >> 16;
      iXLeftStep = iXRightStep + 1;

      iZ = pLeft->iz;
      iZRightStep=(iDZY + iDZX * iXRightStep);
      iZLeftStep=iZRightStep + iDZX;

      iR = pLeft->ir;
      iRRightStep = (iDRY + iDRX * iXRightStep);
      iRLeftStep = iRRightStep + iDRX;

      iG = pLeft->ig;
      iGRightStep = (iDGY + iDGX * iXRightStep);
      iGLeftStep = iGRightStep + iDGX;

      iB = pLeft->ib;
      iBRightStep = (iDBY + iDBX * iXRightStep);
      iBLeftStep = iBRightStep + iDBX;
    }

    if (iUpdateRight) {
      iDX2 = (pr2->ix - pr1->ix);
      iDY2 = (pr2->iy - pr1->iy);
      if (iDY2>0)
        iDXY2 = ( iDX2 << 16) / iDY2;
      else
        iDXY2 = 0;
      iX2 = pr1->ix << 16;
    }

    while (iScanLines>0) {
      iScanLines--;
      unsigned short *pz;
      PIXEL *pp;
      unsigned int tmp,z,zz,rgb,drgbdx;
      int n;
      n = (iX2 >> 16) - iX1;
      pp = pPixelRow + iX1;
      pz = pz1 + iX1;
      z = iZ;
      rgb = (iR << 16) & 0xFFC00000;
      rgb |= (iG >> 5) & 0x000007FF;
      rgb |= (iB << 5) & 0x001FF000;
      drgbdx=_drgbdx;
      while (n>=3) {
        zz=z >> 14;
        if (zz >= pz[0]) {
          tmp=rgb & 0xF81F07E0;
          pp[0]=tmp | (tmp >> 16);
          pz[0]=zz;
        }
        z+=iDZX;
        rgb=(rgb+drgbdx) & ( ~ 0x00200800);
        zz=z >> 14;
        if (zz>=pz[1]) {
          tmp=rgb & 0xF81F07E0;
          pp[1]=tmp | (tmp >> 16);
          pz[1]=zz;
        }
        z+=iDZX;
        rgb=(rgb+drgbdx) & ( ~ 0x00200800);
        zz=z >> 14;
        if (zz>=pz[2]) {
          tmp=rgb & 0xF81F07E0;
          pp[2]=tmp | (tmp >> 16);
          pz[2]=zz;
        }
        z+=iDZX;
        rgb=(rgb+drgbdx) & ( ~ 0x00200800);
        zz=z >> 14;
        if (zz>=pz[3]) {
          tmp=rgb & 0xF81F07E0;
          pp[3]=tmp | (tmp >> 16);
          pz[3]=zz;
        }
        z+=iDZX;
        rgb=(rgb+drgbdx) & ( ~ 0x00200800);
        pz += 4;
        pp += 4;
        n -= 4;
      }
      while (n>=0) {
        zz=z >> 14;
        if (zz >= pz[0]) {
          tmp=rgb & 0xF81F07E0;
          pp[0]=tmp | (tmp >> 16);
          pz[0]=zz;
        }
        z+=iDZX;
        rgb=(rgb+drgbdx) & ( ~ 0x00200800);
        pz += 1;
        pp += 1;
        n -= 1;
      }
      
      iError+=iDerror;
      if (iError > 0) {
        iError-=0x10000;
        iX1+=iXLeftStep;
        iZ+=iZLeftStep;
        iR+=iRLeftStep;
        iG+=iGLeftStep;
        iB+=iBLeftStep;
      }
      else
      {
        iX1+=iXRightStep;
        iZ+=iZRightStep;
        iR+=iRRightStep;
        iG+=iGRightStep;
        iB+=iBRightStep;
      }
      iX2 += iDXY2;
      pPixelRow += zb->iPitchInPixels;
      pz1 += zb->iWidth;
    }
  }
}


void ZB_setTexture(ZBuffer *zb,PIXEL *texture) {
  zb->current_texture=texture;
}

void ZB_DrawTriangleTextured(ZBuffer *zb,
                             ZBufferPoint *p0,
                             ZBufferPoint *p1,
                             ZBufferPoint *p2) {
  PIXEL *texture;
  ZBufferPoint *t,*pr1,*pr2,*pLeft,*pRight;
  float fdx1, fdx2, fdy1, fdy2, fz;
  PIXEL * pPixelRow;
  int iPart,iUpdateLeft,iUpdateRight;
  int tmp,iScanLines,iDX1,iDY1,iDX2,iDY2;
  int iError,iDerror;
  int iX1,iXRightStep,iXLeftStep;

  int iX2=0,iDXY2=0;

  float fDT1, fDT2;
  int iS,iDSX,iDSY,iSRightStep,iSLeftStep;
  int iT,iDTX,iDTY,iTRightStep,iTLeftStep;

  if (p1->iy < p0->iy) {
    t = p0;
    p0 = p1;
    p1 = t;
  }
  if (p2->iy < p0->iy) {
    t = p2;
    p2 = p1;
    p1 = p0;
    p0 = t;
  }
  else if (p2->iy < p1->iy) {
    t = p1;
    p1 = p2;
    p2 = t;
  }

  fdx1 = p1->ix - p0->ix;
  fdy1 = p1->iy - p0->iy;

  fdx2 = p2->ix - p0->ix;
  fdy2 = p2->iy - p0->iy;

  fz = fdx1 * fdy2 - fdx2 * fdy1;
  if (fz == 0)
    return;

  fz = 1.0 / fz;
  fdx1 *= fz;
  fdy1 *= fz;
  fdx2 *= fz;
  fdy2 *= fz;

  fDT1 = p1->is - p0->is;
  fDT2 = p2->is - p0->is;
  iDSX = (int) (fdy2 * fDT1 - fdy1 * fDT2);
  iDSY = (int) (fdx1 * fDT2 - fdx2 * fDT1);

  fDT1 = p1->it - p0->it;
  fDT2 = p2->it - p0->it;
  iDTX = (int) (fdy2 * fDT1 - fdy1 * fDT2);
  iDTY = (int) (fdx1 * fDT2 - fdx2 * fDT1);

  pPixelRow = zb->pbuf + zb->iPitchInPixels * p0->iy;
  texture=zb->current_texture;

  for(iPart=0; iPart<2; iPart++) {
    if (iPart == 0) {
      iUpdateLeft = 1;
      iUpdateRight = 1;
      pLeft = p0;
      pr1 = p0;
      if (fz > 0) {
        pRight = p2;
        pr2 = p1;
      }
      else
      {
        pRight = p1;
        pr2 = p2;
      }
      iScanLines = p1->iy - p0->iy;
    }
    else
    {
      if (fz > 0) {
        iUpdateLeft = 0;
        iUpdateRight = 1;
        pr1 = p1;
        pr2 = p2;
      }
      else
      {
        iUpdateLeft = 1;
        iUpdateRight = 0;
        pLeft = p1;
        pRight = p2;
      }
      iScanLines = p2->iy - p1->iy + 1;
    }

    if (iUpdateLeft) {
      iDY1 = pRight->iy - pLeft->iy;
      iDX1 = pRight->ix - pLeft->ix;
      if (iDY1 > 0)
        tmp = (iDX1 << 16) / iDY1;
      else
        tmp = 0;
      iX1 = pLeft->ix;
      iError = 0;
      iDerror = tmp & 0x0000ffff;
      iXRightStep = tmp >> 16;
      iXLeftStep = iXRightStep + 1;

      iS = pLeft->is;
      iSRightStep = (iDSY + iDSX * iXRightStep);
      iSLeftStep = iSRightStep + iDSX;

      iT = pLeft->it;
      iTRightStep = (iDTY + iDTX * iXRightStep);
      iTLeftStep = iTRightStep + iDTX;
    }

    if (iUpdateRight) {
      iDX2 = (pr2->ix - pr1->ix);
      iDY2 = (pr2->iy - pr1->iy);
      if (iDY2>0)
        iDXY2 = ( iDX2 << 16) / iDY2;
      else
        iDXY2 = 0;
      iX2 = pr1->ix << 16;
    }

    while (iScanLines>0) {
      iScanLines--;
      PIXEL *pp;
      int n;
      unsigned int s = iS, t = iT;
      pp = pPixelRow + iX1;
      n = (iX2 >> 16) - iX1;
      while (n>2) {
        pp[0] = texture[((t & 0x3FC00000) | s) >> 14]; s += iDSX; t += iDTX;
        pp[1] = texture[((t & 0x3FC00000) | s) >> 14]; s += iDSX; t += iDTX;
        pp[2] = texture[((t & 0x3FC00000) | s) >> 14]; s += iDSX; t += iDTX;
        pp[3] = texture[((t & 0x3FC00000) | s) >> 14]; s += iDSX; t += iDTX;
        pp += 4;
        n-=4;
      }
      while (n>-1) {
        pp[0] = texture[((t & 0x3FC00000) | s) >> 14]; s += iDSX; t += iDTX;
        pp += 1;
        n-=1;
      }
      
      iError+=iDerror;
      if (iError > 0) {
        iError-=0x10000;
        iX1+=iXLeftStep;
        iS+=iSLeftStep;
        iT+=iTLeftStep;
      }
      else
      {
        iX1+=iXRightStep;
        iS+=iSRightStep;
        iT+=iTRightStep;
      }
      iX2 += iDXY2;
      pPixelRow += zb->iPitchInPixels;
    }
  }
}

void ZB_DrawTriangleTextured_b(ZBuffer *zb,
                               ZBufferPoint *p0,
                               ZBufferPoint *p1,
                               ZBufferPoint *p2) {
  PIXEL *texture;
  PIXEL value;
  ZBufferPoint *t,*pr1,*pr2,*pLeft,*pRight;
  float fdx1, fdx2, fdy1, fdy2, fz;
  PIXEL * pPixelRow;
  int iPart,iUpdateLeft,iUpdateRight;
  int tmp,iScanLines,iDX1,iDY1,iDX2,iDY2;
  int iError,iDerror;
  int iX1,iXRightStep,iXLeftStep;

  int iX2=0,iDXY2=0;
  float fDT1, fDT2;
  int iS,iDSX,iDSY,iSRightStep,iSLeftStep;
  int iT,iDTX,iDTY,iTRightStep,iTLeftStep;

  if (p1->iy < p0->iy) {
    t = p0;
    p0 = p1;
    p1 = t;
  }
  if (p2->iy < p0->iy) {
    t = p2;
    p2 = p1;
    p1 = p0;
    p0 = t;
  }
  else if (p2->iy < p1->iy) {
    t = p1;
    p1 = p2;
    p2 = t;
  }

  fdx1 = p1->ix - p0->ix;
  fdy1 = p1->iy - p0->iy;

  fdx2 = p2->ix - p0->ix;
  fdy2 = p2->iy - p0->iy;

  fz = fdx1 * fdy2 - fdx2 * fdy1;
  if (fz == 0)
    return;

  fz = 1.0 / fz;
  fdx1 *= fz;
  fdy1 *= fz;
  fdx2 *= fz;
  fdy2 *= fz;

  fDT1 = p1->is - p0->is;
  fDT2 = p2->is - p0->is;
  iDSX = (int) (fdy2 * fDT1 - fdy1 * fDT2);
  iDSY = (int) (fdx1 * fDT2 - fdx2 * fDT1);

  fDT1 = p1->it - p0->it;
  fDT2 = p2->it - p0->it;
  iDTX = (int) (fdy2 * fDT1 - fdy1 * fDT2);
  iDTY = (int) (fdx1 * fDT2 - fdx2 * fDT1);

  pPixelRow = zb->pbuf + zb->iPitchInPixels * p0->iy;
  texture=zb->current_texture;

  for(iPart=0; iPart<2; iPart++) {
    if (iPart == 0) {
      iUpdateLeft = 1;
      iUpdateRight = 1;
      pLeft = p0;
      pr1 = p0;
      if (fz > 0) {
        pRight = p2;
        pr2 = p1;
      }
      else
      {
        pRight = p1;
        pr2 = p2;
      }
      iScanLines = p1->iy - p0->iy;
    }
    else
    {
      if (fz > 0) {
        iUpdateLeft = 0;
        iUpdateRight = 1;
        pr1 = p1;
        pr2 = p2;
      }
      else
      {
        iUpdateLeft = 1;
        iUpdateRight = 0;
        pLeft = p1;
        pRight = p2;
      }
      iScanLines = p2->iy - p1->iy + 1;
    }

    if (iUpdateLeft) {
      iDY1 = pRight->iy - pLeft->iy;
      iDX1 = pRight->ix - pLeft->ix;
      if (iDY1 > 0)
        tmp = (iDX1 << 16) / iDY1;
      else
        tmp = 0;

      iX1 = pLeft->ix;
      iError = 0;
      iDerror = tmp & 0x0000ffff;
      iXRightStep = tmp >> 16;
      iXLeftStep = iXRightStep + 1;

      iS = pLeft->is;
      iSRightStep = (iDSY + iDSX * iXRightStep);
      iSLeftStep = iSRightStep + iDSX;

      iT = pLeft->it;
      iTRightStep = (iDTY + iDTX * iXRightStep);
      iTLeftStep = iTRightStep + iDTX;
    }

    if (iUpdateRight) {
      iDX2 = (pr2->ix - pr1->ix);
      iDY2 = (pr2->iy - pr1->iy);
      if (iDY2>0)
        iDXY2 = ( iDX2 << 16) / iDY2;
      else
        iDXY2 = 0;
      iX2 = pr1->ix << 16;
    }

    while (iScanLines>0) {
      iScanLines--;
      PIXEL *pp;
      int n;
      unsigned int s = iS, t = iT;
      pp = pPixelRow + iX1;
      n = (iX2 >> 16) - iX1;
      while (n>2) {
        value = texture[((t & 0x3FC00000) | s) >> 14]; 
        if (value != 0xF81F) pp[0] = value; 
        s += iDSX; t += iDTX;
        value = texture[((t & 0x3FC00000) | s) >> 14]; 
        if (value != 0xF81F) pp[1] = value; 
        s += iDSX; t += iDTX;
        value = texture[((t & 0x3FC00000) | s) >> 14]; 
        if (value != 0xF81F) pp[2] = value; 
        s += iDSX; t += iDTX;
        value = texture[((t & 0x3FC00000) | s) >> 14]; 
        if (value != 0xF81F) pp[3] = value; 
        s += iDSX; t += iDTX;
        pp += 4;
        n-=4;
      }
      while (n>-1) {
        value = texture[((t & 0x3FC00000) | s) >> 14]; 
        if (value != 0xF81F) pp[0] = value; 
        s += iDSX; t += iDTX;
        pp += 1; n-=1;
      }
      
      iError+=iDerror;
      if (iError > 0) {
        iError-=0x10000;
        iX1+=iXLeftStep;
        iS+=iSLeftStep;
        iT+=iTLeftStep;
      }
      else
      {
        iX1+=iXRightStep;
        iS+=iSRightStep;
        iT+=iTRightStep;
      }
      iX2 += iDXY2;
      pPixelRow += zb->iPitchInPixels;
    }
  }
}


void ZB_DrawTriangleTextured_z(ZBuffer *zb,
                               ZBufferPoint *p0,
                               ZBufferPoint *p1,
                               ZBufferPoint *p2) {
  PIXEL *texture;
  ZBufferPoint *t,*pr1,*pr2,*pLeft,*pRight;
  float fdx1, fdx2, fdy1, fdy2, fz;
  PIXEL * pPixelRow;
  int iPart,iUpdateLeft,iUpdateRight;
  int tmp,iScanLines,iDX1,iDY1,iDX2,iDY2;
  int iError,iDerror;
  int iX1,iXRightStep,iXLeftStep;
  int iX2=0,iDXY2=0;

  unsigned short *pz1;

  float fDZ1, fDZ2;
  int iZ,iDZX,iDZY,iZRightStep,iZLeftStep;

  float fDT1, fDT2;
  int iS,iDSX,iDSY,iSRightStep,iSLeftStep;
  int iT,iDTX,iDTY,iTRightStep,iTLeftStep;

  if (p1->iy < p0->iy) {
    t = p0;
    p0 = p1;
    p1 = t;
  }
  if (p2->iy < p0->iy) {
    t = p2;
    p2 = p1;
    p1 = p0;
    p0 = t;
  }
  else if (p2->iy < p1->iy) {
    t = p1;
    p1 = p2;
    p2 = t;
  }

  fdx1 = p1->ix - p0->ix;
  fdy1 = p1->iy - p0->iy;

  fdx2 = p2->ix - p0->ix;
  fdy2 = p2->iy - p0->iy;

  fz = fdx1 * fdy2 - fdx2 * fdy1;
  if (fz == 0)
    return;

  fz = 1.0 / fz;
  fdx1 *= fz;
  fdy1 *= fz;
  fdx2 *= fz;
  fdy2 *= fz;

  fDZ1 = p1->iz - p0->iz;
  fDZ2 = p2->iz - p0->iz;
  iDZX = (int) (fdy2 * fDZ1 - fdy1 * fDZ2);
  iDZY = (int) (fdx1 * fDZ2 - fdx2 * fDZ1);

  fDT1 = p1->is - p0->is;
  fDT2 = p2->is - p0->is;
  iDSX = (int) (fdy2 * fDT1 - fdy1 * fDT2);
  iDSY = (int) (fdx1 * fDT2 - fdx2 * fDT1);

  fDT1 = p1->it - p0->it;
  fDT2 = p2->it - p0->it;
  iDTX = (int) (fdy2 * fDT1 - fdy1 * fDT2);
  iDTY = (int) (fdx1 * fDT2 - fdx2 * fDT1);

  pPixelRow = zb->pbuf + zb->iPitchInPixels * p0->iy;
  pz1 = zb->zbuf + p0->iy * zb->iWidth;
  texture=zb->current_texture;

  for(iPart=0; iPart<2; iPart++) {
    if (iPart == 0) {
      iUpdateLeft = 1;
      iUpdateRight = 1;
      pLeft = p0;
      pr1 = p0;
      if (fz > 0) {
        pRight = p2;
        pr2 = p1;
      }
      else
      {
        pRight = p1;
        pr2 = p2;
      }
      iScanLines = p1->iy - p0->iy;
    }
    else
    {
      if (fz > 0) {
        iUpdateLeft = 0;
        iUpdateRight = 1;
        pr1 = p1;
        pr2 = p2;
      }
      else
      {
        iUpdateLeft = 1;
        iUpdateRight = 0;
        pLeft = p1;
        pRight = p2;
      }
      iScanLines = p2->iy - p1->iy + 1;
    }

    if (iUpdateLeft) {
      iDY1 = pRight->iy - pLeft->iy;
      iDX1 = pRight->ix - pLeft->ix;
      if (iDY1 > 0)
        tmp = (iDX1 << 16) / iDY1;
      else
        tmp = 0;

      iX1 = pLeft->ix;
      iError = 0;
      iDerror = tmp & 0x0000ffff;
      iXRightStep = tmp >> 16;
      iXLeftStep = iXRightStep + 1;

      iZ = pLeft->iz;
      iZRightStep=(iDZY + iDZX * iXRightStep);
      iZLeftStep=iZRightStep + iDZX;

      iS = pLeft->is;
      iSRightStep = (iDSY + iDSX * iXRightStep);
      iSLeftStep = iSRightStep + iDSX;

      iT = pLeft->it;
      iTRightStep = (iDTY + iDTX * iXRightStep);
      iTLeftStep = iTRightStep + iDTX;
    }

    if (iUpdateRight) {
      iDX2 = (pr2->ix - pr1->ix);
      iDY2 = (pr2->iy - pr1->iy);
      if (iDY2>0)
        iDXY2 = ( iDX2 << 16) / iDY2;
      else
        iDXY2 = 0;
      iX2 = pr1->ix << 16;
    }

    while (iScanLines>0) {
      iScanLines--;
      PIXEL *pp;
      int n;
      unsigned short * pz = pz1+iX1;
      unsigned int zz;
      unsigned int z = iZ;
      unsigned int s = iS, t = iT;
      pp = pPixelRow + iX1;
      n = (iX2 >> 16) - iX1;
      while (n>2) {
        zz=z >> 14;
        if (zz >= pz[0]) {
          pp[0] = texture[((t & 0x3FC00000) | s) >> 14];
          pz[0] = zz;
        }
        z += iDZX; s += iDSX; t += iDTX; zz=z >> 14;
        if (zz >= pz[1]) {
          pp[1] = texture[((t & 0x3FC00000) | s) >> 14];
          pz[1] = zz;
        }
        z += iDZX; s += iDSX; t += iDTX; zz=z >> 14;
        if (zz >= pz[2]) {
          pp[2] = texture[((t & 0x3FC00000) | s) >> 14];
          pz[2] = zz;
        }
        z += iDZX; s += iDSX; t += iDTX; zz=z >> 14;
        if (zz >= pz[3]) {
          pp[3] = texture[((t & 0x3FC00000) | s) >> 14];
          pz[3] = zz;
        }
        z += iDZX; s += iDSX; t += iDTX;
        pz+=4; pp += 4; n-=4;
      }
      while (n>-1) {
        zz=z >> 14;
        if (zz >= pz[0]) {
          pp[0] = texture[((t & 0x3FC00000) | s) >> 14];
          pz[0] = zz;
        }
        z += iDZX; s += iDSX; t += iDTX;
        pz+=1; pp += 1; n-=1;
      }
      
      iError+=iDerror;
      if (iError > 0) {
        iError-=0x10000;
        iX1+=iXLeftStep;
        iZ+=iZLeftStep;
        iS+=iSLeftStep;
        iT+=iTLeftStep;
      }
      else
      {
        iX1+=iXRightStep;
        iZ+=iZRightStep;
        iS+=iSRightStep;
        iT+=iTRightStep;
      }

      iX2 += iDXY2;
      pPixelRow += zb->iPitchInPixels;
      pz1 += zb->iWidth;
    }
  }
}


void ZB_DrawTriangleTextured_zb(ZBuffer *zb,
                                ZBufferPoint *p0,
                                ZBufferPoint *p1,
                                ZBufferPoint *p2) {
  PIXEL *texture;
  PIXEL value;
  ZBufferPoint *t,*pr1,*pr2,*pLeft,*pRight;
  float fdx1, fdx2, fdy1, fdy2, fz;
  PIXEL * pPixelRow;
  int iPart,iUpdateLeft,iUpdateRight;
  int tmp,iScanLines,iDX1,iDY1,iDX2,iDY2;
  int iError,iDerror;
  int iX1,iXRightStep,iXLeftStep;
  int iX2=0,iDXY2=0;
  unsigned short *pz1;
  float fDZ1, fDZ2;
  int iZ,iDZX,iDZY,iZRightStep,iZLeftStep;
  float fDT1, fDT2;
  int iS,iDSX,iDSY,iSRightStep,iSLeftStep;
  int iT,iDTX,iDTY,iTRightStep,iTLeftStep;

  if (p1->iy < p0->iy) {
    t = p0;
    p0 = p1;
    p1 = t;
  }
  if (p2->iy < p0->iy) {
    t = p2;
    p2 = p1;
    p1 = p0;
    p0 = t;
  }
  else if (p2->iy < p1->iy) {
    t = p1;
    p1 = p2;
    p2 = t;
  }

  fdx1 = p1->ix - p0->ix;
  fdy1 = p1->iy - p0->iy;

  fdx2 = p2->ix - p0->ix;
  fdy2 = p2->iy - p0->iy;

  fz = fdx1 * fdy2 - fdx2 * fdy1;
  if (fz == 0)
    return;

  fz = 1.0 / fz;
  fdx1 *= fz;
  fdy1 *= fz;
  fdx2 *= fz;
  fdy2 *= fz;

  fDZ1 = p1->iz - p0->iz;
  fDZ2 = p2->iz - p0->iz;
  iDZX = (int) (fdy2 * fDZ1 - fdy1 * fDZ2);
  iDZY = (int) (fdx1 * fDZ2 - fdx2 * fDZ1);

  fDT1 = p1->is - p0->is;
  fDT2 = p2->is - p0->is;
  iDSX = (int) (fdy2 * fDT1 - fdy1 * fDT2);
  iDSY = (int) (fdx1 * fDT2 - fdx2 * fDT1);

  fDT1 = p1->it - p0->it;
  fDT2 = p2->it - p0->it;
  iDTX = (int) (fdy2 * fDT1 - fdy1 * fDT2);
  iDTY = (int) (fdx1 * fDT2 - fdx2 * fDT1);

  pPixelRow = zb->pbuf + zb->iPitchInPixels * p0->iy;
  pz1 = zb->zbuf + p0->iy * zb->iWidth;
  texture=zb->current_texture;

  for(iPart=0; iPart<2; iPart++) {
    if (iPart == 0) {
      iUpdateLeft = 1;
      iUpdateRight = 1;
      pLeft = p0;
      pr1 = p0;
      if (fz > 0) {
        pRight = p2;
        pr2 = p1;
      }
      else
      {
        pRight = p1;
        pr2 = p2;
      }
      iScanLines = p1->iy - p0->iy;
    }
    else
    {
      if (fz > 0) {
        iUpdateLeft = 0;
        iUpdateRight = 1;
        pr1 = p1;
        pr2 = p2;
      }
      else
      {
        iUpdateLeft = 1;
        iUpdateRight = 0;
        pLeft = p1;
        pRight = p2;
      }
      iScanLines = p2->iy - p1->iy + 1;
    }

    if (iUpdateLeft) {
      iDY1 = pRight->iy - pLeft->iy;
      iDX1 = pRight->ix - pLeft->ix;
      if (iDY1 > 0)
        tmp = (iDX1 << 16) / iDY1;
      else
        tmp = 0;
      iX1 = pLeft->ix;
      iError = 0;
      iDerror = tmp & 0x0000ffff;
      iXRightStep = tmp >> 16;
      iXLeftStep = iXRightStep + 1;

      iZ = pLeft->iz;
      iZRightStep=(iDZY + iDZX * iXRightStep);
      iZLeftStep=iZRightStep + iDZX;

      iS = pLeft->is;
      iSRightStep = (iDSY + iDSX * iXRightStep);
      iSLeftStep = iSRightStep + iDSX;

      iT = pLeft->it;
      iTRightStep = (iDTY + iDTX * iXRightStep);
      iTLeftStep = iTRightStep + iDTX;
    }

    if (iUpdateRight) {
      iDX2 = (pr2->ix - pr1->ix);
      iDY2 = (pr2->iy - pr1->iy);
      if (iDY2>0)
        iDXY2 = ( iDX2 << 16) / iDY2;
      else
        iDXY2 = 0;
      iX2 = pr1->ix << 16;
    }

    while (iScanLines>0) {
      iScanLines--;
      PIXEL *pp;
      int n;
      unsigned short * pz = pz1+iX1;
      unsigned int zz;
      unsigned int z = iZ;
      unsigned int s = iS, t = iT;
      pp = pPixelRow + iX1;
      n = (iX2 >> 16) - iX1;
      while (n>2) {
        zz=z >> 14;
        if (zz >= pz[0]) {
          value = texture[((t & 0x3FC00000) | s) >> 14];
          if (value != 0xF81F) {
            pp[0] = value;
            pz[0] = zz;
          }
        }
        z += iDZX; s += iDSX; t += iDTX; zz=z >> 14;
        if (zz >= pz[1]) {
          value = texture[((t & 0x3FC00000) | s) >> 14];
          if (value != 0xF81F) {
            pp[1] = value;
            pz[1] = zz;
          }
        } 
        z += iDZX; s += iDSX; t += iDTX; zz=z >> 14;
        if (zz >= pz[2]) {
          value = texture[((t & 0x3FC00000) | s) >> 14];
          if (value != 0xF81F) {
            pp[2] = value;
            pz[2] = zz;
          }
        }
        z += iDZX; s += iDSX; t += iDTX; zz=z >> 14;
        if (zz >= pz[3]) {
          value = texture[((t & 0x3FC00000) | s) >> 14];
          if (value != 0xF81F) {
            pp[3] = value;
            pz[3] = zz;
          }
        }
        z += iDZX; s += iDSX; t += iDTX;
        pz+=4; pp += 4; n-=4;
      }
      while (n>-1) {
        zz=z >> 14;
        if (zz >= pz[0]) {
          value = texture[((t & 0x3FC00000) | s) >> 14];
          if ((value) != 0xF81F) {
            pp[0] = value; pz[0] = zz;
          }
        }
        z += iDZX; s += iDSX; t += iDTX;
        pz+=1; pp += 1; n-=1;
      }
      
      iError+=iDerror;
      if (iError > 0) {
        iError-=0x10000;
        iX1+=iXLeftStep;
        iZ+=iZLeftStep;
        iS+=iSLeftStep;
        iT+=iTLeftStep;
      }
      else
      {
        iX1+=iXRightStep;
        iZ+=iZRightStep;
        iS+=iSRightStep;
        iT+=iTRightStep;
      }

      iX2 += iDXY2;
      pPixelRow += zb->iPitchInPixels;
      pz1 += zb->iWidth;
    }
  }
}


void ZB_DrawTriangleTexturedPerspective(ZBuffer *zb,
                                        ZBufferPoint *p0,
                                        ZBufferPoint *p1,
                                        ZBufferPoint *p2) {
  PIXEL *texture;
  float fdzdx,fndzdx,ndszdx,ndtzdx;
  ZBufferPoint *t,*pr1,*pr2,*pLeft,*pRight;
  float fdx1, fdx2, fdy1, fdy2, fz;
  PIXEL * pPixelRow;
  int iPart,iUpdateLeft,iUpdateRight;
  int tmp,iScanLines,iDX1,iDY1,iDX2,iDY2;
  int iError,iDerror;
  int iX1,iXRightStep,iXLeftStep;

  int iX2=0,iDXY2=0;

  float fDZ1, fDZ2;
  int iZ,iDZX,iDZY,iZRightStep,iZLeftStep;
  float fDTZ1, fDTZ2;
  float fS,fDSX,fDSY,fSRightStep,fSLeftStep;
  float fT,fDTX,fDTY,fTRightStep,fTLeftStep;
  float fzz;

  if (p1->iy < p0->iy) {
    t = p0;
    p0 = p1;
    p1 = t;
  }
  if (p2->iy < p0->iy) {
    t = p2;
    p2 = p1;
    p1 = p0;
    p0 = t;
  }
  else if (p2->iy < p1->iy) {
    t = p1;
    p1 = p2;
    p2 = t;
  }

  fdx1 = p1->ix - p0->ix;
  fdy1 = p1->iy - p0->iy;

  fdx2 = p2->ix - p0->ix;
  fdy2 = p2->iy - p0->iy;

  fz = fdx1 * fdy2 - fdx2 * fdy1;
  if (fz == 0)
    return;

  fz = 1.0 / fz;
  fdx1 *= fz;
  fdy1 *= fz;
  fdx2 *= fz;
  fdy2 *= fz;

  fDZ1 = p1->iz - p0->iz;
  fDZ2 = p2->iz - p0->iz;
  iDZX = (int) (fdy2 * fDZ1 - fdy1 * fDZ2);
  iDZY = (int) (fdx1 * fDZ2 - fdx2 * fDZ1);

  fzz=(float) p0->iz;
  p0->fs= (float) p0->is * fzz;
  p0->ft= (float) p0->it * fzz;

  fzz=(float) p1->iz;
  p1->fs= (float) p1->is * fzz;
  p1->ft= (float) p1->it * fzz;

  fzz=(float) p2->iz;
  p2->fs= (float) p2->is * fzz;
  p2->ft= (float) p2->it * fzz;

  fDTZ1 = p1->fs - p0->fs;
  fDTZ2 = p2->fs - p0->fs;
  fDSX = (fdy2 * fDTZ1 - fdy1 * fDTZ2);
  fDSY = (fdx1 * fDTZ2 - fdx2 * fDTZ1);

  fDTZ1 = p1->ft - p0->ft;
  fDTZ2 = p2->ft - p0->ft;
  fDTX = (fdy2 * fDTZ1 - fdy1 * fDTZ2);
  fDTY = (fdx1 * fDTZ2 - fdx2 * fDTZ1);

  pPixelRow = zb->pbuf + zb->iPitchInPixels * p0->iy;
  texture = zb->current_texture;

  fdzdx = (float)iDZX;
  fndzdx = 16 * fdzdx;
  ndszdx = 16 * fDSX;
  ndtzdx = 16 * fDTX;

  for(iPart=0; iPart<2; iPart++) {
    if (iPart == 0) {
      iUpdateLeft = 1;
      iUpdateRight = 1;
      pLeft = p0;
      pr1 = p0;
      if (fz > 0) {
        pRight = p2;
        pr2 = p1;
      }
      else
      {
        pRight = p1;
        pr2 = p2;
      }
      iScanLines = p1->iy - p0->iy;
    }
    else
    {
      if (fz > 0) {
        iUpdateLeft = 0;
        iUpdateRight = 1;
        pr1 = p1;
        pr2 = p2;
      }
      else
      {
        iUpdateLeft = 1;
        iUpdateRight = 0;
        pLeft = p1;
        pRight = p2;
      }
      iScanLines = p2->iy - p1->iy + 1;
    }

    if (iUpdateLeft) {
      iDY1 = pRight->iy - pLeft->iy;
      iDX1 = pRight->ix - pLeft->ix;
      if (iDY1 > 0)
        tmp = (iDX1 << 16) / iDY1;
      else
        tmp = 0;

      iX1 = pLeft->ix;
      iError = 0;
      iDerror = tmp & 0x0000ffff;
      iXRightStep = tmp >> 16;
      iXLeftStep = iXRightStep + 1;

      iZ = pLeft->iz;
      iZRightStep=(iDZY + iDZX * iXRightStep);
      iZLeftStep=iZRightStep + iDZX;

      fS = pLeft->fs;
      fSRightStep = (fDSY + fDSX * iXRightStep);
      fSLeftStep = fSRightStep + fDSX;

      fT = pLeft->ft;
      fTRightStep =(fDTY + fDTX * iXRightStep);
      fTLeftStep = fTRightStep + fDTX;
    }

    if (iUpdateRight) {
      iDX2 = (pr2->ix - pr1->ix);
      iDY2 = (pr2->iy - pr1->iy);
      if (iDY2>0)
        iDXY2 = ( iDX2 << 16) / iDY2;
      else
        iDXY2 = 0;
      iX2 = pr1->ix << 16;
    }

    while (iScanLines>0) {
      iScanLines--;
      unsigned short *pz;
      PIXEL *pp;
      unsigned int s,t;
      int n,iSStep,iTStep;
      float sz,tz,fz,zinv;
      n=(iX2 >> 16)-iX1;
      fz=(float)iZ;
      zinv=1.0 / fz;
      pp = pPixelRow + iX1;
      sz=fS;
      tz=fT;
      while (n>14) {
        float ss,tt;
        ss=(sz * zinv);
        tt=(tz * zinv);
        s=(int) ss;
        t=(int) tt;
        iSStep = (int)( (fDSX - ss*fdzdx)*zinv );
        iTStep = (int)( (fDTX - tt*fdzdx)*zinv );
        fz+=fndzdx;
        zinv=1.0 / fz;

        pp[0]=*(PIXEL *)((char *)texture + (((t & 0x3FC00000) | (s & 0x003FC000)) >> 13));
        s += iSStep; t += iTStep;
        pp[1]=*(PIXEL *)((char *)texture + (((t & 0x3FC00000) | (s & 0x003FC000)) >> 13));
        s += iSStep; t += iTStep;
        pp[2]=*(PIXEL *)((char *)texture + (((t & 0x3FC00000) | (s & 0x003FC000)) >> 13));
        s += iSStep; t += iTStep;
        pp[3]=*(PIXEL *)((char *)texture + (((t & 0x3FC00000) | (s & 0x003FC000)) >> 13));
        s += iSStep; t += iTStep;
        pp[4]=*(PIXEL *)((char *)texture + (((t & 0x3FC00000) | (s & 0x003FC000)) >> 13));
        s += iSStep; t += iTStep;
        pp[5]=*(PIXEL *)((char *)texture + (((t & 0x3FC00000) | (s & 0x003FC000)) >> 13));
        s += iSStep; t += iTStep;
        pp[6]=*(PIXEL *)((char *)texture + (((t & 0x3FC00000) | (s & 0x003FC000)) >> 13));
        s += iSStep; t += iTStep;
        pp[7]=*(PIXEL *)((char *)texture + (((t & 0x3FC00000) | (s & 0x003FC000)) >> 13));
        s += iSStep; t += iTStep;
        pp[8]=*(PIXEL *)((char *)texture + (((t & 0x3FC00000) | (s & 0x003FC000)) >> 13));
        s += iSStep; t += iTStep;
        pp[9]=*(PIXEL *)((char *)texture + (((t & 0x3FC00000) | (s & 0x003FC000)) >> 13));
        s += iSStep; t += iTStep;
        pp[10]=*(PIXEL *)((char *)texture + (((t & 0x3FC00000) | (s & 0x003FC000)) >> 13));
        s += iSStep; t += iTStep;
        pp[11]=*(PIXEL *)((char *)texture + (((t & 0x3FC00000) | (s & 0x003FC000)) >> 13));
        s += iSStep; t += iTStep;
        pp[12]=*(PIXEL *)((char *)texture + (((t & 0x3FC00000) | (s & 0x003FC000)) >> 13));
        s += iSStep; t += iTStep;
        pp[13]=*(PIXEL *)((char *)texture + (((t & 0x3FC00000) | (s & 0x003FC000)) >> 13));
        s += iSStep; t += iTStep;
        pp[14]=*(PIXEL *)((char *)texture + (((t & 0x3FC00000) | (s & 0x003FC000)) >> 13));
        s += iSStep; t += iTStep;
        pp[15]=*(PIXEL *)((char *)texture + (((t & 0x3FC00000) | (s & 0x003FC000)) >> 13));
        s += iSStep; t += iTStep;
        pz+=16; pp += 16; n-=16; sz+=ndszdx; tz+=ndtzdx;
      }

      float ss,tt;
      ss=(sz * zinv);
      tt=(tz * zinv);
      s=(int) ss;
      t=(int) tt;
      iSStep = (int)( (fDSX - ss*fdzdx)*zinv );
      iTStep = (int)( (fDTX - tt*fdzdx)*zinv );
      while (n>-1) {
        pp[0]=*(PIXEL *)((char *)texture + (((t & 0x3FC00000) | (s & 0x003FC000)) >> 13));
        s += iSStep; t += iTStep;
        pz+=1; pp += 1; n-=1;
      }

      iError+=iDerror;
      if (iError > 0) {
        iError-=0x10000;
        iX1+=iXLeftStep;
        iZ+=iZLeftStep;
        fS+=fSLeftStep;
        fT+=fTLeftStep;
      }
      else
      {
        iX1+=iXRightStep;
        iZ+=iZRightStep;
        fS+=fSRightStep;
        fT+=fTRightStep;
      }

      iX2 += iDXY2;
      pPixelRow += zb->iPitchInPixels;
    }
  }
}


void ZB_DrawTriangleTexturedPerspective_b(ZBuffer *zb,
                                          ZBufferPoint *p0,
                                          ZBufferPoint *p1,
                                          ZBufferPoint *p2) {
  PIXEL *texture;
  PIXEL value;
  float fdzdx,fndzdx,ndszdx,ndtzdx;
  ZBufferPoint *t,*pr1,*pr2,*pLeft,*pRight;
  float fdx1, fdx2, fdy1, fdy2, fz;
  PIXEL * pPixelRow;
  int iPart,iUpdateLeft,iUpdateRight;
  int tmp,iScanLines,iDX1,iDY1,iDX2,iDY2;
  int iError,iDerror;
  int iX1,iXRightStep,iXLeftStep;

  int iX2=0,iDXY2=0;
  float fDZ1, fDZ2;
  int iZ,iDZX,iDZY,iZRightStep,iZLeftStep;

  float fDTZ1, fDTZ2;
  float fS,fDSX,fDSY,fSRightStep,fSLeftStep;
  float fT,fDTX,fDTY,fTRightStep,fTLeftStep;

  if (p1->iy < p0->iy) {
    t = p0;
    p0 = p1;
    p1 = t;
  }
  if (p2->iy < p0->iy) {
    t = p2;
    p2 = p1;
    p1 = p0;
    p0 = t;
  }
  else if (p2->iy < p1->iy) {
    t = p1;
    p1 = p2;
    p2 = t;
  }

  fdx1 = p1->ix - p0->ix;
  fdy1 = p1->iy - p0->iy;

  fdx2 = p2->ix - p0->ix;
  fdy2 = p2->iy - p0->iy;

  fz = fdx1 * fdy2 - fdx2 * fdy1;
  if (fz == 0)
    return;

  fz = 1.0 / fz;
  fdx1 *= fz;
  fdy1 *= fz;
  fdx2 *= fz;
  fdy2 *= fz;

  fDZ1 = p1->iz - p0->iz;
  fDZ2 = p2->iz - p0->iz;
  iDZX = (int) (fdy2 * fDZ1 - fdy1 * fDZ2);
  iDZY = (int) (fdx1 * fDZ2 - fdx2 * fDZ1);

  float fzz;
  fzz=(float) p0->iz;
  p0->fs= (float) p0->is * fzz;
  p0->ft= (float) p0->it * fzz;

  fzz=(float) p1->iz;
  p1->fs= (float) p1->is * fzz;
  p1->ft= (float) p1->it * fzz;

  fzz=(float) p2->iz;
  p2->fs= (float) p2->is * fzz;
  p2->ft= (float) p2->it * fzz;

  fDTZ1 = p1->fs - p0->fs;
  fDTZ2 = p2->fs - p0->fs;
  fDSX = (fdy2 * fDTZ1 - fdy1 * fDTZ2);
  fDSY = (fdx1 * fDTZ2 - fdx2 * fDTZ1);

  fDTZ1 = p1->ft - p0->ft;
  fDTZ2 = p2->ft - p0->ft;
  fDTX = (fdy2 * fDTZ1 - fdy1 * fDTZ2);
  fDTY = (fdx1 * fDTZ2 - fdx2 * fDTZ1);

  pPixelRow = zb->pbuf + zb->iPitchInPixels * p0->iy;
  texture = zb->current_texture;
  fdzdx = (float)iDZX;
  fndzdx = 16 * fdzdx;
  ndszdx = 16 * fDSX;
  ndtzdx = 16 * fDTX;

  for(iPart=0; iPart<2; iPart++) {
    if (iPart == 0) {
      iUpdateLeft = 1;
      iUpdateRight = 1;
      pLeft = p0;
      pr1 = p0;
      if (fz > 0) {
        pRight = p2;
        pr2 = p1;
      }
      else
      {
        pRight = p1;
        pr2 = p2;
      }
      iScanLines = p1->iy - p0->iy;
    }
    else
    {
      if (fz > 0) {
        iUpdateLeft = 0;
        iUpdateRight = 1;
        pr1 = p1;
        pr2 = p2;
      }
      else
      {
        iUpdateLeft = 1;
        iUpdateRight = 0;
        pLeft = p1;
        pRight = p2;
      }
      iScanLines = p2->iy - p1->iy + 1;
    }

    if (iUpdateLeft) {
      iDY1 = pRight->iy - pLeft->iy;
      iDX1 = pRight->ix - pLeft->ix;
      if (iDY1 > 0)
        tmp = (iDX1 << 16) / iDY1;
      else
        tmp = 0;

      iX1 = pLeft->ix;
      iError = 0;
      iDerror = tmp & 0x0000ffff;
      iXRightStep = tmp >> 16;
      iXLeftStep = iXRightStep + 1;

      iZ = pLeft->iz;
      iZRightStep=(iDZY + iDZX * iXRightStep);
      iZLeftStep=iZRightStep + iDZX;

      fS = pLeft->fs;
      fSRightStep = (fDSY + fDSX * iXRightStep);
      fSLeftStep = fSRightStep + fDSX;

      fT = pLeft->ft;
      fTRightStep =(fDTY + fDTX * iXRightStep);
      fTLeftStep = fTRightStep + fDTX;
    }

    if (iUpdateRight) {
      iDX2 = (pr2->ix - pr1->ix);
      iDY2 = (pr2->iy - pr1->iy);
      if (iDY2>0)
        iDXY2 = ( iDX2 << 16) / iDY2;
      else
        iDXY2 = 0;
      iX2 = pr1->ix << 16;
    }

    while (iScanLines>0) {
      iScanLines--;
      unsigned short *pz;
      PIXEL * pp = pPixelRow + iX1;
      int iSStep,iTStep;
      int n =(iX2 >> 16)-iX1;
      float fz=(float)iZ;
      float zinv=1.0 / fz;
      float sz=fS;
      float tz=fT;
      int si,ti;
      while (n>14) {
        float ss=(sz * zinv);
        float tt=(tz * zinv);
        si=(int) ss;
        ti=(int) tt;
        iSStep = (int)( (fDSX - ss*fdzdx)*zinv );
        iTStep = (int)( (fDTX - tt*fdzdx)*zinv );
        fz+=fndzdx;
        zinv=1.0 / fz;

        value = *(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13 ));
        if (value != 0xF81F) pp[0] = value;
        si += iSStep; ti += iTStep;
        value = *(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13 ));
        if (value != 0xF81F) pp[1] = value;
        si += iSStep; ti += iTStep;
        value = *(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13 ));
        if (value != 0xF81F) pp[2] = value;
        si += iSStep; ti += iTStep;
        value = *(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13 ));
        if (value != 0xF81F) pp[3] = value;
        si += iSStep; ti += iTStep;
        value = *(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13 ));
        if (value != 0xF81F) pp[4] = value;
        si += iSStep; ti += iTStep;
        value = *(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13 ));
        if (value != 0xF81F) pp[5] = value;
        si += iSStep; ti += iTStep;
        value = *(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13 ));
        if (value != 0xF81F) pp[6] = value;
        si += iSStep; ti += iTStep;
        value = *(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13 ));
        if (value != 0xF81F) pp[7] = value;
        si += iSStep; ti += iTStep;
        value = *(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13 ));
        if (value != 0xF81F) pp[8] = value;
        si += iSStep; ti += iTStep;
        value = *(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13 ));
        if (value != 0xF81F) pp[9] = value;
        si += iSStep; ti += iTStep;
        value = *(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13 ));
        if (value != 0xF81F) pp[10] = value;
        si += iSStep; ti += iTStep;
        value = *(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13 ));
        if (value != 0xF81F) pp[11] = value;
        si += iSStep; ti += iTStep;
        value = *(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13 ));
        if (value != 0xF81F) pp[12] = value;
        si += iSStep; ti += iTStep;
        value = *(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13 ));
        if (value != 0xF81F) pp[13] = value;
        si += iSStep; ti += iTStep;
        value = *(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13 ));
        if (value != 0xF81F) pp[14] = value;
        si += iSStep; ti += iTStep;
        value = *(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13 ));
        if (value != 0xF81F) pp[15] = value;
        si += iSStep; ti += iTStep;
        pz += 16;  pp += 16; n-=16;
        sz+=ndszdx; tz+=ndtzdx;
      }

      float ss,tt;
      ss=(sz * zinv);
      tt=(tz * zinv);
      si=(int) ss;
      ti=(int) tt;
      iSStep = (int)( (fDSX - ss*fdzdx)*zinv );
      iTStep = (int)( (fDTX - tt*fdzdx)*zinv );
      while (n>-1) {
        value = *(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13 ));
        if (value != 0xF81F) 
          pp[0] = value;
        si += iSStep; ti += iTStep; pz+=1; pp+=1; n-=1;
      }

      iError+=iDerror;
      if (iError > 0) {
        iError-=0x10000;
        iX1+=iXLeftStep;
        iZ+=iZLeftStep;
        fS+=fSLeftStep;
        fT+=fTLeftStep;
      }
      else
      {
        iX1+=iXRightStep;
        iZ+=iZRightStep;
        fS+=fSRightStep;
        fT+=fTRightStep;
      }
      iX2 += iDXY2;
      pPixelRow += zb->iPitchInPixels;
    }
  }
}

void ZB_DrawTriangleTexturedPerspective_z(ZBuffer *zb,
                                          ZBufferPoint *p0,
                                          ZBufferPoint *p1,
                                          ZBufferPoint *p2) {
  PIXEL *texture;
  float fdzdx,fndzdx,ndszdx,ndtzdx;
  ZBufferPoint *t,*pr1,*pr2,*pLeft,*pRight;
  float fdx1, fdx2, fdy1, fdy2, fz;
  PIXEL * pPixelRow;
  int iPart,iUpdateLeft,iUpdateRight;
  int tmp,iScanLines,iDX1,iDY1,iDX2,iDY2;
  int iError,iDerror;
  int iX1,iXRightStep,iXLeftStep;

  int iX2=0,iDXY2=0;

  unsigned short *pz1;
  float fDZ1, fDZ2;
  int iZ,iDZX,iDZY,iZRightStep,iZLeftStep;
  float fDTZ1, fDTZ2;
  float fS,fDSX,fDSY,fSRightStep,fSLeftStep;
  float fT,fDTX,fDTY,fTRightStep,fTLeftStep;

  if (p1->iy < p0->iy) {
    t = p0;
    p0 = p1;
    p1 = t;
  }
  if (p2->iy < p0->iy) {
    t = p2;
    p2 = p1;
    p1 = p0;
    p0 = t;
  }
  else if (p2->iy < p1->iy) {
    t = p1;
    p1 = p2;
    p2 = t;
  }

  fdx1 = p1->ix - p0->ix;
  fdy1 = p1->iy - p0->iy;

  fdx2 = p2->ix - p0->ix;
  fdy2 = p2->iy - p0->iy;

  fz = fdx1 * fdy2 - fdx2 * fdy1;
  if (fz == 0)
    return;

  fz = 1.0 / fz;
  fdx1 *= fz;
  fdy1 *= fz;
  fdx2 *= fz;
  fdy2 *= fz;

  fDZ1 = p1->iz - p0->iz;
  fDZ2 = p2->iz - p0->iz;
  iDZX = (int) (fdy2 * fDZ1 - fdy1 * fDZ2);
  iDZY = (int) (fdx1 * fDZ2 - fdx2 * fDZ1);

  float fzz;
  fzz=(float) p0->iz;
  p0->fs= (float) p0->is * fzz;
  p0->ft= (float) p0->it * fzz;

  fzz=(float) p1->iz;
  p1->fs= (float) p1->is * fzz;
  p1->ft= (float) p1->it * fzz;

  fzz=(float) p2->iz;
  p2->fs= (float) p2->is * fzz;
  p2->ft= (float) p2->it * fzz;

  fDTZ1 = p1->fs - p0->fs;
  fDTZ2 = p2->fs - p0->fs;
  fDSX = (fdy2 * fDTZ1 - fdy1 * fDTZ2);
  fDSY = (fdx1 * fDTZ2 - fdx2 * fDTZ1);

  fDTZ1 = p1->ft - p0->ft;
  fDTZ2 = p2->ft - p0->ft;
  fDTX = (fdy2 * fDTZ1 - fdy1 * fDTZ2);
  fDTY = (fdx1 * fDTZ2 - fdx2 * fDTZ1);

  pPixelRow = zb->pbuf + zb->iPitchInPixels * p0->iy;
  pz1 = zb->zbuf + p0->iy * zb->iWidth;

  texture = zb->current_texture;
  fdzdx = (float)iDZX;
  fndzdx = 16 * fdzdx;
  ndszdx = 16 * fDSX;
  ndtzdx = 16 * fDTX;

  for(iPart=0; iPart<2; iPart++) {
    if (iPart == 0) {
      iUpdateLeft = 1;
      iUpdateRight = 1;
      pLeft = p0;
      pr1 = p0;
      if (fz > 0) {
        pRight = p2;
        pr2 = p1;
      }
      else
      {
        pRight = p1;
        pr2 = p2;
      }
      iScanLines = p1->iy - p0->iy;
    }
    else
    {
      if (fz > 0) {
        iUpdateLeft = 0;
        iUpdateRight = 1;
        pr1 = p1;
        pr2 = p2;
      }
      else
      {
        iUpdateLeft = 1;
        iUpdateRight = 0;
        pLeft = p1;
        pRight = p2;
      }
      iScanLines = p2->iy - p1->iy + 1;
    }

    if (iUpdateLeft) {
      iDY1 = pRight->iy - pLeft->iy;
      iDX1 = pRight->ix - pLeft->ix;
      if (iDY1 > 0)
        tmp = (iDX1 << 16) / iDY1;
      else
        tmp = 0;

      iX1 = pLeft->ix;
      iError = 0;
      iDerror = tmp & 0x0000ffff;
      iXRightStep = tmp >> 16;
      iXLeftStep = iXRightStep + 1;

      iZ = pLeft->iz;
      iZRightStep=(iDZY + iDZX * iXRightStep);
      iZLeftStep=iZRightStep + iDZX;

      fS = pLeft->fs;
      fSRightStep = (fDSY + fDSX * iXRightStep);
      fSLeftStep = fSRightStep + fDSX;

      fT = pLeft->ft;
      fTRightStep =(fDTY + fDTX * iXRightStep);
      fTLeftStep = fTRightStep + fDTX;
    }

    if (iUpdateRight) {
      iDX2 = (pr2->ix - pr1->ix);
      iDY2 = (pr2->iy - pr1->iy);
      if (iDY2>0)
        iDXY2 = ( iDX2 << 16) / iDY2;
      else
        iDXY2 = 0;

      iX2 = pr1->ix << 16;
    }

    while (iScanLines>0) {
      iScanLines--;
      unsigned short * pz = pz1 + iX1;
      PIXEL * pp=pPixelRow + iX1;
      unsigned int zz;
      int dsdx,dtdx;
      int n=(iX2 >> 16)-iX1;
      float fz=(float)iZ;
      float zinv=1.0 / fz;
      unsigned int z=iZ;
      int si,ti;
      float sz=fS;
      float tz=fT;

      while (n>14) {
        float ss=(sz * zinv);
        float tt=(tz * zinv);
        si=(int) ss;
        ti=(int) tt;
        dsdx= (int)( (fDSX - ss*fdzdx)*zinv );
        dtdx= (int)( (fDTX - tt*fdzdx)*zinv );
        fz+=fndzdx; zinv=1.0 / fz; zz=z >> 14;
        if (zz >= pz[0]) {
          pp[0]=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          pz[0]=zz;
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[1]) {
          pp[1]=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          pz[1]=zz;
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[2]) {
          pp[2]=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          pz[2]=zz;
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[3]) {
          pp[3]=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          pz[3]=zz;
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[4]) {
          pp[4]=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          pz[4]=zz;
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[5]) {
          pp[5]=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          pz[5]=zz;
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[6]) {
          pp[6]=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          pz[6]=zz;
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[7]) {
          pp[7]=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          pz[7]=zz;
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[8]) {
          pp[8]=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          pz[8]=zz;
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[9]) {
          pp[9]=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          pz[9]=zz;
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[10]) {
          pp[10]=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          pz[10]=zz;
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[11]) {
          pp[11]=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          pz[11]=zz;
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[12]) {
          pp[12]=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          pz[12]=zz;
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[13]) {
          pp[13]=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          pz[13]=zz;
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[14]) {
          pp[14]=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          pz[14]=zz;
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[15]) {
          pp[15]=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          pz[15]=zz;
        }
        z+=iDZX; si+=dsdx; ti+=dtdx;
        pz+=16; pp += 16;  n-=16;
        sz+=ndszdx; tz+=ndtzdx;
      }

      float ss=(sz * zinv);
      float tt=(tz * zinv);
      si=(int) ss;
      ti=(int) tt;
      dsdx= (int)( (fDSX - ss*fdzdx)*zinv );
      dtdx= (int)( (fDTX - tt*fdzdx)*zinv );
      while (n>-1) {
        zz=z >> 14;
        if (zz >= pz[0]) {
          pp[0]=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          pz[0]=zz;
        }
        z+=iDZX; si+=dsdx; ti+=dtdx;
        pz+=1; pp += 1; n-=1;
      }

      iError+=iDerror;
      if (iError > 0) {
        iError-=0x10000;
        iX1+=iXLeftStep;
        iZ+=iZLeftStep;
        fS+=fSLeftStep;
        fT+=fTLeftStep;
      }
      else
      {
        iX1+=iXRightStep;
        iZ+=iZRightStep;
        fS+=fSRightStep;
        fT+=fTRightStep;
      }

      iX2 += iDXY2;
      pPixelRow += zb->iPitchInPixels;
      pz1 += zb->iWidth;
    }
  }
}

void ZB_DrawTriangleTexturedPerspective_zb(ZBuffer *zb,
                                           ZBufferPoint *p0,
                                           ZBufferPoint *p1,
                                           ZBufferPoint *p2) {
  PIXEL *texture;
  PIXEL value;
  float fdzdx,fndzdx,ndszdx,ndtzdx;
  ZBufferPoint *t,*pr1,*pr2,*pLeft,*pRight;
  float fdx1, fdx2, fdy1, fdy2, fz;
  PIXEL * pPixelRow;
  int iPart,iUpdateLeft,iUpdateRight;
  int tmp,iScanLines,iDX1,iDY1,iDX2,iDY2;
  int iError,iDerror;
  int iX1,iXRightStep,iXLeftStep;

  int iX2=0,iDXY2=0;

  unsigned short *pz1;
  float fDZ1, fDZ2;
  int iZ,iDZX,iDZY,iZRightStep,iZLeftStep;
  float fDTZ1, fDTZ2;
  float fS,fDSX,fDSY,fSRightStep,fSLeftStep;
  float fT,fDTX,fDTY,fTRightStep,fTLeftStep;
  float fzz;

  if (p1->iy < p0->iy) {
    t = p0;
    p0 = p1;
    p1 = t;
  }
  if (p2->iy < p0->iy) {
    t = p2;
    p2 = p1;
    p1 = p0;
    p0 = t;
  }
  else if (p2->iy < p1->iy) {
    t = p1;
    p1 = p2;
    p2 = t;
  }

  fdx1 = p1->ix - p0->ix;
  fdy1 = p1->iy - p0->iy;

  fdx2 = p2->ix - p0->ix;
  fdy2 = p2->iy - p0->iy;

  fz = fdx1 * fdy2 - fdx2 * fdy1;
  if (fz == 0)
    return;

  fz = 1.0 / fz;
  fdx1 *= fz;
  fdy1 *= fz;
  fdx2 *= fz;
  fdy2 *= fz;

  fDZ1 = p1->iz - p0->iz;
  fDZ2 = p2->iz - p0->iz;
  iDZX = (int) (fdy2 * fDZ1 - fdy1 * fDZ2);
  iDZY = (int) (fdx1 * fDZ2 - fdx2 * fDZ1);

  fzz=(float) p0->iz;
  p0->fs= (float) p0->is * fzz;
  p0->ft= (float) p0->it * fzz;

  fzz=(float) p1->iz;
  p1->fs= (float) p1->is * fzz;
  p1->ft= (float) p1->it * fzz;

  fzz=(float) p2->iz;
  p2->fs= (float) p2->is * fzz;
  p2->ft= (float) p2->it * fzz;

  fDTZ1 = p1->fs - p0->fs;
  fDTZ2 = p2->fs - p0->fs;
  fDSX = (fdy2 * fDTZ1 - fdy1 * fDTZ2);
  fDSY = (fdx1 * fDTZ2 - fdx2 * fDTZ1);

  fDTZ1 = p1->ft - p0->ft;
  fDTZ2 = p2->ft - p0->ft;
  fDTX = (fdy2 * fDTZ1 - fdy1 * fDTZ2);
  fDTY = (fdx1 * fDTZ2 - fdx2 * fDTZ1);

  pPixelRow = zb->pbuf + zb->iPitchInPixels * p0->iy;
  pz1 = zb->zbuf + p0->iy * zb->iWidth;

  texture = zb->current_texture;
  fdzdx = (float)iDZX;
  fndzdx = 16 * fdzdx;
  ndszdx = 16 * fDSX;
  ndtzdx = 16 * fDTX;

  for(iPart=0; iPart<2; iPart++) {
    if (iPart == 0) {
      iUpdateLeft = 1;
      iUpdateRight = 1;
      pLeft = p0;
      pr1 = p0;
      if (fz > 0) {
        pRight = p2;
        pr2 = p1;
      }
      else
      {
        pRight = p1;
        pr2 = p2;
      }
      iScanLines = p1->iy - p0->iy;
    }
    else
    {
      if (fz > 0) {
        iUpdateLeft = 0;
        iUpdateRight = 1;
        pr1 = p1;
        pr2 = p2;
      }
      else
      {
        iUpdateLeft = 1;
        iUpdateRight = 0;
        pLeft = p1;
        pRight = p2;
      }
      iScanLines = p2->iy - p1->iy + 1;
    }

    if (iUpdateLeft) {
      iDY1 = pRight->iy - pLeft->iy;
      iDX1 = pRight->ix - pLeft->ix;
      if (iDY1 > 0)
        tmp = (iDX1 << 16) / iDY1;
      else
        tmp = 0;
      iX1 = pLeft->ix;
      iError = 0;
      iDerror = tmp & 0x0000ffff;
      iXRightStep = tmp >> 16;
      iXLeftStep = iXRightStep + 1;

      iZ = pLeft->iz;
      iZRightStep=(iDZY + iDZX * iXRightStep);
      iZLeftStep=iZRightStep + iDZX;

      fS = pLeft->fs;
      fSRightStep = (fDSY + fDSX * iXRightStep);
      fSLeftStep = fSRightStep + fDSX;

      fT = pLeft->ft;
      fTRightStep =(fDTY + fDTX * iXRightStep);
      fTLeftStep = fTRightStep + fDTX;
    }

    if (iUpdateRight) {
      iDX2 = (pr2->ix - pr1->ix);
      iDY2 = (pr2->iy - pr1->iy);
      if (iDY2>0)
        iDXY2 = ( iDX2 << 16) / iDY2;
      else
        iDXY2 = 0;
      iX2 = pr1->ix << 16;
    }

    while (iScanLines>0) {
      iScanLines--;
      unsigned short * pz = pz1+iX1;
      PIXEL * pp = pPixelRow + iX1;
      unsigned int zz;
      int dsdx,dtdx;
      int n=(iX2 >> 16)-iX1;
      float fz=(float)iZ;
      float zinv=1.0 / fz;
      int si,ti;
      unsigned int z=iZ;
      float sz = fS;
      float tz = fT;
      while (n>14) {
        float ss=(sz * zinv);
        float tt=(tz * zinv);
        si=(int) ss; ti=(int) tt;
        dsdx= (int)( (fDSX - ss*fdzdx)*zinv );
        dtdx= (int)( (fDTX - tt*fdzdx)*zinv );
        fz+=fndzdx; zinv=1.0 / fz; zz=z >> 14;
        if (zz >= pz[0]) {
          value=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          if (value != 0xF81F) {
            pp[0]=value;
            pz[0]=zz;
          }
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[1]) {
          value=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          if (value != 0xF81F) {
            pp[1]=value;
            pz[1]=zz;
          }
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[2]) {
          value=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          if (value != 0xF81F) {
            pp[2]=value;
            pz[2]=zz;
          }
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[3]) {
          value=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          if (value != 0xF81F) {
            pp[3]=value;
            pz[3]=zz;
          }
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[4]) {
          value=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          if (value != 0xF81F) {
            pp[4]=value;
            pz[4]=zz;
          }
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[5]) {
          value=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          if (value != 0xF81F) {
            pp[5]=value;
            pz[5]=zz;
          }
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[6]) {
          value=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          if (value != 0xF81F) {
            pp[6]=value; pz[6]=zz;
          }
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[7]) {
          value=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          if (value != 0xF81F) {
            pp[7]=value;
            pz[7]=zz;
          }
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[8]) {
          value=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          if (value != 0xF81F) {
            pp[8]=value;
            pz[8]=zz;
          }
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[9]) {
          value=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          if (value != 0xF81F) {
            pp[9]=value;
            pz[9]=zz;
          }
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[10]) {
          value=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          if (value != 0xF81F) {
            pp[10]=value;
            pz[10]=zz;
          }
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[11]) {
          value=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          if (value != 0xF81F) {
            pp[11]=value;
            pz[11]=zz;
          }
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[12]) {
          value=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          if (value != 0xF81F) {
            pp[12]=value;
            pz[12]=zz;
          }
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[13]) {
          value=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          if (value != 0xF81F) {
            pp[13]=value;
            pz[13]=zz;
          }
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[14]) {
          value=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          if (value != 0xF81F) {
            pp[14]=value;
            pz[14]=zz;
          }
        }
        z+=iDZX; si+=dsdx; ti+=dtdx; zz=z >> 14;
        if (zz >= pz[15]) {
          value=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          if (value != 0xF81F) {
            pp[15]=value;
            pz[15]=zz;
          }
        }
        z+=iDZX; si+=dsdx; ti+=dtdx;

        pz+=16; pp += 16; n-=16;
        sz+=ndszdx;
        tz+=ndtzdx;
      }

      float ss=(sz * zinv);
      float tt=(tz * zinv);
      si=(int) ss;
      ti=(int) tt;
      dsdx= (int)( (fDSX - ss*fdzdx)*zinv );
      dtdx= (int)( (fDTX - tt*fdzdx)*zinv );
      
      while (n>-1) {
        zz=z >> 14;
        if (zz >= pz[0]) {
          value=*(PIXEL *)((char *)texture + (((ti & 0x3FC00000) | (si & 0x003FC000)) >> 13));
          if (value != 0xF81F) {
            pp[0]=value;
            pz[0]=zz;
          }
        }
        z+=iDZX; si+=dsdx; ti+=dtdx;
        pz+=1; pp += 1; n-=1;
      }

      iError+=iDerror;
      if (iError > 0) {
        iError-=0x10000;
        iX1+=iXLeftStep;
        iZ+=iZLeftStep;
        fS+=fSLeftStep;
        fT+=fTLeftStep;
      }
      else
      {
        iX1+=iXRightStep;
        iZ+=iZRightStep;
        fS+=fSRightStep;
        fT+=fTRightStep;
      }
      iX2 += iDXY2;
      pPixelRow += zb->iPitchInPixels;
      pz1 += zb->iWidth;
    }
  }
}

void ZB_DrawTriangleTexturedPerspectiveNice(ZBuffer *zb,
                                            ZBufferPoint *p0,
                                            ZBufferPoint *p1,
                                            ZBufferPoint *p2){
  PIXEL *texture;
  float zinv;
  int si,ti;
  ZBufferPoint *t,*pr1,*pr2,*pLeft,*pRight;
  float fdx1, fdx2, fdy1, fdy2, fz;
  PIXEL * pPixelRow;
  int iPart,iUpdateLeft,iUpdateRight;
  int tmp,iScanLines,iDX1,iDY1,iDX2,iDY2;
  int iError,iDerror;
  int iX1,iXRightStep,iXLeftStep;

  int iX2=0,iDXY2=0;

  float fDZ1, fDZ2;
  int iZ,iDZX,iDZY,iZRightStep,iZLeftStep;
  float fDTZ1, fDTZ2;
  float fS,fDSX,fDSY,fSRightStep,fSLeftStep;
  float fT,fDTX,fDTY,fTRightStep,fTLeftStep;
  float fzz;

  if (p1->iy < p0->iy) {
    t = p0;
    p0 = p1;
    p1 = t;
  }
  if (p2->iy < p0->iy) {
    t = p2;
    p2 = p1;
    p1 = p0;
    p0 = t;
  }
  else if (p2->iy < p1->iy) {
    t = p1;
    p1 = p2;
    p2 = t;
  }

  fdx1 = p1->ix - p0->ix;
  fdy1 = p1->iy - p0->iy;

  fdx2 = p2->ix - p0->ix;
  fdy2 = p2->iy - p0->iy;

  fz = fdx1 * fdy2 - fdx2 * fdy1;
  if (fz == 0)
    return;

  fz = 1.0 / fz;
  fdx1 *= fz;
  fdy1 *= fz;
  fdx2 *= fz;
  fdy2 *= fz;

  fDZ1 = p1->iz - p0->iz;
  fDZ2 = p2->iz - p0->iz;
  iDZX = (int) (fdy2 * fDZ1 - fdy1 * fDZ2);
  iDZY = (int) (fdx1 * fDZ2 - fdx2 * fDZ1);

  fzz=(float) p0->iz;
  p0->fs= (float) p0->is * fzz;
  p0->ft= (float) p0->it * fzz;

  fzz=(float) p1->iz;
  p1->fs= (float) p1->is * fzz;
  p1->ft= (float) p1->it * fzz;

  fzz=(float) p2->iz;
  p2->fs= (float) p2->is * fzz;
  p2->ft= (float) p2->it * fzz;

  fDTZ1 = p1->fs - p0->fs;
  fDTZ2 = p2->fs - p0->fs;
  fDSX = (fdy2 * fDTZ1 - fdy1 * fDTZ2);
  fDSY = (fdx1 * fDTZ2 - fdx2 * fDTZ1);

  fDTZ1 = p1->ft - p0->ft;
  fDTZ2 = p2->ft - p0->ft;
  fDTX = (fdy2 * fDTZ1 - fdy1 * fDTZ2);
  fDTY = (fdx1 * fDTZ2 - fdx2 * fDTZ1);

  pPixelRow = zb->pbuf + zb->iPitchInPixels * p0->iy;
  texture=zb->current_texture;

  for(iPart=0; iPart<2; iPart++) {
    if (iPart == 0) {
      iUpdateLeft = 1;
      iUpdateRight = 1;
      pLeft = p0;
      pr1 = p0;
      if (fz > 0) {
        pRight = p2;
        pr2 = p1;
      }
      else
      {
        pRight = p1;
        pr2 = p2;
      }
      iScanLines = p1->iy - p0->iy;
    }
    else
    {
      if (fz > 0) {
        iUpdateLeft = 0;
        iUpdateRight = 1;
        pr1 = p1;
        pr2 = p2;
      }
      else
      {
        iUpdateLeft = 1;
        iUpdateRight = 0;
        pLeft = p1;
        pRight = p2;
      }
      iScanLines = p2->iy - p1->iy + 1;
    }

    if (iUpdateLeft) {
      iDY1 = pRight->iy - pLeft->iy;
      iDX1 = pRight->ix - pLeft->ix;
      if (iDY1 > 0)
        tmp = (iDX1 << 16) / iDY1;
      else
        tmp = 0;
      iX1 = pLeft->ix;
      iError = 0;
      iDerror = tmp & 0x0000ffff;
      iXRightStep = tmp >> 16;
      iXLeftStep = iXRightStep + 1;

      iZ = pLeft->iz;
      iZRightStep=(iDZY + iDZX * iXRightStep);
      iZLeftStep=iZRightStep + iDZX;

      fS = pLeft->fs;
      fSRightStep = (fDSY + fDSX * iXRightStep);
      fSLeftStep = fSRightStep + fDSX;

      fT = pLeft->ft;
      fTRightStep =(fDTY + fDTX * iXRightStep);
      fTLeftStep = fTRightStep + fDTX;
    }

    if (iUpdateRight) {
      iDX2 = (pr2->ix - pr1->ix);
      iDY2 = (pr2->iy - pr1->iy);
      if (iDY2>0)
        iDXY2 = ( iDX2 << 16) / iDY2;
      else
        iDXY2 = 0;
      iX2 = pr1->ix << 16;
    }

    while (iScanLines>0) {
      iScanLines--;
      PIXEL *pp;
      int n;
      unsigned int z = iZ;
      float sz = fS, tz = fT;
      pp = pPixelRow + iX1;
      n = (iX2 >> 16) - iX1;
      while (n>2) {
        zinv= 1.0f / (float) z; si= (int) (sz * zinv); ti= (int) (tz * zinv);
        pp[0] = texture[((ti & 0x3FC00000) | si) >> 14];
        z += iDZX; sz += fDSX; tz += fDTX;

        zinv= 1.0f / (float) z; si= (int) (sz * zinv); ti= (int) (tz * zinv);
        pp[1] = texture[((ti & 0x3FC00000) | si) >> 14];
        z += iDZX; sz += fDSX; tz += fDTX;

        zinv= 1.0f / (float) z; si= (int) (sz * zinv); ti= (int) (tz * zinv);
        pp[2] = texture[((ti & 0x3FC00000) | si) >> 14];
        z += iDZX; sz += fDSX; tz += fDTX;

        zinv= 1.0f / (float) z; si= (int) (sz * zinv); ti= (int) (tz * zinv);
        pp[3] = texture[((ti & 0x3FC00000) | si) >> 14];
        z += iDZX; sz += fDSX; tz += fDTX;

        pp += 4; n-=4;
      }
      while (n>-1) {
        zinv= 1.0f / (float) z;
        si= (int) (sz * zinv);
        ti= (int) (tz * zinv);
        pp[0] = texture[((ti & 0x3FC00000) | si) >> 14];
        z += iDZX; sz += fDSX; tz += fDTX;
        pp += 1; n-=1;
      }

      iError+=iDerror;
      if (iError > 0) {
        iError-=0x10000;
        iX1+=iXLeftStep;
        iZ+=iZLeftStep;
        fS+=fSLeftStep;
        fT+=fTLeftStep;
      }
      else
      {
        iX1+=iXRightStep;
        iZ+=iZRightStep;
        fS+=fSRightStep;
        fT+=fTRightStep;
      }
      iX2 += iDXY2;
      pPixelRow += zb->iPitchInPixels;
    }
  }
}

void ZB_DrawTriangleTexturedPerspectiveNice_b(ZBuffer *zb,
                                              ZBufferPoint *p0,
                                              ZBufferPoint *p1,
                                              ZBufferPoint *p2){
  PIXEL *texture;
  PIXEL value;
  float zinv;
  int si,ti;
  ZBufferPoint *t,*pr1,*pr2,*pLeft,*pRight;
  float fdx1, fdx2, fdy1, fdy2, fz;
  PIXEL * pPixelRow;
  int iPart,iUpdateLeft,iUpdateRight;
  int tmp,iScanLines,iDX1,iDY1,iDX2,iDY2;
  int iError,iDerror;
  int iX1,iXRightStep,iXLeftStep;

  int iX2=0,iDXY2=0;

  float fDZ1, fDZ2;
  int iZ,iDZX,iDZY,iZRightStep,iZLeftStep;
  float fDTZ1, fDTZ2;
  float fS,fDSX,fDSY,fSRightStep,fSLeftStep;
  float fT,fDTX,fDTY,fTRightStep,fTLeftStep;
  float fzz;

  if (p1->iy < p0->iy) {
    t = p0;
    p0 = p1;
    p1 = t;
  }
  if (p2->iy < p0->iy) {
    t = p2;
    p2 = p1;
    p1 = p0;
    p0 = t;
  }
  else if (p2->iy < p1->iy) {
    t = p1;
    p1 = p2;
    p2 = t;
  }

  fdx1 = p1->ix - p0->ix;
  fdy1 = p1->iy - p0->iy;

  fdx2 = p2->ix - p0->ix;
  fdy2 = p2->iy - p0->iy;

  fz = fdx1 * fdy2 - fdx2 * fdy1;
  if (fz == 0)
    return;

  fz = 1.0 / fz;
  fdx1 *= fz;
  fdy1 *= fz;
  fdx2 *= fz;
  fdy2 *= fz;

  fDZ1 = p1->iz - p0->iz;
  fDZ2 = p2->iz - p0->iz;
  iDZX = (int) (fdy2 * fDZ1 - fdy1 * fDZ2);
  iDZY = (int) (fdx1 * fDZ2 - fdx2 * fDZ1);

  fzz=(float) p0->iz;
  p0->fs= (float) p0->is * fzz;
  p0->ft= (float) p0->it * fzz;

  fzz=(float) p1->iz;
  p1->fs= (float) p1->is * fzz;
  p1->ft= (float) p1->it * fzz;

  fzz=(float) p2->iz;
  p2->fs= (float) p2->is * fzz;
  p2->ft= (float) p2->it * fzz;

  fDTZ1 = p1->fs - p0->fs;
  fDTZ2 = p2->fs - p0->fs;
  fDSX = (fdy2 * fDTZ1 - fdy1 * fDTZ2);
  fDSY = (fdx1 * fDTZ2 - fdx2 * fDTZ1);

  fDTZ1 = p1->ft - p0->ft;
  fDTZ2 = p2->ft - p0->ft;
  fDTX = (fdy2 * fDTZ1 - fdy1 * fDTZ2);
  fDTY = (fdx1 * fDTZ2 - fdx2 * fDTZ1);

  pPixelRow = zb->pbuf + zb->iPitchInPixels * p0->iy;
  texture = zb->current_texture;

  for(iPart=0; iPart<2; iPart++) {
    if (iPart == 0) {
      iUpdateLeft = 1;
      iUpdateRight = 1;
      pLeft = p0;
      pr1 = p0;
      if (fz > 0) {
        pRight = p2;
        pr2 = p1;
      }
      else
      {
        pRight = p1;
        pr2 = p2;
      }
      iScanLines = p1->iy - p0->iy;
    }
    else
    {
      if (fz > 0) {
        iUpdateLeft = 0;
        iUpdateRight = 1;
        pr1 = p1;
        pr2 = p2;
      }
      else
      {
        iUpdateLeft = 1;
        iUpdateRight = 0;
        pLeft = p1;
        pRight = p2;
      }
      iScanLines = p2->iy - p1->iy + 1;
    }

    if (iUpdateLeft) {
      iDY1 = pRight->iy - pLeft->iy;
      iDX1 = pRight->ix - pLeft->ix;
      if (iDY1 > 0)
        tmp = (iDX1 << 16) / iDY1;
      else
        tmp = 0;

      iX1 = pLeft->ix;
      iError = 0;
      iDerror = tmp & 0x0000ffff;
      iXRightStep = tmp >> 16;
      iXLeftStep = iXRightStep + 1;

      iZ = pLeft->iz;
      iZRightStep=(iDZY + iDZX * iXRightStep);
      iZLeftStep=iZRightStep + iDZX;

      fS = pLeft->fs;
      fSRightStep = (fDSY + fDSX * iXRightStep);
      fSLeftStep = fSRightStep + fDSX;

      fT = pLeft->ft;
      fTRightStep =(fDTY + fDTX * iXRightStep);
      fTLeftStep = fTRightStep + fDTX;
    }

    if (iUpdateRight) {
      iDX2 = (pr2->ix - pr1->ix);
      iDY2 = (pr2->iy - pr1->iy);
      if (iDY2>0)
        iDXY2 = ( iDX2 << 16) / iDY2;
      else
        iDXY2 = 0;
      iX2 = pr1->ix << 16;
    }

    while (iScanLines>0) {
      iScanLines--;
      PIXEL *pp;
      int n;
      unsigned int z = iZ;
      float sz = fS, tz = fT;
      pp = pPixelRow + iX1;
      n = (iX2 >> 16) - iX1;
      while (n>2) {
        zinv= 1.0 / (float) z; si= (int) (sz * zinv); ti= (int) (tz * zinv);
        value = texture[((ti & 0x3FC00000) | si) >> 14];
        if (value != 0xF81F) pp[0] = value;
        z += iDZX; sz += fDSX; tz += fDTX;

        zinv= 1.0 / (float) z; si= (int) (sz * zinv); ti= (int) (tz * zinv);
        value = texture[((ti & 0x3FC00000) | si) >> 14];
        if (value != 0xF81F) pp[1] = value;
        z += iDZX; sz += fDSX; tz += fDTX;

        zinv= 1.0 / (float) z; si= (int) (sz * zinv); ti= (int) (tz * zinv);
        value = texture[((ti & 0x3FC00000) | si) >> 14];
        if (value != 0xF81F) pp[2] = value;
        z += iDZX; sz += fDSX; tz += fDTX;

        zinv= 1.0 / (float) z; si= (int) (sz * zinv); ti= (int) (tz * zinv);
        value = texture[((ti & 0x3FC00000) | si) >> 14];
        if (value != 0xF81F) pp[3] = value;
        z += iDZX; sz += fDSX; tz += fDTX;

        pp += 4; n-=4;
      }

      while (n>-1) {
        zinv= 1.0 / (float) z; si= (int) (sz * zinv); ti= (int) (tz * zinv);
        value = texture[((ti & 0x3FC00000) | si) >> 14];
        if (value != 0xF81F) pp[0] = value;
        z += iDZX; sz += fDSX; tz += fDTX;
        pp += 1; n-=1;
      }

      iError+=iDerror;
      if (iError > 0) {
        iError-=0x10000;
        iX1+=iXLeftStep;
        iZ+=iZLeftStep;
        fS+=fSLeftStep;
        fT+=fTLeftStep;
      }
      else
      {
        iX1+=iXRightStep;
        iZ+=iZRightStep;
        fS+=fSRightStep;
        fT+=fTRightStep;
      }
      iX2 += iDXY2;
      pPixelRow += zb->iPitchInPixels;
    }
  }
}

void ZB_DrawTriangleTexturedPerspectiveNice_z(ZBuffer *zb,
                                              ZBufferPoint *p0,
                                              ZBufferPoint *p1,
                                              ZBufferPoint *p2){
  PIXEL *texture;
  float zinv;
  int si,ti;
  ZBufferPoint *t,*pr1,*pr2,*pLeft,*pRight;
  float fdx1, fdx2, fdy1, fdy2, fz;
  PIXEL * pPixelRow;
  int iPart,iUpdateLeft,iUpdateRight;
  int tmp,iScanLines,iDX1,iDY1,iDX2,iDY2;
  int iError,iDerror;
  int iX1,iXRightStep,iXLeftStep;

  int iX2=0,iDXY2=0;

  unsigned short *pz1;
  float fDZ1, fDZ2;
  int iZ,iDZX,iDZY,iZRightStep,iZLeftStep;
  float fDTZ1, fDTZ2;
  float fS,fDSX,fDSY,fSRightStep,fSLeftStep;
  float fT,fDTX,fDTY,fTRightStep,fTLeftStep;
  float fzz;

  if (p1->iy < p0->iy) {
    t = p0;
    p0 = p1;
    p1 = t;
  }

  if (p2->iy < p0->iy) {
    t = p2;
    p2 = p1;
    p1 = p0;
    p0 = t;
  }
  else if (p2->iy < p1->iy) {
    t = p1;
    p1 = p2;
    p2 = t;
  }

  fdx1 = p1->ix - p0->ix;
  fdy1 = p1->iy - p0->iy;

  fdx2 = p2->ix - p0->ix;
  fdy2 = p2->iy - p0->iy;

  fz = fdx1 * fdy2 - fdx2 * fdy1;
  if (fz == 0)
    return;

  fz = 1.0 / fz;
  fdx1 *= fz;
  fdy1 *= fz;
  fdx2 *= fz;
  fdy2 *= fz;


  fDZ1 = p1->iz - p0->iz;
  fDZ2 = p2->iz - p0->iz;
  iDZX = (int) (fdy2 * fDZ1 - fdy1 * fDZ2);
  iDZY = (int) (fdx1 * fDZ2 - fdx2 * fDZ1);

  fzz=(float) p0->iz;
  p0->fs= (float) p0->is * fzz;
  p0->ft= (float) p0->it * fzz;

  fzz=(float) p1->iz;
  p1->fs= (float) p1->is * fzz;
  p1->ft= (float) p1->it * fzz;

  fzz=(float) p2->iz;
  p2->fs= (float) p2->is * fzz;
  p2->ft= (float) p2->it * fzz;

  fDTZ1 = p1->fs - p0->fs;
  fDTZ2 = p2->fs - p0->fs;
  fDSX = (fdy2 * fDTZ1 - fdy1 * fDTZ2);
  fDSY = (fdx1 * fDTZ2 - fdx2 * fDTZ1);

  fDTZ1 = p1->ft - p0->ft;
  fDTZ2 = p2->ft - p0->ft;
  fDTX = (fdy2 * fDTZ1 - fdy1 * fDTZ2);
  fDTY = (fdx1 * fDTZ2 - fdx2 * fDTZ1);

  pPixelRow = zb->pbuf + zb->iPitchInPixels * p0->iy;
  pz1 = zb->zbuf + p0->iy * zb->iWidth;
  texture=zb->current_texture;

  for(iPart=0; iPart<2; iPart++) {
    if (iPart == 0) {
      iUpdateLeft = 1;
      iUpdateRight = 1;
      pLeft = p0;
      pr1 = p0;
      if (fz > 0) {
        pRight = p2;
        pr2 = p1;
      }
      else
      {
        pRight = p1;
        pr2 = p2;
      }
      iScanLines = p1->iy - p0->iy;
    }
    else
    {
      if (fz > 0) {
        iUpdateLeft = 0;
        iUpdateRight = 1;
        pr1 = p1;
        pr2 = p2;
      }
      else
      {
        iUpdateLeft = 1;
        iUpdateRight = 0;
        pLeft = p1;
        pRight = p2;
      }
      iScanLines = p2->iy - p1->iy + 1;
    }

    if (iUpdateLeft) {
      iDY1 = pRight->iy - pLeft->iy;
      iDX1 = pRight->ix - pLeft->ix;
      if (iDY1 > 0)
        tmp = (iDX1 << 16) / iDY1;
      else
        tmp = 0;
      iX1 = pLeft->ix;
      iError = 0;
      iDerror = tmp & 0x0000ffff;
      iXRightStep = tmp >> 16;
      iXLeftStep = iXRightStep + 1;

      iZ = pLeft->iz;
      iZRightStep=(iDZY + iDZX * iXRightStep);
      iZLeftStep=iZRightStep + iDZX;

      fS = pLeft->fs;
      fSRightStep = (fDSY + fDSX * iXRightStep);
      fSLeftStep = fSRightStep + fDSX;

      fT = pLeft->ft;
      fTRightStep =(fDTY + fDTX * iXRightStep);
      fTLeftStep = fTRightStep + fDTX;
    }

    if (iUpdateRight) {
      iDX2 = (pr2->ix - pr1->ix);
      iDY2 = (pr2->iy - pr1->iy);
      if (iDY2>0)
        iDXY2 = ( iDX2 << 16) / iDY2;
      else
        iDXY2 = 0;
      iX2 = pr1->ix << 16;
    }

    while (iScanLines>0) {
      iScanLines--;
      PIXEL *pp;
      int n;
      unsigned short * pz = pz1+iX1;
      unsigned int zz;
      unsigned int z = iZ;
      float sz = fS, tz = fT;
      pp = pPixelRow + iX1;
      n = (iX2 >> 16) - iX1;
      while (n>2) {
        zz=z >> 14;
        if (zz >= pz[0]) {
          zinv= 1.0 / (float) z; si= (int) (sz * zinv); ti= (int) (tz * zinv);
          pp[0]=texture[((ti & 0x3FC00000) | si) >> 14];
          pz[0]=zz;
        }
        z += iDZX; sz += fDSX; tz += fDTX; zz=z >> 14;
        if (zz >= pz[1]) {
          zinv= 1.0 / (float) z; si= (int) (sz * zinv); ti= (int) (tz * zinv);
          pp[1]=texture[((ti & 0x3FC00000) | si) >> 14];
          pz[1]=zz;
        }
        z += iDZX; sz += fDSX; tz += fDTX; zz=z >> 14;
        if (zz >= pz[2]) {
          zinv= 1.0 / (float) z; si= (int) (sz * zinv); ti= (int) (tz * zinv);
          pp[2]=texture[((ti & 0x3FC00000) | si) >> 14];
          pz[2]=zz;
        }
        z += iDZX; sz += fDSX; tz += fDTX; zz=z >> 14;
        if (zz >= pz[3]) {
          zinv= 1.0 / (float) z; si= (int) (sz * zinv); ti= (int) (tz * zinv);
          pp[3]=texture[((ti & 0x3FC00000) | si) >> 14];
          pz[3]=zz;
        }
        z += iDZX; sz += fDSX; tz += fDTX;

        pz+=4; pp += 4; n-=4;
      }

      while (n>-1) {
        zz=z >> 14;
        if (zz >= pz[0]) {
          zinv= 1.0 / (float) z; si= (int) (sz * zinv); ti= (int) (tz * zinv);
          pp[0]=texture[((ti & 0x3FC00000) | si) >> 14];
          pz[0]=zz;
        }
        z += iDZX; sz += fDSX; tz += fDTX;
        pz+=1; pp += 1; n-=1;
      }

      iError+=iDerror;
      if (iError > 0) {
        iError-=0x10000;
        iX1+=iXLeftStep;
        iZ+=iZLeftStep;
        fS+=fSLeftStep;
        fT+=fTLeftStep;
      }
      else
      {
        iX1+=iXRightStep;
        iZ+=iZRightStep;
        fS+=fSRightStep;
        fT+=fTRightStep;
      }
      iX2 += iDXY2;
      pPixelRow += zb->iPitchInPixels;
      pz1 += zb->iWidth;
    }
  }
}


void ZB_DrawTriangleTexturedPerspectiveNice_zb(ZBuffer *zb,
                                               ZBufferPoint *p0,
                                               ZBufferPoint *p1,
                                               ZBufferPoint *p2){
  PIXEL *texture;
  PIXEL value;
  float zinv;
  int si,ti;
  ZBufferPoint *t,*pr1,*pr2,*pLeft,*pRight;
  float fdx1, fdx2, fdy1, fdy2, fz;
  PIXEL * pPixelRow;
  int iPart,iUpdateLeft,iUpdateRight;
  int tmp,iScanLines,iDX1,iDY1,iDX2,iDY2;
  int iError,iDerror;
  int iX1,iXRightStep,iXLeftStep;

  int iX2=0,iDXY2=0;

  unsigned short *pz1;

  float fDZ1, fDZ2;
  int iZ,iDZX,iDZY,iZRightStep,iZLeftStep;
  float fDTZ1, fDTZ2;
  float fS,fDSX,fDSY,fSRightStep,fSLeftStep;
  float fT,fDTX,fDTY,fTRightStep,fTLeftStep;
  float fzz;

  if (p1->iy < p0->iy) {
    t = p0;
    p0 = p1;
    p1 = t;
  }
  if (p2->iy < p0->iy) {
    t = p2;
    p2 = p1;
    p1 = p0;
    p0 = t;
  }
  else if (p2->iy < p1->iy) {
    t = p1;
    p1 = p2;
    p2 = t;
  }

  fdx1 = p1->ix - p0->ix;
  fdy1 = p1->iy - p0->iy;

  fdx2 = p2->ix - p0->ix;
  fdy2 = p2->iy - p0->iy;

  fz = fdx1 * fdy2 - fdx2 * fdy1;
  if (fz == 0)
    return;

  fz = 1.0 / fz;
  fdx1 *= fz;
  fdy1 *= fz;
  fdx2 *= fz;
  fdy2 *= fz;

  fDZ1 = p1->iz - p0->iz;
  fDZ2 = p2->iz - p0->iz;
  iDZX = (int) (fdy2 * fDZ1 - fdy1 * fDZ2);
  iDZY = (int) (fdx1 * fDZ2 - fdx2 * fDZ1);

  fzz=(float) p0->iz;
  p0->fs= (float) p0->is * fzz;
  p0->ft= (float) p0->it * fzz;

  fzz=(float) p1->iz;
  p1->fs= (float) p1->is * fzz;
  p1->ft= (float) p1->it * fzz;

  fzz=(float) p2->iz;
  p2->fs= (float) p2->is * fzz;
  p2->ft= (float) p2->it * fzz;

  fDTZ1 = p1->fs - p0->fs;
  fDTZ2 = p2->fs - p0->fs;
  fDSX = (fdy2 * fDTZ1 - fdy1 * fDTZ2);
  fDSY = (fdx1 * fDTZ2 - fdx2 * fDTZ1);

  fDTZ1 = p1->ft - p0->ft;
  fDTZ2 = p2->ft - p0->ft;
  fDTX = (fdy2 * fDTZ1 - fdy1 * fDTZ2);
  fDTY = (fdx1 * fDTZ2 - fdx2 * fDTZ1);

  pPixelRow = zb->pbuf + zb->iPitchInPixels * p0->iy;
  pz1 = zb->zbuf + p0->iy * zb->iWidth;
  texture=zb->current_texture;

  for(iPart=0; iPart<2; iPart++) {
    if (iPart == 0) {
      iUpdateLeft = 1;
      iUpdateRight = 1;
      pLeft = p0;
      pr1 = p0;
      if (fz > 0) {
        pRight = p2;
        pr2 = p1;
      }
      else
      {
        pRight = p1;
        pr2 = p2;
      }
      iScanLines = p1->iy - p0->iy;
    }
    else
    {
      if (fz > 0) {
        iUpdateLeft = 0;
        iUpdateRight = 1;
        pr1 = p1;
        pr2 = p2;
      }
      else
      {
        iUpdateLeft = 1;
        iUpdateRight = 0;
        pLeft = p1;
        pRight = p2;
      }
      iScanLines = p2->iy - p1->iy + 1;
    }

    if (iUpdateLeft) {
      iDY1 = pRight->iy - pLeft->iy;
      iDX1 = pRight->ix - pLeft->ix;
      if (iDY1 > 0)
        tmp = (iDX1 << 16) / iDY1;
      else
        tmp = 0;
      iX1 = pLeft->ix;
      iError = 0;
      iDerror = tmp & 0x0000ffff;
      iXRightStep = tmp >> 16;
      iXLeftStep = iXRightStep + 1;

      iZ = pLeft->iz;
      iZRightStep=(iDZY + iDZX * iXRightStep);
      iZLeftStep=iZRightStep + iDZX;

      fS = pLeft->fs;
      fSRightStep = (fDSY + fDSX * iXRightStep);
      fSLeftStep = fSRightStep + fDSX;

      fT = pLeft->ft;
      fTRightStep =(fDTY + fDTX * iXRightStep);
      fTLeftStep = fTRightStep + fDTX;
    }

    if (iUpdateRight) {
      iDX2 = (pr2->ix - pr1->ix);
      iDY2 = (pr2->iy - pr1->iy);
      if (iDY2>0)
        iDXY2 = ( iDX2 << 16) / iDY2;
      else
        iDXY2 = 0;
      iX2 = pr1->ix << 16;
    }

    while (iScanLines>0) {
      iScanLines--;
      PIXEL *pp;
      int n;
      unsigned short * pz = pz1+iX1;
      unsigned int zz;
      unsigned int z = iZ;
      float sz = fS, tz = fT;
      pp = pPixelRow + iX1;
      n = (iX2 >> 16) - iX1;
      while (n>2) {
        zz=z >> 14;
        if (zz >= pz[0]) {
          zinv= 1.0 / (float) z; si= (int) (sz * zinv); ti= (int) (tz * zinv);
          value=texture[((ti & 0x3FC00000) | si) >> 14];
          if (value != 0xF81F) {
            pp[0] = value;
            pz[0] = zz;
          }
        }
        z += iDZX; sz += fDSX; tz += fDTX; zz=z >> 14;
        if (zz >= pz[1]) {
          zinv= 1.0 / (float) z; si= (int) (sz * zinv); ti= (int) (tz * zinv);
          value=texture[((ti & 0x3FC00000) | si) >> 14];
          if (value != 0xF81F) {
            pp[1] = value;
            pz[1] = zz;
          }
        }
        z += iDZX; sz += fDSX; tz += fDTX; zz=z >> 14;
        if (zz >= pz[2]) {
          zinv= 1.0 / (float) z; si= (int) (sz * zinv); ti= (int) (tz * zinv);
          value=texture[((ti & 0x3FC00000) | si) >> 14];
          if (value != 0xF81F) {
            pp[2] = value;
            pz[2] = zz;
          }
        }
        z += iDZX; sz += fDSX; tz += fDTX; zz=z >> 14;
        if (zz >= pz[3]) {
          zinv= 1.0 / (float) z; si= (int) (sz * zinv); ti= (int) (tz * zinv);
          value=texture[((ti & 0x3FC00000) | si) >> 14];
          if (value != 0xF81F) {
            pp[3] = value;
            pz[3] = zz;
          }
        }
        z += iDZX; sz += fDSX; tz += fDTX;
        pz+=4; pp += 4; n-=4;
      }

      while (n>-1) {
        zz=z >> 14;
        if (zz >= pz[0]) {
          zinv= 1.0 / (float) z; si= (int) (sz * zinv); ti= (int) (tz * zinv);
          value=texture[((ti & 0x3FC00000) | si) >> 14];
          if (value != 0xF81F) {
            pp[0] = value;
            pz[0] = zz;
          }
        }
        z += iDZX; sz += fDSX; tz += fDTX;
        pz+=1; pp += 1; n-=1;
      }


      iError+=iDerror;
      if (iError > 0) {
        iError-=0x10000;
        iX1+=iXLeftStep;
        iZ+=iZLeftStep;
        fS+=fSLeftStep;
        fT+=fTLeftStep;
      }
      else
      {
        iX1+=iXRightStep;
        iZ+=iZRightStep;
        fS+=fSRightStep;
        fT+=fTRightStep;
      }

      iX2 += iDXY2;
      pPixelRow += zb->iPitchInPixels;
      pz1 += zb->iWidth;
    }
  }
}

void tgl_draw_triangle_fill(TGLContext *c,
                            TGLVertex *p0,
                            TGLVertex *p1,
                            TGLVertex *p2){
  if (c->texture_2d_enabled) {
    ZB_setTexture(c->zb,c->current_texture->images[0].pixmap);
    if (c->depth_test) {
      if (c->blend_enabled) {
        switch (c->perspective_hint){
        case GL_DONT_CARE:
          ZB_DrawTriangleTexturedPerspective_zb(c->zb,&p0->zp,&p1->zp,&p2->zp);
          break;
        case GL_NICEST:
          ZB_DrawTriangleTexturedPerspectiveNice_zb(c->zb,&p0->zp,&p1->zp,&p2->zp);
          break;
        case GL_FASTEST:
          ZB_DrawTriangleTextured_zb(c->zb,&p0->zp,&p1->zp,&p2->zp);
        }
      }
      else
      {
        switch (c->perspective_hint){
        case GL_DONT_CARE:
          ZB_DrawTriangleTexturedPerspective_z(c->zb,&p0->zp,&p1->zp,&p2->zp);
          break;
        case GL_NICEST:
          ZB_DrawTriangleTexturedPerspectiveNice_z(c->zb,&p0->zp,&p1->zp,&p2->zp);
          break;
        case GL_FASTEST:
          ZB_DrawTriangleTextured_z(c->zb,&p0->zp,&p1->zp,&p2->zp);
        }
      }
    }
    else
    {
      if (c->blend_enabled) {
        switch (c->perspective_hint){
        case GL_DONT_CARE:
          ZB_DrawTriangleTexturedPerspective_b(c->zb,&p0->zp,&p1->zp,&p2->zp);
          break;
        case GL_NICEST:
          ZB_DrawTriangleTexturedPerspectiveNice_b(c->zb,&p0->zp,&p1->zp,&p2->zp);
          break;
        case GL_FASTEST:
          ZB_DrawTriangleTextured_b(c->zb,&p0->zp,&p1->zp,&p2->zp);
        }
      }
      else
      {
        switch (c->perspective_hint){
        case GL_DONT_CARE:
          ZB_DrawTriangleTexturedPerspective(c->zb,&p0->zp,&p1->zp,&p2->zp);
          break;
        case GL_NICEST:
          ZB_DrawTriangleTexturedPerspectiveNice(c->zb,&p0->zp,&p1->zp,&p2->zp);
          break;
        case GL_FASTEST:
          ZB_DrawTriangleTextured(c->zb,&p0->zp,&p1->zp,&p2->zp);
        }
      }
    }
  }
  else
  {
    if (c->depth_test) {
      if (c->current_shade_model == GL_SMOOTH)
        ZB_DrawTriangleSmooth_z(c->zb,&p0->zp,&p1->zp,&p2->zp);
      else
        ZB_DrawTriangleFlat_z(c->zb,&p0->zp,&p1->zp,&p2->zp);
    }
    else
    {
      if (c->current_shade_model == GL_SMOOTH)
        ZB_DrawTriangleSmooth(c->zb,&p0->zp,&p1->zp,&p2->zp);
      else
        ZB_DrawTriangleFlat(c->zb,&p0->zp,&p1->zp,&p2->zp);
    }
  }
}

void tgl_draw_triangle_line(TGLContext *c,TGLVertex *p0,TGLVertex *p1,TGLVertex *p2){
  if (c->depth_test) {
    if (p0->edge_flag) ZB_DrawLine_z(c->zb,&p0->zp,&p1->zp);
    if (p1->edge_flag) ZB_DrawLine_z(c->zb,&p1->zp,&p2->zp);
    if (p2->edge_flag) ZB_DrawLine_z(c->zb,&p2->zp,&p0->zp);
  }
  else
  {
    if (p0->edge_flag) ZB_DrawLine(c->zb,&p0->zp,&p1->zp);
    if (p1->edge_flag) ZB_DrawLine(c->zb,&p1->zp,&p2->zp);
    if (p2->edge_flag) ZB_DrawLine(c->zb,&p2->zp,&p0->zp);
  }
}

void tgl_draw_triangle_point(TGLContext *c, TGLVertex *p0, TGLVertex *p1, TGLVertex *p2){
  if (c->depth_test) {
    if (p0->edge_flag) ZB_DrawPoint_z(c->zb,&p0->zp);
    if (p1->edge_flag) ZB_DrawPoint_z(c->zb,&p1->zp);
    if (p2->edge_flag) ZB_DrawPoint_z(c->zb,&p2->zp);
  }
  else
  {
    if (p0->edge_flag) ZB_DrawPoint(c->zb,&p0->zp);
    if (p1->edge_flag) ZB_DrawPoint(c->zb,&p1->zp);
    if (p2->edge_flag) ZB_DrawPoint(c->zb,&p2->zp);
  }
}

void tgl_compile_op(TGLContext *c,TGLParam *p) {
  int op,op_size;
  TGLParamBuffer *ob,*ob1;
  int index,i;

  op=p[0].o;
  op_size=op_table_size[op];
  index=c->current_op_buffer_index;
  ob=c->current_op_buffer;

  if ((index + op_size) > 510) {
    ob1=tgl_zalloc(sizeof(TGLParamBuffer));
    ob1->next=((void *)0);
    ob->next=ob1;
    ob->ops[index ].o=OP_NextBuffer;
    ob->ops[index+1].p=(void *)ob1;
    c->current_op_buffer=ob1;
    ob=ob1;
    index=0;
  }

  for(i=0;i<op_size;i++) {
    ob->ops[index]=p[i];
    index++;
  }
  c->current_op_buffer_index=index;
}

void tgl_add_op(TGLParam *p) {
  int op = p[0].o;
  if (tgl_ctx->exec_flag) {
    op_table_func[op](tgl_ctx,p);
  }
  if (tgl_ctx->compile_flag) {
    tgl_compile_op(tgl_ctx,p);
  }
}

TGLTexture * tgl_find_texture(TGLContext *c,int h) {
  TGLTexture * t = c->shared_state.texture_hash_table[h % 256];
  while (t!=((void *)0)) {
    if (t->handle == h) return t;
    t=t->next;
  }
  return ((void *)0);
}

void tgl_free_texture(TGLContext *c,int h) {
  int i;
  TGLTexture * t = tgl_find_texture(c,h);
  if (t->prev==((void *)0)) {
    TGLTexture ** ht = &c->shared_state.texture_hash_table[t->handle % 256];
    *ht=t->next;
  }
  else
  {
    t->prev->next=t->next;
  }
  if (t->next!=((void *)0))
    t->next->prev=t->prev;

  for (i=0;i<11;i++) {
    TGLImage *im = &t->images[i];
    if (im->pixmap != ((void *)0))
      tgl_free(im->pixmap);
  }
  tgl_free(t);
}

TGLTexture *tgl_alloc_texture(TGLContext *c, int h){
  TGLTexture * t = tgl_zalloc(sizeof(TGLTexture));
  TGLTexture ** ht = &c->shared_state.texture_hash_table[h % 256];
  t->next = *ht;
  t->prev = ((void *)0);
  if (t->next != ((void *)0))
    t->next->prev=t;

  *ht = t;
  t->handle = h;
  return t;
}
static void tgl_calc_buf(TGLSpecBuf *buf, const float shininess) {
  int i;
  float val, inc;
  val = 0.0f;
  inc = 1.0f/1024;
  for (i = 0; i <= 1024; i++) {
    buf->buf[i] = pow(val, shininess);
    val += inc;
  }
}

TGLSpecBuf * tgl_specbuf_get_buffer(TGLContext *c, const int shininess_i, const float shininess) {
  TGLSpecBuf *found, *oldest;
  found = oldest = c->specbuf_first;
  while (found && found->shininess_i != shininess_i) {
    if (found->last_used < oldest->last_used) {
      oldest = found;
    }
    found = found->next;
  }
  if (found) {
    found->last_used = c->specbuf_used_counter++;
    return found;
  }
  if (oldest ==((void *)0) || c->specbuf_num_buffers < 8) {

    TGLSpecBuf *buf = tgl_malloc(sizeof(TGLSpecBuf));
    c->specbuf_num_buffers++;
    buf->next = c->specbuf_first;
    c->specbuf_first = buf;
    buf->last_used = c->specbuf_used_counter++;
    buf->shininess_i = shininess_i;
    tgl_calc_buf(buf, shininess);
    return buf;
  }


  oldest->shininess_i = shininess_i;
  oldest->last_used = c->specbuf_used_counter++;
  tgl_calc_buf(oldest, shininess);
  return oldest;
}

static inline float clampf01(float a) {
  if (a<0.0f)
    return 0.0f;
  else if (a>1.0f)
    return 1.0f;
  else
    return a;
}

void tgl_shade_vertex(TGLContext *c, TGLVertex *v){
  TGLLight *l;
  V3 n,s,d;
  float dist,tmp,att,dot,dot_spot,dot_spec;
  int twoside = c->light_model_two_side;

  TGLMaterial * m = &c->materials[0];

  n.v[0] = v->normal.v[0];
  n.v[1] = v->normal.v[1];
  n.v[2] = v->normal.v[2];

  float R = m->emission.v[0] + m->ambient.v[0]*c->ambient_light_model.v[0];
  float G = m->emission.v[1] + m->ambient.v[1]*c->ambient_light_model.v[1];
  float B = m->emission.v[2] + m->ambient.v[2]*c->ambient_light_model.v[2];
  float A = clampf01(m->diffuse.v[3]);

  for(l = c->first_light; l!=((void *)0); l=l->next) {
    float lR,lB,lG;
    lR = l->ambient.v[0] * m->ambient.v[0];
    lG = l->ambient.v[1] * m->ambient.v[1];
    lB = l->ambient.v[2] * m->ambient.v[2];

    if (l->position.v[3] == 0) {
      d.v[0] = l->position.v[0];
      d.v[1] = l->position.v[1];
      d.v[2] = l->position.v[2];
      att=1;
    }
    else
    {
      d.v[0] = l->position.v[0] - v->ec.v[0];
      d.v[1] = l->position.v[1] - v->ec.v[1];
      d.v[2] = l->position.v[2] - v->ec.v[2];
      dist = d.v[0]*d.v[0] + d.v[1]*d.v[1] + d.v[2]*d.v[2];
      if (dist!=0.0f) {
        dist=sqrtf(dist);
        tmp=1/dist;
        d.v[0] *= tmp;
        d.v[1] *= tmp;
        d.v[2] *= tmp;
      }
      att=1.0f/( l->attenuation[0] + dist*(l->attenuation[1] + dist*l->attenuation[2]) );
    }
    dot=d.v[0]*n.v[0] + d.v[1]*n.v[1] + d.v[2]*n.v[2];

    if (twoside && dot < 0)
      dot = -dot;

    if (dot>0) {
      lR += dot * l->diffuse.v[0] * m->diffuse.v[0];
      lG += dot * l->diffuse.v[1] * m->diffuse.v[1];
      lB += dot * l->diffuse.v[2] * m->diffuse.v[2];
      if (l->spot_cutoff != 180) {
        dot_spot=-(d.v[0]*l->norm_spot_direction.v[0] + d.v[1]*l->norm_spot_direction.v[1] + d.v[2]*l->norm_spot_direction.v[2]);
        if (twoside && dot_spot < 0)
          dot_spot = -dot_spot;

        if (dot_spot < l->cos_spot_cutoff) {
          continue;
        }
        else
        {
          if (l->spot_exponent > 0) {
            att=att*pow(dot_spot,l->spot_exponent);
          }
        }
      }

      if (c->local_light_model) {
        V3 vcoord;
        vcoord.v[0] = v->ec.v[0];
        vcoord.v[1] = v->ec.v[1];
        vcoord.v[2] = v->ec.v[2];
        tgl_V3_Norm(&vcoord);
        s.v[0] = d.v[0]-vcoord.v[0];
        s.v[1] = d.v[1]-vcoord.v[0];
        s.v[2] = d.v[2]-vcoord.v[0];
      }
      else
      {
        s.v[0] = d.v[0];
        s.v[1] = d.v[1];
        s.v[2] = d.v[2]+1.0;
      }
      dot_spec=n.v[0]*s.v[0] + n.v[1]*s.v[1] + n.v[2]*s.v[2];
      if (twoside && dot_spec < 0)
        dot_spec = -dot_spec;

      if (dot_spec>0) {
        TGLSpecBuf *specbuf;
        int idx;
        tmp=s.v[0]*s.v[0] + s.v[1]*s.v[1] + s.v[2]*s.v[2];
        if (tmp!=0.0f) {
          tmp=sqrtf(tmp);
          dot_spec=dot_spec / tmp;
        }

        specbuf = tgl_specbuf_get_buffer(c, m->shininess_i, m->shininess);
        idx = (int)(dot_spec*1024);
        if (idx > 1024)
          idx = 1024;

        dot_spec = specbuf->buf[idx];
        lR += dot_spec * l->specular.v[0] * m->specular.v[0];
        lG += dot_spec * l->specular.v[1] * m->specular.v[1];
        lB += dot_spec * l->specular.v[2] * m->specular.v[2];
      }
    }

    R += att * lR;
    G += att * lG;
    B += att * lB;
  }

  v->color.v[0] = clampf01(R);
  v->color.v[1] = clampf01(G);
  v->color.v[2] = clampf01(B);
  v->color.v[3] = A;
}

void inline tgl_vertex_transform(TGLContext * c, TGLVertex * v) {
  float *m;
  V4 *n;

  if (c->lighting_enabled) {
    m = &c->matrix_stack_ptr[0]->m[0][0];
    v->ec.v[0] = (v->coord.v[0]*m[ 0] + v->coord.v[1]*m[ 1] + v->coord.v[2]*m[2] + m[ 3]);
    v->ec.v[1] = (v->coord.v[0]*m[ 4] + v->coord.v[1]*m[ 5] + v->coord.v[2]*m[6] + m[ 7]);
    v->ec.v[2] = (v->coord.v[0]*m[ 8] + v->coord.v[1]*m[ 9] + v->coord.v[2]*m[10] + m[11]);
    v->ec.v[3] = (v->coord.v[0]*m[12] + v->coord.v[1]*m[13] + v->coord.v[2]*m[14] + m[15]);

    m = &c->matrix_stack_ptr[1]->m[0][0];
    v->pc.v[0] = (v->ec.v[0]*m[ 0] + v->ec.v[1]*m[ 1] + v->ec.v[2]*m[ 2] + v->ec.v[3]*m[ 3]);
    v->pc.v[1] = (v->ec.v[0]*m[ 4] + v->ec.v[1]*m[ 5] + v->ec.v[2]*m[ 6] + v->ec.v[3]*m[ 7]);
    v->pc.v[2] = (v->ec.v[0]*m[ 8] + v->ec.v[1]*m[ 9] + v->ec.v[2]*m[10] + v->ec.v[3]*m[11]);
    v->pc.v[3] = (v->ec.v[0]*m[12] + v->ec.v[1]*m[13] + v->ec.v[2]*m[14] + v->ec.v[3]*m[15]);

    m = &c->matrix_model_view_inv.m[0][0];
    n = &c->current_normal;

    v->normal.v[0] = (n->v[0]*m[0] + n->v[1]*m[1] + n->v[2]*m[ 2]);
    v->normal.v[1] = (n->v[0]*m[4] + n->v[1]*m[5] + n->v[2]*m[ 6]);
    v->normal.v[2] = (n->v[0]*m[8] + n->v[1]*m[9] + n->v[2]*m[10]);

    if (c->normalize_enabled) {
      tgl_V3_Norm(&v->normal);
    }
  }
  else
  {
    m = &c->matrix_model_projection.m[0][0];
    v->pc.v[0] = (v->coord.v[0]*m[0] + v->coord.v[1]*m[1] + v->coord.v[2]*m[ 2] + m[ 3]);
    v->pc.v[1] = (v->coord.v[0]*m[4] + v->coord.v[1]*m[5] + v->coord.v[2]*m[ 6] + m[ 7]);
    v->pc.v[2] = (v->coord.v[0]*m[8] + v->coord.v[1]*m[9] + v->coord.v[2]*m[10] + m[11]);
    if (c->matrix_model_projection_no_w_transform) {
      v->pc.v[3] = m[15];
    }
    else
    {
      v->pc.v[3] = (v->coord.v[0] *m[12] + v->coord.v[1]*m[13] + v->coord.v[2]*m[14] + m[15]);
    }
  }

  v->clip_code = tgl_clipcode(v->pc.v[0], v->pc.v[1], v->pc.v[2], v->pc.v[3]);
}

void tglopVertex(TGLContext * c, TGLParam * p) {
  TGLVertex *v;
  int n, i, cnt;
  if (c->ignore_in_begin==0 && c->in_begin==0){
    glError=GL_INVALID_OPERATION;
    return;
  }

  n = c->vertex_n;
  cnt = c->vertex_cnt;
  cnt++;
  c->vertex_cnt = cnt;

  if (n >= c->vertex_max) {
    TGLVertex *newarray;
    c->vertex_max <<= 1;
    newarray = tgl_malloc(sizeof(TGLVertex) * c->vertex_max);
    memcpy(newarray, c->vertex, n * sizeof(TGLVertex));
    tgl_free(c->vertex);
    c->vertex = newarray;
  }

  v = &c->vertex[n];
  n++;

  v->coord.v[0] = p[1].f;
  v->coord.v[1] = p[2].f;
  v->coord.v[2] = p[3].f;
  v->coord.v[3] = p[4].f;

  tgl_vertex_transform(c, v);

  if (c->lighting_enabled) {
    tgl_shade_vertex(c, v);
  }
  else
  {
    v->color = c->current_color;
  }

  if (c->texture_2d_enabled) {
    if (c->apply_texture_matrix) {
      tgl_M4_MulV4(&v->tex_coord, c->matrix_stack_ptr[2], &c->current_tex_coord);
    }
    else
    {
      v->tex_coord = c->current_tex_coord;
    }
  }

  if (v->clip_code == 0)
    tgl_transform_to_viewport(c, v);

  v->edge_flag = c->current_edge_flag;
  switch (c->begin_type) {

  case GL_POINTS:
    tgl_draw_point(c, &c->vertex[0]);
    n = 0;
    break;

  case GL_LINES:
    if (n == 2) {
      tgl_draw_line(c, &c->vertex[0], &c->vertex[1]);
      n = 0;
    }
    break;
  case GL_LINE_STRIP:
  case GL_LINE_LOOP:
    if (n == 1) {
      c->vertex[2] = c->vertex[0];
    }
    else if (n == 2) {
      tgl_draw_line(c, &c->vertex[0], &c->vertex[1]);
      c->vertex[0] = c->vertex[1];
      n = 1;
    }
    break;

  case GL_TRIANGLES:
    if (n == 3) {
      tgl_draw_triangle(c, &c->vertex[0], &c->vertex[1], &c->vertex[2]);
      n = 0;
    }
    break;

  case GL_TRIANGLE_STRIP:
    if (cnt > 2) {
      if (n == 3)
        n = 0;

      switch(cnt & 1) {
      case 0:
        tgl_draw_triangle(c,&c->vertex[2],&c->vertex[1],&c->vertex[0]);
        break;

      default:
      case 1:
        tgl_draw_triangle(c,&c->vertex[0],&c->vertex[1],&c->vertex[2]);
        break;
      }
    }
    break;

  case GL_TRIANGLE_FAN:
    if (n == 3) {
      tgl_draw_triangle(c, &c->vertex[0], &c->vertex[1], &c->vertex[2]);
      c->vertex[1] = c->vertex[2];
      n = 2;
    }
    break;

  case GL_QUADS:
    if (n == 4) {
      c->vertex[2].edge_flag = 0;
      tgl_draw_triangle(c, &c->vertex[0], &c->vertex[1], &c->vertex[2]);
      c->vertex[2].edge_flag = 1;
      c->vertex[0].edge_flag = 0;
      tgl_draw_triangle(c, &c->vertex[0], &c->vertex[2], &c->vertex[3]);
      n = 0;
    }
    break;

  case GL_QUAD_STRIP:
    if (n == 4) {
      tgl_draw_triangle(c, &c->vertex[0], &c->vertex[1], &c->vertex[2]);
      tgl_draw_triangle(c, &c->vertex[1], &c->vertex[3], &c->vertex[2]);
      for (i = 0; i < 2; i++)
        c->vertex[i] = c->vertex[i + 2];
      n = 2;
    }
    break;
  case GL_POLYGON:
    break;

  default:
    glError=GL_INVALID_ENUM;
    return;
  }

  c->vertex_n = n;
}

void glVertex4f(float x, float y, float z, float w) {
  tgl_p[0].o=OP_Vertex;
  tgl_p[1].f=x;
  tgl_p[2].f=y;
  tgl_p[3].f=z;
  tgl_p[4].f=w;
  tgl_add_op(tgl_p);
}

void glVertex4fv(float *v) {
  glVertex4f(v[0],v[1],v[2],v[3]);
}
void glVertex3f(float x, float y, float z) {
  glVertex4f(x,y,z,1);
}

void glVertex3fv(float *v) {
  glVertex4f(v[0],v[1],v[2],1);
}
void glVertex2f(float x, float y) {
  glVertex4f(x,y,0,1);
}
void glVertex2fv(float *v) {
  glVertex4f(v[0],v[1],0,1);
}

void tglopNormal(TGLContext * c, TGLParam * p) {
  c->current_normal.v[0] = p[1].f;
  c->current_normal.v[1] = p[2].f;
  c->current_normal.v[2] = p[3].f;
  c->current_normal.v[3] = 0;
}
void glNormal3f(float x, float y, float z) {
  tgl_p[0].o=OP_Normal;
  tgl_p[1].f=x;
  tgl_p[2].f=y;
  tgl_p[3].f=z;
  tgl_add_op(tgl_p);
}
void glNormal3fv(float *v) {
  glNormal3f(v[0],v[1],v[2]);
}

void tglopMaterial(TGLContext *c,TGLParam *p) {
  int mode = p[1].i;
  int type = p[2].i;
  float *v = &p[3].f;
  int i;
  TGLMaterial *m;

  if (mode == GL_FRONT_AND_BACK) {
    p[1].i = GL_FRONT;
    tglopMaterial(c,p);
    mode = GL_BACK;
  }

  if (mode == GL_FRONT)
    m = &c->materials[0];
  else
    m = &c->materials[1];

  switch(type) {
  case GL_EMISSION:
    for(i=0;i<4;i++)
      m->emission.v[i]=v[i];
    break;
  case GL_AMBIENT:
    for(i=0;i<4;i++)
      m->ambient.v[i]=v[i];
    break;
  case GL_DIFFUSE:
    for(i=0;i<4;i++)
      m->diffuse.v[i]=v[i];
    break;
  case GL_SPECULAR:
    for(i=0;i<4;i++)
      m->specular.v[i]=v[i];
    break;
  case GL_SHININESS:
    m->shininess=v[0];
    m->shininess_i = (v[0]/128.0f)*1024;
    break;
  case GL_AMBIENT_AND_DIFFUSE:
    for(i=0;i<4;i++)
      m->diffuse.v[i]=v[i];
    for(i=0;i<4;i++)
      m->ambient.v[i]=v[i];
    break;
  }
}

void glMaterialfv(int mode,int type,float *v){
  int i,n;
  tgl_p[0].o=OP_Material;
  tgl_p[1].i=mode;
  tgl_p[2].i=type;
  if (type == GL_SHININESS)
    n=1;
  else
    n=4;
  for(i=0;i<4;i++)
    tgl_p[3+i].f=v[i];

  for(i=n;i<4;i++)
    tgl_p[3+i].f=0;

  tgl_add_op(tgl_p);
}

void glMaterialf(int mode,int type,float v) {
  int i;
  tgl_p[0].o=OP_Material;
  tgl_p[1].i=mode;
  tgl_p[2].i=type;
  tgl_p[3].f=v;
  for(i=0;i<3;i++)
   tgl_p[4+i].f=0;
  tgl_add_op(tgl_p);
}

void tglopColorMaterial(TGLContext *c,TGLParam *p) {
  c->current_color_material_mode=p[1].i;
  c->current_color_material_type=p[2].i;
}
void glColorMaterial(int mode,int type){
  tgl_p[0].o=OP_ColorMaterial;
  tgl_p[1].i=mode;
  tgl_p[2].i=type;
  tgl_add_op(tgl_p);
}


void tglopColor(TGLContext * c, TGLParam * p) {
  c->current_color.v[0] = p[1].f;
  c->current_color.v[1] = p[2].f;
  c->current_color.v[2] = p[3].f;
  c->current_color.v[3] = p[4].f;

  c->current_ui_color[0] = p[5].c;
  c->current_ui_color[1] = p[6].c;
  c->current_ui_color[2] = p[7].c;

  if (c->color_material_enabled) {
    TGLParam q[7];
    q[0].o = OP_Material;
    q[1].i = c->current_color_material_mode;
    q[2].i = c->current_color_material_type;
    q[3].f = p[1].f;
    q[4].f = p[2].f;
    q[5].f = p[3].f;
    q[6].f = p[4].f;
    tglopMaterial(c, q);
  }
}

void glColor4f(float r, float g, float b, float a) {
  tgl_p[0].o=OP_Color;
  tgl_p[1].f=r;
  tgl_p[2].f=g;
  tgl_p[3].f=b;
  tgl_p[4].f=a;

  tgl_p[5].c = (unsigned int) (r * (( (1<<16)-(1<<10) ) - ( (1<<10) )) + ( (1<<10) ));
  tgl_p[6].c = (unsigned int) (g * (( (1<<16)-(1<<9) ) - ( (1<<9) )) + ( (1<<9) ));
  tgl_p[7].c = (unsigned int) (b * (( (1<<16)-(1<<10) ) - ( (1<<10) )) + ( (1<<10) ));
  tgl_add_op(tgl_p);
}

void glColor4fv(float *v) {
  glColor4f(v[0],v[1],v[2],v[3]);
}

void glColor3f(float r, float g, float b) {
  glColor4f(r,g,b,1);
}

void glColor3fv(float *v) {
  glColor4f(v[0],v[1],v[2],1);
}

void tglopTexCoord(TGLContext * c, TGLParam * p) {
  c->current_tex_coord.v[0] = p[1].f;
  c->current_tex_coord.v[1] = p[2].f;
  c->current_tex_coord.v[2] = p[3].f;
  c->current_tex_coord.v[3] = p[4].f;
}
void glTexCoord4f(float s,float t,float r,float q) {
  tgl_p[0].o=OP_TexCoord;
  tgl_p[1].f=s;
  tgl_p[2].f=t;
  tgl_p[3].f=r;
  tgl_p[4].f=q;
  tgl_add_op(tgl_p);
}
void glTexCoord4fv(float *v) {
  glTexCoord4f(v[0],v[1],v[2],v[3]);
}
void glTexCoord2f(float s,float t) {
  glTexCoord4f(s,t,0,1);
}
void glTexCoord2fv(float *v) {
  glTexCoord4f(v[0],v[1],0,1);
}

int glGetError(void){
  int ret = glError;
  glError = 0;
  return ret;
}

const char * glGetString(int pname){
  if (tgl_ctx==((void *)0)) {
    glError=GL_INVALID_OPERATION;
    return ((void *)0);
  }
  else
  {
    switch(pname) {
    case GL_VENDOR:
      return "FBTinyGL 1.8";
    case GL_RENDERER:
      if (sizeof(size_t)==4)
        return "32-bit CPU renderer";
      else if (sizeof(size_t)==8)
        return "64-bit CPU renderer";
      else
        return "generic CPU renderer";
    case GL_VERSION:
      return "1.1.0";
    default:
      glError=GL_INVALID_ENUM;
      return ((void *)0);
    }
  }
}

void glGetIntegerv(int pname, int *params) {
  if (!tgl_ctx)
    return;
  switch(pname) {
  case GL_VIEWPORT:
    params[0] = tgl_ctx->viewport.iX;
    params[1] = tgl_ctx->viewport.iY;
    params[2] = tgl_ctx->viewport.iWidth;
    params[3] = tgl_ctx->viewport.iHeight;
    return;
  case GL_PERSPECTIVE_CORRECTION_HINT:
    *params = tgl_ctx->perspective_hint;
    return;
  case GL_BLEND:
    *params = tgl_ctx->blend_enabled;
    return;
  case GL_DEPTH_TEST:
    *params = tgl_ctx->depth_test;
    return;
  case GL_TEXTURE_2D:
    *params = tgl_ctx->texture_2d_enabled;
    return;
  case GL_LIGHTING:
    *params = tgl_ctx->lighting_enabled;
    return;
  case GL_COLOR_MATERIAL:
    *params = tgl_ctx->color_material_enabled;
    return;
  case GL_NORMALIZE:
    *params = tgl_ctx->normalize_enabled;
    return;
  case GL_MAX_MODELVIEW_STACK_DEPTH:
    *params = 32;
    return;
  case GL_MAX_PROJECTION_STACK_DEPTH:
    *params = 8;
    return;
  case GL_MAX_TEXTURE_STACK_DEPTH:
    *params = 8;
    return;
  case GL_MAX_TEXTURE_SIZE:
    *params = 256;
    return;
  case GL_MAX_LIGHTS:
    *params = 8;
    return;
  default:
    glError=GL_INVALID_ENUM;
  }
}

void tgl_GetMatrix(int pname, float *v) {
  int mnr = 0;
  switch (pname) {
  case GL_TEXTURE_MATRIX   : mnr++;
  case GL_PROJECTION_MATRIX: mnr++;
  case GL_MODELVIEW_MATRIX :
    {
      float *p = &tgl_ctx->matrix_stack_ptr[mnr]->m[0][0];
      memcpy(v,p,16*sizeof(float));
    }
    break;
  }
}

void glGetFloatv(int pname, float *v) {
  int i;
  int mnr = 0;

  switch (pname) {
  case GL_TEXTURE_MATRIX:
    mnr++;
  case GL_PROJECTION_MATRIX:
    mnr++;
  case GL_MODELVIEW_MATRIX:
    {
      float *p = &tgl_ctx->matrix_stack_ptr[mnr]->m[0][0];
      for (i = 0; i < 4; i++) {
        *v++ = p[0];
        *v++ = p[4];
        *v++ = p[8];
        *v++ = p[12];
        p++;
      }
    }
    break;
  case GL_LINE_WIDTH:
    *v = 1.0f;
    break;
  case GL_LINE_WIDTH_RANGE:
    v[0] = v[1] = 1.0f;
    break;
  case GL_POINT_SIZE:
    *v = 1.0f;
    break;
  case GL_POINT_SIZE_RANGE:
    v[0] = v[1] = 1.0f;
  default:
    glError=GL_INVALID_ENUM;
    break;
  }
}

void tglopEdgeFlag(TGLContext * c, TGLParam * p) {
  c->current_edge_flag = p[1].i;
}
void glEdgeFlag(int flag) {
  tgl_p[0].o=OP_EdgeFlag;
  tgl_p[1].i=flag;
  tgl_add_op(tgl_p);
}

void tglopShadeModel(TGLContext *c,TGLParam *p) {
  c->current_shade_model=p[1].i;;
}

void glShadeModel(int mode) {
  if ((mode == GL_FLAT) || (mode == GL_SMOOTH)) {
    tgl_p[0].o=OP_ShadeModel;
    tgl_p[1].i=mode;
    tgl_add_op(tgl_p);
  }
  else
    glError=GL_INVALID_ENUM;
}

void tglopCullFace(TGLContext *c,TGLParam *p) {
  c->current_cull_face=p[1].i;
}

void glCullFace(int mode) {
  if (mode == GL_BACK || mode == GL_FRONT || mode == GL_FRONT_AND_BACK){
    tgl_p[0].o=OP_CullFace;
    tgl_p[1].i=mode;
    tgl_add_op(tgl_p);
  }
  else
    glError=GL_INVALID_ENUM;
}

void tglopFrontFace(TGLContext *c,TGLParam *p) {
  c->current_front_face=p[1].i;
}
void glFrontFace(int mode) {
  if (mode == GL_CCW || mode == GL_CW){
    mode = (mode != GL_CCW);
    tgl_p[0].o=OP_FrontFace;
    tgl_p[1].i=mode;
    tgl_add_op(tgl_p);
  }
  else
    glError=GL_INVALID_ENUM;
}

void tglopPolygonMode(TGLContext *c,TGLParam *p) {
  switch(p[1].i) {
  case GL_BACK:
    c->polygon_mode_back =p[2].i;
    break;
  case GL_FRONT:
    c->polygon_mode_front=p[2].i;
    break;
  case GL_FRONT_AND_BACK:
    c->polygon_mode_front=p[2].i;
    c->polygon_mode_back =p[2].i;
    break;
  default:
    glError=GL_INVALID_ENUM;
  }
}
void glPolygonMode(int face,int mode) {
  if ((face == GL_BACK || face == GL_FRONT || face == GL_FRONT_AND_BACK) &&
      (mode == GL_POINT || mode == GL_LINE || mode == GL_FILL)) {
    tgl_p[0].o=OP_PolygonMode;
    tgl_p[1].i=face;
    tgl_p[2].i=mode;
    tgl_add_op(tgl_p);
  }
  else
    glError=GL_INVALID_ENUM;
}

void tgl_enable_disable_light(TGLContext *c,int light,int v) {
  TGLLight *l=&c->lights[light];
  if (v && !l->enabled) {
    l->enabled = 1;
    l->next = c->first_light;
    c->first_light = l;
    l->prev = ((void *)0);
  }
  else if (!v && l->enabled) {
    l->enabled = 0;
    if (l->prev == ((void *)0))
      c->first_light=l->next;
    else
      l->prev->next=l->next;
    if (l->next != ((void *)0))
      l->next->prev=l->prev;
  }
}

void tglopEnableDisable(TGLContext *c,TGLParam *p){
  int code=p[1].i;
  int v=p[2].i;

  switch(code) {
  case GL_CULL_FACE:
    c->cull_face_enabled=v;
    break;
  case GL_LIGHTING:
    c->lighting_enabled=v;
    break;
  case GL_COLOR_MATERIAL:
    c->color_material_enabled=v;
      break;
  case GL_TEXTURE_2D:
    c->texture_2d_enabled=v;
    break;
  case GL_NORMALIZE:
    c->normalize_enabled=v;
    break;
  case GL_DEPTH_TEST:
    c->depth_test = v;
    break;
  case GL_BLEND:
    c->blend_enabled = v;
    break;
  default:
    if (code>=GL_LIGHT0 && code<GL_LIGHT0+8) {
      tgl_enable_disable_light(c,code - GL_LIGHT0, v);
    }
    else
      glError=GL_INVALID_ENUM;
    break;
  }
}
void glEnable(int cap) {
  tgl_p[0].o=OP_EnableDisable;
  tgl_p[1].i=cap;
  tgl_p[2].i=1;
  tgl_add_op(tgl_p);
}

void glDisable(int cap) {
  tgl_p[0].o=OP_EnableDisable;
  tgl_p[1].i=cap;
  tgl_p[2].i=0;
  tgl_add_op(tgl_p);
}

void tgl_eval_viewport(TGLContext * c) {
  TGLViewport *v    = &c->viewport;
  float fZSize = (1 << 30);
  float fZHalf = (fZSize     - 0.5f) * 0.5f;
  float fWHalf = (v->iWidth  - 0.5f) * 0.5f;
  float fHHalf = (v->iHeight - 0.5f) * 0.5f;

  v->trans.v[0] = fWHalf + v->iX;
  v->trans.v[1] = fHHalf + v->iY;
  v->trans.v[2] = fZHalf + (1 << 14) / 2;

  v->scale.v[0] = fWHalf;
  v->scale.v[1] = -fHHalf;
  v->scale.v[2] = -fZHalf;
}
void tglopViewport(TGLContext *c, TGLParam *p) {
  int iX = p[1].i;
  int iY = p[2].i;
  int iWidth = p[3].i;
  int iHeight = p[4].i;

  if (iX<0)
    iX=0;
  else if (iX > c->zb->iWidth-2)
    iX = c->zb->iWidth-2;

  if (iY<0)
    iY=0;
  else if (iY > c->zb->iHeight-2)
    iY = c->zb->iHeight-2;

  if (iWidth<1)
    iWidth=1;

  if (iHeight<1)
    iHeight=1;

  if (iX+iWidth > c->zb->iWidth)
    iWidth = c->zb->iWidth-iX;

  if (iY+iHeight > c->zb->iHeight)
    iHeight = c->zb->iHeight-iY;

  if (c->viewport.iX != iX ||
      c->viewport.iY != iY ||
      c->viewport.iWidth != iWidth ||
      c->viewport.iHeight != iHeight) {

    c->viewport.iX = iX;
    c->viewport.iY = iY;
    c->viewport.iWidth = iWidth;
    c->viewport.iHeight = iHeight;

    c->viewport.iNotUpToDate = 1;
  }
}
void glViewport(int iX,int iY,int iWidth,int iHeight) {
  tgl_p[0].o = OP_Viewport;
  tgl_p[1].i = iX;
  tgl_p[2].i = iY;
  tgl_p[3].i = iWidth;
  tgl_p[4].i = iHeight;
  tgl_add_op(tgl_p);
}

void tglopBegin(TGLContext * c, TGLParam * p) {
  M4 tmp;
  int type = p[1].i;

  if (c->ignore_in_begin==0 && c->in_begin!=0){
    glError=GL_INVALID_OPERATION;
    return;
  }

  c->begin_type = type;

  if (c->ignore_in_begin==0)
    c->in_begin = 1;

  c->vertex_n = 0;
  c->vertex_cnt = 0;

  if (c->iModelProjectionNotUpToDate) {
    if (c->lighting_enabled) {
      tgl_M4_Inverse(&tmp, c->matrix_stack_ptr[0]);
      tgl_M4_Transpose(&c->matrix_model_view_inv, &tmp);
    }
    else
    {
      float *m = &c->matrix_model_projection.m[0][0];
      tgl_M4_Mul(&c->matrix_model_projection, c->matrix_stack_ptr[1], c->matrix_stack_ptr[0]);
      if (m[12] == 0.0 && m[13] == 0.0 && m[14] == 0.0)
        c->matrix_model_projection_no_w_transform = 1;
      else
        c->matrix_model_projection_no_w_transform = 0;
    }
    c->apply_texture_matrix = !tgl_M4_IsIdentity(c->matrix_stack_ptr[2]);
    c->iModelProjectionNotUpToDate = 0;
  }

  if (c->viewport.iNotUpToDate) {
    tgl_eval_viewport(c);
    c->viewport.iNotUpToDate = 0;
  }

  if (c->render_mode == GL_SELECT) {
    c->draw_triangle_front = tgl_draw_triangle_select;
    c->draw_triangle_back  = tgl_draw_triangle_select;
  }
  else
  {
    switch (c->polygon_mode_front) {
    case GL_POINT:
      c->draw_triangle_front = tgl_draw_triangle_point;
      break;
    case GL_LINE:
      c->draw_triangle_front = tgl_draw_triangle_line;
      break;
    default:
      c->draw_triangle_front = tgl_draw_triangle_fill;
      break;
    }

    switch (c->polygon_mode_back) {
    case GL_POINT:
      c->draw_triangle_back = tgl_draw_triangle_point;
      break;
    case GL_LINE:
      c->draw_triangle_back = tgl_draw_triangle_line;
      break;
    default:
      c->draw_triangle_back = tgl_draw_triangle_fill;
      break;
    }
  }
}

void glBegin(int imode) {
  tgl_p[0].o=OP_Begin;
  tgl_p[1].i=imode;
  tgl_add_op(tgl_p);
}

void tglopEnd(TGLContext * c, TGLParam * param) {
  if (c->ignore_in_begin==0 && c->in_begin !=1) {
    glError=GL_INVALID_OPERATION;
    return;
  }
  if (c->begin_type == GL_LINE_LOOP) {
    if (c->vertex_cnt >= 3) {
      tgl_draw_line(c, &c->vertex[0], &c->vertex[2]);
    }
  }
  else if (c->begin_type == GL_POLYGON) {
    int i = c->vertex_cnt;
    while (i > 2) {
      i--;
      tgl_draw_triangle(c, &c->vertex[i], &c->vertex[0], &c->vertex[i - 1]);
    }
  }
  c->in_begin = 0;
}
void glEnd(void) {
  tgl_p[0].o=OP_End;
  tgl_add_op(tgl_p);
}

void tglopMatrixMode(TGLContext *c,TGLParam *p) {
  int mode=p[1].i;
  switch(mode) {
  case GL_MODELVIEW:
    c->matrix_mode=0;
    break;
  case GL_PROJECTION:
    c->matrix_mode=1;
    break;
  case GL_TEXTURE:
    c->matrix_mode=2;
    break;
  default:
    glError=GL_INVALID_ENUM;
    break;
  }
}
void glMatrixMode(int mode) {
  tgl_p[0].o=OP_MatrixMode;
  tgl_p[1].i=mode;
  tgl_add_op(tgl_p);
}

void tglopLoadMatrix(TGLContext *c,TGLParam *p) {
  M4 *m;
  TGLParam *q;

  m=c->matrix_stack_ptr[c->matrix_mode];
  q=p+1;

  m->m[0][0]=q[ 0].f;
  m->m[1][0]=q[ 1].f;
  m->m[2][0]=q[ 2].f;
  m->m[3][0]=q[ 3].f;

  m->m[0][1]=q[ 4].f;
  m->m[1][1]=q[ 5].f;
  m->m[2][1]=q[ 6].f;
  m->m[3][1]=q[ 7].f;

  m->m[0][2]=q[ 8].f;
  m->m[1][2]=q[ 9].f;
  m->m[2][2]=q[10].f;
  m->m[3][2]=q[11].f;

  m->m[0][3]=q[12].f;
  m->m[1][3]=q[13].f;
  m->m[2][3]=q[14].f;
  m->m[3][3]=q[15].f;

  c->iModelProjectionNotUpToDate=(c->matrix_mode<2);
}
void glLoadMatrixf(const float *m) {
  tgl_p[ 0].o=OP_LoadMatrix;

  tgl_p[ 1].f=m[ 0];
  tgl_p[ 2].f=m[ 1];
  tgl_p[ 3].f=m[ 2];
  tgl_p[ 4].f=m[ 3];

  tgl_p[ 5].f=m[ 4];
  tgl_p[ 6].f=m[ 5];
  tgl_p[ 7].f=m[ 6];
  tgl_p[ 8].f=m[ 7];

  tgl_p[ 9].f=m[ 8];
  tgl_p[10].f=m[ 9];
  tgl_p[11].f=m[10];
  tgl_p[12].f=m[11];

  tgl_p[13].f=m[12];
  tgl_p[14].f=m[13];
  tgl_p[15].f=m[14];
  tgl_p[16].f=m[15];

  tgl_add_op(tgl_p);
}

void tglopLoadIdentity(TGLContext *c,TGLParam *p) {
  tgl_M4_Identity(c->matrix_stack_ptr[c->matrix_mode]);
  c->iModelProjectionNotUpToDate=(c->matrix_mode<2);
}
void glLoadIdentity(void) {
  tgl_p[0].o=OP_LoadIdentity;
  tgl_add_op(tgl_p);
}

void tglopMultMatrix(TGLContext *c,TGLParam *p) {
  M4 m;
  TGLParam *q;
  q=p+1;

  m.m[0][0]=q[0].f;
  m.m[1][0]=q[1].f;
  m.m[2][0]=q[2].f;
  m.m[3][0]=q[3].f;

  m.m[0][1]=q[4].f;
  m.m[1][1]=q[5].f;
  m.m[2][1]=q[6].f;
  m.m[3][1]=q[7].f;

  m.m[0][2]=q[8].f;
  m.m[1][2]=q[9].f;
  m.m[2][2]=q[10].f;
  m.m[3][2]=q[11].f;

  m.m[0][3]=q[12].f;
  m.m[1][3]=q[13].f;
  m.m[2][3]=q[14].f;
  m.m[3][3]=q[15].f;

  tgl_M4_MulLeft(c->matrix_stack_ptr[c->matrix_mode],&m);
  c->iModelProjectionNotUpToDate=(c->matrix_mode<2);
}
void glMultMatrixf(const float *m) {
  int i;
  tgl_p[0].o=OP_MultMatrix;
  for(i=0;i<16;i++) {
    tgl_p[i+1].f=m[i];
  }
  tgl_add_op(tgl_p);
}

void tglopPushMatrix(TGLContext *c,TGLParam *p) {
  int n=c->matrix_mode;
  M4 *m;

  if (c->in_begin!=0){
    glError=GL_INVALID_OPERATION;
    return;
  }

  if ( (c->matrix_stack_ptr[n] - c->matrix_stack[n] + 1 ) >= c->matrix_stack_depth_max[n] ){
    glError=GL_STACK_OVERFLOW;
    return;
  }

  m=++c->matrix_stack_ptr[n];
  tgl_M4_Move(&m[0],&m[-1]);
  c->iModelProjectionNotUpToDate=(c->matrix_mode<2);
}
void glPushMatrix(void) {
  tgl_p[0].o=OP_PushMatrix;
  tgl_add_op(tgl_p);
}

void tglopPopMatrix(TGLContext *c,TGLParam *p) {
  int n=c->matrix_mode;

  if (c->in_begin!=0) {
    glError=GL_INVALID_OPERATION;
    return;
  }

  if (c->matrix_stack_ptr[n] <= c->matrix_stack[n]){
    glError=GL_STACK_UNDERFLOW;
    return;
  }
  c->matrix_stack_ptr[n]--;
  c->iModelProjectionNotUpToDate=(c->matrix_mode<2);
}
void glPopMatrix(void) {
  tgl_p[0].o=OP_PopMatrix;
  tgl_add_op(tgl_p);
}

void tglopRotate(TGLContext *c, TGLParam *p) {
  M4 m;
  float u[3];
  float angle = p[1].f * 3.141592654 / 180.0;
  u[0]=p[2].f;
  u[1]=p[3].f;
  u[2]=p[4].f;

  int dir_code = ((u[0] != 0)<<2) | ((u[1] != 0)<<1) | (u[2] != 0);

  switch(dir_code) {
  case 0:
    tgl_M4_Identity(&m);
    break;
  case 4:
    if (u[0] < 0) angle=-angle;
    tgl_M4_Rotate(&m,angle,0);
    break;
  case 2:
    if (u[1] < 0) angle=-angle;
    tgl_M4_Rotate(&m,angle,1);
    break;
  case 1:
    if (u[2] < 0) angle=-angle;
    tgl_M4_Rotate(&m,angle,2);
    break;
  default:
    {
      float c,c1,s;
      float u00,u11,u22,u01c1,u20c1,u12c1,u0s,u1s,u2s;
      float len = u[0]*u[0] + u[1]*u[1] + u[2]*u[2];
      if (len == 0.0f)
        return;

      len = 1.0f / sqrtf(len);
      u[0] *= len;
      u[1] *= len;
      u[2] *= len;

      c=cosf(angle);
      c1=1.0f-c;
      s=sinf(angle);

      u00=u[0]*u[0];
      u11=u[1]*u[1];
      u22=u[2]*u[2];

      u01c1=u[0]*u[1]*c1;
      u20c1=u[2]*u[0]*c1;
      u12c1=u[1]*u[2]*c1;

      u0s=u[0]*s;
      u1s=u[1]*s;
      u2s=u[2]*s;

      m.m[0][0]=u00   + c*(1-u00);
      m.m[1][0]=u01c1 - u2s;
      m.m[2][0]=u20c1 + u1s;
      m.m[3][0]=0.0f;

      m.m[0][1]=u01c1 + u2s;
      m.m[1][1]=u11   + c*(1-u11);
      m.m[2][1]=u12c1 - u0s;
      m.m[3][1]=0.0f;

      m.m[0][2]=u20c1 - u1s;
      m.m[1][2]=u12c1 + u0s;
      m.m[2][2]=u22   + c*(1-u22);
      m.m[3][2]=0.0f;

      m.m[0][3]=0.0f;
      m.m[1][3]=0.0f;
      m.m[2][3]=0.0f;
      m.m[3][3]=1.0f;
    }
  }

  tgl_M4_MulLeft(c->matrix_stack_ptr[c->matrix_mode],&m);
  c->iModelProjectionNotUpToDate=(c->matrix_mode<2);
}
void glRotatef(float angle, float x, float y, float z) {
  tgl_p[0].o=OP_Rotate;
  tgl_p[1].f=angle;
  tgl_p[2].f=x;
  tgl_p[3].f=y;
  tgl_p[4].f=z;
  tgl_add_op(tgl_p);
}

void tglopTranslate(TGLContext *c,TGLParam *p) {
  float x=p[1].f;
  float y=p[2].f;
  float z=p[3].f;
  float * m = &c->matrix_stack_ptr[c->matrix_mode]->m[0][0];
  m[ 3] = x*m[ 0] + y*m[ 1] + z*m[ 2] + m[ 3];
  m[ 7] = x*m[ 4] + y*m[ 5] + z*m[ 6] + m[ 7];
  m[11] = x*m[ 8] + y*m[ 9] + z*m[10] + m[11];
  m[15] = x*m[12] + y*m[13] + z*m[14] + m[15];
  c->iModelProjectionNotUpToDate=(c->matrix_mode<2);
}

void glTranslatef(float x,float y,float z) {
  tgl_p[0].o=OP_Translate;
  tgl_p[1].f=x;
  tgl_p[2].f=y;
  tgl_p[3].f=z;
  tgl_add_op(tgl_p);
}

void tglopScale(TGLContext *c, TGLParam *p) {
  float x=p[1].f;
  float y=p[2].f;
  float z=p[3].f;
  float * m = &c->matrix_stack_ptr[c->matrix_mode]->m[0][0];
  m[ 0] *= x; m[ 1] *= y; m[ 2] *= z;
  m[ 4] *= x; m[ 5] *= y; m[ 6] *= z;
  m[ 8] *= x; m[ 9] *= y; m[10] *= z;
  m[12] *= x; m[13] *= y; m[14] *= z;
  c->iModelProjectionNotUpToDate=(c->matrix_mode<2);
}
void glScalef(float x,float y,float z) {
  tgl_p[0].o=OP_Scale;
  tgl_p[1].f=x;
  tgl_p[2].f=y;
  tgl_p[3].f=z;
  tgl_add_op(tgl_p);
}

void tglopFrustum(TGLContext *c,TGLParam *p) {
  M4 m;
  float l = p[1].f;
  float r = p[2].f;
  float b = p[3].f;
  float t = p[4].f;
  float n = p[5].f;
  float f = p[6].f;

  float rl = r-l;
  float tb = t-b;
  float fn = f-n;
  float n2 = 2.0f*n;
  float x = n2 / rl;
  float y = n2 / tb;
  float A = (r+l) / rl;
  float B = (t+b) / tb;
  float C = -(f+n) / fn;
  float D = -(f*n2) / fn;

  float * pr=&m.m[0][0];
  pr[ 0]= x; pr[ 1]=0; pr[ 2]= A; pr[ 3]=0;
  pr[ 4]= 0; pr[ 5]=y; pr[ 6]= B; pr[ 7]=0;
  pr[ 8]= 0; pr[ 9]=0; pr[10]= C; pr[11]=D;
  pr[12]= 0; pr[13]=0; pr[14]=-1; pr[15]=0;

  tgl_M4_MulLeft(c->matrix_stack_ptr[c->matrix_mode],&m);

  c->iModelProjectionNotUpToDate=(c->matrix_mode<2);
}
void glFrustum(double left,double right,double bottom,double top, double near,double farv){
  tgl_p[0].o=OP_Frustum;
  tgl_p[1].f=left;
  tgl_p[2].f=right;
  tgl_p[3].f=bottom;
  tgl_p[4].f=top;
  tgl_p[5].f=near;
  tgl_p[6].f=farv;
  tgl_add_op(tgl_p);
}

void tglopLight(TGLContext *c,TGLParam *p) {
  V4 v;
  TGLLight *l;
  int i,n,type;
  int light=p[1].i;
  if (light<GL_LIGHT0 || light>GL_LIGHT0+7 ){
    glError=GL_INVALID_ENUM;
    return;
  }

  n=light-GL_LIGHT0;
  l=&c->lights[n];

  v.v[0]=p[3].f;
  v.v[1]=p[4].f;
  v.v[2]=p[5].f;
  v.v[3]=p[6].f;

  type=p[2].i;

  switch(type) {
  case GL_AMBIENT:
    l->ambient=v;
    break;

  case GL_DIFFUSE:
    l->diffuse=v;
    break;

  case GL_SPECULAR:
    l->specular=v;
    break;

  case GL_POSITION:
    {
      V4 pos;
      tgl_M4_MulV4(&pos,c->matrix_stack_ptr[0],&v);

      l->position=pos;

      if (l->position.v[3] == 0) {
        l->norm_position.v[0]=pos.v[0];
        l->norm_position.v[1]=pos.v[1];
        l->norm_position.v[2]=pos.v[2];
        tgl_V3_Norm(&l->norm_position);
      }
    }
    break;

  case GL_SPOT_DIRECTION:
    for(i=0;i<3;i++) {
      l->spot_direction.v[i]=v.v[i];
      l->norm_spot_direction.v[i]=v.v[i];
    }
    tgl_V3_Norm(&l->norm_spot_direction);
    break;

  case GL_SPOT_EXPONENT:
    l->spot_exponent=v.v[0];
    break;

  case GL_SPOT_CUTOFF:
    {
      float a=v.v[0];
      l->spot_cutoff=a;
      if (a != 180)
        l->cos_spot_cutoff=cosf(a * 3.141592654 / 180.0);
    }
    break;

  case GL_CONSTANT_ATTENUATION:
    l->attenuation[0]=v.v[0];
    break;

  case GL_LINEAR_ATTENUATION:
    l->attenuation[1]=v.v[0];
    break;

  case GL_QUADRATIC_ATTENUATION:
    l->attenuation[2]=v.v[0];
    break;

  default:
    glError=GL_INVALID_ENUM;
  }
}

void glLightfv(int light,int type,float *v){
  tgl_p[0].o=OP_Light;
  tgl_p[1].i=light;
  tgl_p[2].i=type;
  tgl_p[3].f=v[0];
  tgl_p[4].f=v[1];
  tgl_p[5].f=v[2];
  tgl_p[6].f=v[3];
  tgl_add_op(tgl_p);
}

void glLightf(int light,int type,float v){
  tgl_p[0].o=OP_Light;
  tgl_p[1].i=light;
  tgl_p[2].i=type;
  tgl_p[3].f=v;
  tgl_p[4].f=0.0f;
  tgl_p[5].f=0.0f;
  tgl_p[6].f=0.0f;
  tgl_add_op(tgl_p);
}

void tglopLightModel(TGLContext *c, TGLParam *p) {
  int pname=p[1].i;
  float *v=&p[2].f;
  int i;

  switch(pname) {
  case GL_LIGHT_MODEL_AMBIENT:
    for(i=0;i<4;i++)
      c->ambient_light_model.v[i]=v[i];
    break;
  case GL_LIGHT_MODEL_LOCAL_VIEWER:
    c->local_light_model=(int)v[0];
    break;
  case GL_LIGHT_MODEL_TWO_SIDE:
    c->light_model_two_side = (int)v[0];
    break;
  default:
    glError=GL_INVALID_ENUM;
    break;
  }
}
void glLightModeli(int pname, int param) {
 tgl_p[0].o=OP_LightModel;
  tgl_p[1].i=pname;
  tgl_p[2].f=(float)param;
  tgl_p[3].f=0.0f;
  tgl_p[4].f=0.0f;
  tgl_p[5].f=0.0f;
  tgl_p[6].f=0.0f;
  tgl_add_op(tgl_p);
}
void glLightModelfv(int pname, float *param) {
  tgl_p[0].o=OP_LightModel;
  tgl_p[1].i=pname;
  tgl_p[3].f=param[0];
  tgl_p[4].f=param[1];
  tgl_p[5].f=param[2];
  tgl_p[6].f=param[3];
  tgl_add_op(tgl_p);
}

void tglopClear(TGLContext *c, TGLParam *p) {
  int mask=p[1].i;
  int z=0;
  int r=(int)(c->clear_color.v[0]*65535);
  int g=(int)(c->clear_color.v[1]*65535);
  int b=(int)(c->clear_color.v[2]*65535);
  if ((mask & GL_DEPTH_BUFFER_BIT) || (mask & GL_COLOR_BUFFER_BIT))
    ZB_clear(c->zb,mask & GL_DEPTH_BUFFER_BIT,z, mask & GL_COLOR_BUFFER_BIT,r,g,b);
  else
    glError=GL_INVALID_ENUM;
}
void glClear(int mask){
  tgl_p[0].o=OP_Clear;
  tgl_p[1].i=mask;
  tgl_add_op(tgl_p);
}

void tglopClearColor(TGLContext *c,TGLParam *p) {
  c->clear_color.v[0] = p[1].f;
  c->clear_color.v[1] = p[2].f;
  c->clear_color.v[2] = p[3].f;
  c->clear_color.v[3] = p[4].f;
}
void glClearColor(float r, float g, float b, float a){
  tgl_p[0].o=OP_ClearColor;
  tgl_p[1].f=r;
  tgl_p[2].f=g;
  tgl_p[3].f=b;
  tgl_p[4].f=a;
  tgl_add_op(tgl_p);
}

void glGenTextures(int n, unsigned int *textures){
  int i,imax = 0;
  if ( (n<0) || (textures==((void *)0)) ) {
    glError=GL_INVALID_VALUE;
    return;
  }

  for(i=0; i<256; i++) {
    TGLTexture * t = tgl_ctx->shared_state.texture_hash_table[i];
    while (t != ((void *)0)) {
      if (t->handle > imax)
        imax = t->handle;
      t = t->next;
    }
  }
  for(i=0; i<n; i++) {
    textures[i] = imax+i+1;
  }
}

void glDeleteTextures(int n, const unsigned int *textures) {
  int i;
  if ( (n<0) || (textures==((void *)0)) ) {
    glError=GL_INVALID_VALUE;
    return;
  }

  for (i=0; i<n; i++) {
    TGLTexture * t = tgl_find_texture(tgl_ctx, textures[i]);
    if (t != ((void *)0)) {
      if (t==tgl_ctx->current_texture) {
        glBindTexture(GL_TEXTURE_2D,0);
      }
      tgl_free_texture(tgl_ctx,textures[i]);
    }
  }
}

void tglopBindTexture(TGLContext *c, TGLParam *p) {
  int target=p[1].i;
  int texture=p[2].i;
                                                ;
  if (target != GL_TEXTURE_2D) {
    glError=GL_INVALID_ENUM;
    return;
  }
  TGLTexture *t = tgl_find_texture(c,texture);
  if (t==((void *)0)) {
    t=tgl_alloc_texture(c,texture);
  }
  c->current_texture=t;
}
void glBindTexture(int target,int texture){
  tgl_p[0].o=OP_BindTexture;
  tgl_p[1].i=target;
  tgl_p[2].i=texture;
  tgl_add_op(tgl_p);
}

void tglopTexImage2D(TGLContext *c,TGLParam *p) {
  int target    =p[1].i;
  int level     =p[2].i;
  int components=p[3].i;
  int width     =p[4].i;
  int height    =p[5].i;
  /*  int border    =p[6].i; ignored */
  int format    =p[7].i;
  int type      =p[8].i;
  void *pixels  =p[9].p;
  unsigned char *pixels1;
  int do_free;

  if ((target != GL_TEXTURE_2D) || (format != GL_RGB) || (type != GL_UNSIGNED_BYTE) ) {
    glError=GL_INVALID_ENUM;
    return;
  }

  if (!(width > 0 && height > 0)) {
    glError=GL_INVALID_VALUE;
    return;
  }

  if (!(components>2 && components<5)) {
    glError=GL_INVALID_VALUE;
    return;
  }

  if (!(level==0)) {
    glError=GL_INVALID_VALUE;
    return;
  }

  do_free=0;

  if (width != 256 || height != 256 || components !=3) {
    pixels1 = tgl_malloc(256 * 256 * 3);
    if (pixels1==((void *)0)){
      glError=GL_OUT_OF_MEMORY;
      return;
    }

    if (components==3) {
      tgl_resizeImageNoInterpolateRGB_RGB(pixels1,256,256,pixels,width,height);
    } else {
      tgl_resizeImageNoInterpolateRGBA_RGB(pixels1,256,256,pixels,width,height);
    }
    do_free = 1;
    width = 256;
    height = 256;
  }
  else
  {
    pixels1 = pixels;
  }

  TGLImage *im = &c->current_texture->images[level];
  im->xsize = width;
  im->ysize = height;

  if (im->pixmap != ((void *)0)){
    tgl_free(im->pixmap);
  }
  im->pixmap=tgl_malloc(width*height*2);
  if (im->pixmap==((void *)0)){
    if (do_free) {
      tgl_free(pixels1);
    }
    glError=GL_OUT_OF_MEMORY;
    return;
  }
  if (im->pixmap) {
    tgl_convertRGB_to_5R6G5B(im->pixmap,pixels1,width,height);
  }

  if (do_free) {
    tgl_free(pixels1);
  }
}
void glTexImage2D( int target, int level, int components,
                   int width, int height, int border,
                   int format, int type, void *pixels){
  tgl_p[0].o=OP_TexImage2D;
  tgl_p[1].i=target;
  tgl_p[2].i=level;  /* must be 0 */
  tgl_p[3].i=components;
  tgl_p[4].i=width;
  tgl_p[5].i=height;
  tgl_p[6].i=border;  /* ignored */
  tgl_p[7].i=format;
  tgl_p[8].i=type;
  tgl_p[9].p=pixels;
  tgl_add_op(tgl_p);
}


int glRenderMode(int mode) {
  int result=0;
  switch(tgl_ctx->render_mode) {
  case GL_RENDER:
    break;
  case GL_SELECT:
    if (tgl_ctx->select_overflow) {
      result=-tgl_ctx->select_hits;
    } else {
      result=tgl_ctx->select_hits;
    }
    tgl_ctx->select_overflow=0;
    tgl_ctx->select_ptr=tgl_ctx->select_buffer;
    tgl_ctx->name_stack_size=0;
    break;
  }

  switch(mode) {
  case GL_RENDER:
    tgl_ctx->render_mode=GL_RENDER;
    break;
  case GL_SELECT:
    tgl_ctx->render_mode=GL_SELECT;
    tgl_ctx->select_ptr=tgl_ctx->select_buffer;
    tgl_ctx->select_hits=0;
    tgl_ctx->select_overflow=0;
    tgl_ctx->select_hit=((void *)0);
    break;
  default:
    glError=GL_INVALID_ENUM;
  }
  return result;
}

void glSelectBuffer(int isize, unsigned int *buf) {
  if (tgl_ctx->render_mode != GL_SELECT) {
    glError=GL_INVALID_OPERATION;
    return;
  }
  tgl_ctx->select_buffer=buf;
  tgl_ctx->select_size=isize;
}

void tglopInitNames(TGLContext *c, TGLParam *p){
  if (c->render_mode == GL_SELECT) {
    c->name_stack_size=0;
    c->select_hit=((void *)0);
  }
}
void glInitNames(void){
  tgl_p[0].o=OP_InitNames;
  tgl_add_op(tgl_p);
}

void tglopPushName(TGLContext *c, TGLParam *p){
  if (c->render_mode != GL_SELECT) {
    glError=GL_INVALID_OPERATION;
    return;
  }
  if (c->name_stack_size>63) {
    glError=GL_STACK_OVERFLOW;
    return;
  }
  c->name_stack[c->name_stack_size++]=p[1].i;
  c->select_hit=((void *)0);
}
void glPushName(unsigned int name){
  tgl_p[0].o=OP_PushName;
  tgl_p[1].i=name;
  tgl_add_op(tgl_p);
}

void tglopPopName(TGLContext *c, TGLParam *p){
  if (c->render_mode != GL_SELECT) {
    glError=GL_INVALID_OPERATION;
    return;
  }

  if (c->name_stack_size<=0) {
    glError=GL_STACK_UNDERFLOW;
    return;
  }
  c->name_stack_size--;
  c->select_hit=((void *)0);
}
void glPopName(void){
  tgl_p[0].o=OP_PopName;
  tgl_add_op(tgl_p);
}

void tglopLoadName(TGLContext *c, TGLParam *p){
  if (c->render_mode != GL_SELECT) {
    glError=GL_INVALID_OPERATION;
    return;
  }
  if (c->name_stack_size<=0) {
    glError=GL_STACK_UNDERFLOW;
    return;
  }
  c->name_stack[c->name_stack_size-1]=p[1].i;
  c->select_hit=((void *)0);
}
void glLoadName(unsigned int name){
  tgl_p[0].o=OP_LoadName;
  tgl_p[1].i=name;
  tgl_add_op(tgl_p);
}

void glFlush(void){
}

void glDepthFunc(int func) {
}

void tglopHint(TGLContext *c, TGLParam *p) {
  c->perspective_hint=p[2].i;
}
void glHint(int target,int mode){
  if ((target != GL_PERSPECTIVE_CORRECTION_HINT) || (mode != GL_FASTEST && mode != GL_DONT_CARE && mode != GL_NICEST)) {
    glError=GL_INVALID_ENUM;
    return;
  }
  tgl_p[0].o=OP_Hint;
  tgl_p[1].i=target;
  tgl_p[2].i=mode;
  tgl_add_op(tgl_p);
}

void tglopArrayElement(TGLContext *c, TGLParam *param) {
  float f1=0.0f,f2=0.0f,f3=0.0f,f4=0.1f;
  void * p;
  signed char * sp8;
  unsigned char * up8;
  signed short * sp16;
  unsigned short * up16;
  signed int * sp32;
  unsigned int * up32;
  float * fp;
  float * dp;
  int states = c->client_states;
  int idx = param[1].i;

  if (states & (1 << 1)) {

    p = c->color_array;
    switch (c->color_array_type) {
      case GL_FLOAT:
        p += idx * (c->color_array_size*sizeof(float) + c->color_array_stride);
        fp = p;
        f1 = fp[0];
        f2 = fp[1];
        f3 = fp[2];
        f4 = c->color_array_size > 3 ? fp[3] : 1.0f;
        break;
      case GL_BYTE:

        p += idx * (c->color_array_size + c->color_array_stride);
        sp8=p;
        f1 = (float)sp8[0]; if((f1)<0) (f1)*=1.0f/(float)0x80; else (f1)*=1.0f/(float)0x7f;;
        f2 = (float)sp8[1]; if((f2)<0) (f2)*=1.0f/(float)0x80; else (f2)*=1.0f/(float)0x7f;;
        f3 = (float)sp8[2]; if((f3)<0) (f3)*=1.0f/(float)0x80; else (f3)*=1.0f/(float)0x7f;;
        f4 = c->color_array_size > 3 ? (float)sp8[3] : (float)0x80f; if((f4)<0) (f4)*=1.0f/(float)0x80; else (f4)*=1.0f/(float)0x7f;;

        break;
      case GL_UNSIGNED_BYTE:

        p += idx * (c->color_array_size + c->color_array_stride);
        up8=p;
        f1 = (float)up8[0]; (f1)*=1.0f/(float)0xff;;
        f2 = (float)up8[1]; (f2)*=1.0f/(float)0xff;;
        f3 = (float)up8[2]; (f3)*=1.0f/(float)0xff;;
        f4 = c->color_array_size > 3 ? (float)up8[3] : (float)0xff; (f4)*=1.0f/(float)0xff;;

        break;
      case GL_SHORT:

        p += idx * (c->color_array_size*sizeof(short) + c->color_array_stride);
        sp16=p;
        f1 = (float)sp16[0]; if((f1)<0) (f1)*=1.0f/(float)0x8000; else (f1)*=1.0f/(float)0x7fff;;
        f2 = (float)sp16[1]; if((f2)<0) (f2)*=1.0f/(float)0x8000; else (f2)*=1.0f/(float)0x7fff;;
        f3 = (float)sp16[2]; if((f3)<0) (f3)*=1.0f/(float)0x8000; else (f3)*=1.0f/(float)0x7fff;;
        f4 = c->color_array_size > 3 ? (float)sp16[3] : (float)0x7fff; if((f4)<0) (f4)*=1.0f/(float)0x8000; else (f4)*=1.0f/(float)0x7fff;;

        break;
      case GL_UNSIGNED_SHORT:

        p += idx * (c->color_array_size*sizeof(unsigned short) + c->color_array_stride);
        up16 = p;
        f1 = (float)up16[0]; (f1)*=1.0f/(float)0xffff;
        f2 = (float)up16[1]; (f2)*=1.0f/(float)0xffff;
        f3 = (float)up16[2]; (f3)*=1.0f/(float)0xffff;
        f4 = c->color_array_size > 3 ? (float)up16[3] : (float)0xffff; (f4)*=1.0f/(float)0xffff;

        break;
      case GL_INT:

        p += idx * (c->color_array_size*sizeof(int) + c->color_array_stride);
        sp32 = p;
        f1 = (float)sp32[0]; if((f1)<0) (f1)*=1.0f/(float)0x80000000; else (f1)*=1.0f/(float)0x7fffffff;;
        f2 = (float)sp32[1]; if((f2)<0) (f2)*=1.0f/(float)0x80000000; else (f2)*=1.0f/(float)0x7fffffff;;
        f3 = (float)sp32[2]; if((f3)<0) (f3)*=1.0f/(float)0x80000000; else (f3)*=1.0f/(float)0x7fffffff;;
        f4 = c->color_array_size > 3 ? (float)sp32[3] : (float)0x7fffffff; if((f4)<0) (f4)*=1.0f/(float)0x80000000; else (f4)*=1.0f/(float)0x7fffffff;;

        break;
      case GL_UNSIGNED_INT:

        p += idx * (c->color_array_size*sizeof(unsigned int) + c->color_array_stride);
        up32 = p;
        f1 = (float)up32[0]; (f1)*=1.0f/(float)0xffffffff;;
        f2 = (float)up32[1]; (f2)*=1.0f/(float)0xffffffff;;
        f3 = (float)up32[2]; (f3)*=1.0f/(float)0xffffffff;;
        f4 = c->color_array_size > 3 ? (float)up32[3] : (float)0xffffffff; (f4)*=1.0f/(float)0xffffffff;;

        break;
      case GL_DOUBLE:
        p += idx * (c->color_array_size*sizeof(double) + c->color_array_stride);
        dp = p;
        f1 = (float)dp[0];
        f2 = (float)dp[1];
        f3 = (float)dp[2];
        f4 = c->color_array_size > 3 ? (float)dp[3] : 1.0f;
      break;
    }
    tgl_p[1].f = f1;
    tgl_p[2].f = f2;
    tgl_p[3].f = f3;
    tgl_p[4].f = f4;
    tgl_p[5].c = (unsigned int) (f1 * (( (1<<16)-(1<<10) ) - ( (1<<10) )) + ( (1<<10) ));
    tgl_p[6].c = (unsigned int) (f2 * (( (1<<16)-(1<<9) ) - ( (1<<9) )) + ( (1<<9) ));
    tgl_p[7].c = (unsigned int) (f3 * (( (1<<16)-(1<<10) ) - ( (1<<10) )) + ( (1<<10) ));
    tglopColor(c, tgl_p);
  }

  if (states & (1 << 2)) {

    p = c->normal_array;
    switch (c->normal_array_type) {
      case GL_FLOAT:
        p += idx * (3*sizeof(float) + c->normal_array_stride);
        fp = p;
        f1 = fp[0];
        f2 = fp[1];
        f3 = fp[2];
        break;
      case GL_BYTE:

        p += idx * (3*sizeof(signed char) + c->normal_array_stride);
        sp8=p;
        f1 = (float)sp8[0]; if((f1)<0) (f1)*=1.0f/(float)0x80; else (f1)*=1.0f/(float)0x7f;;
        f2 = (float)sp8[1]; if((f2)<0) (f2)*=1.0f/(float)0x80; else (f2)*=1.0f/(float)0x7f;;
        f3 = (float)sp8[2]; if((f3)<0) (f3)*=1.0f/(float)0x80; else (f3)*=1.0f/(float)0x7f;;

        break;
      case GL_SHORT:

        p += idx * (3*sizeof(short) + c->normal_array_stride);
        sp16=p;
        f1 = (float)sp16[0]; if((f1)<0) (f1)*=1.0f/(float)0x8000; else (f1)*=1.0f/(float)0x7fff;;
        f2 = (float)sp16[1]; if((f2)<0) (f2)*=1.0f/(float)0x8000; else (f2)*=1.0f/(float)0x7fff;;
        f3 = (float)sp16[2]; if((f3)<0) (f3)*=1.0f/(float)0x8000; else (f3)*=1.0f/(float)0x7fff;;

        break;
      case GL_INT:

        p += idx * (3*sizeof(int) + c->normal_array_stride);
        sp32 = p;
        f1 = (float)sp32[0]; if((f1)<0) (f1)*=1.0f/(float)0x80000000; else (f1)*=1.0f/(float)0x7fffffff;;
        f2 = (float)sp32[1]; if((f2)<0) (f2)*=1.0f/(float)0x80000000; else (f2)*=1.0f/(float)0x7fffffff;;
        f3 = (float)sp32[2]; if((f3)<0) (f3)*=1.0f/(float)0x80000000; else (f3)*=1.0f/(float)0x7fffffff;;

        break;
      case GL_DOUBLE:
        p += idx * (3*sizeof(double) + c->normal_array_stride);
        dp = p;
        f1 = (float)dp[0];
        f2 = (float)dp[1];
        f3 = (float)dp[2];
        break;
    }
    c->current_normal.v[0] = f1;
    c->current_normal.v[1] = f2;
    c->current_normal.v[2] = f3;
    c->current_normal.v[3] = 0.0f;
  }

  if (states & (1 << 3)) {

    p = c->texcoord_array;
    switch (c->texcoord_array_type) {
      case GL_FLOAT:
        p += idx * (c->texcoord_array_size*sizeof(float) + c->texcoord_array_stride);
        fp = p;
        f1 = fp[0];
        f2 = c->texcoord_array_size > 1 ? fp[1] : 0.0f;
        f3 = c->texcoord_array_size > 2 ? fp[2] : 0.0f;
        f4 = c->texcoord_array_size > 3 ? fp[3] : 1.0f;
        break;
      case GL_SHORT:

        p += idx * (c->texcoord_array_size*sizeof(short) + c->texcoord_array_stride);
        sp16=p;
        f1 = (float)sp16[0]; if((f1)<0) (f1)*=1.0f/(float)0x8000; else (f1)*=1.0f/(float)0x7fff;;
        f2 = c->texcoord_array_size > 1 ? (float)sp16[1] : 0.0f; if((f2)<0) (f2)*=1.0f/(float)0x8000; else (f2)*=1.0f/(float)0x7fff;;
        f3 = c->texcoord_array_size > 2 ? (float)sp16[2] : 0.0f; if((f3)<0) (f3)*=1.0f/(float)0x8000; else (f3)*=1.0f/(float)0x7fff;;
        f4 = c->texcoord_array_size > 3 ? (float)sp16[3] : (float)0x7fff; if((f4)<0) (f4)*=1.0f/(float)0x8000; else (f4)*=1.0f/(float)0x7fff;;

        break;
      case GL_INT:

        p += idx * (c->color_array_size*sizeof(int) + c->texcoord_array_stride);
        sp32 = p;
        f1 = (float)sp32[0]; if((f1)<0) (f1)*=1.0f/(float)0x80000000; else (f1)*=1.0f/(float)0x7fffffff;;
        f2 = c->texcoord_array_size > 1 ? (float)sp32[1] : 0.0f; if((f2)<0) (f2)*=1.0f/(float)0x80000000; else (f2)*=1.0f/(float)0x7fffffff;;
        f3 = c->texcoord_array_size > 2 ? (float)sp32[2] : 0.0f; if((f3)<0) (f3)*=1.0f/(float)0x80000000; else (f3)*=1.0f/(float)0x7fffffff;;
        f4 = c->texcoord_array_size > 3 ? (float)sp32[3] : (float)0x7fff; if((f4)<0) (f4)*=1.0f/(float)0x80000000; else (f4)*=1.0f/(float)0x7fffffff;;

        break;
      case GL_DOUBLE:
        p += idx * (c->texcoord_array_size*sizeof(double) + c->texcoord_array_stride);
        dp = p;
        f1 = (float)dp[0];
        f2 = c->texcoord_array_size > 1 ? (float)dp[1] : 0.0f;
        f3 = c->texcoord_array_size > 2 ? (float)dp[2] : 0.0f;
        f4 = c->texcoord_array_size > 3 ? (float)dp[3] : 1.0f;
        break;
    }
    c->current_tex_coord.v[0] = f1;
    c->current_tex_coord.v[1] = f2;
    c->current_tex_coord.v[2] = f3;
    c->current_tex_coord.v[3] = f4;
  }

  if (states & (1 << 4)) {
    p = c->edgeflag_array;
    p += idx * (sizeof(int) + c->edgeflag_array_stride);
    sp32=p;
    c->current_edge_flag = *sp32;
  }

  if (states & (1 << 0)) {

    p = c->vertex_array;
    switch (c->vertex_array_type) {
      case GL_FLOAT:
        p += idx * (c->vertex_array_size*sizeof(float) + c->vertex_array_stride);
        fp = p;
        f1 = fp[0];
        f2 = fp[1];
        f3 = c->vertex_array_size > 2 ? fp[2] : 0.0f;
        f4 = c->vertex_array_size > 3 ? fp[3] : 1.0f;
        break;
      case GL_SHORT:

        p += idx * (c->vertex_array_size*sizeof(short) + c->vertex_array_stride);
        sp16=p;
        f1 = (float)sp16[0]; if((f1)<0) (f1)*=1.0f/(float)0x8000; else (f1)*=1.0f/(float)0x7fff;;
        f2 = (float)sp16[1]; if((f2)<0) (f2)*=1.0f/(float)0x8000; else (f2)*=1.0f/(float)0x7fff;;
        f3 = c->vertex_array_size > 2 ? (float)sp16[2] : 0.0f; if((f3)<0) (f3)*=1.0f/(float)0x8000; else (f3)*=1.0f/(float)0x7fff;;
        f4 = c->vertex_array_size > 3 ? (float)sp16[3] : (float)0x7fff; if((f4)<0) (f4)*=1.0f/(float)0x8000; else (f4)*=1.0f/(float)0x7fff;;

        break;
      case GL_INT:

        p += idx * (c->vertex_array_size*sizeof(int) + c->vertex_array_stride);
        sp32 = p;
        f1 = (float)sp32[0]; if((f1)<0) (f1)*=1.0f/(float)0x80000000; else (f1)*=1.0f/(float)0x7fffffff;;
        f2 = (float)sp32[1]; if((f2)<0) (f2)*=1.0f/(float)0x80000000; else (f2)*=1.0f/(float)0x7fffffff;;
        f3 = c->vertex_array_size > 2 ? (float)sp32[2] : 0.0f; if((f3)<0) (f3)*=1.0f/(float)0x80000000; else (f3)*=1.0f/(float)0x7fffffff;;
        f4 = c->vertex_array_size > 3 ? (float)sp32[3] : (float)0x7fff; if((f4)<0) (f4)*=1.0f/(float)0x80000000; else (f4)*=1.0f/(float)0x7fffffff;;

        break;
      case GL_DOUBLE:
        p += idx * (c->vertex_array_size*sizeof(double) + c->vertex_array_stride);
        dp = p;
        f1 = (float)dp[0];
        f2 = (float)dp[1];
        f3 = c->vertex_array_size > 2 ? (float)dp[2] : 0.0f;
        f4 = c->vertex_array_size > 3 ? (float)dp[3] : 1.0f;
        break;
    }
    tgl_p[1].f = f1;
    tgl_p[2].f = f2;
    tgl_p[3].f = f3;
    tgl_p[4].f = f4;
    tglopVertex(c, tgl_p);
  }
}

void glArrayElement(int idx) {
  if(tgl_ctx->ignore_in_begin==0 && tgl_ctx->in_begin==0){
    glError=GL_INVALID_OPERATION;
    return;
  }
  tgl_p[0].o = OP_ArrayElement;
  tgl_p[1].i = idx;
  tgl_add_op(tgl_p);
}


void tglopDrawArrays(TGLContext *c, TGLParam *param) {
  int imode = param[1].i;
  int i,idx = param[2].i;
  int icount = param[3].i;
  c->ignore_in_begin=1;
  glBegin(imode);
    for (i=0; i<icount; i++) {
      glArrayElement(idx);
      idx += 1;
    }
  glEnd();
  c->ignore_in_begin=0;
}

void glDrawArrays(int imode, int ifirst, int icount){
  if(tgl_ctx->in_begin!=0) {
    glError=GL_INVALID_OPERATION;
    return;
  }

  if(imode!=GL_POINTS &&
     imode!=GL_LINE_STRIP &&
     imode!=GL_LINE_LOOP &&
     imode!=GL_LINES &&
     imode!=GL_TRIANGLE_STRIP &&
     imode!=GL_TRIANGLE_FAN &&
     imode!=GL_TRIANGLES &&
     imode!=GL_QUAD_STRIP &&
     imode!=GL_QUADS &&
     imode!=GL_POLYGON) {
    glError=GL_INVALID_ENUM;
    return;
  }

  if (icount<1 || ifirst<0) {
    glError=GL_INVALID_VALUE;
    return;
  }

  tgl_p[0].o = OP_DrawArrays;
  tgl_p[1].i = imode;
  tgl_p[2].i = ifirst;
  tgl_p[3].i = icount;
  tgl_add_op(tgl_p);
}

void tglopEnableClientState(TGLContext *c, TGLParam *p) {
  c->client_states |= p[1].i;
}
void glEnableClientState(int earray) {
  if(tgl_ctx->in_begin!=0){
    glError=GL_INVALID_OPERATION;
    return;
  }
  switch(earray) {
  case GL_VERTEX_ARRAY:
    tgl_p[1].i = (1 << 0);
    break;
  case GL_NORMAL_ARRAY:
    tgl_p[1].i = (1 << 2);
    break;
  case GL_COLOR_ARRAY:
    tgl_p[1].i = (1 << 1);
    break;
  case GL_TEXTURE_COORD_ARRAY:
    tgl_p[1].i = (1 << 3);
    break;
  case GL_EDGE_FLAG_ARRAY:
    tgl_p[1].i = (1 << 4);
    break;
  default:
    glError=GL_INVALID_ENUM;
    return;
  }
  tgl_p[0].o = OP_EnableClientState;
  tgl_add_op(tgl_p);
}

void tglopDisableClientState(TGLContext *c, TGLParam *p) {
  c->client_states &= p[1].i;
}

void glDisableClientState(int earray) {
  if(tgl_ctx->in_begin!=0){
    glError=GL_INVALID_OPERATION;
    return;
  }
  switch(earray) {
  case GL_VERTEX_ARRAY:
    tgl_p[1].i = ~(1 << 0);
    break;
  case GL_NORMAL_ARRAY:
    tgl_p[1].i = ~(1 << 2);
    break;
  case GL_COLOR_ARRAY:
    tgl_p[1].i = ~(1 << 1);
    break;
  case GL_TEXTURE_COORD_ARRAY:
    tgl_p[1].i = ~(1 << 3);
    break;
  case GL_EDGE_FLAG_ARRAY:
    tgl_p[1].i = ~(1 << 4);
    break;
  default:
    glError=GL_INVALID_ENUM;
    return;
  }
  tgl_p[0].o = OP_DisableClientState;
  tgl_add_op(tgl_p);
}

void tglopVertexPointer(TGLContext *c, TGLParam *p) {
  c->vertex_array_size = p[1].i;
  c->vertex_array_type = p[2].i;
  c->vertex_array_stride = p[3].i;
  c->vertex_array = p[4].p;
}
void glVertexPointer(int isize, int itype, int istride, const void *pointer) {
  if(tgl_ctx->in_begin!=0){
    glError=GL_INVALID_OPERATION;
    return;
  }
  if (istride<0 || pointer==((void *)0)){
    glError = GL_INVALID_VALUE;
    return;
  }
  if ((itype != GL_SHORT) && (itype != GL_INT) &&
      (itype != GL_FLOAT) && (itype != GL_DOUBLE)) {
    glError=GL_INVALID_ENUM;
    return;
  }
  if (isize<2 || isize>4) {
    glError=GL_INVALID_VALUE;
    return;
  }
  tgl_p[0].o = OP_VertexPointer;
  tgl_p[1].i = isize;
  tgl_p[2].i = itype;
  tgl_p[3].i = istride;
  tgl_p[4].p = (void*)pointer;
  tgl_add_op(tgl_p);
}

void tglopColorPointer(TGLContext *c, TGLParam *p) {
  c->color_array_size = p[1].i;
  c->color_array_type = p[2].i;
  c->color_array_stride = p[3].i;
  c->color_array = p[4].p;
}
void glColorPointer(int isize, int itype, int istride, const void *pointer){
  if(tgl_ctx->in_begin!=0){
    glError=GL_INVALID_OPERATION;
    return;
  }
  if (istride<0 || pointer==((void *)0)){
    glError = GL_INVALID_VALUE;
    return;
  }
  if ((itype != GL_BYTE) && (itype != GL_UNSIGNED_BYTE) &&
      (itype != GL_SHORT) && (itype != GL_UNSIGNED_SHORT) &&
      (itype != GL_INT) && (itype != GL_UNSIGNED_INT) &&
      (itype != GL_FLOAT) && (itype != GL_DOUBLE)) {
    glError=GL_INVALID_ENUM;
    return;
  }

  if ( isize<3 || isize>4) {
    glError=GL_INVALID_VALUE;
    return;
  }
  tgl_p[0].o = OP_ColorPointer;
  tgl_p[1].i = isize;
  tgl_p[2].i = itype;
  tgl_p[3].i = istride;
  tgl_p[4].p = (void*)pointer;
  tgl_add_op(tgl_p);
}

void tglopNormalPointer(TGLContext *c, TGLParam *p) {
  c->normal_array_type = p[1].i;
  c->normal_array_stride = p[2].i;
  c->normal_array = p[3].p;
}
void glNormalPointer(int itype, int istride, const void *pointer) {
  if(tgl_ctx->in_begin!=0){
    glError=GL_INVALID_OPERATION;
    return;
  }
  if (istride<0 || pointer==((void *)0)){
    glError = GL_INVALID_VALUE;
    return;
  }

  if ((itype != GL_BYTE) &&
      (itype != GL_SHORT) &&
      (itype != GL_INT) &&
      (itype != GL_FLOAT) &&
      (itype != GL_DOUBLE)) {
    glError=GL_INVALID_ENUM;
    return;
  }
  tgl_p[0].o = OP_NormalPointer;
  tgl_p[1].i = itype;
  tgl_p[2].i = istride;
  tgl_p[3].p = (void*)pointer;
  tgl_add_op(tgl_p);
}

void tglopTexCoordPointer(TGLContext *c, TGLParam *p) {
  c->texcoord_array_size = p[1].i;
  c->texcoord_array_type = p[2].i;
  c->texcoord_array_stride = p[3].i;
  c->texcoord_array = p[4].p;
}

void glTexCoordPointer(int isize, int itype, int istride, const void *pointer) {
  if(tgl_ctx->in_begin!=0){
    glError=GL_INVALID_OPERATION;
    return;
  }
  if (istride<0 || pointer==((void *)0)){
    glError = GL_INVALID_VALUE;
    return;
  }

  if ((itype != GL_BYTE) &&
      (itype != GL_SHORT) &&
      (itype != GL_INT) &&
      (itype != GL_FLOAT) &&
      (itype != GL_DOUBLE)) {
    glError=GL_INVALID_ENUM;
    return;
  }
  if (isize<1 || isize>4){
    glError=GL_INVALID_VALUE;
    return;
  }
  tgl_p[0].o = OP_TexCoordPointer;
  tgl_p[1].i = isize;
  tgl_p[2].i = itype;
  tgl_p[3].i = istride;
  tgl_p[4].p = (void*)pointer;
  tgl_add_op(tgl_p);
}

void tglopEdgeFlagPointer(TGLContext *c, TGLParam *p) {
  c->edgeflag_array_stride = p[1].i;
  c->edgeflag_array = p[2].p;
}
void glEdgeFlagPointer(int istride, const void *pointer) {
  if(tgl_ctx->in_begin!=0){
    glError=GL_INVALID_OPERATION;
    return;
  }
  if (istride<0 || pointer==((void *)0)){
    glError = GL_INVALID_VALUE;
    return;
  }
  tgl_p[0].o = OP_EdgeFlagPointer;
  tgl_p[1].i = istride;
  tgl_p[2].p = (void*)pointer;
  tgl_add_op(tgl_p);
}



static TGLList *tgl_find_list(TGLContext *c, unsigned int iList) {
  return c->shared_state.lists[iList];
}

static void tgl_delete_list(TGLContext *c, int iList){
  TGLList *l = tgl_find_list(c,iList);
  if (l == ((void *)0)) {
    glError = GL_INVALID_OPERATION;
    return;
  }
  TGLParamBuffer *pb = l->first_op_buffer;
  while (pb != ((void *)0)) {
    TGLParamBuffer *pNext = pb->next;
    tgl_free(pb);
    pb=pNext;
  }
  tgl_free(l);
  c->shared_state.lists[iList]=((void *)0);
}

static TGLList * tgl_alloc_list(TGLContext *c, int iList){
  TGLList *l = tgl_zalloc(sizeof(TGLList));
  TGLParamBuffer *ob = tgl_zalloc(sizeof(TGLParamBuffer));
  ob->next=((void *)0);
  l->first_op_buffer=ob;
  ob->ops[0].o=OP_EndList;
  c->shared_state.lists[iList]=l;
  return l;
}


void tglopEndList(TGLContext *c,TGLParam *p) {

}


void tglopNextBuffer(TGLContext *c,TGLParam *p) {

}


void tglopCallList(TGLContext *c, TGLParam *p) {
  int ilist=p[1].i;
  TGLList * l = tgl_find_list(c,ilist);
  if (l == ((void *)0)) {
    glError=GL_INVALID_VALUE;
    return;
  }

  p=l->first_op_buffer->ops;
  while (1) {
    int op=p[0].o;
    if (op == OP_EndList)
      break;
    if (op == OP_NextBuffer) {
      p=(TGLParam *)p[1].p;
    }
    else
    {
      op_table_func[op](c,p);
      p+=op_table_size[op];
    }
  }
}

void glCallList(unsigned int ilist){
  tgl_p[0].o=OP_CallList;
  tgl_p[1].i=ilist;
  tgl_add_op(tgl_p);
}

void glNewList(unsigned int ilist, int mode){
  TGLList *l;
  if (tgl_ctx->compile_flag != 0){
    glError=GL_INVALID_OPERATION;
    return;
  }

  if ((mode != GL_COMPILE) && (mode != GL_COMPILE_AND_EXECUTE)) {
    glError = GL_INVALID_ENUM;
    return;
  }

  l=tgl_find_list(tgl_ctx,ilist);

  if (l != ((void *)0))
    tgl_delete_list(tgl_ctx,ilist);

  l = tgl_alloc_list(tgl_ctx,ilist);

  tgl_ctx->current_op_buffer = l->first_op_buffer;
  tgl_ctx->current_op_buffer_index = 0;

  tgl_ctx->compile_flag = 1;
  tgl_ctx->exec_flag = (mode == GL_COMPILE_AND_EXECUTE);
}

void glEndList(void){
  if (tgl_ctx->compile_flag == 0){
    glError = GL_INVALID_OPERATION;
    return;
  }

  tgl_p[0].o = OP_EndList;
  tgl_compile_op(tgl_ctx,tgl_p);

  tgl_ctx->compile_flag = 0;
  tgl_ctx->exec_flag = 1;
}

int glIsList(unsigned int ilist){
  TGLList *l = tgl_find_list(tgl_ctx, ilist);
  return (l != ((void *)0));
}

unsigned int glGenLists(int range){
  int icount=0,i,ilist;
  TGLList ** lists = tgl_ctx->shared_state.lists;
  for (i=0; i<1024; i++) {
    if (lists[i] == ((void *)0)) {
      icount++;
      if (icount == range) {
        ilist=i-range+1;
        for (i=0; i<range; i++) {
          tgl_alloc_list(tgl_ctx,ilist+i);
        }
        return ilist;
      }
    }
    else
    {
      icount=0;
    }
  }
  glError = GL_OUT_OF_MEMORY;
  return 0;
}


void tgl_InitTextures(TGLContext *c) {
  c->texture_2d_enabled = 0;
  c->current_texture = tgl_find_texture(c,0);
}

void tgl_InitSharedState(TGLContext *c) {
  TGLSharedState *s = &c->shared_state;
  s->lists=tgl_zalloc(sizeof(TGLList *) * 1024);
  s->texture_hash_table = tgl_zalloc(sizeof(TGLTexture *) * 256);
  tgl_alloc_texture(c,0);
}

void tgl_EndSharedState(TGLContext *c){
  TGLSharedState *s=&c->shared_state;
  int i;

  for(i=0;i<1024;i++) {

  }
  tgl_free(s->lists);
  tgl_free(s->texture_hash_table);
}

int tgl_IsInit(void){
  return (tgl_ctx != ((void *)0));
}

void tgl_Init(void *zbuffer1) {
  ZBuffer * zbuffer = (ZBuffer *)zbuffer1;
  TGLContext * c = tgl_zalloc(sizeof(TGLContext));
  tgl_ctx = c;
  c->zb = zbuffer;

  c->vertex_max = 32;
  c->vertex = tgl_malloc(32*sizeof(TGLVertex));

  TGLViewport *v = &c->viewport;
  v->iX = 0;
  v->iY = 0;
  v->iWidth = zbuffer->iWidth;
  v->iHeight = zbuffer->iHeight;
  v->iNotUpToDate = 1;

  tgl_InitSharedState(c);

  c->exec_flag = 1;
  c->compile_flag = 0;
  c->in_begin = 0;

  c->ignore_in_begin = 0;

  int i;
  for(i=0; i<8; i++) {
    TGLLight * l = &c->lights[i];
    l->ambient = tgl_V4_New(0,0,0,1);
    l->diffuse = tgl_V4_New(1,1,1,1);
    l->specular = tgl_V4_New(1,1,1,1);
    l->position = tgl_V4_New(0,0,1,0);

    l->norm_position = tgl_V3_New(0,0,1);
    l->spot_direction = tgl_V3_New(0,0,-1);
    l->norm_spot_direction = tgl_V3_New(0,0,-1);

    l->spot_exponent = 0;
    l->spot_cutoff = 180;
    l->attenuation[0] = 1;
    l->attenuation[1] = 0;
    l->attenuation[2] = 0;
    l->enabled = 0;
  }
  c->first_light=((void *)0);
  c->ambient_light_model = tgl_V4_New(0.2,0.2,0.2,1);
  c->local_light_model = 0;
  c->lighting_enabled = 0;
  c->light_model_two_side = 0;

  for (i=0; i<2; i++) {
    TGLMaterial * m = &c->materials[i];
    m->emission = tgl_V4_New(0,0,0,1);
    m->ambient = tgl_V4_New(0.2,0.2,0.2,1);
    m->diffuse = tgl_V4_New(0.8,0.8,0.8,1);
    m->specular = tgl_V4_New(0,0,0,1);
    m->shininess = 0;
  }
  c->current_color_material_mode = GL_FRONT_AND_BACK;
  c->current_color_material_type = GL_AMBIENT_AND_DIFFUSE;
  c->color_material_enabled = 0;


  tgl_InitTextures(c);
  c->blend_enabled=0;

  c->current_color.v[0] = 1.0;
  c->current_color.v[1] = 1.0;
  c->current_color.v[2] = 1.0;
  c->current_color.v[3] = 1.0;

  c->current_ui_color[0] = 65535;
  c->current_ui_color[1] = 65535;
  c->current_ui_color[2] = 65535;

  c->current_normal.v[0] = 1.0;
  c->current_normal.v[1] = 0.0;
  c->current_normal.v[2] = 0.0;
  c->current_normal.v[3] = 0.0;

  c->current_edge_flag = 1;

  c->current_tex_coord.v[0] = 0;
  c->current_tex_coord.v[1] = 0;
  c->current_tex_coord.v[2] = 0;
  c->current_tex_coord.v[3] = 1;

  c->polygon_mode_front = GL_FILL;
  c->polygon_mode_back = GL_FILL;

  c->current_front_face = 0;
  c->current_cull_face = GL_BACK;
  c->cull_face_enabled = 0;

  c->perspective_hint = GL_DONT_CARE;

  c->current_shade_model= GL_SMOOTH;

  c->clear_color.v[0] = 0;
  c->clear_color.v[1] = 0;
  c->clear_color.v[2] = 0;
  c->clear_color.v[3] = 0;

  c->render_mode = GL_RENDER;
  c->select_buffer = ((void *)0);

  c->name_stack_size = 0;

  c->matrix_mode = 0;

  c->matrix_stack_depth_max[0] = 32;
  c->matrix_stack_depth_max[1] = 8;
  c->matrix_stack_depth_max[2] = 8;

  c->matrix_stack[0] = tgl_zalloc(c->matrix_stack_depth_max[0] * sizeof(M4));
  c->matrix_stack[1] = tgl_zalloc(c->matrix_stack_depth_max[1] * sizeof(M4));
  c->matrix_stack[2] = tgl_zalloc(c->matrix_stack_depth_max[2] * sizeof(M4));

  c->matrix_stack_ptr[0] = c->matrix_stack[0];
  c->matrix_stack_ptr[1] = c->matrix_stack[1];
  c->matrix_stack_ptr[2] = c->matrix_stack[2];

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glMatrixMode(GL_TEXTURE);
  glLoadIdentity();
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  c->iModelProjectionNotUpToDate=1;

  c->client_states = 0;

  c->specbuf_first = ((void *)0);
  c->specbuf_used_counter = 0;
  c->specbuf_num_buffers = 0;

  c->depth_test = 0;
}

void tgl_Close(void) {
  if (tgl_ctx != ((void *)0)) {
    tgl_EndSharedState(tgl_ctx);
    tgl_free(tgl_ctx);
    tgl_ctx = ((void *)0);
  }
}

static int kernel8[4*4] = {
    0 * 256, 8 * 256, 2 * 256, 10 * 256,
   12 * 256, 4 * 256, 14 * 256, 6 * 256,
    3 * 256, 11 * 256, 1 * 256, 9 * 256,
   15 * 256, 7 * 256, 13 * 256, 5 * 256,
};


void ZB_initDither(ZBuffer *zb, int nb_colors, unsigned char *color_indexes, int * color_table) {
  int c,r,g,b,i,index,r1,g1,b1;

  for(i=0;i<nb_colors;i++)
    color_table[i]=0;

  zb->nb_colors=nb_colors;
  zb->ctable=tgl_malloc(nb_colors * sizeof(int));

  for (r = 0; r < 5; r++) {
    for (g = 0; g < 9; g++) {
      for (b = 0; b < 5; b++) {
        r1=(r*255) / (5 - 1);
        g1=(g*255) / (9 - 1);
        b1=(b*255) / (5 - 1);
        index=((b) + (g) * 5 + (r) * (5 * 9));
        c=(r1 << 16) | (g1 << 8) | b1;
        zb->ctable[index]=c;
        color_table[index]=c;
      }
    }
  }

  zb->dctable=tgl_malloc( (1 << 15) );

  for(i=0;i<(1 << 15);i++) {
    r=(i >> 12) & 0x7;
    g=(i >> 8) & 0xF;
    b=(i >> 3) & 0x7;
    index=((b) + (g) * 5 + (r) * (5 * 9));
    zb->dctable[i]=color_indexes[index];
  }
}

void ZB_closeDither(ZBuffer *zb) {
  tgl_free(zb->ctable);
  tgl_free(zb->dctable);
}

void ZB_ditherFrameBuffer(ZBuffer *zb,unsigned char *buf, int iPitch) {
  int xk,yk,yk4,x,y,c1,c2;
  unsigned char *dest1;
  unsigned short *pPixel;
  int r_d,g_d,b_d;
  unsigned char *ctable=zb->dctable;
  unsigned char *dest;
  unsigned short *pp;
                                                   ;

  for(yk=0;yk<4;yk++) {
    yk4=yk*4;
    for(xk=0;xk<4;xk+=2) {
      c1=kernel8[yk4+xk+1];
      r_d =((c1 << 2) & 0xF800) >> 2;
      g_d = (c1 >> 4) & 0x07C0;
      b_d = (c1 >> 9) & 0x001F;

      c2=kernel8[yk4+xk  ];
      r_d |=(((c2 << 2) & 0xF800) >> 2) << 16;
      g_d |= ((c2 >> 4) & 0x07C0) << 16;
      b_d |= ((c2 >> 9) & 0x001F) << 16;

      g_d = b_d | g_d;

      dest1 = buf + (yk * iPitch) + xk;
      pPixel= zb->pbuf + yk * zb->iPitchInPixels + xk;

      for(y=yk;y<zb->iHeight;y+=4) {
        dest=dest1;
        pp=pPixel;
        for(x=xk;x<zb->iWidth;x+=16) {
          {
            int v=*(unsigned int *)(pp+(0));
            int g=(v & 0x07DF07DF) + g_d;
            int r=(((v & 0xF800F800) >> 2) + r_d) & 0x70007000;
            int t=r | g;
            int c=ctable[t & 0xFFFF] | (ctable[t >> 16] << 8);
            *(unsigned short *)(dest+(0))=c;
          };
          {
            int v=*(unsigned int *)(pp+(4));
            int g=(v & 0x07DF07DF) + g_d;
            int r=(((v & 0xF800F800) >> 2) + r_d) & 0x70007000;
            int t=r | g;
            int c=ctable[t & 0xFFFF] | (ctable[t >> 16] << 8);
            *(unsigned short *)(dest+(4))=c;
          };
          {
            int v=*(unsigned int *)(pp+(8));
            int g=(v & 0x07DF07DF) + g_d;
            int r=(((v & 0xF800F800) >> 2) + r_d) & 0x70007000;
            int t=r | g;
            int c=ctable[t & 0xFFFF] | (ctable[t >> 16] << 8);
            *(unsigned short *)(dest+(8))=c;
          };
          {
            int v=*(unsigned int *)(pp+(12));
            int g=(v & 0x07DF07DF) + g_d;
            int r=(((v & 0xF800F800) >> 2) + r_d) & 0x70007000;
            int t=r | g;
            int c=ctable[t & 0xFFFF] | (ctable[t >> 16] << 8);
            *(unsigned short *)(dest+(12))=c;
          };
          pp+=16; dest+=16;
        }
        dest1+=iPitch*4;
        pPixel+=zb->iWidth*4;
      }
    }
  }
}

ZBuffer *ZB_open(int xsize, int ysize, int mode, int nb_colors, unsigned char *color_indexes, int *color_table, void *frame_buffer){
  ZBuffer *zb = tgl_malloc(sizeof(ZBuffer));
  if (zb == ((void *)0))
    return ((void *)0);

  zb->iMode = mode;
  zb->iWidth = xsize;
  zb->iHeight = ysize;
  zb->iPitchInBytes = (xsize * 2 + 15) & ~15;
  zb->iPitchInPixels = zb->iPitchInBytes >> 1;
  switch (mode) {
  case 2:
    ZB_initDither(zb, nb_colors, color_indexes, color_table);
    break;
  case 1:
  case 3:
  case 4:
    zb->nb_colors = 0;
    break;
  default:
    goto error;
  }

  size_t isize = zb->iWidth * zb->iHeight * sizeof(unsigned short);
  zb->zbuf = tgl_malloc(isize);
  if (zb->zbuf == ((void *)0))
    goto error;

  if (frame_buffer == ((void *)0)) {
    isize = zb->iHeight * zb->iPitchInBytes;
    zb->pbuf = tgl_malloc(isize);
    if (zb->pbuf == ((void *)0)) {
      tgl_free(zb->zbuf);
      goto error;
    }
    zb->frame_buffer_allocated = 1;
  }
  else
  {
    zb->frame_buffer_allocated = 0;
    zb->pbuf = frame_buffer;
  }

  zb->current_texture = ((void *)0);
  return zb;

error:
  tgl_free(zb);
  return ((void *)0);
}

void ZB_close(ZBuffer * zb) {
  if (zb==((void *)0))
    return;

  if (tgl_ctx)
    tgl_Close();

  if (zb->iMode == 2)
    ZB_closeDither(zb);

  if (zb->frame_buffer_allocated)
    tgl_free(zb->pbuf);

  tgl_free(zb->zbuf);
  tgl_free(zb);
}

void ZB_resize(ZBuffer * zb, void *frame_buffer, int iWidth, int iHeight) {
  int isize;
  iWidth = iWidth & ~3;

  zb->iWidth = iWidth;
  zb->iHeight = iHeight;
  zb->iPitchInBytes = (iWidth * 2 + 15) & ~15;
  zb->iPitchInPixels = zb->iPitchInBytes >> 1;
  isize = zb->iWidth * zb->iHeight * sizeof(unsigned short);

  tgl_free(zb->zbuf);
  zb->zbuf = tgl_malloc(isize);

  if (zb->frame_buffer_allocated) {
    tgl_free(zb->pbuf);
    zb->frame_buffer_allocated=0;
  }

  if (frame_buffer == ((void *)0)) {
    zb->pbuf = tgl_malloc(zb->iHeight * zb->iPitchInBytes);
    zb->frame_buffer_allocated = 1;
  }
  else
  {
    zb->pbuf = frame_buffer;
    zb->frame_buffer_allocated = 0;
  }
}

static void ZB_copyBuffer(ZBuffer * zb, void *buf, int iPitch){
  unsigned char * drow = buf;
  PIXEL * srow = zb->pbuf;
  int y,n = zb->iWidth * 2;
  for (y = 0; y < zb->iHeight; y++) {
    memcpy(drow,srow, n);
    drow += iPitch;
    srow += zb->iPitchInPixels;
  }
}

static void ZB_copyFrameBufferRGB32(ZBuffer * zb, void *buf, int dpitch){
  int x,y;
  PIXEL * srow = zb->pbuf;
  PIXEL * spixel;
  unsigned char * drow = buf;
  unsigned char * dpixel;
  int spitch = zb->iPitchInPixels;
  for(y=0;y<zb->iHeight;y++){
    spixel=srow;
    dpixel=drow;

    for(x=0;x<zb->iWidth;x++){
      dpixel[0]=(*spixel & 0x001F) << 3;
      dpixel[1]=(*spixel & 0x07E0) >> 3;
      dpixel[2]=(*spixel & 0xF800) >> 8;
      dpixel[3]=0xFF;
      spixel+=1;
      dpixel+=4;
    }
    srow+=spitch;
    drow+=dpitch;
  }
}

static void ZB_copyFrameBufferRGB24(ZBuffer * zb, void *buf, int iPitch) {
  unsigned int *p, *p1, w0, w1, w2, v0, v1;
  int y, n;

  PIXEL * q = zb->pbuf;
  p1 = (unsigned int *) buf;
  iPitch = iPitch * 3;

  for (y = 0; y < zb->iHeight; y++) {
    p = p1;
    n = zb->iWidth >> 2;
    do {
      v0 = *(unsigned int *) q;
      v1 = *(unsigned int *) (q + 2);
      {
        unsigned int r1,g1,b1,gb1,g2,b2,gb2;
        v0 = (v0 << 16) | (v0 >> 16);
        v1 = (v1 << 16) | (v1 >> 16);
        r1 = (v0 & 0xF800F800);
        g1 = (v0 & 0x07E007E0) << 5;
        b1 = (v0 & 0x001F001F) << 3;
        gb1 = g1 | b1;
        w0 = ((gb1 & 0x0000FFFF) << 8) | (r1 << 16) | (r1 >> 24);
        g2 = (v1 & 0x07E007E0) << 5;
        b2 = (v1 & 0x001F001F) << 3;
        gb2 = g2 | b2;
        w1 = (gb1 & 0xFFFF0000) | (v1 & 0xF800) | ((gb2 >> 8) & 0xff);
        w2 = (gb2 << 24) | ((v1 & 0xF8000000) >> 8) | (gb2 >> 16);
      };
      p[0] = w0;
      p[1] = w1;
      p[2] = w2;

      q += 4;
      p += 3;
    } while (--n > 0);

    p1 += iPitch;
  }
}

void ZB_copyFrameBuffer(ZBuffer * zb, void *buf, int iPitch) {
  switch (zb->iMode) {
  case 2:
    ZB_ditherFrameBuffer(zb, buf, iPitch);
    break;
  case 1:
    ZB_copyBuffer(zb, buf, iPitch);
    break;
  case 4:
    ZB_copyFrameBufferRGB24(zb, buf, iPitch);
    break;
  case 3:
    ZB_copyFrameBufferRGB32(zb, buf, iPitch);
    break;
  }
}

void ZB_clear(ZBuffer * zb, int clear_z, int z, int clear_color, int r, int g, int b){
  if (clear_z)
    memset_s(zb->zbuf, z, zb->iWidth * zb->iHeight);

  if (clear_color) {
    PIXEL color = (((r) & 0xF800) | (((g) >> 5) & 0x07E0) | ((b) >> 11));
    PIXEL * pp = zb->pbuf;
    int y;
    for (y = 0; y < zb->iHeight; y++) {
      memset_s(pp, color, zb->iWidth);
      pp += zb->iPitchInPixels;
    }
  }
}
