From 8b2c10bcd4cdc4808f09e8e012ea26e364160390 Mon Sep 17 00:00:00 2001 From: Jason Edmeades Date: Tue, 30 Sep 2003 00:21:07 +0000 Subject: [PATCH] MaxActiveLights means number of concurrent lights, but any number can be set up. Change support for lights into a linked list and only set up an equivalent gl light when the light is enabled. --- dlls/d3d8/d3d8_private.h | 43 +++- dlls/d3d8/device.c | 560 ++++++++++++++++++++++++++++++++++++----------- dlls/d3d8/directx.c | 32 +-- dlls/d3d8/stateblock.c | 106 ++++++--- 4 files changed, 564 insertions(+), 177 deletions(-) diff --git a/dlls/d3d8/d3d8_private.h b/dlls/d3d8/d3d8_private.h index 14a9796562b..569526a28e3 100644 --- a/dlls/d3d8/d3d8_private.h +++ b/dlls/d3d8/d3d8_private.h @@ -67,7 +67,6 @@ extern int num_lock; /* Device caps */ #define MAX_PALETTES 256 #define MAX_STREAMS 16 -#define MAX_ACTIVE_LIGHTS 8 #define MAX_CLIPPLANES D3DMAXUSERCLIPPLANES #define MAX_LEVELS 256 @@ -174,6 +173,27 @@ typedef struct PSHADEROUTPUTDATA8 { D3DSHADERVECTOR oDepth; } PSHADEROUTPUTDATA8; +/* + * Private definitions for internal use only + */ +typedef struct PLIGHTINFOEL PLIGHTINFOEL; +struct PLIGHTINFOEL { + D3DLIGHT8 OriginalParms; + 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; +}; /* * Macros @@ -326,10 +346,6 @@ struct IDirect3DDevice8Impl IDirect3DStateBlockImpl *StateBlock; IDirect3DStateBlockImpl *UpdateStateBlock; - /* Other required values */ - float lightPosn[MAX_ACTIVE_LIGHTS][4]; - float lightDirn[MAX_ACTIVE_LIGHTS][4]; - /* palettes texture management */ PALETTEENTRY palettes[MAX_PALETTES][256]; UINT currentPalette; @@ -354,6 +370,7 @@ struct IDirect3DDevice8Impl Window win; GLXContext render_ctx; Drawable drawable; + GLint maxConcurrentLights; /* OpenGL Extension related */ @@ -1015,9 +1032,7 @@ extern HRESULT WINAPI IDirect3DVolumeTexture8Impl_AddDirtyBox(LPDIRECT3D /* Note: Very long winded but I do not believe gl Lists will */ /* resolve everything we need, so doing it manually for now */ typedef struct SAVEDSTATES { - BOOL lightEnable[MAX_ACTIVE_LIGHTS]; BOOL Indices; - BOOL lights[MAX_ACTIVE_LIGHTS]; BOOL material; BOOL stream_source[MAX_STREAMS]; BOOL textures[8]; @@ -1059,9 +1074,6 @@ struct IDirect3DStateBlockImpl { SAVEDSTATES Changed; SAVEDSTATES Set; - /* Light Enable */ - BOOL lightEnable[MAX_ACTIVE_LIGHTS]; - /* ClipPlane */ double clipplane[MAX_CLIPPLANES][4]; @@ -1081,7 +1093,7 @@ struct IDirect3DStateBlockImpl { DWORD texture_state[8][HIGHEST_TEXTURE_STATE]; /* Lights */ - D3DLIGHT8 lights[MAX_ACTIVE_LIGHTS]; + PLIGHTINFOEL *lights; /* NOTE: active GL lights must be front of the chain */ /* Material */ D3DMATERIAL8 material; @@ -1321,4 +1333,13 @@ extern LONG primCounter; #define TRACE_VECTOR(name) TRACE( #name "=(%f, %f, %f, %f)\n", name.x, name.y, name.z, name.w); #define TRACE_STRIDED(sd,name) TRACE( #name "=(data:%p, stride:%ld, type:%ld)\n", sd->u.s.name.lpData, sd->u.s.name.dwStride, sd->u.s.name.dwType); +#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; \ + } \ +} + #endif /* __WINE_D3DX8_PRIVATE_H */ diff --git a/dlls/d3d8/device.c b/dlls/d3d8/device.c index 24e11a0f911..1d23f16a6bb 100644 --- a/dlls/d3d8/device.c +++ b/dlls/d3d8/device.c @@ -144,6 +144,95 @@ void setupTextureStates(LPDIRECT3DDEVICE8 iface, DWORD Stage, DWORD Flags) { TRACE("-----------------------> Updated the texture at stage %ld to have new texture state information\n", Stage); } +/* Convert the D3DLIGHT8 properties into equivalent gl lights */ +void setup_light(LPDIRECT3DDEVICE8 iface, LONG Index, PLIGHTINFOEL *lightInfo) { + + float quad_att; + float colRGBA[] = {0.0, 0.0, 0.0, 0.0}; + ICOM_THIS(IDirect3DDevice8Impl,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(); +} + /* Setup this textures matrix */ static void set_texture_matrix(float *smat, DWORD flags) { @@ -407,7 +496,7 @@ HRESULT WINAPI IDirect3DDevice8Impl_Present(LPDIRECT3DDEVICE8 iface, CONST REC frames++; /* every 1.5 seconds */ if (time - prev_time > 1500) { - TRACE_(fps)("@@@ %.2ffps\n", 1000.0*frames/(time - prev_time)); + TRACE_(fps)("@ approx %.2ffps\n", 1000.0*frames/(time - prev_time)); prev_time = time; frames = 0; } @@ -1411,6 +1500,8 @@ HRESULT WINAPI IDirect3DDevice8Impl_SetTransform(LPDIRECT3DDEVICE8 iface, D3DT } } else if (d3dts == D3DTS_VIEW) { /* handle the VIEW matrice */ + + PLIGHTINFOEL *lightChain = NULL; float identity[16] = {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1}; This->modelview_valid = FALSE; This->view_ident = !memcmp(lpmatrix, identity, 16*sizeof(float)); @@ -1427,11 +1518,13 @@ HRESULT WINAPI IDirect3DDevice8Impl_SetTransform(LPDIRECT3DDEVICE8 iface, D3DT */ /* Reset lights */ - for (k = 0; k < GL_LIMITS(lights); k++) { - glLightfv(GL_LIGHT0 + k, GL_POSITION, This->lightPosn[k]); + lightChain = This->StateBlock->lights; + while (lightChain && lightChain->glIndex != -1) { + glLightfv(GL_LIGHT0 + lightChain->glIndex, GL_POSITION, lightChain->lightPosn); checkGLcall("glLightfv posn"); - glLightfv(GL_LIGHT0 + k, GL_SPOT_DIRECTION, This->lightDirn[k]); + glLightfv(GL_LIGHT0 + lightChain->glIndex, GL_SPOT_DIRECTION, lightChain->lightDirn); checkGLcall("glLightfv dirn"); + lightChain = lightChain->next; } /* Reset Clipping Planes if clipping is enabled */ for (k = 0; k < GL_LIMITS(clipplanes); k++) { @@ -1586,19 +1679,84 @@ HRESULT WINAPI IDirect3DDevice8Impl_GetMaterial(LPDIRECT3DDEVICE8 iface, D3DMA return D3D_OK; } +/* 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 IDirect3DDevice8Impl_SetLight(LPDIRECT3DDEVICE8 iface, DWORD Index, CONST D3DLIGHT8* pLight) { - float colRGBA[] = {0.0, 0.0, 0.0, 0.0}; float rho; - float quad_att; + PLIGHTINFOEL *object, *temp; ICOM_THIS(IDirect3DDevice8Impl,iface); TRACE("(%p) : Idx(%ld), pLight(%p)\n", This, Index, pLight); - if (Index >= GL_LIMITS(lights)) { - TRACE("Cannot handle more lights than device supports\n"); - return D3DERR_INVALIDCALL; + /* 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(D3DLIGHT8)); + 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 didnt 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, @@ -1607,90 +1765,32 @@ HRESULT WINAPI IDirect3DDevice8Impl_SetLight(LPDIRECT3DDEVICE8 iface, DWORD In 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); - This->UpdateStateBlock->Changed.lights[Index] = TRUE; - This->UpdateStateBlock->Set.lights[Index] = TRUE; - memcpy(&This->UpdateStateBlock->lights[Index], pLight, sizeof(D3DLIGHT8)); - - /* Handle recording of state blocks */ - if (This->isRecordingState) { - TRACE("Recording... not performing anything\n"); - return D3D_OK; - } - - ENTER_GL(); - - /* Diffuse: */ - colRGBA[0] = pLight->Diffuse.r; - colRGBA[1] = pLight->Diffuse.g; - colRGBA[2] = pLight->Diffuse.b; - colRGBA[3] = pLight->Diffuse.a; - glLightfv(GL_LIGHT0+Index, GL_DIFFUSE, colRGBA); - checkGLcall("glLightfv"); - - /* Specular */ - colRGBA[0] = pLight->Specular.r; - colRGBA[1] = pLight->Specular.g; - colRGBA[2] = pLight->Specular.b; - colRGBA[3] = pLight->Specular.a; - glLightfv(GL_LIGHT0+Index, GL_SPECULAR, colRGBA); - checkGLcall("glLightfv"); - - /* Ambient */ - colRGBA[0] = pLight->Ambient.r; - colRGBA[1] = pLight->Ambient.g; - colRGBA[2] = pLight->Ambient.b; - colRGBA[3] = pLight->Ambient.a; - glLightfv(GL_LIGHT0+Index, GL_AMBIENT, colRGBA); - checkGLcall("glLightfv"); - - /* 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]); - - /* Attenuation - Are these right? guessing... */ - glLightf(GL_LIGHT0+Index, GL_CONSTANT_ATTENUATION, pLight->Attenuation0); - checkGLcall("glLightf"); - glLightf(GL_LIGHT0+Index, GL_LINEAR_ATTENUATION, pLight->Attenuation1); - checkGLcall("glLightf"); - - quad_att = 1.4/(pLight->Range*pLight->Range); - if (quad_att < pLight->Attenuation2) quad_att = pLight->Attenuation2; - glLightf(GL_LIGHT0+Index, GL_QUADRATIC_ATTENUATION, quad_att); - checkGLcall("glLightf"); + /* Save away the information */ + memcpy(&object->OriginalParms, pLight, sizeof(D3DLIGHT8)); switch (pLight->Type) { case D3DLIGHT_POINT: /* Position */ - This->lightPosn[Index][0] = pLight->Position.x; - This->lightPosn[Index][1] = pLight->Position.y; - This->lightPosn[Index][2] = pLight->Position.z; - This->lightPosn[Index][3] = 1.0; - glLightfv(GL_LIGHT0+Index, GL_POSITION, &This->lightPosn[Index][0]); - checkGLcall("glLightfv"); - - glLightf(GL_LIGHT0 + Index, GL_SPOT_CUTOFF, 180); - checkGLcall("glLightf"); - + 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_SPOT: /* Position */ - This->lightPosn[Index][0] = pLight->Position.x; - This->lightPosn[Index][1] = pLight->Position.y; - This->lightPosn[Index][2] = pLight->Position.z; - This->lightPosn[Index][3] = 1.0; - glLightfv(GL_LIGHT0+Index, GL_POSITION, &This->lightPosn[Index][0]); - checkGLcall("glLightfv"); + object->lightPosn[0] = pLight->Position.x; + object->lightPosn[1] = pLight->Position.y; + object->lightPosn[2] = pLight->Position.z; + object->lightPosn[3] = 1.0; /* Direction */ - This->lightDirn[Index][0] = pLight->Direction.x; - This->lightDirn[Index][1] = pLight->Direction.y; - This->lightDirn[Index][2] = pLight->Direction.z; - This->lightDirn[Index][3] = 1.0; - glLightfv(GL_LIGHT0+Index, GL_SPOT_DIRECTION, &This->lightDirn[Index][0]); - checkGLcall("glLightfv"); + 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 @@ -1705,89 +1805,293 @@ HRESULT WINAPI IDirect3DDevice8Impl_SetLight(LPDIRECT3DDEVICE8 iface, DWORD In rho = pLight->Theta + (pLight->Phi - pLight->Theta)/(2*pLight->Falloff); } if (rho < 0.0001) rho = 0.0001f; - glLightf(GL_LIGHT0 + Index, GL_SPOT_EXPONENT, -0.3/log(cos(rho/2))); - glLightf(GL_LIGHT0 + Index, GL_SPOT_CUTOFF, pLight->Phi*90/M_PI); + object->exponent = -0.3/log(cos(rho/2)); + object->cutoff = pLight->Phi*90/M_PI; /* FIXME: Range */ break; + case D3DLIGHT_DIRECTIONAL: /* Direction */ - This->lightPosn[Index][0] = -pLight->Direction.x; - This->lightPosn[Index][1] = -pLight->Direction.y; - This->lightPosn[Index][2] = -pLight->Direction.z; - This->lightPosn[Index][3] = 0.0; - glLightfv(GL_LIGHT0+Index, GL_POSITION, &This->lightPosn[Index][0]); /* Note gl uses w position of 0 for direction! */ - checkGLcall("glLightfv"); - - glLightf(GL_LIGHT0+Index, GL_SPOT_CUTOFF, 180.0f); - glLightf(GL_LIGHT0+Index, GL_SPOT_EXPONENT, 0.0f); - - + 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; + default: FIXME("Unrecognized light type %d\n", pLight->Type); } - /* Restore the modelview matrix */ - glPopMatrix(); - - LEAVE_GL(); + /* Update the live definitions if the light is currently assigned a glIndex */ + if (object->glIndex != -1) { + setup_light(iface, object->glIndex, object); + } + DUMP_LIGHT_CHAIN(); return D3D_OK; } HRESULT WINAPI IDirect3DDevice8Impl_GetLight(LPDIRECT3DDEVICE8 iface, DWORD Index,D3DLIGHT8* pLight) { - ICOM_THIS(IDirect3DDevice8Impl,iface); + PLIGHTINFOEL *lightInfo = NULL; + ICOM_THIS(IDirect3DDevice8Impl,iface); TRACE("(%p) : Idx(%ld), pLight(%p)\n", This, Index, pLight); - if (Index >= GL_LIMITS(lights)) { - TRACE("Cannot handle more lights than device supports\n"); + /* 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, &This->StateBlock->lights[Index], sizeof(D3DLIGHT8)); + memcpy(pLight, &lightInfo->OriginalParms, sizeof(D3DLIGHT8)); return D3D_OK; } HRESULT WINAPI IDirect3DDevice8Impl_LightEnable(LPDIRECT3DDEVICE8 iface, DWORD Index,BOOL Enable) { + PLIGHTINFOEL *lightInfo = NULL; ICOM_THIS(IDirect3DDevice8Impl,iface); TRACE("(%p) : Idx(%ld), enable? %d\n", This, Index, Enable); - if (Index >= GL_LIMITS(lights)) { - TRACE("Cannot handle more lights than device supports\n"); - return D3DERR_INVALIDCALL; - } - - This->UpdateStateBlock->Changed.lightEnable[Index] = TRUE; - This->UpdateStateBlock->Set.lightEnable[Index] = TRUE; - This->UpdateStateBlock->lightEnable[Index] = Enable; - - /* Handle recording of state blocks */ + /* If recording state block, just add to end of lights chain with changedEnable set to true */ if (This->isRecordingState) { - TRACE("Recording... not performing anything\n"); + 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; } - - ENTER_GL(); - if (Enable) { - glEnable(GL_LIGHT0 + Index); - checkGLcall("glEnable GL_LIGHT0+Index"); - } else { - glDisable(GL_LIGHT0 + Index); - checkGLcall("glDisable GL_LIGHT0+Index"); + + /* 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) { + D3DLIGHT8 lightParms; + /* Warning - untested code :-) Prob safe to change fixme to a trace but + wait until someone confirms it seems to work! */ + FIXME("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; + IDirect3DDevice8Impl_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; + } } - LEAVE_GL(); + /* 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(); + + } + } + } + DUMP_LIGHT_CHAIN(); return D3D_OK; } HRESULT WINAPI IDirect3DDevice8Impl_GetLightEnable(LPDIRECT3DDEVICE8 iface, DWORD Index,BOOL* pEnable) { - ICOM_THIS(IDirect3DDevice8Impl,iface); + + PLIGHTINFOEL *lightInfo = NULL; + ICOM_THIS(IDirect3DDevice8Impl,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 (Index >= GL_LIMITS(lights)) { - TRACE("Cannot handle more lights than device supports\n"); + if (lightInfo == NULL) { + TRACE("Light enabled state requested but light not defined\n"); return D3DERR_INVALIDCALL; } - - *pEnable = This->StateBlock->lightEnable[Index]; + *pEnable = lightInfo->lightEnabled; return D3D_OK; } HRESULT WINAPI IDirect3DDevice8Impl_SetClipPlane(LPDIRECT3DDEVICE8 iface, DWORD Index,CONST float* pPlane) { diff --git a/dlls/d3d8/directx.c b/dlls/d3d8/directx.c index d6d4fc6a3e7..7977fe8ac6b 100644 --- a/dlls/d3d8/directx.c +++ b/dlls/d3d8/directx.c @@ -160,9 +160,9 @@ UINT WINAPI IDirect3D8Impl_GetAdapterModeCount (LPDIRECT3D8 iface, int i = 0; while (EnumDisplaySettingsExW(NULL, i, &DevModeW, 0)) { - TRACE("(%p}->(Adapter: %d) => %d\n", This, Adapter, i); i++; } + TRACE("(%p}->(Adapter: %d) => %d\n", This, Adapter, i); return i; } else { FIXME("Adapter not primary display\n"); @@ -190,8 +190,12 @@ HRESULT WINAPI IDirect3D8Impl_EnumAdapterModes (LPDIRECT3D8 iface, { pMode->Width = DevModeW.dmPelsWidth; pMode->Height = DevModeW.dmPelsHeight; + bpp = DevModeW.dmBitsPerPel; pMode->RefreshRate = D3DADAPTER_DEFAULT; - bpp = DevModeW.dmBitsPerPel; + if (DevModeW.dmFields&DM_DISPLAYFREQUENCY) + { + pMode->RefreshRate = DevModeW.dmDisplayFrequency; + } } else { @@ -229,16 +233,18 @@ HRESULT WINAPI IDirect3D8Impl_GetAdapterDisplayMode (LPDIRECT3D8 iface, } if (Adapter == 0) { /* Display */ - HDC hdc; int bpp = 0; + DEVMODEW DevModeW; - pMode->Width = GetSystemMetrics(SM_CXSCREEN); - pMode->Height = GetSystemMetrics(SM_CYSCREEN); - pMode->RefreshRate = 85; /*FIXME: How to identify? */ - - hdc = CreateDCA("DISPLAY", NULL, NULL, NULL); - bpp = GetDeviceCaps(hdc, BITSPIXEL); - DeleteDC(hdc); + EnumDisplaySettingsExW(NULL, (DWORD)-1, &DevModeW, 0); + pMode->Width = DevModeW.dmPelsWidth; + pMode->Height = DevModeW.dmPelsHeight; + bpp = DevModeW.dmBitsPerPel; + pMode->RefreshRate = D3DADAPTER_DEFAULT; + if (DevModeW.dmFields&DM_DISPLAYFREQUENCY) + { + pMode->RefreshRate = DevModeW.dmDisplayFrequency; + } switch (bpp) { case 8: pMode->Format = D3DFMT_R3G3B2; break; @@ -621,7 +627,7 @@ HRESULT WINAPI IDirect3D8Impl_GetDeviceCaps(LPDIRECT3D8 iface, UINT Adapter, D TRACE("GLCaps: GL_MAX_CLIP_PLANES=%ld\n", pCaps->MaxUserClipPlanes); glGetIntegerv(GL_MAX_LIGHTS, &gl_max); - pCaps->MaxActiveLights = min(MAX_ACTIVE_LIGHTS, gl_max); + pCaps->MaxActiveLights = gl_max; TRACE("GLCaps: GL_MAX_LIGHTS=%ld\n", pCaps->MaxActiveLights); #if defined(GL_ARB_vertex_blend) @@ -716,7 +722,7 @@ static void IDirect3D8Impl_FillGLCaps(LPDIRECT3D8 iface, Display* display) { TRACE("ClipPlanes support - num Planes=%d\n", gl_max); glGetIntegerv(GL_MAX_LIGHTS, &gl_max); - This->gl_info.max_lights = min(MAX_ACTIVE_LIGHTS, gl_max); + This->gl_info.max_lights = gl_max; TRACE("Lights support - max lights=%d\n", gl_max); /* Parse the gl supported features, in theory enabling parts of our code appropriately */ @@ -1151,7 +1157,7 @@ HRESULT WINAPI IDirect3D8Impl_CreateDevice (LPDIRECT3D8 iface, object->proj_valid = 0; object->view_ident = 1; object->last_was_rhw = 0; - + glGetIntegerv(GL_MAX_LIGHTS, &object->maxConcurrentLights); TRACE("(%p,%d) All defaults now set up, leaving CreateDevice with %p\n", This, Adapter, object); return D3D_OK; } diff --git a/dlls/d3d8/stateblock.c b/dlls/d3d8/stateblock.c index ce66495cb23..9184a95977e 100644 --- a/dlls/d3d8/stateblock.c +++ b/dlls/d3d8/stateblock.c @@ -321,9 +321,31 @@ HRESULT WINAPI IDirect3DDeviceImpl_CreateStateBlock(IDirect3DDevice8Impl* This, object->Changed.texture_state[j][SavedVertexStates_T[i]] = TRUE; } } - for (i = 0; i < GL_LIMITS(lights); i++) { - object->Changed.lightEnable[i] = TRUE; - object->Changed.lights[i] = TRUE; + + /* Duplicate light chain */ + { + PLIGHTINFOEL *src = NULL; + PLIGHTINFOEL *dst = NULL; + PLIGHTINFOEL *newEl = NULL; + + src = This->StateBlock->lights; + object->lights = NULL; + + while (src) { + newEl = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PLIGHTINFOEL)); + if (newEl == NULL) return D3DERR_OUTOFVIDEOMEMORY; + memcpy(newEl, src, sizeof(PLIGHTINFOEL)); + newEl->prev = dst; + newEl->changed = TRUE; + newEl->enabledChanged = TRUE; + if (dst == NULL) { + object->lights = newEl; + } else { + dst->next = newEl; + } + dst = newEl; + src = src->next; + } } } else { @@ -337,7 +359,16 @@ HRESULT WINAPI IDirect3DDeviceImpl_CreateStateBlock(IDirect3DDevice8Impl* This, /** yakkk temporary waiting for Release */ HRESULT WINAPI IDirect3DDeviceImpl_DeleteStateBlock(IDirect3DDevice8Impl* This, IDirect3DStateBlockImpl* pSB) { + PLIGHTINFOEL *tmp; + TRACE("(%p) : freeing StateBlock %p\n", This, pSB); + tmp = pSB->lights; + if (tmp) tmp = tmp->next; + while (tmp != NULL) { + HeapFree(GetProcessHeap(), 0, (void *)(tmp->prev)); + tmp = tmp->next; + } + if (tmp) HeapFree(GetProcessHeap(), 0, (void *)tmp); HeapFree(GetProcessHeap(), 0, (void *)pSB); return D3D_OK; } @@ -397,12 +428,13 @@ HRESULT WINAPI IDirect3DDeviceImpl_ApplyStateBlock(IDirect3DDevice8Impl* This, if (pSB->blockType == D3DSBT_RECORDED || pSB->blockType == D3DSBT_ALL || pSB->blockType == D3DSBT_VERTEXSTATE) { - for (i = 0; i < GL_LIMITS(lights); i++) { - - if (pSB->Set.lightEnable[i] && pSB->Changed.lightEnable[i]) - IDirect3DDevice8Impl_LightEnable(iface, i, pSB->lightEnable[i]); - if (pSB->Set.lights[i] && pSB->Changed.lights[i]) - IDirect3DDevice8Impl_SetLight(iface, i, &pSB->lights[i]); + PLIGHTINFOEL *toDo = pSB->lights; + while (toDo != NULL) { + if (toDo->changed) + IDirect3DDevice8Impl_SetLight(iface, toDo->OriginalIndex, &toDo->OriginalParms); + if (toDo->enabledChanged) + IDirect3DDevice8Impl_LightEnable(iface, toDo->OriginalIndex, toDo->lightEnabled); + toDo = toDo->next; } if (pSB->Set.vertexShader && pSB->Changed.vertexShader) @@ -514,6 +546,7 @@ HRESULT WINAPI IDirect3DDeviceImpl_ApplyStateBlock(IDirect3DDevice8Impl* This, HRESULT WINAPI IDirect3DDeviceImpl_CaptureStateBlock(IDirect3DDevice8Impl* This, IDirect3DStateBlockImpl* updateBlock) { LPDIRECT3DDEVICE8 iface = (LPDIRECT3DDEVICE8) This; + PLIGHTINFOEL *tmp; TRACE("(%p) : Updating state block %p ------------------v \n", This, updateBlock); @@ -521,14 +554,18 @@ HRESULT WINAPI IDirect3DDeviceImpl_CaptureStateBlock(IDirect3DDevice8Impl* This, if (updateBlock->blockType != D3DSBT_RECORDED) { IDirect3DStateBlockImpl* tmpBlock; IDirect3DDeviceImpl_CreateStateBlock(This, updateBlock->blockType, &tmpBlock); + + /* Note just swap the light chains over so when deleting, the old one goes */ + tmp = updateBlock->lights; memcpy(updateBlock, tmpBlock, sizeof(IDirect3DStateBlockImpl)); - IDirect3DDeviceImpl_DeleteStateBlock(This, tmpBlock); + tmpBlock->lights = tmp; - /* FIXME: This will record states of new lights! May need to have and save set_lights - across this action */ + /* Delete the temporary one (which points to the old light chain though */ + IDirect3DDeviceImpl_DeleteStateBlock(This, tmpBlock); } else { int i, j; + PLIGHTINFOEL *src; /* Recorded => Only update 'changed' values */ if (updateBlock->Set.vertexShader && updateBlock->VertexShader != This->StateBlock->VertexShader) { @@ -538,23 +575,42 @@ HRESULT WINAPI IDirect3DDeviceImpl_CaptureStateBlock(IDirect3DDevice8Impl* This, /* TODO: Vertex Shader Constants */ - for (i = 0; i < GL_LIMITS(lights); i++) { - if (updateBlock->Set.lightEnable[i] && This->StateBlock->lightEnable[i] != updateBlock->lightEnable[i]) { - TRACE("Updating light enable for light %d to %d\n", i, This->StateBlock->lightEnable[i]); - updateBlock->lightEnable[i] = This->StateBlock->lightEnable[i]; - } - - if (updateBlock->Set.lights[i] && memcmp(&This->StateBlock->lights[i], - &updateBlock->lights[i], - sizeof(D3DLIGHT8)) != 0) { - TRACE("Updating lights for light %d\n", i); - memcpy(&updateBlock->lights[i], &This->StateBlock->lights[i], sizeof(D3DLIGHT8)); - } + /* Lights... For a recorded state block, we just had a chain of actions to perform, + so we need to walk that chain and update any actions which differ */ + src = updateBlock->lights; + while (src != NULL) { + PLIGHTINFOEL *realLight = NULL; + + /* Locate the light in the live lights */ + realLight = This->StateBlock->lights; + while (realLight != NULL && realLight->OriginalIndex != src->OriginalIndex) realLight = realLight->next; + + if (realLight == NULL) { + FIXME("A captured light no longer exists...?\n"); + } else { + + /* If 'changed' then its a SetLight command. Rather than comparing to see + if the OriginalParms have changed and then copy them (twice through + memory) just do the copy */ + if (src->changed) { + TRACE("Updating lights for light %ld\n", src->OriginalIndex); + memcpy(&src->OriginalParms, &realLight->OriginalParms, sizeof(PLIGHTINFOEL)); + } + + /* If 'enabledChanged' then its a LightEnable command */ + if (src->enabledChanged) { + TRACE("Updating lightEnabled for light %ld\n", src->OriginalIndex); + src->lightEnabled = realLight->lightEnabled; + } + + } + + src = src->next; } + if (updateBlock->Set.pixelShader && updateBlock->PixelShader != This->StateBlock->PixelShader) { TRACE("Updating pixel shader to %ld\n", This->StateBlock->PixelShader); - updateBlock->lights[i] = This->StateBlock->lights[i]; IDirect3DDevice8Impl_SetVertexShader(iface, updateBlock->PixelShader); } -- 2.11.4.GIT