Esempio n. 1
0
void AmjuGLDX9::Scale(float x, float y, float z)
{
  AMJU_CALL_STACK;

  Assert(s_matrixMode == AmjuGL::AMJU_MODELVIEW_MATRIX);

  D3DXMATRIX m;
  D3DXMatrixIdentity(&m);
  D3DXMatrixScaling(&m, x, y, z); 
  g_matrixStack->MultMatrixLocal(&m);
  dd->SetTransform(D3DTS_WORLD, g_matrixStack->GetTop());
}
Esempio n. 2
0
void AmjuGLDX9::RotateY(float degs)
{
  AMJU_CALL_STACK;

  Assert(s_matrixMode == AmjuGL::AMJU_MODELVIEW_MATRIX);

  D3DXMATRIX m;
  D3DXMatrixIdentity(&m);
  D3DXMatrixRotationY(&m, D3DXToRadian(degs)); 
  g_matrixStack->MultMatrixLocal( &m);
  dd->SetTransform(D3DTS_WORLD, g_matrixStack->GetTop());
}
Esempio n. 3
0
void AmjuGLDX9::LookAt(float eyeX, float eyeY, float eyeZ, float x, float y, float z, float upX, float upY, float upZ)
{
  AMJU_CALL_STACK;

  D3DXVECTOR3 vEyePt(eyeX, eyeY, eyeZ);
  D3DXVECTOR3 vLookatPt(x, y, z);
  D3DXVECTOR3 vUpVec(upX, upY, upZ);
  D3DXMATRIX matLookAt;
  D3DXMatrixLookAtRH( &matLookAt, &vEyePt, &vLookatPt, &vUpVec );

  g_matrixStack->LoadIdentity();
  g_matrixStack->LoadMatrix( &matLookAt );
}
Esempio n. 4
0
void AmjuGLDX9::Translate(float x, float y, float z)
{
  AMJU_CALL_STACK;

  Assert(s_matrixMode == AmjuGL::AMJU_MODELVIEW_MATRIX);

  // TODO Use current matrix mode, or scrap the idea of 
  // manipulating proj and tex mats
  D3DXMATRIX m;
  D3DXMatrixIdentity(&m);
  D3DXMatrixTranslation(&m, x, y, z);

  g_matrixStack->MultMatrixLocal( &m);
  // WORLD -- VIEW matrix is always just identity
  dd->SetTransform( D3DTS_WORLD, g_matrixStack->GetTop() );
}
Esempio n. 5
0
void AmjuGLDX9::SetIdentity()
{
  AMJU_CALL_STACK;

  if (s_matrixMode == AmjuGL::AMJU_MODELVIEW_MATRIX)
  {
    g_matrixStack->LoadIdentity();
    dd->SetTransform(D3DTS_WORLD, g_matrixStack->GetTop());
  }
  else if (s_matrixMode == AmjuGL::AMJU_PROJECTION_MATRIX)
  {
    D3DXMATRIX mat;
    D3DXMatrixIdentity(&mat);
    dd->SetTransform(D3DTS_PROJECTION, &mat);
  }
}
Esempio n. 6
0
void Node::render()
{
   LPD3DXMATRIXSTACK stack = getStack();

   stack->Push();
   stack->MultMatrixLocal(&_matrix);

   getDevice()->SetTransform(D3DTS_WORLDMATRIX(0), stack->GetTop());

   _object->render();

   for (int i = 0; i != _children.size(); i++)
      _children[i]->render();

   stack->Pop();
}
Esempio n. 7
0
void AmjuGLDX9::PopMatrix()
{
  AMJU_CALL_STACK;

  if (s_matrixMode == AmjuGL::AMJU_MODELVIEW_MATRIX)
  {
    g_matrixStack->Pop();
    D3DXMATRIX* m = g_matrixStack->GetTop();
    // We always load Identity before Pushing, right ?
    Assert(m);
    dd->SetTransform(D3DTS_WORLD,  m);
  }
  else
  {
    Assert(0);
  }
}
Esempio n. 8
0
void AmjuGLDX9::MultMatrix(const float m[16])
{
  D3DXMATRIX d3dm(
    m[0], m[1], m[2],  m[3],
    m[4], m[5], m[6],  m[7],
    m[8], m[9], m[10], m[11],
    m[12], m[13], m[14], m[15]);

  if (s_matrixMode == AmjuGL::AMJU_MODELVIEW_MATRIX)
  {
    g_matrixStack->MultMatrixLocal(&d3dm);
    dd->SetTransform(D3DTS_WORLD, g_matrixStack->GetTop());
  }
  else if (s_matrixMode == AmjuGL::AMJU_PROJECTION_MATRIX)
  {
    Assert(0);
  }
}
Esempio n. 9
0
void AmjuGLDX9::RotateZ(float degs)
{
  AMJU_CALL_STACK;

  D3DXMATRIX m;
  D3DXMatrixIdentity(&m);
  D3DXMatrixRotationZ(&m, D3DXToRadian(degs)); 

  if (s_matrixMode == AmjuGL::AMJU_MODELVIEW_MATRIX)
  {
    g_matrixStack->MultMatrixLocal( &m);
    dd->SetTransform(D3DTS_WORLD, g_matrixStack->GetTop());
  }
  else
  {
    matProj *= m;
    dd->SetTransform(D3DTS_PROJECTION, &matProj);  
  }
}
Esempio n. 10
0
void AmjuGLDX9::InitFrame(float clearR, float clearG, float clearB)
{
  AMJU_CALL_STACK;

  dd->BeginScene();


  dd->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
    D3DCOLOR_COLORVALUE(clearR, clearG, clearB, 1.0f ), 1.0f, 0 );

  g_matrixStack->LoadIdentity();
}
Esempio n. 11
0
void AmjuGLDX9::PushMatrix()
{
  AMJU_CALL_STACK;

  if (s_matrixMode == AmjuGL::AMJU_MODELVIEW_MATRIX)
  {
    g_matrixStack->Push();
  }
  else
  {
    Assert(0);
  }
}
Esempio n. 12
0
void AmjuGLDX9::GetMatrix(AmjuGL::MatrixMode mm, float result[16])
{
  AMJU_CALL_STACK;

  switch (mm)
  {
  case AmjuGL::AMJU_MODELVIEW_MATRIX:
    {
    const D3DXMATRIX* pm = g_matrixStack->GetTop();
    memcpy(result, pm, 16 * sizeof(float)); 
    }
    break;

  case AmjuGL::AMJU_PROJECTION_MATRIX:
    memcpy(result, &matProj, 16 * sizeof(float));     
    break;
  
  case AmjuGL::AMJU_TEXTURE_MATRIX:
    // Not supported
    break;
  }
}
Esempio n. 13
0
namespace Amju
{
LPDIRECT3DDEVICE9 AmjuGLDX9::dd = 0;

// Modelview matrix stack
static LPD3DXMATRIXSTACK g_matrixStack = NULL;

// Projection matrix
static D3DXMATRIX matProj;

static D3DVIEWPORT9 s_viewPort;

static AmjuGL::MatrixMode s_matrixMode = AmjuGL::AMJU_MODELVIEW_MATRIX;

AmjuGLDX9::AmjuGLDX9(WNDPROC wndproc)
{
  AMJU_CALL_STACK;

  m_wndproc = wndproc;
}

/*
void AmjuGLDX9::Shutdown()
{
  AMJU_CALL_STACK;

  // TODO
  // Add to AmjuGL
    if( g_matrixStack != NULL )
        g_matrixStack->Release();
}
*/

void AmjuGLDX9::BeginScene()
{
  AMJU_CALL_STACK;

  // Done in Init[Scene] so we clear buffers before calling [dx9]BeginScene
}

void AmjuGLDX9::EndScene()
{
  AMJU_CALL_STACK;

  dd->EndScene();
  dd->Present( NULL, NULL, NULL, NULL );
}

void AmjuGLDX9::Flip()
{
  //ValidateRect((HWND)GetHWnd(), 0);
}

void AmjuGLDX9::Viewport(int x, int y, int w, int h)
{
  /*
  AMJU_CALL_STACK;

  s_viewPort.X      = x;
  s_viewPort.Y      = y;
  s_viewPort.Width  = w;
  s_viewPort.Height = h;
  s_viewPort.MinZ   = 0.0f;
  s_viewPort.MaxZ   = 1.0f;

  dd->SetViewport(&s_viewPort);
  */
}

void AmjuGLDX9::Init()
{
  AMJU_CALL_STACK;

  // Set cullmode 
	dd->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);

  Enable(AmjuGL::AMJU_DEPTH_READ);
  Disable(AmjuGL::AMJU_LIGHTING);

  D3DXCreateMatrixStack( 0, &g_matrixStack );

  // Set view matrix to identity and leave it alone
  D3DXMATRIX matView;
  D3DXMatrixIdentity(&matView);
  dd->SetTransform(D3DTS_VIEW, &matView);

  // Set texture sampling: if you don't do this you get horible aliased textures.
  // TODO Make sure mipmapping is enabled.
	dd->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
	dd->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
  // Mipmap filter
  // This shows that mip mapping is on - LINEAR looks best.
  // TODO Turn this on when we create mip map data!
//  dd->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT); // LINEAR);

  // Set up blend mode
  dd->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
  dd->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
  dd->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

  // Ignore vertex colours when doing lighting calculations
  //  -- makes no difference ??!
  dd->SetRenderState( D3DRS_COLORVERTEX, FALSE );
}

void AmjuGLDX9::InitFrame(float clearR, float clearG, float clearB)
{
  AMJU_CALL_STACK;

  dd->BeginScene();


  dd->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
    D3DCOLOR_COLORVALUE(clearR, clearG, clearB, 1.0f ), 1.0f, 0 );

  g_matrixStack->LoadIdentity();
}

void AmjuGLDX9::SetPerspectiveProjection(
  float fov, float aspectRatio, float nearDist, float farDist)
{
  AMJU_CALL_STACK;

  // Use RH version for compatibility with gluPerspective() etc
  D3DXMatrixPerspectiveFovRH(
    &matProj, 
    D3DXToRadian(fov), 
    aspectRatio, 
    nearDist,  
    farDist); 

  dd->SetTransform(D3DTS_PROJECTION, &matProj);
}

void AmjuGLDX9::LookAt(float eyeX, float eyeY, float eyeZ, float x, float y, float z, float upX, float upY, float upZ)
{
  AMJU_CALL_STACK;

  D3DXVECTOR3 vEyePt(eyeX, eyeY, eyeZ);
  D3DXVECTOR3 vLookatPt(x, y, z);
  D3DXVECTOR3 vUpVec(upX, upY, upZ);
  D3DXMATRIX matLookAt;
  D3DXMatrixLookAtRH( &matLookAt, &vEyePt, &vLookatPt, &vUpVec );

  g_matrixStack->LoadIdentity();
  g_matrixStack->LoadMatrix( &matLookAt );
}

void AmjuGLDX9::SetColour(float r, float g, float b, float a)
{
  AMJU_CALL_STACK;

  // Set up a material -- do we need to do this???
  D3DMATERIAL9 material;

  ZeroMemory( &material, sizeof(D3DMATERIAL9) );

  material.Diffuse.r = r;
  material.Diffuse.g = g;
  material.Diffuse.b = b;
  material.Diffuse.a = a;

  material.Ambient.r = r;
  material.Ambient.g = g;
  material.Ambient.b = b;
  material.Ambient.a = a;

  material.Emissive.r = 0.0f;
  material.Emissive.g = 0.0f;
  material.Emissive.b = 0.0f;
  material.Emissive.a = 0.0f; // ???

  const float SPEC = 0;
  material.Specular.r = SPEC;
  material.Specular.g = SPEC;
  material.Specular.b = SPEC;
  material.Specular.a = SPEC;

  // Set the Power value to a small number to make the highlight's size bigger.
  material.Power = 5.0f;

  // Another way to ignore per-vertex colors is to simply force all lighting 
  // calculations to use your material settings explicitly like so...

  dd->SetRenderState( D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL );
//  dd->SetRenderState( D3DRS_SPECULARMATERIALSOURCE, D3DMCS_MATERIAL );
  dd->SetRenderState( D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL );
//  dd->SetRenderState( D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL );

  dd->SetMaterial(&material);

}

static const uint32 D3DFVF_AMJU_VERTEX = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1;
struct AMJU_FVF
{
  D3DVECTOR pos;
  D3DVECTOR normal;
  float m_vecTex[2];
};

void AmjuGLDX9::DrawLine(const AmjuGL::Vec3& v1, const AmjuGL::Vec3& v2)
{
  AMJU_CALL_STACK;

}

/*
void AmjuGLDX9::DrawQuad(AmjuGL::Vert* verts)
{
  AMJU_CALL_STACK;

  AmjuGL::Tris tris;
  AmjuGL::Tri t1;
  t1.m_verts[0] = verts[0];
  t1.m_verts[1] = verts[1];
  t1.m_verts[2] = verts[2];
  tris.push_back(t1);
  AmjuGL::Tri t2;
  t2.m_verts[0] = verts[2];
  t2.m_verts[1] = verts[3];
  t2.m_verts[2] = verts[0];
  tris.push_back(t2);
  DrawTriList(tris);
}

void AmjuGLDX9::DrawQuadList(const AmjuGL::Quads& quads)
{
  AMJU_CALL_STACK;

  // TODO Quad List doesn't exist in DX9. Nor in GL ES, so we need a reusable
  //  function to convert to Tri list. 
}
*/

void AmjuGLDX9::DrawTriList(const AmjuGL::Tris& tris)
{
  AMJU_CALL_STACK;

  int numTris = tris.size();

  dd->SetFVF(D3DFVF_AMJU_VERTEX);
  dd->DrawPrimitiveUP(
    D3DPT_TRIANGLELIST, 
    numTris,
    &tris[0],
    sizeof(AMJU_FVF));
}

void AmjuGLDX9::DrawIndexedTriList(
  const AmjuGL::Verts& verts,
  const AmjuGL::IndexedTriList& indexes)
{
  AMJU_CALL_STACK;

  // TODO
}

void AmjuGLDX9::SetMatrixMode(AmjuGL::MatrixMode m) 
{
  AMJU_CALL_STACK;

  s_matrixMode = m;
}

void AmjuGLDX9::SetIdentity()
{
  AMJU_CALL_STACK;

  if (s_matrixMode == AmjuGL::AMJU_MODELVIEW_MATRIX)
  {
    g_matrixStack->LoadIdentity();
    dd->SetTransform(D3DTS_WORLD, g_matrixStack->GetTop());
  }
  else if (s_matrixMode == AmjuGL::AMJU_PROJECTION_MATRIX)
  {
    D3DXMATRIX mat;
    D3DXMatrixIdentity(&mat);
    dd->SetTransform(D3DTS_PROJECTION, &mat);
  }
}

void AmjuGLDX9::MultMatrix(const float m[16])
{
  D3DXMATRIX d3dm(
    m[0], m[1], m[2],  m[3],
    m[4], m[5], m[6],  m[7],
    m[8], m[9], m[10], m[11],
    m[12], m[13], m[14], m[15]);

  if (s_matrixMode == AmjuGL::AMJU_MODELVIEW_MATRIX)
  {
    g_matrixStack->MultMatrixLocal(&d3dm);
    dd->SetTransform(D3DTS_WORLD, g_matrixStack->GetTop());
  }
  else if (s_matrixMode == AmjuGL::AMJU_PROJECTION_MATRIX)
  {
    Assert(0);
  }
}

void AmjuGLDX9::PushMatrix()
{
  AMJU_CALL_STACK;

  if (s_matrixMode == AmjuGL::AMJU_MODELVIEW_MATRIX)
  {
    g_matrixStack->Push();
  }
  else
  {
    Assert(0);
  }
}

void AmjuGLDX9::PopMatrix()
{
  AMJU_CALL_STACK;

  if (s_matrixMode == AmjuGL::AMJU_MODELVIEW_MATRIX)
  {
    g_matrixStack->Pop();
    D3DXMATRIX* m = g_matrixStack->GetTop();
    // We always load Identity before Pushing, right ?
    Assert(m);
    dd->SetTransform(D3DTS_WORLD,  m);
  }
  else
  {
    Assert(0);
  }
}

void AmjuGLDX9::Translate(float x, float y, float z)
{
  AMJU_CALL_STACK;

  Assert(s_matrixMode == AmjuGL::AMJU_MODELVIEW_MATRIX);

  // TODO Use current matrix mode, or scrap the idea of 
  // manipulating proj and tex mats
  D3DXMATRIX m;
  D3DXMatrixIdentity(&m);
  D3DXMatrixTranslation(&m, x, y, z);

  g_matrixStack->MultMatrixLocal( &m);
  // WORLD -- VIEW matrix is always just identity
  dd->SetTransform( D3DTS_WORLD, g_matrixStack->GetTop() );
}

void AmjuGLDX9::Scale(float x, float y, float z)
{
  AMJU_CALL_STACK;

  Assert(s_matrixMode == AmjuGL::AMJU_MODELVIEW_MATRIX);

  D3DXMATRIX m;
  D3DXMatrixIdentity(&m);
  D3DXMatrixScaling(&m, x, y, z); 
  g_matrixStack->MultMatrixLocal(&m);
  dd->SetTransform(D3DTS_WORLD, g_matrixStack->GetTop());
}

void AmjuGLDX9::RotateX(float degs)
{
  AMJU_CALL_STACK;

  Assert(s_matrixMode == AmjuGL::AMJU_MODELVIEW_MATRIX);

  D3DXMATRIX m;
  D3DXMatrixIdentity(&m);
  D3DXMatrixRotationX(&m, D3DXToRadian(degs)); 
  g_matrixStack->MultMatrixLocal( &m);
  dd->SetTransform(D3DTS_WORLD, g_matrixStack->GetTop());
}

void AmjuGLDX9::RotateY(float degs)
{
  AMJU_CALL_STACK;

  Assert(s_matrixMode == AmjuGL::AMJU_MODELVIEW_MATRIX);

  D3DXMATRIX m;
  D3DXMatrixIdentity(&m);
  D3DXMatrixRotationY(&m, D3DXToRadian(degs)); 
  g_matrixStack->MultMatrixLocal( &m);
  dd->SetTransform(D3DTS_WORLD, g_matrixStack->GetTop());
}

void AmjuGLDX9::RotateZ(float degs)
{
  AMJU_CALL_STACK;

  D3DXMATRIX m;
  D3DXMatrixIdentity(&m);
  D3DXMatrixRotationZ(&m, D3DXToRadian(degs)); 

  if (s_matrixMode == AmjuGL::AMJU_MODELVIEW_MATRIX)
  {
    g_matrixStack->MultMatrixLocal( &m);
    dd->SetTransform(D3DTS_WORLD, g_matrixStack->GetTop());
  }
  else
  {
    matProj *= m;
    dd->SetTransform(D3DTS_PROJECTION, &matProj);  
  }
}

void AmjuGLDX9::GetMatrix(AmjuGL::MatrixMode mm, float result[16])
{
  AMJU_CALL_STACK;

  switch (mm)
  {
  case AmjuGL::AMJU_MODELVIEW_MATRIX:
    {
    const D3DXMATRIX* pm = g_matrixStack->GetTop();
    memcpy(result, pm, 16 * sizeof(float)); 
    }
    break;

  case AmjuGL::AMJU_PROJECTION_MATRIX:
    memcpy(result, &matProj, 16 * sizeof(float));     
    break;
  
  case AmjuGL::AMJU_TEXTURE_MATRIX:
    // Not supported
    break;
  }
}

void AmjuGLDX9::PushAttrib(uint32 attrib)
{
  AMJU_CALL_STACK;

}

void AmjuGLDX9::PopAttrib()
{
  AMJU_CALL_STACK;

}

static void EnableZWrite(bool writeToZ)
{
  AMJU_CALL_STACK;

  // TODO Better function name
  AmjuGLDX9::dd->SetRenderState(D3DRS_ZWRITEENABLE, writeToZ ? D3DZB_TRUE : D3DZB_FALSE);
}

void AmjuGLDX9::Enable(uint32 flag)
{
  AMJU_CALL_STACK;

  switch (flag)
  {
  case AmjuGL::AMJU_DEPTH_WRITE:
    EnableZWrite(true);
    break;

  case AmjuGL::AMJU_DEPTH_READ:
    dd->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
    break;

  case AmjuGL::AMJU_LIGHTING:
    dd->SetRenderState(D3DRS_LIGHTING, TRUE);
    dd->LightEnable( 0, TRUE );
    {
      const float A = 0.5f;
      dd->SetRenderState( D3DRS_AMBIENT, D3DCOLOR_COLORVALUE(A, A, A, 1.0f ) );
    }
    break;

  case AmjuGL::AMJU_BLEND:
    dd->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
    break;

  case AmjuGL::AMJU_TEXTURE_2D:
//    dd->SetTexture(0, mostRecentlyUsedTexture); // TODO
    break;
  }
}

void AmjuGLDX9::Disable(uint32 flag)
{
  AMJU_CALL_STACK;

  switch (flag)
  {
  case AmjuGL::AMJU_DEPTH_WRITE:
    EnableZWrite(false);
    break;

  case AmjuGL::AMJU_DEPTH_READ:
    dd->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
    break;

  case AmjuGL::AMJU_LIGHTING:
    // TODO TEMP TEST
    // If lighting is disabled, set the ambient colour to white.
    // Then the material colour's ambient value will be used.
    dd->SetRenderState( D3DRS_AMBIENT, D3DCOLOR_COLORVALUE( 1.0f, 1.0f, 1.0f, 1.0f ) );
    
//    dd->SetRenderState(D3DRS_LIGHTING, FALSE);
//    dd->LightEnable( 0, FALSE);
    break;

  case AmjuGL::AMJU_BLEND:
    dd->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
    break;

  case AmjuGL::AMJU_TEXTURE_2D:
    dd->SetTexture(0, 0); 
    break;
  }
}

void AmjuGLDX9::DestroyTextureHandle(AmjuGL::TextureHandle*)
{
  AMJU_CALL_STACK;

  // TODO Release texture 
}

static void CopyRGBATexture(uint8* texels, int pitch, const uint8* data, int width, int height)
{
  AMJU_CALL_STACK;

  for (int y = 0; y < height; y++)
  {
    uint8* row = texels + y * pitch;

    for (int x = 0; x < width; x++)
    {
      row[0] = data[2]; 
      row[1] = data[1];
      row[2] = data[0];
      row[3] = data[3]; // Alpha

      row += 4;
      data += 4;
    }
  }
}

static void CopyRGBTexture(uint8* texels, int pitch, const uint8* data, int width, int height)
{
  AMJU_CALL_STACK;

  for (int y = 0; y < height; y++)
  {
    uint8* row = texels + y * pitch;

    for (int x = 0; x < width; x++)
    {
        // RGB -> BGR
      row[0] = data[2];
      row[1] = data[1];
      row[2] = data[0];
      // RGB texture still has 16 bytes per pixel apprently.
      row[3] = 0;
      row += 4;
      data += 3;
    }
  }
}

void AmjuGLDX9::SetTexture(
  AmjuGL::TextureHandle* th, 
  AmjuGL::TextureType tt, 
  AmjuGL::TextureDepth td,  
  int width, 
  int height, 
  uint8* data)
{
  AMJU_CALL_STACK;

  HRESULT res = D3DXCreateTexture(
    dd, //LPDIRECT3DDEVICE9 pDevice,
    width, //UINT Width,
    height, //UINT Height,
    0, //  Mip Levels: 0 means create all
    D3DUSAGE_DYNAMIC, //DWORD Usage,
    (td == AmjuGL::AMJU_RGB ? D3DFMT_R8G8B8 : D3DFMT_A8R8G8B8), //D3DFORMAT Format,
    D3DPOOL_DEFAULT, // Pool,
    reinterpret_cast<LPDIRECT3DTEXTURE9*>(th) //LPDIRECT3DTEXTURE9 * ppTexture
  );

  LPDIRECT3DTEXTURE9 pTex = reinterpret_cast<LPDIRECT3DTEXTURE9>(*th);

  D3DLOCKED_RECT lockedRect;
  pTex->LockRect(0, &lockedRect, NULL, 0);

  switch (td)
  {
  case AmjuGL::AMJU_RGB:
    CopyRGBTexture((uint8*)lockedRect.pBits, lockedRect.Pitch, data, width, height); 
    break;
  case AmjuGL::AMJU_RGBA:
    CopyRGBATexture((uint8*)lockedRect.pBits, lockedRect.Pitch, data, width, height); 
    break;
  }

  pTex->UnlockRect(0);

  // TODO Create data for each mipmap level.
  // NB We want the same functionality as screenshot shrinking.
  IDirect3DSurface9 * pSurfaceLevel;
  for (unsigned int iLevel = 0; iLevel < pTex->GetLevelCount(); iLevel++)
  {
    pTex->GetSurfaceLevel(iLevel, &pSurfaceLevel);

    // TODO Write this mip map
    
    pSurfaceLevel->Release();
  }
}

void AmjuGLDX9::UseTexture(AmjuGL::TextureHandle th)
{
  AMJU_CALL_STACK;

  LPDIRECT3DTEXTURE9 pTex = reinterpret_cast<LPDIRECT3DTEXTURE9>(th);
  dd->SetTexture(0, pTex);

  // TODO Maybe we need to set that we want to modulate the current colour
  // with the texture.

  // Setup our texture. Using Textures introduces the texture stage states,
  // which govern how Textures get blended together (in the case of multiple
  // Textures) and lighting information. In this case, we are modulating
  // (blending) our texture with the diffuse color of the vertices.
//  dd->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );
//  dd->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
//  dd->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
}

void AmjuGLDX9::SetTextureMode(AmjuGL::TextureType tt)
{
  AMJU_CALL_STACK;

}

void AmjuGLDX9::GetScreenshot(unsigned char* buffer, int w, int h)
{
  AMJU_CALL_STACK;

  IDirect3DSurface9* pSurface = 0;

  dd->GetBackBuffer(
    0, // UINT  iSwapChain,
    0, // UINT BackBuffer,
    D3DBACKBUFFER_TYPE_MONO, // D3DBACKBUFFER_TYPE Type,
    &pSurface); //IDirect3DSurface9 ** ppBackBuffer

  D3DSURFACE_DESC desc;
  pSurface->GetDesc(&desc);

  D3DLOCKED_RECT lockedRect;
  HRESULT hr = pSurface->LockRect(&lockedRect, NULL, D3DLOCK_READONLY);
  if (SUCCEEDED(hr))
  {
    // Do we know if we are windowed or not ? We have to crop the window edges if so.

    // Copy surface into buffer. 
    // NB Buffer format is R8G8B8
    //h = MIN(h, desc.Height);
    //w = MIN(w, desc.Width);
    for (int i = 0; i < h; i++)
    {
      unsigned char* rowStart = (unsigned char*)lockedRect.pBits + i * lockedRect.Pitch;
      unsigned char* bufStart = buffer + i * w * 3;
      for (int j = 0; j < w; j++)
      {
        bufStart[0] = rowStart[0]; 
        bufStart[1] = rowStart[1]; 
        bufStart[2] = rowStart[2];
        bufStart += 3;
        rowStart += 4;
      }
    }
  }

  pSurface->UnlockRect(); // even if LockRect failed ?
  pSurface->Release(); // ?
}

void AmjuGLDX9::DrawLighting(
  const AmjuGL::LightColour& globalAmbient, 
  const AmjuGL::LightColour& lightAmbient,
  const AmjuGL::LightColour& lightDiffuse,
  const AmjuGL::LightColour& lightSpecular,
  const AmjuGL::Vec3& lightPos)
{
  AMJU_CALL_STACK;

  D3DLIGHT9         g_pLight0;

  g_pLight0.Type = D3DLIGHT_DIRECTIONAL;

  // Light direction seems to be backwards compared to OpenGL/Wii 
  D3DXVECTOR3 vecDir = D3DXVECTOR3(-lightPos.m_x, lightPos.m_y, -lightPos.m_z);

  D3DXVec3Normalize( (D3DXVECTOR3*)&g_pLight0.Direction, &vecDir);

  // We can set global ambient with SetRenderState, OR set it here
  const float A = 0.0f;
  const float D = 1.0f;

  g_pLight0.Ambient.r = A;
  g_pLight0.Ambient.g = A;
  g_pLight0.Ambient.b = A;
  g_pLight0.Ambient.a = 1.0f;

  g_pLight0.Diffuse.r = D;
  g_pLight0.Diffuse.g = D;
  g_pLight0.Diffuse.b = D;
  g_pLight0.Diffuse.a = 1.0f;

  // This would do something if we set a specular component in the material
  const float SPEC = 0;
  g_pLight0.Specular.r = SPEC;
  g_pLight0.Specular.g = SPEC;
  g_pLight0.Specular.b = SPEC;
  g_pLight0.Specular.a = SPEC;

  dd->SetLight( 0, &g_pLight0 );
  dd->LightEnable( 0, TRUE );

  // Tutorial says:
  // Be careful when setting up ambient lighting. You can very easily wash 
  // out the material's specular highlights.
  // TODO does this do anything ?
//  dd->SetRenderState( D3DRS_AMBIENT, D3DCOLOR_COLORVALUE( 0.5f, 0.5f, 0.5f, 1.0f ) );
}

Shader* AmjuGLDX9::LoadShader(const std::string& shaderFileName)
{
  DX9Shader* s = new DX9Shader;
  // TODO Add "dx9" to shader file name
  if (!s->Load(shaderFileName + ".fx"))
  {
    // TODO Report Error ? Or gracefully fall back to null shader ?
    delete s;
    return new ShaderNull;
  }
  return s;
}

} // namespace