/*
 GNU Maverik - a system for managing display and interaction in 
               Virtual Environment applications.
 Copyright (C) 1999-2001 Advanced Interfaces Group

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA


 The authors can be contacted via:
 www   - http://aig.cs.man.ac.uk
 email - maverik@aig.cs.man.ac.uk
 mail  - Advanced Interfaces Group, Room 2.94, Computer Science Building, 
         University of Manchester, Manchester, M13 9PL, UK
*/


#include "mavlib_gfx.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <windows.h>

#include "mavlib_d3d.h"

/* test threshold for 1 bit alpha textures */
#define MAVLIB_ALPHA_THRESHOLD 128

/* loaded textures */
LPDIRECTDRAWSURFACE7 mavlib_textures[GFX_MAX_TEXTURE];

/* references for primitive vertex creation */
int mavlib_vert= 0;
int mavlib_mode= 0;

/* options */
int mav_opt_bindTextures= MAV_TRUE;
int mav_opt_shareContexts= MAV_TRUE;
int mav_opt_trackMatrix= MAV_FALSE;
int mav_opt_texComps= 4;

int mavlib_matrixmode= MAV_MODELVIEW;
int mavlib_voodoo= MAV_FALSE;

char *mav_gfx_vendor= NULL;
char *mav_gfx_renderer= NULL;
char *mav_gfx_version= NULL;

/* current read pixel buffer */
int mavlib_gfx_readBuffer= 0;

/* background colour */
DWORD mavlib_bk= 0;

/* vertex lists */
D3DVERTEX mavlib_vertList[256];
D3DLVERTEX mavlib_colVertList[256];

/* current normal */
MAV_vector mavlib_lastNorm;

/* current texture coordinate */
MAV_texCoord mavlib_lastTex;

/* current colour */
int mavlib_gfxColour= 0;

/* colour (not material) mode */
int mavlib_colMode= 0;

/* matrix (modelview or projection) */
int mavlib_matMode= 0;

/* matrices */
MAV_matrix mavlib_worldMat;
MAV_matrix mavlib_projMat;

/* current set texture */
int mavlib_texSet= -1;

/* function to get a D3DCOLORVALUE from a 4 float array */
D3DCOLORVALUE mavlib_convertMavColour (float col[4])
{
  D3DCOLORVALUE rv;

  rv.r= col[0];
  rv.g= col[1];
  rv.b= col[2];
  rv.a= col[3];

  return rv;
}

/* Option for tracking matrix transformations */

void mavlib_trackMatrix(void)
{
  mav_win_current->viewMat= mav_gfxMatrixGet();

  if (mav_opt_trackMatrix==MAV_PROJANDVIEW) mav_win_current->pdvMat= mav_matrixMult(mav_win_current->projMat, mav_win_current->viewMat);
}

/* Matrix conversion - D3D is row majored */
MAV_matrix mavlib_convertD3DMatrix (D3DMATRIX d3dmat)
{
  MAV_matrix rv;

  rv.mat[0][0]= d3dmat._11;
  rv.mat[1][0]= d3dmat._12;
  rv.mat[2][0]= d3dmat._13;
  rv.mat[3][0]= d3dmat._14;
  rv.mat[0][1]= d3dmat._21;
  rv.mat[1][1]= d3dmat._22;
  rv.mat[2][1]= d3dmat._23;
  rv.mat[3][1]= d3dmat._24;
  rv.mat[0][2]= d3dmat._31;
  rv.mat[1][2]= d3dmat._32;
  rv.mat[2][2]= d3dmat._33;
  rv.mat[3][2]= d3dmat._34;
  rv.mat[0][3]= d3dmat._41;
  rv.mat[1][3]= d3dmat._42;
  rv.mat[2][3]= d3dmat._43;
  rv.mat[3][3]= d3dmat._44;

  return rv;
}

D3DMATRIX mavlib_convertMavMatrix (MAV_matrix mavmat)
{
  D3DMATRIX rv;

  rv._11= mavmat.mat[0][0];
  rv._21= mavmat.mat[0][1];
  rv._31= mavmat.mat[0][2];
  rv._41= mavmat.mat[0][3];
  rv._12= mavmat.mat[1][0];
  rv._22= mavmat.mat[1][1];
  rv._32= mavmat.mat[1][2];
  rv._42= mavmat.mat[1][3];
  rv._13= mavmat.mat[2][0];
  rv._23= mavmat.mat[2][1];
  rv._33= mavmat.mat[2][2];
  rv._43= mavmat.mat[2][3];
  rv._14= mavmat.mat[3][0];
  rv._24= mavmat.mat[3][1];
  rv._34= mavmat.mat[3][2];
  rv._44= mavmat.mat[3][3];

  return rv;
}

/* current clip planes */
int mavlib_clips= 0;

void mav_gfxClipPlaneSet(int id, MAV_clipPlane cp)
{
  D3DVALUE cplane[4];
  MAV_vector pt;
  MAV_vector norm2;

/* need to convert plane to view coordinates */
  norm2.x= mav_vectorDotProduct (cp.norm, mav_win_current->right);
  norm2.y= mav_vectorDotProduct (cp.norm, mav_win_current->up);
  norm2.z= -1.0 * mav_vectorDotProduct (cp.norm, mav_win_current->view);

/* get a (world) point on plane */
  pt.x= -cp.d/cp.norm.x;
  pt.y= 0;
  pt.z= 0;

/* transform into view space */
  pt= mav_vectorMult4x4 (pt, mavlib_worldMat);

  cplane[0]= -norm2.x;
  cplane[1]= -norm2.y;
  cplane[2]= -norm2.z;
  cplane[3]= mav_vectorDotProduct (pt, norm2);

  MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetClipPlane (mavlib_D3DDevice, 1<<id,
	cplane), "failed to set clip plane");
}

void mav_gfxClipPlanesSet(MAV_clipPlanes *cp)
{
  int index;

  for (index= 0; index < cp->num; index ++)
    mav_gfxClipPlaneSet(index, cp->planes[index]);
}

void mav_gfxClipPlaneEnable (int id)
{
/* mavlib_clips needs to have all current clip planes */
  mavlib_clips |= 1<<id;
  MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderState (mavlib_D3DDevice,
		D3DRENDERSTATE_CLIPPLANEENABLE, mavlib_clips),
		"failed to enable clip plane");
}

void mav_gfxClipPlaneDisable (int id)
{
 if (mavlib_clips & (1<<id)) {
    mavlib_clips -= 1<<id;
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderState (mavlib_D3DDevice,
		D3DRENDERSTATE_CLIPPLANEENABLE, mavlib_clips),
		"failed to disable clip plane");
  }
}

void mav_gfxBackgroundColourSet(float r, float g, float b)
{
/* store for use in mav_gfxClearC and mav_gfxClearCZ */
  mavlib_bk= (((int)(r * 255.0))<<16) + (((int)(g*255.0))<<8) +
		(((int)(b*255.0)));
}

void mav_gfxClearC(void)
{
  MAVD3DERR (mavlib_D3DDevice->lpVtbl->Clear (mavlib_D3DDevice, 0, NULL,
		D3DCLEAR_TARGET, mavlib_bk, 1.0, 0),
		"failed to clear back buffer");
}

void mav_gfxClearZ(void)
{
  MAVD3DERR (mavlib_D3DDevice->lpVtbl->Clear (mavlib_D3DDevice, 0, NULL,
		D3DCLEAR_ZBUFFER, mavlib_bk, 1.0, 0),
		"failed to clear Z buffer");
}

void mav_gfxClearA(void)
{
}

void mav_gfxClearCZ(void)
{
  MAVD3DERR (mavlib_D3DDevice->lpVtbl->Clear (mavlib_D3DDevice, 0, NULL,
		D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, mavlib_bk, 1.0, 0),
		"failed to clear buffers");
}

void mav_gfxPolygonModeSet(int i)
{
  if (i==MAV_POLYGON_LINE)
  {
/* wireframe */
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderState (mavlib_D3DDevice,
	D3DRENDERSTATE_FILLMODE, D3DFILL_WIREFRAME),
	"failed to set wireframe mode");
  }
  else
  {
/* solid */
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderState (mavlib_D3DDevice,
	D3DRENDERSTATE_FILLMODE, D3DFILL_SOLID),
	"failed to set solid mode");
  }
}

void mav_gfxMultiSampleSet(int i)
{
}

void mav_gfxFinish(void)
{
}

void mav_gfxFlush(void)
{
}

void mav_gfxMatrixPush(void)
{
  if (mavlib_matMode==MAV_PROJECTION) {
/* projection matrix */
    mav_matrixStackPush (mavlib_projMat);
  } else {
/* world matrix */
    mav_matrixStackPush (mavlib_worldMat);
  }
}

void mav_gfxMatrixPop(void)
{
  D3DMATRIX d3dmat;
  MAV_matrix popmat;

/* get matrix from stack */
  popmat= mav_matrixStackGet ();
  mav_matrixStackPop ();
  d3dmat= mavlib_convertMavMatrix (popmat);

  if (mavlib_matMode==MAV_PROJECTION) {
/* projection matrix */
    mavlib_projMat= popmat;
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTransform (mavlib_D3DDevice,
	D3DTRANSFORMSTATE_PROJECTION, &d3dmat),
	"Error: failed to pop projection matrix");
  } else {
/* world matrix */
    mavlib_worldMat= popmat;
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTransform (mavlib_D3DDevice,
	D3DTRANSFORMSTATE_WORLD, &d3dmat),
	"failed to pop modelview matrix");
  }

  if (mav_opt_trackMatrix) mavlib_trackMatrix();
}

MAV_matrix mavlib_matrixTranspose(MAV_matrix mat)
{
/* not used */
  MAV_matrix rv;
  int i,j;
  
  for (i=0; i<4; i++) {
    for (j=0; j<4; j++) {
      rv.mat[i][j]= mat.mat[j][i];
    }
  }

  return rv;
}

void mav_gfxMatrixMult(MAV_matrix mat)
{
  D3DMATRIX d3dmat;

  if (mavlib_matMode==MAV_PROJECTION) {
/* projection matrix */
    mavlib_projMat= mav_matrixMult (mavlib_projMat, mat);
    d3dmat= mavlib_convertMavMatrix (mavlib_projMat);

/* use Set rather than Multiply so we don't have to multiply */
/* the matrix twice */
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTransform (mavlib_D3DDevice,
		D3DTRANSFORMSTATE_PROJECTION, &d3dmat),
		"failed to multiply projection matrix");
  } else {
/* world matrix */
    mavlib_worldMat= mav_matrixMult (mavlib_worldMat, mat);
    d3dmat= mavlib_convertMavMatrix (mavlib_worldMat);

/* use Set */
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTransform (mavlib_D3DDevice,
		D3DTRANSFORMSTATE_WORLD, &d3dmat),
		"failed to multiply modelview matrix");
  }

  if (mav_opt_trackMatrix) mavlib_trackMatrix();
}

void mav_gfxMatrixMode(int mode)
{
/* MAV_PROJECTION or MAV_MODELVIEW */
  mavlib_matMode= mode;
}

MAV_matrix mav_gfxMatrixGet(void)
{
  MAV_matrix mav_mat;

  if (mavlib_matMode==MAV_PROJECTION) {
/* projection */
    mav_mat= mavlib_projMat;
  } else {
/* world - should be up to date in mavlib_worldMat */
    mav_mat= mavlib_worldMat;
  }

  return mav_mat;
}

void mav_gfxMatrixLoad(MAV_matrix mat)
{
  D3DMATRIX d3dmat;

  if (mavlib_matMode==MAV_PROJECTION) {
/* projection */
    mavlib_projMat= mat;
    d3dmat= mavlib_convertMavMatrix (mat);
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTransform (mavlib_D3DDevice,
		D3DTRANSFORMSTATE_PROJECTION, &d3dmat),
		"failed to load projection matrix");
  } else {
/* world */
    MAV_matrix id_mat;

    mavlib_worldMat= mat;
    d3dmat= mavlib_convertMavMatrix (mat);
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTransform (mavlib_D3DDevice,
		D3DTRANSFORMSTATE_WORLD, &d3dmat),
		"failed to load modelview matrix");

/* reset view matrix to identity (in case it has been fiddled with) */
    id_mat= MAV_ID_MATRIX;
    d3dmat= mavlib_convertMavMatrix (id_mat);
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTransform (mavlib_D3DDevice,
		D3DTRANSFORMSTATE_VIEW, &d3dmat),
		"failed to load view matrix");
  }

  if (mav_opt_trackMatrix) mavlib_trackMatrix();
}

void mav_gfxMatrixTranslate(MAV_vector t)
{
  MAV_matrix mat;
  D3DMATRIX d3dmat;

/* compute translation matrix */
  mat= MAV_ID_MATRIX;

  mat.mat[0][3]= t.x;
  mat.mat[1][3]= t.y;
  mat.mat[2][3]= t.z;

  if (mavlib_matMode==MAV_PROJECTION) {
/* projection */
    mavlib_projMat= mav_matrixMult (mavlib_projMat, mat);
    d3dmat= mavlib_convertMavMatrix (mavlib_projMat);
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTransform (mavlib_D3DDevice,
	D3DTRANSFORMSTATE_PROJECTION, &d3dmat),
	"failed to translate projection matrix");
  } else {
/* world */
    mavlib_worldMat= mav_matrixMult (mavlib_worldMat, mat);
    d3dmat= mavlib_convertMavMatrix (mavlib_worldMat);
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTransform (mavlib_D3DDevice,
	D3DTRANSFORMSTATE_WORLD, &d3dmat),
	"failed to translate modelview matrix");
  }

  if (mav_opt_trackMatrix) mavlib_trackMatrix();
}

void mav_gfxMatrixScale(float x, float y, float z)
{
  MAV_matrix mat;
  D3DMATRIX d3dmat;

/* compute scale matrix */
  mat= MAV_ID_MATRIX;

  mat.mat[0][0]= x;
  mat.mat[1][1]= y;
  mat.mat[2][2]= z;

  if (mavlib_matMode==MAV_PROJECTION) {
/* projection */
    mavlib_projMat= mav_matrixMult (mavlib_projMat, mat);
    d3dmat= mavlib_convertMavMatrix (mavlib_projMat);
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTransform (mavlib_D3DDevice,
	D3DTRANSFORMSTATE_PROJECTION, &d3dmat),
	"Error: failed to scale projection matrix");
  } else {
/* world */
    mavlib_worldMat= mav_matrixMult (mat, mavlib_worldMat);
    d3dmat= mavlib_convertMavMatrix (mavlib_worldMat);
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTransform (mavlib_D3DDevice,
	D3DTRANSFORMSTATE_WORLD, &d3dmat),
	"failed to scale modelview matrix");
  }

  if (mav_opt_trackMatrix) mavlib_trackMatrix();
}

void mav_gfxPerspectiveSet(float ncp, float fcp, float fov, float aspect)
{
/* create a perspective transformation matrix that swaps from right */
/* to left handed co-ordinate systems */
  D3DMATRIX mat;
  float c,s,Q;

  c= (float) cos(MAV_PI*fov/360.0);
  s= (float) sin(MAV_PI*fov/360.0);

  Q= s/(1.0-ncp/fcp);

  mat._11= c/aspect;
  mat._12= 0;
  mat._13= 0;
  mat._14= 0;
  mat._21= 0;
  mat._22= c;
  mat._23= 0;
  mat._24= 0;
  mat._31= 0;
  mat._32= 0;
  mat._33= -Q;
  mat._34= -s;
  mat._41= 0;
  mat._42= 0;
  mat._43= -Q*ncp;
  mat._44= 0;

  mavlib_projMat= mavlib_convertD3DMatrix (mat);

  MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTransform (mavlib_D3DDevice,
		D3DTRANSFORMSTATE_PROJECTION, &mat),
		"failed to set perspective");
}

void mav_gfxOrthogonalSet(float left, float right, float top, float bottom, float nr, float fr)
{
  MAV_matrix mat;
  D3DMATRIX d3dmat;

/* compute orthogonal matrix */
  mat= MAV_ID_MATRIX;

  mat.mat[0][0]= 2.0/(right-left);
  mat.mat[1][1]= 2.0/(bottom-top);
  mat.mat[2][2]= 1.0/(fr-nr);
  mat.mat[0][3]= -1.0-2.0*left/(right-left);
  mat.mat[1][3]= -1.0-2.0*top/(bottom-top);
  mat.mat[2][3]= -nr/(fr-nr);

  mavlib_projMat= mat;

  d3dmat= mavlib_convertMavMatrix (mat);

/* glOrtho multiples rather than replaces projection matrix so do the same */
  MAVD3DERR (mavlib_D3DDevice->lpVtbl->MultiplyTransform (mavlib_D3DDevice,
		D3DTRANSFORMSTATE_PROJECTION, &d3dmat),
		"failed to set orthogonal projection");
}

void mav_gfxPolygonBegin(void)
{
/* reset vertex reference variables */
  mavlib_vert= 0;
  mavlib_mode= 1;
}

void mav_gfxPolygonEnd(void)
{
/* use lit (i.e. coloured) or unlit vertex list depending on mode */
  if (mavlib_colMode) {
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->DrawPrimitive (mavlib_D3DDevice,
	D3DPT_TRIANGLEFAN, D3DFVF_LVERTEX, mavlib_colVertList,
	mavlib_vert, 0),
	"failed to draw polygon");
  } else {
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->DrawPrimitive (mavlib_D3DDevice,
	D3DPT_TRIANGLEFAN, D3DFVF_VERTEX, mavlib_vertList,
	mavlib_vert, 0),
	"failed to draw polygon");
  }
  mavlib_mode= 0;
}

void mav_gfxTrianglesBegin(void)
{
/* reset vertex reference variables */
  mavlib_mode= 1;
  mavlib_vert= 0;
}

void mav_gfxTrianglesEnd(void)
{
/* use lit (i.e. coloured) or unlit vertex list depending on mode */
  if (mavlib_colMode) {
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->DrawPrimitive (mavlib_D3DDevice,
	D3DPT_TRIANGLELIST, D3DFVF_LVERTEX, mavlib_colVertList,
	mavlib_vert, 0),
	"failed to draw triangles");
  } else {
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->DrawPrimitive (mavlib_D3DDevice,
	D3DPT_TRIANGLELIST, D3DFVF_VERTEX, mavlib_vertList,
	mavlib_vert, 0),
	"failed to draw triangles");
  }

  mavlib_mode= 0;
}

void mav_gfxVertex(MAV_vector v)
{
  if (mavlib_mode!=0) {
/* in a valid Begin-End scope */
    if (mavlib_colMode) {
/* lit vertex (in D3D this means the application sets the colour, i.e. */
/* no lighting calculation will take place) */

/* set vertex */
      mavlib_colVertList[mavlib_vert].x= v.x;
      mavlib_colVertList[mavlib_vert].y= v.y;
      mavlib_colVertList[mavlib_vert].z= v.z;

/* set current texture coordinate */
      mavlib_colVertList[mavlib_vert].tu= mavlib_lastTex.s;
      mavlib_colVertList[mavlib_vert].tv= mavlib_lastTex.t;

/* set current colour */
      mavlib_colVertList[mavlib_vert].color= mavlib_gfxColour;
    } else {
/* unlit vertex - lighting calculation will be done by D3D */

/* set vertex */
      mavlib_vertList[mavlib_vert].x= v.x;
      mavlib_vertList[mavlib_vert].y= v.y;
      mavlib_vertList[mavlib_vert].z= v.z;

/* set current texture coordinate */
      mavlib_vertList[mavlib_vert].tu= mavlib_lastTex.s;
      mavlib_vertList[mavlib_vert].tv= mavlib_lastTex.t;

/* set current normal */
      mavlib_vertList[mavlib_vert].nx= mavlib_lastNorm.x;
      mavlib_vertList[mavlib_vert].ny= mavlib_lastNorm.y;
      mavlib_vertList[mavlib_vert].nz= mavlib_lastNorm.z;
    }

/* point to next vertex */
    mavlib_vert ++;
  }
}

void mav_gfxNormal(MAV_vector v)
{
/* remember normal for vertex operations */
  mavlib_lastNorm= v;
}

void mav_gfxLightSet(MAV_light info)
{
}

void mav_gfxLightUse(MAV_light info)
{
  if (info.defined) {
    D3DLIGHT7 d3dlight;

    ZeroMemory (&d3dlight, sizeof (d3dlight));
    d3dlight.dltType= D3DLIGHT_DIRECTIONAL;
    d3dlight.dcvDiffuse= mavlib_convertMavColour (info.diffuse);
    d3dlight.dcvSpecular= mavlib_convertMavColour (info.specular);
    d3dlight.dcvAmbient= mavlib_convertMavColour (info.ambient);

/* simulate position with a directional light */
    d3dlight.dvDirection.x= -info.pos.x;
    d3dlight.dvDirection.y= -info.pos.y;
    d3dlight.dvDirection.z= -info.pos.z;

    MAVD3DWARN (mavlib_D3DDevice->lpVtbl->SetLight (mavlib_D3DDevice,
	info.index, &d3dlight),
	"failed to set light");

    MAVD3DERR (mavlib_D3DDevice->lpVtbl->LightEnable (mavlib_D3DDevice,
	info.index, TRUE),
	"failed to enable light");
  }
}

void mav_gfxLightPos(MAV_light info)
{
  if (info.defined) {
    D3DLIGHT7 d3dlight;

/* get light info */
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->GetLight (mavlib_D3DDevice,
	info.index, &d3dlight),
	"failed to get light");

/* update light direction */
    d3dlight.dvDirection.x= -info.pos.x;
    d3dlight.dvDirection.y= -info.pos.y;
    d3dlight.dvDirection.z= -info.pos.z;

    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetLight (mavlib_D3DDevice,
	info.index, &d3dlight),
	"failed to set light");

    MAVD3DERR (mavlib_D3DDevice->lpVtbl->LightEnable (mavlib_D3DDevice,
	info.index, TRUE),
	"failed to enable light");
  }
}

void mav_gfxTextureEnv1Set(int v)
{
  switch (v) {
    case 1:
/* bilinear interpolation, wrap mode */
      MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
		0, D3DTSS_MINFILTER, D3DTFN_LINEAR),
		"failed to set texture min filter");
      MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
		0, D3DTSS_MAGFILTER, D3DTFG_LINEAR),
		"failed to set texture mag filter");
      MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
		0, D3DTSS_ADDRESS, D3DTADDRESS_WRAP),
		"failed to set texture wrap");
      MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
		0, D3DTSS_MIPFILTER, D3DTFP_NONE),
		"failed to reset texture mip filter");
      break;
    case 2:
/* bilinear interpolation, clamp mode */
      MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
		0, D3DTSS_MINFILTER, D3DTFN_LINEAR),
		"failed to set texture min filter");
      MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
		0, D3DTSS_MAGFILTER, D3DTFG_LINEAR),
		"failed to set texture mag filter");
      MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
		0, D3DTSS_ADDRESS, D3DTADDRESS_CLAMP),
		"failed to set texture clamp");
      MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
		0, D3DTSS_MIPFILTER, D3DTFP_NONE),
		"failed to reset texture mip filter");
      break;
    case 3:
/* trilinear (mipmapped) interpolation, wrap mode */
/* (mip filters override min and mag filters) */
      MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
		0, D3DTSS_MIPFILTER, D3DTFP_LINEAR),
		"failed to set texture mip filter");
      MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
		0, D3DTSS_ADDRESS, D3DTADDRESS_WRAP),
		"failed to set texture wrap");
      break;
    case 4:
/* trilinear (mipmapped) interpolation, clamp mode */
      MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
		0, D3DTSS_MIPFILTER, D3DTFP_LINEAR),
		"failed to set texture mip filter");
      MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
		0, D3DTSS_ADDRESS, D3DTADDRESS_CLAMP),
		"failed to set texture clamp");
      break;
  }
}

void mav_gfxTextureEnv2Set(int v)
{
/* this is deal with in mav_gfxColouringModeUse */
#if 0
  switch (v) {
    case 1:
modulate tex
    case 2:
decal tex
  }
#endif
}

void mav_gfxColouringModeUse(MAV_palette *p, int mode)
{
  if (mode==MAV_COLOUR) 
  {
/* colour (no textures or lighting) */

/* remember which vertex list to use */
    mavlib_colMode= 1;

/* disable texturing */
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
	0, D3DTSS_COLOROP, D3DTOP_DISABLE),
	"failed to disable texture colour op");
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
	0, D3DTSS_ALPHAOP, D3DTOP_DISABLE),
	"failed to disable texture alpha op");
  } 
  else if (mode==MAV_MATERIAL) 
  {
/* lighting */

    mavlib_colMode= 0;

/* disable textures */
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
	0, D3DTSS_COLOROP, D3DTOP_DISABLE),
	"failed to disable texture colour op");
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
	0, D3DTSS_ALPHAOP, D3DTOP_DISABLE),
	"failed to disable texture alpha op");
  }
  else if (mode==MAV_TEXTURE) 
  {
/* textures */

/* use coloured vertex list */
    mavlib_colMode= 1;

/* enable textures */
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
	0, D3DTSS_COLOROP, D3DTOP_SELECTARG1),
	"failed to set texture colour op (select arg1)");
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
	0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1),
	"failed to set texture alpha op (select arg1)");

    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
	0, D3DTSS_COLORARG1, D3DTA_TEXTURE),
	"failed to set texture colour arg1 (texture)");
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
	0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE),
	"failed to set texture alpha arg1 (texture)");
  }
  else if (mode==MAV_BLENDED_TEXTURE) 
  {
/* texture alpha blended with surface */

    mavlib_colMode= 0;

/* enable texturing */
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
	0, D3DTSS_COLORARG1, D3DTA_TEXTURE),
	"failed to set texture colour arg1 (texture)");
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
	0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE),
	"failed to set texture alpha arg1 (texture)");

    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
	0, D3DTSS_COLORARG2, D3DTA_DIFFUSE),
	"failed to set texture colour arg2 (diffuse)");
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
	0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE),
	"failed to set texture alpha arg2 (diffuse)");

    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
	0, D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA),
	"failed to set texture colour op (blend texture alpha)");
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
	0, D3DTSS_ALPHAOP, D3DTOP_BLENDTEXTUREALPHA),
	"failed to set texture alpha op (blend texture alpha)");
  }
  else if (mode==MAV_LIT_TEXTURE) 
  {
/* texture with lights */

    mavlib_colMode= 0;

/* enable texture with diffuse light */
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
	0, D3DTSS_COLORARG1, D3DTA_TEXTURE),
	"failed to set texture colour arg1 (texture)");
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
	0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE),
	"failed to set texture alpha arg1 (texture)");

    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
	0, D3DTSS_COLORARG2, D3DTA_DIFFUSE),
	"failed to set texture colour arg2 (diffuse)");
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
	0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE),
	"failed to set texture alpha arg2 (diffuse)");

    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
	0, D3DTSS_COLOROP, D3DTOP_MODULATE),
	"failed to set texture colour op (modulate)");
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTextureStageState (mavlib_D3DDevice,
	0, D3DTSS_ALPHAOP, D3DTOP_MODULATE),
	"failed to set texture alpha op (modulate)");
  }
}

void mav_gfxVisualInfoGet(int *r, int *g, int *b, int *a, int *d, int *db, int *ar, int *ag, int *ab, int *aa, int *sb, int *msb)
{
}

void mav_gfxStripQBegin(void)
{
/* reset vertex reference variables */
  mavlib_mode= 1;
  mavlib_vert= 0;
}

void mav_gfxStripQEnd(void)
{
/* use lit (i.e. coloured) or unlit vertex list depending on mode */
  if (mavlib_colMode) {
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->DrawPrimitive (mavlib_D3DDevice,
	D3DPT_TRIANGLESTRIP, D3DFVF_LVERTEX, mavlib_colVertList,
	mavlib_vert, 0),
	"failed to draw Q strip");
  } else {
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->DrawPrimitive (mavlib_D3DDevice,
	D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX, mavlib_vertList,
	mavlib_vert, 0),
	"failed to draw Q strip");
  }
  mavlib_mode= 0;
}

void mav_gfxStripTBegin(void)
{
/* reset vertex reference variables */
  mavlib_mode= 1;
  mavlib_vert= 0;
}

void mav_gfxStripTEnd(void)
{
/* use lit (i.e. coloured) or unlit vertex list depending on mode */
  if (mavlib_colMode) {
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->DrawPrimitive (mavlib_D3DDevice,
	D3DPT_TRIANGLESTRIP, D3DFVF_LVERTEX, mavlib_colVertList,
	mavlib_vert, 0),
	"failed to draw T strip");
  } else {
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->DrawPrimitive (mavlib_D3DDevice,
	D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX, mavlib_vertList,
	mavlib_vert, 0),
	"failed to draw T strip");
  }
  mavlib_mode= 0;
}

void mav_gfxTexCoord(MAV_texCoord t)
{
/* remember for vertex operations */
  mavlib_lastTex= t;
}

void mav_gfxLineClosedBegin(void)
{
/* reset vertex reference variables */
  mavlib_mode= 1;
  mavlib_vert= 0;
}

void mav_gfxLineClosedEnd(void)
{
/* use lit (i.e. coloured) or unlit vertex list depending on mode */
/* not that lighting calculation for a light makes much sense, but hey */
  if (mavlib_colMode) {
    mavlib_colVertList[mavlib_vert]= mavlib_colVertList[0];
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->DrawPrimitive (mavlib_D3DDevice,
	D3DPT_LINESTRIP, D3DFVF_LVERTEX, mavlib_colVertList,
	mavlib_vert+1, 0),
	"failed to draw closed line");
  } else {
    mavlib_vertList[mavlib_vert]= mavlib_vertList[0];
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->DrawPrimitive (mavlib_D3DDevice,
	D3DPT_LINESTRIP, D3DFVF_VERTEX, mavlib_vertList,
	mavlib_vert+1, 0),
	"failed to draw closed line");
  }
  mavlib_mode= 0;
}

void mav_gfxLineBegin(void)
{
/* reset vertex reference variables */
  mavlib_mode= 1;
  mavlib_vert= 0;
}

void mav_gfxLineEnd(void)
{
/* use lit (i.e. coloured) or unlit vertex list depending on mode */
  if (mavlib_colMode) {
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->DrawPrimitive (mavlib_D3DDevice,
	D3DPT_LINESTRIP, D3DFVF_LVERTEX, mavlib_colVertList,
	mavlib_vert, 0),
	"failed to draw line");
  } else {
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->DrawPrimitive (mavlib_D3DDevice,
	D3DPT_LINESTRIP, D3DFVF_VERTEX, mavlib_vertList,
	mavlib_vert, 0),
	"failed to draw line");
  }
  mavlib_mode= 0;
}

void mav_gfxMeshTBegin(void)
{
/* StripT does all necessary variable updates */
  mav_gfxStripTBegin ();
}

void mav_gfxMeshTEnd(void)
{
  mav_gfxStripTEnd ();
}


void mav_gfxColourSet(MAV_colour col)
{
}

void mav_gfxColourUse(MAV_colour col)
{
/* current colour */
  mavlib_gfxColour= (((int)(col.colour[3] * 255.0))<<24) +
		 (((int)(col.colour[0] * 255.0))<<16) +
		 (((int)(col.colour[1]*255.0))<<8) +
		 (((int)(col.colour[2]*255.0)));

/* set colour for 2D text */
  mavlib_col_r= col.colour[0]*255;
  mavlib_col_g= col.colour[1]*255;
  mavlib_col_b= col.colour[2]*255;
}

void mav_gfxMaterialSet(MAV_material mat)
{
}

void mav_gfxMaterialUse(MAV_material mat)
{
  D3DMATERIAL7 d3dmat;

  ZeroMemory (&d3dmat, sizeof (d3dmat));

  d3dmat.ambient= mavlib_convertMavColour (mat.ambient);
  d3dmat.diffuse= mavlib_convertMavColour (mat.diffuse);
  d3dmat.specular= mavlib_convertMavColour (mat.specular);
  d3dmat.emissive= mavlib_convertMavColour (mat.emission);

  d3dmat.power= mat.shine;

  MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetMaterial (mavlib_D3DDevice,
	&d3dmat),
	"failed to set material");
}

void mavlib_loadTextureImage (MAV_texture *tex, LPDIRECTDRAWSURFACE7 d3dtex, int level)
{
/* copy image data (one level if a mipmap) from a Maverik texture into */
/* a D3D texture surface */
  DDSURFACEDESC2 ddsd;
  int xp, yp;
  DWORD *dwpix;
  int pix_step;
  unsigned char *ptr;
  int i;
  unsigned char *memptr;
  DWORD alpha_bits;
  int single_alpha= 0;

/* lock surface - this gives us bit masks for red, green, blue and alpha, */
/* and a pointer to the surface data */
  MAVD3DERR (d3dtex->lpVtbl->Lock (d3dtex,
	NULL, &ddsd, DDLOCK_WAIT, NULL),
	"failed to lock texture surface");

/* blank the texture surface */
  ZeroMemory (ddsd.lpSurface, ddsd.lPitch*ddsd.dwHeight);

/* i is the (byte) index into the Maverik image data */
  i= 0;

/* check where to copy from */
  if (level==0) {
    memptr= (char *) tex->mem;
  } else {
    memptr= (char *) tex->mipmap[level-1];
  }

/* get bytes per pixel */
  pix_step= ddsd.ddpfPixelFormat.dwRGBBitCount/8;

  if (ddsd.ddpfPixelFormat.dwRGBBitCount==16) {
/* check for single alpha bit (only in 16 bit textures) */
    if (ddsd.ddpfPixelFormat.dwRGBAlphaBitMask==
	(ddsd.ddpfPixelFormat.dwRGBAlphaBitMask & 0x8001))
      single_alpha= 1;
  }

/* copy the image data */
  for (yp=0; yp<ddsd.dwHeight; yp++) {
    ptr= (char *) ddsd.lpSurface;

/* lPitch is the width (in bytes) of a line - not necessarily the same */
/* as dwRGBBitCount/8 * dwWidth as lines may be padded */
    ptr += yp * ddsd.lPitch;

    for (xp=0; xp<ddsd.dwWidth; xp++) {
/* get a DWORD pointer into the surface data */
      dwpix= (DWORD *) ptr;

      if (single_alpha) {
/* single alpha bit - use arbitrary test for alpha */
	if (memptr[i+3]>MAVLIB_ALPHA_THRESHOLD)
	  alpha_bits= ddsd.ddpfPixelFormat.dwRGBAlphaBitMask;
	else
	  alpha_bits= 0;
      } else {
/* multiple alpha bits */
	alpha_bits= (DWORD) (ddsd.ddpfPixelFormat.dwRGBAlphaBitMask *
			((float)memptr[i+3]/255.0)) &
			ddsd.ddpfPixelFormat.dwRGBAlphaBitMask;
      }

/* get RGBA (use + because this DWORD might be part of another pixel) */
      *dwpix += ((DWORD)(ddsd.ddpfPixelFormat.dwRBitMask *
			((float)memptr[i]/255.0)) &
		ddsd.ddpfPixelFormat.dwRBitMask) +
		((DWORD)(ddsd.ddpfPixelFormat.dwGBitMask *
			((float)memptr[i+1]/255.0)) &
		ddsd.ddpfPixelFormat.dwGBitMask) +
		((DWORD)(ddsd.ddpfPixelFormat.dwBBitMask *
			((float)memptr[i+2]/255.0)) &
		ddsd.ddpfPixelFormat.dwBBitMask) +
		alpha_bits;
/* alpha_bits will be zero for a non-alpha texture surface */

/* move to next pixel in Maverik image ... */
      i += 4;
/* ... and in texture surface */
      ptr += pix_step;
    }
  }

/* unlock the texture surface */
  MAVD3DERR (d3dtex->lpVtbl->Unlock (d3dtex, NULL),
	"failed to unlock texture surface");
}

#define L2 0.301029995

void mav_gfxTextureSet(MAV_texture *tex, MAV_texEnvFn pTexEnv)
{
  DDSURFACEDESC2 ddsd;
  LPDIRECTDRAWSURFACE7 miptex;
  LPDIRECTDRAWSURFACE7 nextmiptex;
  int width;
  int height;
  int x,y;
  int i, j;
  unsigned char *fromtex;
  unsigned char *totex;
  int thistex;

/* delete previous texture */
  if (mavlib_textures[tex->id]) {
    mavlib_textures[tex->id]->lpVtbl->Release (mavlib_textures[tex->id]);

    if (tex->nmaps>0) {
/* free mipmap data */
      for (i=0; i<tex->nmaps; i++)
	mav_free (tex->mipmap[i]);
      mav_free (tex->xsize);
      mav_free (tex->ysize);
      mav_free (tex->mipmap);
      tex->nmaps= 0;
    }
  }

/* create surface */
  ZeroMemory (&ddsd, sizeof (ddsd));

  ddsd.dwSize= sizeof (ddsd);
  ddsd.dwFlags= DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH|
		DDSD_PIXELFORMAT|DDSD_TEXTURESTAGE;

  ddsd.ddsCaps.dwCaps= DDSCAPS_TEXTURE;

  ddsd.dwWidth= tex->width;
  ddsd.dwHeight= tex->height;

  if (tex->mipmapped) {
/* compute (maximum) number of levels */
    if (tex->height>tex->width) {
      tex->nmaps= log10(tex->width)/L2;
    } else {
      tex->nmaps= log10(tex->height)/L2;
    }

    if (tex->nmaps>0) {
/* set surface to create mipmap surfaces automatically */
/* (they will still need to have image data written to them though) */
      ddsd.dwFlags |= DDSD_MIPMAPCOUNT;
      ddsd.ddsCaps.dwCaps |= DDSCAPS_COMPLEX|DDSCAPS_MIPMAP;
      ddsd.dwMipMapCount= tex->nmaps;
    }
  } else {
/* no mipmaps */
    tex->nmaps= 0;
  }

/* choose a suitable pixel format */
/* if D3D device has no alpha textures RGBA will be the same as RGB */
  if (tex->transparent)
    memcpy (&ddsd.ddpfPixelFormat, &mavlib_texRGBA, sizeof (DDPIXELFORMAT));
  else
    memcpy (&ddsd.ddpfPixelFormat, &mavlib_texRGB, sizeof (DDPIXELFORMAT));

/* system memory for software, let D3D manage for hardware */
  if (mavlib_gfx_software)
    ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
  else
    ddsd.ddsCaps.dwCaps2= DDSCAPS2_TEXTUREMANAGE;

  MAVD3DERR (mavlib_DD->lpVtbl->CreateSurface (mavlib_DD, &ddsd,
	&mavlib_textures[tex->id], NULL),
	"failed to create texture surface");

/* copy main texture image onto texture surface */
  mavlib_loadTextureImage (tex, mavlib_textures[tex->id], 0);

/* create mipmap levels */
  if (tex->nmaps>0) {
    tex->xsize= (int *) mav_malloc (tex->nmaps*sizeof (int));
    tex->ysize= (int *) mav_malloc (tex->nmaps*sizeof (int));
    tex->mipmap= (unsigned long **) mav_malloc (tex->nmaps *
			sizeof (unsigned long *));

    width= tex->width;
    height= tex->height;

/* get handle to the first mipmap level */
    ddsd.ddsCaps.dwCaps= DDSCAPS_TEXTURE|DDSCAPS_MIPMAP;

    MAVD3DERR (mavlib_textures[tex->id]->lpVtbl->GetAttachedSurface (
	mavlib_textures[tex->id], &ddsd.ddsCaps, &miptex),
	"failed to get mipmap surface");

    for (i=0; i<tex->nmaps; i++) {
      if (width>=2) width/=2;
      if (height>=2) height/=2;

      tex->xsize[i]= width;
      tex->ysize[i]= height;

      tex->mipmap[i]= (unsigned long *) mav_malloc (width*height*
			sizeof (unsigned long));

/* top level mipmap copies from main texture image, other level mipmaps */
/* copy from the mipmap one level above */
      if (i==0) {
        fromtex= (unsigned char *) tex->mem;
      } else {
	fromtex= (unsigned char *) tex->mipmap[i-1];
      }

/* point to mipmap being copied to */
      totex= (unsigned char *) tex->mipmap[i];

/* generate (memory) mipmap */
      for (y=0; y<height; y++) {
        for (x= 0; x<width; x++) {
	    thistex= 16*y*width+8*x;
	  for (j=0; j<4; j++) {
/* 4 pixels have equal weighting into new pixel */
	    totex[4*y*width+4*x+j]= (fromtex[thistex+j] +
			fromtex[thistex+4+j] +
			fromtex[thistex+8*width+j] +
			fromtex[thistex+8*width+4+j])/4;
	  }
        }
      }

/* load mipmap image into mipmap texture surface */
      mavlib_loadTextureImage (tex, miptex, i+1);

/* get next mipmap texture surface */
      if (i<tex->nmaps) {
        ddsd.ddsCaps.dwCaps= DDSCAPS_TEXTURE|DDSCAPS_MIPMAP;
        if (miptex->lpVtbl->GetAttachedSurface (miptex,
		&ddsd.ddsCaps, &nextmiptex) != DD_OK) {
/* surface wasn't created - adjust mipmap count */
/* hopefully this should leave no lost memory areas */
	  tex->nmaps= i;
	}
      }

/* free this level and point to next level */
      miptex->lpVtbl->Release (miptex);

      miptex= nextmiptex;
    }
  }
}


void mav_gfxTextureUse(MAV_texture tex, MAV_texEnvFn pTexEnv)
{
  if (mavlib_textures[tex.id]) {
/* set texture */
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTexture (mavlib_D3DDevice,
		0, mavlib_textures[tex.id]),
		"failed to set texture");

    mavlib_texSet= tex.id;
  } else {
    mavlib_texSet= -1;
  }

/* set texture environment */
  if (tex.texEnv) {
    (*(tex.texEnv))(&tex);
  } else {
    if (pTexEnv) (*pTexEnv)(&tex);
  }
}

void mav_gfxLightingModelSet(MAV_lightingModel info)
{
}

void mav_gfxLightingModelUse(MAV_lightingModel info)
{
  DWORD amb;

/* compute ambient light colour */
  amb= (((int)(info.ambient[0] * 255.0))<<16) +
		(((int)(info.ambient[1]*255.0))<<8) +
		(((int)(info.ambient[2]*255.0)));

/* enable ambient light */
  MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderState (mavlib_D3DDevice,
	D3DRENDERSTATE_AMBIENT, amb),
	"failed to set ambient light");


/* enable specular light */
  MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderState (mavlib_D3DDevice,
	D3DRENDERSTATE_SPECULARENABLE, TRUE),
	"failed to enable specular light");


/* enable lighting computations */
  MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderState (mavlib_D3DDevice,
	D3DRENDERSTATE_LIGHTING, TRUE),
	"failed to enable lighting");
}

void mav_gfxBufferReadSet(int buf)
{
  mavlib_gfx_readBuffer= buf;
}

void mav_gfxPixelRead(int x, int y, int width, int height, unsigned long *mem)
{
  char *image;
  int i;
  int im_i;

/* get image data */
  image= mav_malloc (width*height*3);
  mav_gfxPixelReadUByte (x, y, width, height, image);

  im_i= 0;
/* copy (with 255 alpha) to mem */
  for (i=0; i<width*height; i++) {
    mem[i]= (image[im_i]<<24) + (image[im_i+1]<<16) + (image[im_i+2]<<8) + 255;
    im_i += 3;
  }

/* free image data */
  mav_free (image);
}

void mav_gfxPixelReadUByte(int x, int y, int width, int height, unsigned char *mem)
{
  LPDIRECTDRAWSURFACE7 temp_surf;
  LPDIRECTDRAWSURFACE7 buff;
  DDSURFACEDESC2 ddsd;
  RECT rect;
  int r,g,b;
  int pix_step;
  char *ptr;
  DWORD *dwpix;
  int xp, yp;
  int i= 0;

/* need client rect info for front buffer reads */
/*
  if (mavlib_gfx_readBuffer==MAV_FRONT) {
    buff= mavlib_D3DSurface;
  } else */{
    buff= mavlib_backBuffer;
  }

/* create a surface to blt to (locking front or back buffer surfaces */
/* causes problems) */
  ZeroMemory (&ddsd, sizeof (ddsd));
  ddsd.dwSize= sizeof (ddsd);
  ddsd.dwFlags= DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT;
  ddsd.ddsCaps.dwCaps= DDSCAPS_OFFSCREENPLAIN|DDSCAPS_SYSTEMMEMORY;
  ddsd.dwWidth= width;
  ddsd.dwHeight= height;

  MAVD3DERR (mavlib_DD->lpVtbl->CreateSurface (mavlib_DD, &ddsd,
		&temp_surf, NULL),
		"failed to create pixel buffer surface");

/* blt from the buffer to the new surface (blt rect right and bottom are */
/* non-inclusive) */
  rect.left= x;
  rect.right= x+width;
  rect.top= mav_win_current->height-y-height;
  rect.bottom= mav_win_current->height-y;

  MAVD3DERR (temp_surf->lpVtbl->Blt (temp_surf,
		NULL, buff, &rect, DDBLT_WAIT, NULL),
		"failed to blt to pixel buffer");

/* lock surface - this gives us bit masks for red, green, blue and alpha, */
/* and a pointer to the surface data */
  MAVD3DERR (temp_surf->lpVtbl->Lock (temp_surf,
	NULL, &ddsd, DDLOCK_WAIT, NULL),
	"failed to lock pixel buffer");

/* get pixel byte step */
  pix_step= ddsd.ddpfPixelFormat.dwRGBBitCount/8;

/* copy pixels */
  for (yp=height-1; yp>=0; yp--) {
    ptr= (char *) ddsd.lpSurface;

/* lPitch is the width (in bytes) of a line - not necessarily the same */
/* as dwRGBBitCount * dwWidth as lines may be padded */
    ptr += yp * ddsd.lPitch;

    for (xp=0; xp<width; xp++) {
/* get RGB */
      dwpix= (DWORD *) ptr;

      r= (255*(ddsd.ddpfPixelFormat.dwRBitMask & *dwpix))/
		ddsd.ddpfPixelFormat.dwRBitMask;
      g= (255*(ddsd.ddpfPixelFormat.dwGBitMask & *dwpix))/
		ddsd.ddpfPixelFormat.dwGBitMask;
      b= (255*(ddsd.ddpfPixelFormat.dwBBitMask & *dwpix))/
		ddsd.ddpfPixelFormat.dwBBitMask;

      mem[i]= r;
      mem[i+1]= g;
      mem[i+2]= b;

/* move to next pixel */
      ptr += pix_step;
      i += 3;
    }
  }

  MAVD3DERR (temp_surf->lpVtbl->Unlock (temp_surf, NULL),
		"failed to unlock pixel buffer");

/* release surface */
  temp_surf->lpVtbl->Release (temp_surf);
}

void mav_gfxPixelDraw(int w, int h, float *v)
{
  LPDIRECTDRAWSURFACE7 temp_surf;
  DDSURFACEDESC2 ddsd;
  RECT rect;
  int pix_step;
  char *ptr;
  DWORD *dwpix;
  int xp, yp;
  int i= 0;

/* create a surface to blt from */
  ZeroMemory (&ddsd, sizeof (ddsd));
  ddsd.dwSize= sizeof (ddsd);
  ddsd.dwFlags= DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT;
  ddsd.ddsCaps.dwCaps= DDSCAPS_OFFSCREENPLAIN|DDSCAPS_SYSTEMMEMORY;
  ddsd.dwWidth= w;
  ddsd.dwHeight= h;

  MAVD3DERR (mavlib_DD->lpVtbl->CreateSurface (mavlib_DD, &ddsd,
		&temp_surf, NULL),
		"failed to create pixel buffer surface");


/* lock surface - this gives us bit masks for red, green, blue and alpha, */
/* and a pointer to the surface data */
  MAVD3DERR (temp_surf->lpVtbl->Lock (temp_surf,
	NULL, &ddsd, DDLOCK_WAIT, NULL),
	"failed to lock pixel buffer");

/* blank the surface */
  ZeroMemory (ddsd.lpSurface, ddsd.lPitch*ddsd.dwHeight);

/* get pixel byte step */
  pix_step= ddsd.ddpfPixelFormat.dwRGBBitCount/8;

/* copy pixels */
  for (yp=h-1; yp>=0; yp--) {
    ptr= (char *) ddsd.lpSurface;

/* lPitch is the width (in bytes) of a line - not necessarily the same */
/* as dwRGBBitCount * dwWidth as lines may be padded */
    ptr += yp * ddsd.lPitch;

    for (xp=0; xp<w; xp++) {
/* set RGB */
      dwpix= (DWORD *) ptr;

      *dwpix += ((DWORD)(ddsd.ddpfPixelFormat.dwRBitMask * v[i]) &
		ddsd.ddpfPixelFormat.dwRBitMask) +
		((DWORD)(ddsd.ddpfPixelFormat.dwGBitMask * v[i+1]) &
		ddsd.ddpfPixelFormat.dwGBitMask) +
		((DWORD)(ddsd.ddpfPixelFormat.dwBBitMask * v[i+2]) &
		ddsd.ddpfPixelFormat.dwBBitMask) +
		ddsd.ddpfPixelFormat.dwRGBAlphaBitMask;

/* move to next pixel */
      ptr += pix_step;
      i += 3;
    }
  }

  MAVD3DERR (temp_surf->lpVtbl->Unlock (temp_surf, NULL),
		"failed to unlock pixel buffer");

/* blt to the back buffer from the new surface */
/* blt from current 2d raster position */
  rect.left= mavlib_2d_x;
  rect.right= mavlib_2d_x+w;
  rect.top= mavlib_2d_y-h;
  rect.bottom= mavlib_2d_y;

  MAVD3DERR (mavlib_backBuffer->lpVtbl->Blt (mavlib_backBuffer,
		&rect, temp_surf, NULL, DDBLT_WAIT, NULL),
		"failed to blt to back buffer");

/* release surface */
  temp_surf->lpVtbl->Release (temp_surf);
}

void mav_gfxNormalizeSet(int i)
{
  if (i) 
  {
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderState (mavlib_D3DDevice,
	D3DRENDERSTATE_NORMALIZENORMALS, TRUE),
	"failed to set normalize");
  }
  else
  {
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderState (mavlib_D3DDevice,
	D3DRENDERSTATE_NORMALIZENORMALS, FALSE),
	"failed to reset normalize");
  }
}

void mav_gfxBackfaceCullSet(int i)
{
  if (i) 
  {
/* need clockwise vertex culling */
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderState (mavlib_D3DDevice,
	D3DRENDERSTATE_CULLMODE, D3DCULL_CW),
	"failed to set cull state");
  }
  else
  {
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderState (mavlib_D3DDevice,
	D3DRENDERSTATE_CULLMODE, D3DCULL_NONE),
	"failed to reset cull state");
  }
}

int mav_gfxBackfaceCullGet()
{
  int rv= MAV_FALSE;
  DWORD cull_state;

  MAVD3DERR (mavlib_D3DDevice->lpVtbl->GetRenderState (mavlib_D3DDevice,
	D3DRENDERSTATE_CULLMODE, &cull_state),
	"failed to get cull state");

  if (cull_state!=D3DCULL_NONE) rv= MAV_TRUE;

  return (int) rv;
}

void mav_gfxDepthTestSet(int i)
{
  if (i) 
  {
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderState (mavlib_D3DDevice,
		D3DRENDERSTATE_ZENABLE, TRUE),
		"couldn't enable Z buffer testing");
  }
  else
  {
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderState (mavlib_D3DDevice,
		D3DRENDERSTATE_ZENABLE, FALSE),
		"couldn't disable Z buffer testing");
  }
}

void mav_gfxDepthMaskSet(int i)
{
/* for D3D ZWRITEENABLE is the opposite of depth masking */

  if (i) 
  {
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderState (mavlib_D3DDevice,
		D3DRENDERSTATE_ZWRITEENABLE, FALSE),
		"couldn't enable Z buffer masking");
  }
  else
  {
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderState (mavlib_D3DDevice,
		D3DRENDERSTATE_ZWRITEENABLE, TRUE),
		"couldn't disable Z buffer masking");
  }
}

void mav_gfxViewPortSet(int x, int y, int width, int height)
{
  D3DVIEWPORT7 vp;

/* clear display */
  mav_gfxClearC ();

  vp.dwX= x;

/* D3D y=0 is top of screen, need to swap to bottom for opengl */
/* behaviour */
  vp.dwY= mav_win_current->height - y - height;
  vp.dwWidth= width;
  vp.dwHeight= height;
  vp.dvMinZ= 0.0;
  vp.dvMaxZ= 1.0;

/* if mav_win_current->height is not accurate you may get a warning */
  MAVD3DWARN (mavlib_D3DDevice->lpVtbl->SetViewport (mavlib_D3DDevice,
		&vp),
		"failed to set viewport (gfx command)");
}

void mav_gfxRasterPosSet(MAV_vector v)
{
/* transpose into screen space */
  v= mav_vectorMult4x4 (v, mavlib_worldMat);
  v= mav_vectorMult4x4 (v, mavlib_projMat);

/* save x and y pixel values */
  mavlib_2d_x= 0.5*(v.x+1.0) * (float)mav_win_current->width;
  mavlib_2d_y= 0.5*(1.0-v.y) * (float)mav_win_current->height;
}

void mav_gfxRasterPos2DSet(float x, float y)
{
  MAV_vector vt;

/* make a vector at z=0 */
  vt.x= x;
  vt.y= y;
  vt.z= 0;

/* transpose into screen space */
  vt= mav_vectorMult4x4 (vt, mavlib_worldMat);
  vt= mav_vectorMult4x4 (vt, mavlib_projMat);

/* save x and y pixel values */
  mavlib_2d_x= 0.5*(vt.x+1.0) * (float)mav_win_current->width;
  mavlib_2d_y= 0.5*(1.0-vt.y) * (float)mav_win_current->height;
}

void mav_gfxLineWidthSet(float lw)
{
}

float mav_gfxLineWidthGet(void)
{
  float rv=1;

  return rv;
}

void mav_gfxLineStippleSet(int factor, unsigned short pattern)
{
  D3DLINEPATTERN lp;
  DWORD *thing;

  if (factor>0)
  {
    lp.wRepeatFactor= factor;
    lp.wLinePattern= pattern;
  } else {
    lp.wRepeatFactor= 1;
    lp.wLinePattern= 0xffff;
  }

/* I'm sure there's a better way of doing this cast, but what the hey */
  thing= (DWORD *) &lp;

  MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderState (mavlib_D3DDevice,
		D3DRENDERSTATE_LINEPATTERN, *thing),
		"failed to set line stipple");
}

void mav_gfxBlendSet(int v)
{
  switch (v) {
  case MAV_BLEND_OFF:
/* alpha blending off */
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderState (mavlib_D3DDevice,
	D3DRENDERSTATE_ALPHABLENDENABLE, FALSE),
	"failed to disable alpha blending");
    break;

  case MAV_BLEND_1:
/* set alpha blending to alpha, 1-alpha */
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderState (mavlib_D3DDevice,
	D3DRENDERSTATE_ALPHABLENDENABLE, TRUE),
	"failed to enable alpha blending");
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderState (mavlib_D3DDevice,
	D3DRENDERSTATE_SRCBLEND, D3DBLEND_SRCALPHA),
	"failed to set alpha src mode");
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderState (mavlib_D3DDevice,
	D3DRENDERSTATE_DESTBLEND, D3DBLEND_INVSRCALPHA),
	"failed to set alpha dest mode");
    break;
  }    
}

void mav_gfxAccumSet(int mode, float val)
{
  switch (mode) {
  case MAV_ACCUM_ACCUM:

  case MAV_ACCUM_LOAD:
    break;

  case MAV_ACCUM_RETURN:
    break;

  case MAV_ACCUM_ADD:
    break;

  case MAV_ACCUM_MULT:
    break;
  }
}

int mav_gfxListsNew(int range)
{
  return -1;
}

void mav_gfxListNew(int list, int mode)
{
}

void mav_gfxListEnd(void)
{
}

void mav_gfxListExec(int list)
{
}

void mav_gfxListsExec(int n, int *lists)
{
}

void mav_gfxListsDelete(int list, int range)
{
}
