Implement Material, Clip plane and Light support plus
MultiplyTransform.

diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c
index 610e4c5..bd0867c 100644
--- a/dlls/wined3d/device.c
+++ b/dlls/wined3d/device.c
@@ -35,6 +35,94 @@
 /**********************************************************
  * Utility functions follow
  **********************************************************/
+/* Convert the D3DLIGHT properties into equivalent gl lights */
+void setup_light(IWineD3DDevice *iface, LONG Index, PLIGHTINFOEL *lightInfo) {
+
+    float quad_att;
+    float colRGBA[] = {0.0, 0.0, 0.0, 0.0};
+    IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
+
+    /* Light settings are affected by the model view in OpenGL, the View transform in direct3d*/
+    glMatrixMode(GL_MODELVIEW);
+    glPushMatrix();
+    glLoadMatrixf((float *) &This->stateBlock->transforms[D3DTS_VIEW].u.m[0][0]);
+
+    /* Diffuse: */
+    colRGBA[0] = lightInfo->OriginalParms.Diffuse.r;
+    colRGBA[1] = lightInfo->OriginalParms.Diffuse.g;
+    colRGBA[2] = lightInfo->OriginalParms.Diffuse.b;
+    colRGBA[3] = lightInfo->OriginalParms.Diffuse.a;
+    glLightfv(GL_LIGHT0+Index, GL_DIFFUSE, colRGBA);
+    checkGLcall("glLightfv");
+
+    /* Specular */
+    colRGBA[0] = lightInfo->OriginalParms.Specular.r;
+    colRGBA[1] = lightInfo->OriginalParms.Specular.g;
+    colRGBA[2] = lightInfo->OriginalParms.Specular.b;
+    colRGBA[3] = lightInfo->OriginalParms.Specular.a;
+    glLightfv(GL_LIGHT0+Index, GL_SPECULAR, colRGBA);
+    checkGLcall("glLightfv");
+
+    /* Ambient */
+    colRGBA[0] = lightInfo->OriginalParms.Ambient.r;
+    colRGBA[1] = lightInfo->OriginalParms.Ambient.g;
+    colRGBA[2] = lightInfo->OriginalParms.Ambient.b;
+    colRGBA[3] = lightInfo->OriginalParms.Ambient.a;
+    glLightfv(GL_LIGHT0+Index, GL_AMBIENT, colRGBA);
+    checkGLcall("glLightfv");
+
+    /* Attenuation - Are these right? guessing... */
+    glLightf(GL_LIGHT0+Index, GL_CONSTANT_ATTENUATION,  lightInfo->OriginalParms.Attenuation0);
+    checkGLcall("glLightf");
+    glLightf(GL_LIGHT0+Index, GL_LINEAR_ATTENUATION,    lightInfo->OriginalParms.Attenuation1);
+    checkGLcall("glLightf");
+
+    quad_att = 1.4/(lightInfo->OriginalParms.Range*lightInfo->OriginalParms.Range);
+    if (quad_att < lightInfo->OriginalParms.Attenuation2) quad_att = lightInfo->OriginalParms.Attenuation2;
+    glLightf(GL_LIGHT0+Index, GL_QUADRATIC_ATTENUATION, quad_att);
+    checkGLcall("glLightf");
+
+    switch (lightInfo->OriginalParms.Type) {
+    case D3DLIGHT_POINT:
+        /* Position */
+        glLightfv(GL_LIGHT0+Index, GL_POSITION, &lightInfo->lightPosn[0]);
+        checkGLcall("glLightfv");
+        glLightf(GL_LIGHT0 + Index, GL_SPOT_CUTOFF, lightInfo->cutoff);
+        checkGLcall("glLightf");
+        /* FIXME: Range */
+        break;
+
+    case D3DLIGHT_SPOT:
+        /* Position */
+        glLightfv(GL_LIGHT0+Index, GL_POSITION, &lightInfo->lightPosn[0]);
+        checkGLcall("glLightfv");
+        /* Direction */
+        glLightfv(GL_LIGHT0+Index, GL_SPOT_DIRECTION, &lightInfo->lightDirn[0]);
+        checkGLcall("glLightfv");
+        glLightf(GL_LIGHT0 + Index, GL_SPOT_EXPONENT, lightInfo->exponent);
+        checkGLcall("glLightf");
+        glLightf(GL_LIGHT0 + Index, GL_SPOT_CUTOFF, lightInfo->cutoff);
+        checkGLcall("glLightf");
+        /* FIXME: Range */
+        break;
+
+    case D3DLIGHT_DIRECTIONAL:
+        /* Direction */
+        glLightfv(GL_LIGHT0+Index, GL_POSITION, &lightInfo->lightPosn[0]); /* Note gl uses w position of 0 for direction! */
+        checkGLcall("glLightfv");
+        glLightf(GL_LIGHT0+Index, GL_SPOT_CUTOFF, lightInfo->cutoff);
+        checkGLcall("glLightf");
+        glLightf(GL_LIGHT0+Index, GL_SPOT_EXPONENT, 0.0f);
+        checkGLcall("glLightf");
+        break;
+
+    default:
+        FIXME("Unrecognized light type %d\n", lightInfo->OriginalParms.Type);
+    }
+
+    /* Restore the modelview matrix */
+    glPopMatrix();
+}
 
 /**********************************************************
  * IWineD3DDevice implementation follows
@@ -194,7 +282,7 @@
 }
 
 /*****
- * Get / Set Transform
+ * Get / Set & Multipy Transform
  *****/
 HRESULT  WINAPI  IWineD3DDeviceImpl_SetTransform(IWineD3DDevice *iface, D3DTRANSFORMSTATETYPE d3dts, CONST D3DMATRIX* lpmatrix) {
     IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
@@ -264,10 +352,7 @@
         }
 
     } else if (d3dts == D3DTS_VIEW) { /* handle the VIEW matrice */
-
-#if 0 /* TODO: */
         unsigned int k;
-#endif
 
         /* If we are changing the View matrix, reset the light and clipping planes to the new view   
          * NOTE: We have to reset the positions even if the light/plane is not currently
@@ -277,18 +362,16 @@
         
         This->modelview_valid = FALSE;
         This->view_ident = !memcmp(lpmatrix, identity, 16*sizeof(float));
-#if 0 /* TODO: */
         PLIGHTINFOEL *lightChain = NULL;
-#endif
+
         glMatrixMode(GL_MODELVIEW);
         checkGLcall("glMatrixMode(GL_MODELVIEW)");
         glPushMatrix();
         glLoadMatrixf((float *)lpmatrix);
         checkGLcall("glLoadMatrixf(...)");
 
-#if 0 /* TODO: */
         /* Reset lights */
-        lightChain = This->StateBlock->lights;
+        lightChain = This->stateBlock->lights;
         while (lightChain && lightChain->glIndex != -1) {
             glLightfv(GL_LIGHT0 + lightChain->glIndex, GL_POSITION, lightChain->lightPosn);
             checkGLcall("glLightfv posn");
@@ -296,12 +379,12 @@
             checkGLcall("glLightfv dirn");
             lightChain = lightChain->next;
         }
+
         /* Reset Clipping Planes if clipping is enabled */
         for (k = 0; k < GL_LIMITS(clipplanes); k++) {
-            glClipPlane(GL_CLIP_PLANE0 + k, This->StateBlock->clipplane[k]);
+            glClipPlane(GL_CLIP_PLANE0 + k, This->stateBlock->clipplane[k]);
             checkGLcall("glClipPlane");
         }
-#endif
         glPopMatrix();
 
     } else { /* What was requested!?? */
@@ -320,6 +403,626 @@
     return D3D_OK;
 }
 
+HRESULT WINAPI IWineD3DDeviceImpl_MultiplyTransform(IWineD3DDevice *iface, D3DTRANSFORMSTATETYPE State, CONST D3DMATRIX* pMatrix) {
+    D3DMATRIX *mat = NULL;
+    D3DMATRIX temp;
+
+    /* Note: Using 'updateStateBlock' rather then 'stateblock' in the code below
+        means it will be recorded in a state block change, but iworks regardless of recording being on. 
+        If this is found to be wrong, change to StateBlock.                             */
+    IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
+    TRACE("(%p) : For state %u\n", This, State);
+
+    if (State < HIGHEST_TRANSFORMSTATE)
+    {
+        mat = &This->updateStateBlock->transforms[State];
+    } else {
+        FIXME("Unhandled transform state!!\n");
+    }
+
+    /* Copied from ddraw code:  */
+    temp.u.s._11 = (mat->u.s._11 * pMatrix->u.s._11) + (mat->u.s._21 * pMatrix->u.s._12) + (mat->u.s._31 * pMatrix->u.s._13) + (mat->u.s._41 * pMatrix->u.s._14);
+    temp.u.s._21 = (mat->u.s._11 * pMatrix->u.s._21) + (mat->u.s._21 * pMatrix->u.s._22) + (mat->u.s._31 * pMatrix->u.s._23) + (mat->u.s._41 * pMatrix->u.s._24);
+    temp.u.s._31 = (mat->u.s._11 * pMatrix->u.s._31) + (mat->u.s._21 * pMatrix->u.s._32) + (mat->u.s._31 * pMatrix->u.s._33) + (mat->u.s._41 * pMatrix->u.s._34);
+    temp.u.s._41 = (mat->u.s._11 * pMatrix->u.s._41) + (mat->u.s._21 * pMatrix->u.s._42) + (mat->u.s._31 * pMatrix->u.s._43) + (mat->u.s._41 * pMatrix->u.s._44);
+
+    temp.u.s._12 = (mat->u.s._12 * pMatrix->u.s._11) + (mat->u.s._22 * pMatrix->u.s._12) + (mat->u.s._32 * pMatrix->u.s._13) + (mat->u.s._42 * pMatrix->u.s._14);
+    temp.u.s._22 = (mat->u.s._12 * pMatrix->u.s._21) + (mat->u.s._22 * pMatrix->u.s._22) + (mat->u.s._32 * pMatrix->u.s._23) + (mat->u.s._42 * pMatrix->u.s._24);
+    temp.u.s._32 = (mat->u.s._12 * pMatrix->u.s._31) + (mat->u.s._22 * pMatrix->u.s._32) + (mat->u.s._32 * pMatrix->u.s._33) + (mat->u.s._42 * pMatrix->u.s._34);
+    temp.u.s._42 = (mat->u.s._12 * pMatrix->u.s._41) + (mat->u.s._22 * pMatrix->u.s._42) + (mat->u.s._32 * pMatrix->u.s._43) + (mat->u.s._42 * pMatrix->u.s._44);
+
+    temp.u.s._13 = (mat->u.s._13 * pMatrix->u.s._11) + (mat->u.s._23 * pMatrix->u.s._12) + (mat->u.s._33 * pMatrix->u.s._13) + (mat->u.s._43 * pMatrix->u.s._14);
+    temp.u.s._23 = (mat->u.s._13 * pMatrix->u.s._21) + (mat->u.s._23 * pMatrix->u.s._22) + (mat->u.s._33 * pMatrix->u.s._23) + (mat->u.s._43 * pMatrix->u.s._24);
+    temp.u.s._33 = (mat->u.s._13 * pMatrix->u.s._31) + (mat->u.s._23 * pMatrix->u.s._32) + (mat->u.s._33 * pMatrix->u.s._33) + (mat->u.s._43 * pMatrix->u.s._34);
+    temp.u.s._43 = (mat->u.s._13 * pMatrix->u.s._41) + (mat->u.s._23 * pMatrix->u.s._42) + (mat->u.s._33 * pMatrix->u.s._43) + (mat->u.s._43 * pMatrix->u.s._44);
+
+    temp.u.s._14 = (mat->u.s._14 * pMatrix->u.s._11) + (mat->u.s._24 * pMatrix->u.s._12) + (mat->u.s._34 * pMatrix->u.s._13) + (mat->u.s._44 * pMatrix->u.s._14);
+    temp.u.s._24 = (mat->u.s._14 * pMatrix->u.s._21) + (mat->u.s._24 * pMatrix->u.s._22) + (mat->u.s._34 * pMatrix->u.s._23) + (mat->u.s._44 * pMatrix->u.s._24);
+    temp.u.s._34 = (mat->u.s._14 * pMatrix->u.s._31) + (mat->u.s._24 * pMatrix->u.s._32) + (mat->u.s._34 * pMatrix->u.s._33) + (mat->u.s._44 * pMatrix->u.s._34);
+    temp.u.s._44 = (mat->u.s._14 * pMatrix->u.s._41) + (mat->u.s._24 * pMatrix->u.s._42) + (mat->u.s._34 * pMatrix->u.s._43) + (mat->u.s._44 * pMatrix->u.s._44);
+
+    /* Apply change via set transform - will reapply to eg. lights this way */
+    IWineD3DDeviceImpl_SetTransform(iface, State, &temp);
+    return D3D_OK;
+}
+
+/*****
+ * Get / Set Light
+ *   WARNING: This code relies on the fact that D3DLIGHT8 == D3DLIGHT9
+ *****/
+/* Note lights are real special cases. Although the device caps state only eg. 8 are supported,
+   you can reference any indexes you want as long as that number max are enabled are any
+   one point in time! Therefore since the indexes can be anything, we need a linked list of them.
+   However, this causes stateblock problems. When capturing the state block, I duplicate the list,
+   but when recording, just build a chain pretty much of commands to be replayed.                  */
+   
+HRESULT WINAPI IWineD3DDeviceImpl_SetLight(IWineD3DDevice *iface, DWORD Index, CONST WINED3DLIGHT* pLight) {
+    float rho;
+    PLIGHTINFOEL *object, *temp;
+
+    IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
+    TRACE("(%p) : Idx(%ld), pLight(%p)\n", This, Index, pLight);
+
+    /* If recording state block, just add to end of lights chain */
+    if (This->isRecordingState) {
+        object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PLIGHTINFOEL));
+        if (NULL == object) {
+            return D3DERR_OUTOFVIDEOMEMORY;
+        }
+        memcpy(&object->OriginalParms, pLight, sizeof(D3DLIGHT9));
+        object->OriginalIndex = Index;
+        object->glIndex = -1;
+        object->changed = TRUE;
+
+        /* Add to the END of the chain of lights changes to be replayed */
+        if (This->updateStateBlock->lights == NULL) {
+            This->updateStateBlock->lights = object;
+        } else {
+            temp = This->updateStateBlock->lights;
+            while (temp->next != NULL) temp=temp->next;
+            temp->next = object;
+        }
+        TRACE("Recording... not performing anything more\n");
+        return D3D_OK;
+    }
+
+    /* Ok, not recording any longer so do real work */
+    object = This->stateBlock->lights;
+    while (object != NULL && object->OriginalIndex != Index) object = object->next;
+
+    /* If we didn't find it in the list of lights, time to add it */
+    if (object == NULL) {
+        PLIGHTINFOEL *insertAt,*prevPos;
+
+        object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PLIGHTINFOEL));
+        if (NULL == object) {
+            return D3DERR_OUTOFVIDEOMEMORY;
+        }
+        object->OriginalIndex = Index;
+        object->glIndex = -1;
+
+        /* Add it to the front of list with the idea that lights will be changed as needed 
+           BUT after any lights currently assigned GL indexes                             */
+        insertAt = This->stateBlock->lights;
+        prevPos  = NULL;
+        while (insertAt != NULL && insertAt->glIndex != -1) {
+            prevPos  = insertAt;
+            insertAt = insertAt->next;
+        }
+
+        if (insertAt == NULL && prevPos == NULL) { /* Start of list */
+            This->stateBlock->lights = object;
+        } else if (insertAt == NULL) { /* End of list */
+            prevPos->next = object;
+            object->prev = prevPos;
+        } else { /* Middle of chain */
+            if (prevPos == NULL) {
+                This->stateBlock->lights = object;
+            } else {
+                prevPos->next = object;
+            }
+            object->prev = prevPos;
+            object->next = insertAt;
+            insertAt->prev = object;
+        }
+    }
+
+    /* Initialze the object */
+    TRACE("Light %ld setting to type %d, Diffuse(%f,%f,%f,%f), Specular(%f,%f,%f,%f), Ambient(%f,%f,%f,%f)\n", Index, pLight->Type,
+          pLight->Diffuse.r, pLight->Diffuse.g, pLight->Diffuse.b, pLight->Diffuse.a,
+          pLight->Specular.r, pLight->Specular.g, pLight->Specular.b, pLight->Specular.a,
+          pLight->Ambient.r, pLight->Ambient.g, pLight->Ambient.b, pLight->Ambient.a);
+    TRACE("... Pos(%f,%f,%f), Dirn(%f,%f,%f)\n", pLight->Position.x, pLight->Position.y, pLight->Position.z,
+          pLight->Direction.x, pLight->Direction.y, pLight->Direction.z);
+    TRACE("... Range(%f), Falloff(%f), Theta(%f), Phi(%f)\n", pLight->Range, pLight->Falloff, pLight->Theta, pLight->Phi);
+
+    /* Save away the information */
+    memcpy(&object->OriginalParms, pLight, sizeof(D3DLIGHT9));
+
+    switch (pLight->Type) {
+    case D3DLIGHT_POINT:
+        /* Position */
+        object->lightPosn[0] = pLight->Position.x;
+        object->lightPosn[1] = pLight->Position.y;
+        object->lightPosn[2] = pLight->Position.z;
+        object->lightPosn[3] = 1.0f;
+        object->cutoff = 180.0f;
+        /* FIXME: Range */
+        break;
+
+    case D3DLIGHT_DIRECTIONAL:
+        /* Direction */
+        object->lightPosn[0] = -pLight->Direction.x;
+        object->lightPosn[1] = -pLight->Direction.y;
+        object->lightPosn[2] = -pLight->Direction.z;
+        object->lightPosn[3] = 0.0;
+        object->exponent     = 0.0f;
+        object->cutoff       = 180.0f;
+        break;
+
+    case D3DLIGHT_SPOT:
+        /* Position */
+        object->lightPosn[0] = pLight->Position.x;
+        object->lightPosn[1] = pLight->Position.y;
+        object->lightPosn[2] = pLight->Position.z;
+        object->lightPosn[3] = 1.0;
+
+        /* Direction */
+        object->lightDirn[0] = pLight->Direction.x;
+        object->lightDirn[1] = pLight->Direction.y;
+        object->lightDirn[2] = pLight->Direction.z;
+        object->lightDirn[3] = 1.0;
+
+        /*
+         * opengl-ish and d3d-ish spot lights use too different models for the
+         * light "intensity" as a function of the angle towards the main light direction,
+         * so we only can approximate very roughly.
+         * however spot lights are rather rarely used in games (if ever used at all).
+         * furthermore if still used, probably nobody pays attention to such details.
+         */
+        if (pLight->Falloff == 0) {
+            rho = 6.28f;
+        } else {
+            rho = pLight->Theta + (pLight->Phi - pLight->Theta)/(2*pLight->Falloff);
+        }
+        if (rho < 0.0001) rho = 0.0001f;
+        object->exponent = -0.3/log(cos(rho/2));
+        object->cutoff = pLight->Phi*90/M_PI;
+
+        /* FIXME: Range */
+        break;
+
+    default:
+        FIXME("Unrecognized light type %d\n", pLight->Type);
+    }
+
+    /* Update the live definitions if the light is currently assigned a glIndex */
+    if (object->glIndex != -1) {
+        setup_light(iface, object->glIndex, object);
+    }
+    return D3D_OK;
+}
+
+HRESULT WINAPI IWineD3DDeviceImpl_GetLight(IWineD3DDevice *iface, DWORD Index, WINED3DLIGHT* pLight) {
+    PLIGHTINFOEL *lightInfo = NULL;
+    IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface; 
+    TRACE("(%p) : Idx(%ld), pLight(%p)\n", This, Index, pLight);
+    
+    /* Locate the light in the live lights */
+    lightInfo = This->stateBlock->lights;
+    while (lightInfo != NULL && lightInfo->OriginalIndex != Index) lightInfo = lightInfo->next;
+
+    if (lightInfo == NULL) {
+        TRACE("Light information requested but light not defined\n");
+        return D3DERR_INVALIDCALL;
+    }
+
+    memcpy(pLight, &lightInfo->OriginalParms, sizeof(D3DLIGHT9));
+    return D3D_OK;
+}
+
+/*****
+ * Get / Set Light Enable 
+ *   (Note for consistency, renamed d3dx function by adding the 'set' prefix)
+ *****/
+HRESULT WINAPI IWineD3DDeviceImpl_SetLightEnable(IWineD3DDevice *iface, DWORD Index, BOOL Enable) {
+    PLIGHTINFOEL *lightInfo = NULL;
+    IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
+    TRACE("(%p) : Idx(%ld), enable? %d\n", This, Index, Enable);
+
+    /* If recording state block, just add to end of lights chain with changedEnable set to true */
+    if (This->isRecordingState) {
+        lightInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PLIGHTINFOEL));
+        if (NULL == lightInfo) {
+            return D3DERR_OUTOFVIDEOMEMORY;
+        }
+        lightInfo->OriginalIndex = Index;
+        lightInfo->glIndex = -1;
+        lightInfo->enabledChanged = TRUE;
+
+        /* Add to the END of the chain of lights changes to be replayed */
+        if (This->updateStateBlock->lights == NULL) {
+            This->updateStateBlock->lights = lightInfo;
+        } else {
+            PLIGHTINFOEL *temp = This->updateStateBlock->lights;
+            while (temp->next != NULL) temp=temp->next;
+            temp->next = lightInfo;
+        }
+        TRACE("Recording... not performing anything more\n");
+        return D3D_OK;
+    }
+
+    /* Not recording... So, locate the light in the live lights */
+    lightInfo = This->stateBlock->lights;
+    while (lightInfo != NULL && lightInfo->OriginalIndex != Index) lightInfo = lightInfo->next;
+
+    /* Special case - enabling an undefined light creates one with a strict set of parms! */
+    if (lightInfo == NULL) {
+        D3DLIGHT9 lightParms;
+        /* Warning - untested code :-) Prob safe to change fixme to a trace but
+             wait until someone confirms it seems to work!                     */
+        TRACE("Light enabled requested but light not defined, so defining one!\n"); 
+        lightParms.Type = D3DLIGHT_DIRECTIONAL;
+        lightParms.Diffuse.r = 1.0;
+        lightParms.Diffuse.g = 1.0;
+        lightParms.Diffuse.b = 1.0;
+        lightParms.Diffuse.a = 0.0;
+        lightParms.Specular.r = 0.0;
+        lightParms.Specular.g = 0.0;
+        lightParms.Specular.b = 0.0;
+        lightParms.Specular.a = 0.0;
+        lightParms.Ambient.r = 0.0;
+        lightParms.Ambient.g = 0.0;
+        lightParms.Ambient.b = 0.0;
+        lightParms.Ambient.a = 0.0;
+        lightParms.Position.x = 0.0;
+        lightParms.Position.y = 0.0;
+        lightParms.Position.z = 0.0;
+        lightParms.Direction.x = 0.0;
+        lightParms.Direction.y = 0.0;
+        lightParms.Direction.z = 1.0;
+        lightParms.Range = 0.0;
+        lightParms.Falloff = 0.0;
+        lightParms.Attenuation0 = 0.0;
+        lightParms.Attenuation1 = 0.0;
+        lightParms.Attenuation2 = 0.0;
+        lightParms.Theta = 0.0;
+        lightParms.Phi = 0.0;
+        IWineD3DDeviceImpl_SetLight(iface, Index, &lightParms);
+
+        /* Search for it again! Should be fairly quick as near head of list */
+        lightInfo = This->stateBlock->lights;
+        while (lightInfo != NULL && lightInfo->OriginalIndex != Index) lightInfo = lightInfo->next;
+        if (lightInfo == NULL) {
+            FIXME("Adding default lights has failed dismally\n");
+            return D3DERR_INVALIDCALL;
+        }
+    }
+
+    /* OK, we now have a light... */
+    if (Enable == FALSE) {
+
+        /* If we are disabling it, check it was enabled, and
+           still only do something if it has assigned a glIndex (which it should have!)   */
+        if ((lightInfo->lightEnabled == TRUE) && (lightInfo->glIndex != -1)) {
+            TRACE("Disabling light set up at gl idx %ld\n", lightInfo->glIndex);
+            ENTER_GL();
+            glDisable(GL_LIGHT0 + lightInfo->glIndex);
+            checkGLcall("glDisable GL_LIGHT0+Index");
+            LEAVE_GL();
+        } else {
+            TRACE("Nothing to do as light was not enabled\n");
+        }
+        lightInfo->lightEnabled = FALSE;
+    } else {
+
+        /* We are enabling it. If it is enabled, its really simple */
+        if (lightInfo->lightEnabled == TRUE) {
+            /* nop */
+            TRACE("Nothing to do as light was enabled\n");
+
+        /* If it already has a glIndex, its still simple */
+        } else if (lightInfo->glIndex != -1) {
+            TRACE("Reusing light as already set up at gl idx %ld\n", lightInfo->glIndex);
+            lightInfo->lightEnabled = TRUE;
+            ENTER_GL();
+            glEnable(GL_LIGHT0 + lightInfo->glIndex);
+            checkGLcall("glEnable GL_LIGHT0+Index already setup");
+            LEAVE_GL();
+
+        /* Otherwise got to find space - lights are ordered gl indexes first */
+        } else {
+            PLIGHTINFOEL *bsf  = NULL;
+            PLIGHTINFOEL *pos  = This->stateBlock->lights;
+            PLIGHTINFOEL *prev = NULL;
+            int           Index= 0;
+            int           glIndex = -1;
+
+            /* Try to minimize changes as much as possible */
+            while (pos != NULL && pos->glIndex != -1 && Index < This->maxConcurrentLights) {
+
+                /* Try to remember which index can be replaced if necessary */
+                if (bsf==NULL && pos->lightEnabled == FALSE) {
+                    /* Found a light we can replace, save as best replacement */
+                    bsf = pos;
+                }
+
+                /* Step to next space */
+                prev = pos;
+                pos = pos->next;
+                Index ++;
+            }
+
+            /* If we have too many active lights, fail the call */
+            if ((Index == This->maxConcurrentLights) && (bsf == NULL)) {
+                FIXME("Program requests too many concurrent lights\n");
+                return D3DERR_INVALIDCALL;
+
+            /* If we have allocated all lights, but not all are enabled,
+               reuse one which is not enabled                           */
+            } else if (Index == This->maxConcurrentLights) {
+                /* use bsf - Simply swap the new light and the BSF one */
+                PLIGHTINFOEL *bsfNext = bsf->next;
+                PLIGHTINFOEL *bsfPrev = bsf->prev;
+
+                /* Sort out ends */
+                if (lightInfo->next != NULL) lightInfo->next->prev = bsf;
+                if (bsf->prev != NULL) {
+                    bsf->prev->next = lightInfo;
+                } else {
+                    This->stateBlock->lights = lightInfo;
+                }
+
+                /* If not side by side, lots of chains to update */
+                if (bsf->next != lightInfo) {
+                    lightInfo->prev->next = bsf;
+                    bsf->next->prev = lightInfo;
+                    bsf->next       = lightInfo->next;
+                    bsf->prev       = lightInfo->prev;
+                    lightInfo->next = bsfNext;
+                    lightInfo->prev = bsfPrev;
+
+                } else {
+                    /* Simple swaps */
+                    bsf->prev = lightInfo;
+                    bsf->next = lightInfo->next;
+                    lightInfo->next = bsf;
+                    lightInfo->prev = bsfPrev;
+                }
+
+
+                /* Update states */
+                glIndex = bsf->glIndex;
+                bsf->glIndex = -1;
+                lightInfo->glIndex = glIndex;
+                lightInfo->lightEnabled = TRUE;
+
+                /* Finally set up the light in gl itself */
+                TRACE("Replacing light which was set up at gl idx %ld\n", lightInfo->glIndex);
+                ENTER_GL();
+                setup_light(iface, glIndex, lightInfo);
+                glEnable(GL_LIGHT0 + glIndex);
+                checkGLcall("glEnable GL_LIGHT0 new setup");
+                LEAVE_GL();
+
+            /* If we reached the end of the allocated lights, with space in the
+               gl lights, setup a new light                                     */
+            } else if (pos->glIndex == -1) {
+
+                /* We reached the end of the allocated gl lights, so already 
+                    know the index of the next one!                          */
+                glIndex = Index;
+                lightInfo->glIndex = glIndex;
+                lightInfo->lightEnabled = TRUE;
+
+                /* In an ideal world, its already in the right place */
+                if (lightInfo->prev == NULL || lightInfo->prev->glIndex!=-1) {
+                   /* No need to move it */
+                } else {
+                    /* Remove this light from the list */
+                    lightInfo->prev->next = lightInfo->next;
+                    if (lightInfo->next != NULL) {
+                        lightInfo->next->prev = lightInfo->prev;
+                    }
+
+                    /* Add in at appropriate place (inbetween prev and pos) */
+                    lightInfo->prev = prev;
+                    lightInfo->next = pos;
+                    if (prev == NULL) {
+                        This->stateBlock->lights = lightInfo;
+                    } else {
+                        prev->next = lightInfo;
+                    }
+                    if (pos != NULL) {
+                        pos->prev = lightInfo;
+                    }
+                }
+
+                /* Finally set up the light in gl itself */
+                TRACE("Defining new light at gl idx %ld\n", lightInfo->glIndex);
+                ENTER_GL();
+                setup_light(iface, glIndex, lightInfo);
+                glEnable(GL_LIGHT0 + glIndex);
+                checkGLcall("glEnable GL_LIGHT0 new setup");
+                LEAVE_GL();
+                
+            }
+        }
+    }
+    return D3D_OK;
+}
+
+HRESULT WINAPI IWineD3DDeviceImpl_GetLightEnable(IWineD3DDevice *iface, DWORD Index,BOOL* pEnable) {
+
+    PLIGHTINFOEL *lightInfo = NULL;
+    IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface; 
+    TRACE("(%p) : for idx(%ld)\n", This, Index);
+    
+    /* Locate the light in the live lights */
+    lightInfo = This->stateBlock->lights;
+    while (lightInfo != NULL && lightInfo->OriginalIndex != Index) lightInfo = lightInfo->next;
+
+    if (lightInfo == NULL) {
+        TRACE("Light enabled state requested but light not defined\n");
+        return D3DERR_INVALIDCALL;
+    }
+    *pEnable = lightInfo->lightEnabled;
+    return D3D_OK;
+}
+
+/*****
+ * Get / Set Clip Planes
+ *****/
+HRESULT WINAPI IWineD3DDeviceImpl_SetClipPlane(IWineD3DDevice *iface, DWORD Index, CONST float *pPlane) {
+    IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
+    TRACE("(%p) : for idx %ld, %p\n", This, Index, pPlane);
+
+    /* Validate Index */
+    if (Index >= GL_LIMITS(clipplanes)) {
+        TRACE("Application has requested clipplane this device doesn't support\n");
+        return D3DERR_INVALIDCALL;
+    }
+
+    This->updateStateBlock->changed.clipplane[Index] = TRUE;
+    This->updateStateBlock->set.clipplane[Index] = TRUE;
+    This->updateStateBlock->clipplane[Index][0] = pPlane[0];
+    This->updateStateBlock->clipplane[Index][1] = pPlane[1];
+    This->updateStateBlock->clipplane[Index][2] = pPlane[2];
+    This->updateStateBlock->clipplane[Index][3] = pPlane[3];
+
+    /* Handle recording of state blocks */
+    if (This->isRecordingState) {
+        TRACE("Recording... not performing anything\n");
+        return D3D_OK;
+    }
+
+    /* Apply it */
+
+    ENTER_GL();
+
+    /* Clip Plane settings are affected by the model view in OpenGL, the View transform in direct3d */
+    glMatrixMode(GL_MODELVIEW);
+    glPushMatrix();
+    glLoadMatrixf((float *) &This->stateBlock->transforms[D3DTS_VIEW].u.m[0][0]);
+
+    TRACE("Clipplane [%f,%f,%f,%f]\n", 
+	  This->updateStateBlock->clipplane[Index][0], 
+	  This->updateStateBlock->clipplane[Index][1],
+      This->updateStateBlock->clipplane[Index][2], 
+	  This->updateStateBlock->clipplane[Index][3]);
+    glClipPlane(GL_CLIP_PLANE0 + Index, This->updateStateBlock->clipplane[Index]);
+    checkGLcall("glClipPlane");
+
+    glPopMatrix();
+    LEAVE_GL();
+
+    return D3D_OK;
+}
+
+HRESULT WINAPI IWineD3DDeviceImpl_GetClipPlane(IWineD3DDevice *iface, DWORD Index, float *pPlane) {
+    IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
+    TRACE("(%p) : for idx %ld\n", This, Index);
+
+    /* Validate Index */
+    if (Index >= GL_LIMITS(clipplanes)) {
+        TRACE("Application has requested clipplane this device doesn't support\n");
+        return D3DERR_INVALIDCALL;
+    }
+
+    pPlane[0] = This->stateBlock->clipplane[Index][0];
+    pPlane[1] = This->stateBlock->clipplane[Index][1];
+    pPlane[2] = This->stateBlock->clipplane[Index][2];
+    pPlane[3] = This->stateBlock->clipplane[Index][3];
+    return D3D_OK;
+}
+
+/*****
+ * Get / Set Clip Plane Status
+ *   WARNING: This code relies on the fact that D3DCLIPSTATUS8 == D3DCLIPSTATUS9
+ *****/
+HRESULT  WINAPI  IWineD3DDeviceImpl_SetClipStatus(IWineD3DDevice *iface, CONST WINED3DCLIPSTATUS* pClipStatus) {
+    IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
+    FIXME("(%p) : stub\n", This);
+    if (NULL == pClipStatus) {
+      return D3DERR_INVALIDCALL;
+    }
+    This->updateStateBlock->clip_status.ClipUnion = pClipStatus->ClipUnion;
+    This->updateStateBlock->clip_status.ClipIntersection = pClipStatus->ClipIntersection;
+    return D3D_OK;
+}
+
+HRESULT  WINAPI  IWineD3DDeviceImpl_GetClipStatus(IWineD3DDevice *iface, WINED3DCLIPSTATUS* pClipStatus) {
+    IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
+    FIXME("(%p) : stub\n", This);    
+    if (NULL == pClipStatus) {
+      return D3DERR_INVALIDCALL;
+    }
+    pClipStatus->ClipUnion = This->updateStateBlock->clip_status.ClipUnion;
+    pClipStatus->ClipIntersection = This->updateStateBlock->clip_status.ClipIntersection;
+    return D3D_OK;
+}
+
+/*****
+ * Get / Set Material
+ *   WARNING: This code relies on the fact that D3DMATERIAL8 == D3DMATERIAL9
+ *****/
+HRESULT WINAPI IWineD3DDeviceImpl_SetMaterial(IWineD3DDevice *iface, CONST WINED3DMATERIAL* pMaterial) {
+    IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
+
+    This->updateStateBlock->changed.material = TRUE;
+    This->updateStateBlock->set.material = TRUE;
+    memcpy(&This->updateStateBlock->material, pMaterial, sizeof(WINED3DMATERIAL));
+
+    /* Handle recording of state blocks */
+    if (This->isRecordingState) {
+        TRACE("Recording... not performing anything\n");
+        return D3D_OK;
+    }
+
+    ENTER_GL();
+    TRACE("(%p) : Diffuse (%f,%f,%f,%f)\n", This, pMaterial->Diffuse.r, pMaterial->Diffuse.g, pMaterial->Diffuse.b, pMaterial->Diffuse.a);
+    TRACE("(%p) : Ambient (%f,%f,%f,%f)\n", This, pMaterial->Ambient.r, pMaterial->Ambient.g, pMaterial->Ambient.b, pMaterial->Ambient.a);
+    TRACE("(%p) : Specular (%f,%f,%f,%f)\n", This, pMaterial->Specular.r, pMaterial->Specular.g, pMaterial->Specular.b, pMaterial->Specular.a);
+    TRACE("(%p) : Emissive (%f,%f,%f,%f)\n", This, pMaterial->Emissive.r, pMaterial->Emissive.g, pMaterial->Emissive.b, pMaterial->Emissive.a);
+    TRACE("(%p) : Power (%f)\n", This, pMaterial->Power);
+
+    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (float*) &This->updateStateBlock->material.Ambient);
+    checkGLcall("glMaterialfv");
+    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, (float*) &This->updateStateBlock->material.Diffuse);
+    checkGLcall("glMaterialfv");
+
+    /* Only change material color if specular is enabled, otherwise it is set to black */
+#if 0 /* TODO */
+    if (This->StateBlock->renderstate[D3DRS_SPECULARENABLE]) {
+       glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (float*) &This->UpdateStateBlock->material.Specular);
+       checkGLcall("glMaterialfv");
+    } else {
+#endif
+    {
+       float black[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+       glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, &black[0]);
+       checkGLcall("glMaterialfv");
+    }
+    glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, (float*) &This->updateStateBlock->material.Emissive);
+    checkGLcall("glMaterialfv");
+    glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, This->updateStateBlock->material.Power);
+    checkGLcall("glMaterialf");
+
+    LEAVE_GL();
+    return D3D_OK;
+}
+
+HRESULT WINAPI IWineD3DDeviceImpl_GetMaterial(IWineD3DDevice *iface, WINED3DMATERIAL* pMaterial) {
+    IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
+    memcpy(pMaterial, &This->updateStateBlock->material, sizeof (WINED3DMATERIAL));
+    TRACE("(%p) : Diffuse (%f,%f,%f,%f)\n", This, pMaterial->Diffuse.r, pMaterial->Diffuse.g, pMaterial->Diffuse.b, pMaterial->Diffuse.a);
+    TRACE("(%p) : Ambient (%f,%f,%f,%f)\n", This, pMaterial->Ambient.r, pMaterial->Ambient.g, pMaterial->Ambient.b, pMaterial->Ambient.a);
+    TRACE("(%p) : Specular (%f,%f,%f,%f)\n", This, pMaterial->Specular.r, pMaterial->Specular.g, pMaterial->Specular.b, pMaterial->Specular.a);
+    TRACE("(%p) : Emissive (%f,%f,%f,%f)\n", This, pMaterial->Emissive.r, pMaterial->Emissive.g, pMaterial->Emissive.b, pMaterial->Emissive.a);
+    TRACE("(%p) : Power (%f)\n", This, pMaterial->Power);
+    return D3D_OK;
+}
+
 /*****
  * Scene related functions
  *****/
@@ -378,5 +1081,16 @@
     IWineD3DDeviceImpl_GetStreamSource,
     IWineD3DDeviceImpl_SetTransform,
     IWineD3DDeviceImpl_GetTransform,
+    IWineD3DDeviceImpl_MultiplyTransform,
+    IWineD3DDeviceImpl_SetLight,
+    IWineD3DDeviceImpl_GetLight,
+    IWineD3DDeviceImpl_SetLightEnable,
+    IWineD3DDeviceImpl_GetLightEnable,
+    IWineD3DDeviceImpl_SetClipPlane,
+    IWineD3DDeviceImpl_GetClipPlane,
+    IWineD3DDeviceImpl_SetClipStatus,
+    IWineD3DDeviceImpl_GetClipStatus,
+    IWineD3DDeviceImpl_SetMaterial,
+    IWineD3DDeviceImpl_GetMaterial,
     IWineD3DDeviceImpl_BeginScene
 };
diff --git a/dlls/wined3d/indexbuffer.c b/dlls/wined3d/indexbuffer.c
index 8ee3efb..617cd18 100644
--- a/dlls/wined3d/indexbuffer.c
+++ b/dlls/wined3d/indexbuffer.c
@@ -24,6 +24,7 @@
 #include "wined3d_private.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
+#define GLINFO_LOCATION ((IWineD3DImpl *)(((IWineD3DDeviceImpl *)This->resource.wineD3DDevice)->wineD3D))->gl_info
 
 /* *******************************************
    IWineD3DIndexBuffer IUnknown parts follow
@@ -52,7 +53,7 @@
         IWineD3DDevice_Release(This->resource.wineD3DDevice);
         HeapFree(GetProcessHeap(), 0, This);
     } else {
-        IUnknown_Release(This->resource.parent);  /* Released the reference to the d3dx VB */
+        IUnknown_Release(This->resource.parent);  /* Released the reference to the d3dx object */
     }
     return ref;
 }
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index 8abcae1..dc68095 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -24,6 +24,7 @@
 #define __WINE_WINED3D_PRIVATE_H
 
 #include <stdarg.h>
+#include <math.h>
 #define NONAMELESSUNION
 #define NONAMELESSSTRUCT
 #define COBJMACROS
@@ -77,6 +78,7 @@
                          /* Highest value in D3DTRANSFORMSTATETYPE */
 #define WINED3D_VSHADER_MAX_CONSTANTS  96   
                          /* Maximum number of constants provided to the shaders */
+#define MAX_CLIPPLANES  D3DMAXUSERCLIPPLANES
 
 #define checkGLcall(A) \
 { \
@@ -108,11 +110,43 @@
             checkGLcall("glActiveTextureARB");
 #endif
 
+/* Macro to dump out the current state of the light chain */
+#define DUMP_LIGHT_CHAIN()                    \
+{                                             \
+  PLIGHTINFOEL *el = This->stateBlock->lights;\
+  while (el) {                                \
+    TRACE("Light %p (glIndex %ld, d3dIndex %ld, enabled %d)\n", el, el->glIndex, el->OriginalIndex, el->lightEnabled);\
+    el = el->next;                            \
+  }                                           \
+}
+
 typedef struct IWineD3DStateBlockImpl IWineD3DStateBlockImpl;
 
 extern const float identity[16];
 
 /*****************************************************************************
+ * Internal representation of a light
+ */
+typedef struct PLIGHTINFOEL PLIGHTINFOEL;
+struct PLIGHTINFOEL {
+    WINED3DLIGHT OriginalParms; /* Note D3D8LIGHT == D3D9LIGHT */
+    DWORD        OriginalIndex;
+    LONG         glIndex;
+    BOOL         lightEnabled;
+    BOOL         changed;
+    BOOL         enabledChanged;
+
+    /* Converted parms to speed up swapping lights */
+    float                         lightPosn[4];
+    float                         lightDirn[4];
+    float                         exponent;
+    float                         cutoff;
+
+    PLIGHTINFOEL *next;
+    PLIGHTINFOEL *prev;
+};
+
+/*****************************************************************************
  * IWineD3D implementation structure
  */
 typedef struct IWineD3DImpl
@@ -242,9 +276,11 @@
 /*   Note: Very long winded but gl Lists are not flexible enough */
 /*   to resolve everything we need, so doing it manually for now */
 typedef struct SAVEDSTATES {
+        BOOL                      material;
         BOOL                      fvf;
         BOOL                      stream_source[MAX_STREAMS];
         BOOL                      transform[HIGHEST_TRANSFORMSTATE];
+        BOOL                      clipplane[MAX_CLIPPLANES];
 } SAVEDSTATES;
 
 struct IWineD3DStateBlockImpl
@@ -273,6 +309,16 @@
     /* Transform */
     D3DMATRIX                 transforms[HIGHEST_TRANSFORMSTATE];
 
+    /* Lights */
+    PLIGHTINFOEL             *lights; /* NOTE: active GL lights must be front of the chain */
+    
+    /* Clipping */
+    double                    clipplane[MAX_CLIPPLANES][4];
+    WINED3DCLIPSTATUS         clip_status;
+
+    /* Material */
+    WINED3DMATERIAL           material;
+
 };
 
 extern IWineD3DStateBlockVtbl IWineD3DStateBlock_Vtbl;