[d3d] Light management API fix to handle an unlimited number of lights

Antoine Chavasse a.chavasse at gmail.com
Sun Jun 5 13:08:09 CDT 2005


Direct3D allows to set any number of light, and limits the number of
lights that can be active at the same time.

The current wine code allows to setup only 16 lights. It made Anarchy
Online crash, because it tried to setup more lights than that and
didn't like to get returned an error when doing so.

This patch is a rewrite of the d3d light management api and some related code.

Another patch with test cases for the light api will follow.

ChangeLog:
 - Rewrote the light management API to allow for an unlimited amount
of lights to be set,
   and only a subset of them to be enabled.

Index: dlls/ddraw/d3d_private.h
===================================================================
RCS file: /home/wine/wine/dlls/ddraw/d3d_private.h,v
retrieving revision 1.43
diff -u -p -r1.43 d3d_private.h
--- dlls/ddraw/d3d_private.h	1 Jun 2005 19:52:25 -0000	1.43
+++ dlls/ddraw/d3d_private.h	5 Jun 2005 17:38:23 -0000
@@ -225,8 +225,10 @@ struct IDirect3DDeviceImpl
     DWORD material;
 
     /* Light parameters */
-    DWORD active_lights, set_lights;
-    D3DLIGHT7 light_parameters[MAX_LIGHTS];
+    DWORD num_set_lights;
+    DWORD max_active_lights;
+    LPD3DLIGHT7 light_parameters;
+    DWORD *active_lights;
 
     /* clipping planes */
     DWORD max_clipping_planes;
Index: dlls/ddraw/d3ddevice/main.c
===================================================================
RCS file: /home/wine/wine/dlls/ddraw/d3ddevice/main.c,v
retrieving revision 1.56
diff -u -p -r1.56 main.c
--- dlls/ddraw/d3ddevice/main.c	1 Jun 2005 19:52:25 -0000	1.56
+++ dlls/ddraw/d3ddevice/main.c	5 Jun 2005 17:38:29 -0000
@@ -642,9 +642,17 @@ Main_IDirect3DDeviceImpl_7_GetLight(LPDI
     ICOM_THIS_FROM(IDirect3DDeviceImpl, IDirect3DDevice7, iface);
     TRACE("(%p/%p)->(%08lx,%p)\n", This, iface, dwLightIndex, lpLight);
 
-    if (dwLightIndex > MAX_LIGHTS) return DDERR_INVALIDPARAMS;
+    if (dwLightIndex >= This->num_set_lights)
+        return DDERR_INVALIDPARAMS;
+
     *lpLight = This->light_parameters[dwLightIndex];
 
+    /* If dltType is zero, then this light has never been set, either
+       by calling SetLight or implicitely by calling EnableLight without
+       calling SetLight first. */
+    if (lpLight->dltType == 0)
+        return DDERR_INVALIDPARAMS;
+
     if (TRACE_ON(ddraw)) {
         TRACE(" returning light : \n");
 	dump_D3DLIGHT7(lpLight);
@@ -950,12 +958,25 @@ Main_IDirect3DDeviceImpl_7_GetLightEnabl
                                           DWORD dwLightIndex,
                                           BOOL* pbEnable)
 {
+    int i;
+
     ICOM_THIS_FROM(IDirect3DDeviceImpl, IDirect3DDevice7, iface);
     TRACE("(%p/%p)->(%08lx,%p)\n", This, iface, dwLightIndex, pbEnable);
 
-    if (dwLightIndex > MAX_LIGHTS) *pbEnable = 0;
-    else *pbEnable = ((0x00000001 << dwLightIndex) & This->active_lights) != 0;
-    
+    *pbEnable = 0;
+    if (dwLightIndex >= This->num_set_lights)
+        return DDERR_INVALIDPARAMS;
+
+    /* If dltType is zero, then this light has never been set, either
+       by calling SetLight or implicitely by calling EnableLight without
+       calling SetLight first. */
+    if (This->light_parameters[dwLightIndex].dltType == 0)
+        return DDERR_INVALIDPARAMS;
+
+    for (i = 0; i < This->max_active_lights; i++)
+        if (This->active_lights[i] == dwLightIndex)
+            *pbEnable = TRUE;
+
     TRACE(" returning %d.\n", *pbEnable);
 
     return DD_OK;
Index: dlls/ddraw/d3ddevice/mesa.c
===================================================================
RCS file: /home/wine/wine/dlls/ddraw/d3ddevice/mesa.c,v
retrieving revision 1.172
diff -u -p -r1.172 mesa.c
--- dlls/ddraw/d3ddevice/mesa.c	1 Jun 2005 19:52:25 -0000	1.172
+++ dlls/ddraw/d3ddevice/mesa.c	5 Jun 2005 17:38:39 -0000
@@ -428,6 +428,11 @@ GL_IDirect3DDeviceImpl_7_3T_2T_1T_Releas
 	/* And warn the D3D object that this device is no longer active... */
 	This->d3d->d3d_removed_device(This->d3d, This);
 
+        /* Free light arrays */
+        if (This->light_parameters)
+            HeapFree(GetProcessHeap(), 0, This->light_parameters);
+        HeapFree(GetProcessHeap(), 0, This->active_lights);
+
 	HeapFree(GetProcessHeap(), 0, This->world_mat);
 	HeapFree(GetProcessHeap(), 0, This->view_mat);
 	HeapFree(GetProcessHeap(), 0, This->proj_mat);
@@ -2430,6 +2435,30 @@ GL_IDirect3DDeviceImpl_7_SetMaterial(LPD
     return DD_OK;
 }
 
+static LPD3DLIGHT7 get_light(IDirect3DDeviceImpl *This, DWORD dwLightIndex)
+{
+    if (dwLightIndex >= This->num_set_lights)
+    {
+        /* Extend, or allocate the light parameters array. */
+        DWORD newlightnum = dwLightIndex + 1;
+        LPD3DLIGHT7 newarrayptr = NULL;
+
+        if (This->light_parameters)
+            newarrayptr = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+                This->light_parameters, newlightnum * sizeof(D3DLIGHT7));
+        else
+            newarrayptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+                newlightnum * sizeof(D3DLIGHT7));
+
+        if (!newarrayptr)
+            return NULL;
+
+        This->light_parameters = newarrayptr;
+        This->num_set_lights = newlightnum;
+    }
+
+    return &This->light_parameters[dwLightIndex];
+}
 
 HRESULT WINAPI
 GL_IDirect3DDeviceImpl_7_SetLight(LPDIRECT3DDEVICE7 iface,
@@ -2445,9 +2474,14 @@ GL_IDirect3DDeviceImpl_7_SetLight(LPDIRE
 	dump_D3DLIGHT7(lpLight);
     }
     
-    if (dwLightIndex >= MAX_LIGHTS) return DDERR_INVALIDPARAMS;
-    This->set_lights |= 0x00000001 << dwLightIndex;
-    This->light_parameters[dwLightIndex] = *lpLight;
+    LPD3DLIGHT7 lpdestlight = get_light( This, dwLightIndex );
+    /* DirectX7 documentation states that this function can return
+       DDERR_OUTOFMEMORY, so we do just that in case of an allocation
+       error (which is the only reason why get_light() can fail). */
+    if( !lpdestlight )
+        return DDERR_OUTOFMEMORY;
+
+    *lpdestlight = *lpLight;
 
     /* Some checks to print out nice warnings :-) */
     switch (lpLight->dltType) {
@@ -2478,32 +2512,82 @@ GL_IDirect3DDeviceImpl_7_LightEnable(LPD
 				     DWORD dwLightIndex,
 				     BOOL bEnable)
 {
+    int lightslot = -1, i;
     ICOM_THIS_FROM(IDirect3DDeviceImpl, IDirect3DDevice7, iface);
     TRACE("(%p/%p)->(%08lx,%d)\n", This, iface, dwLightIndex, bEnable);
     
-    if (dwLightIndex >= MAX_LIGHTS) return DDERR_INVALIDPARAMS;
+    LPD3DLIGHT7 lpdestlight = get_light(This, dwLightIndex);
+    /* The DirectX doc isn't as explicit as for SetLight as whether we can
+       return this from this function, but it doesn't state otherwise. */
+    if (!lpdestlight)
+        return DDERR_OUTOFMEMORY;
+
+    /* If this light hasn't been set, initialise it with default values. */
+    if (lpdestlight->dltType == 0)
+    {
+        TRACE("setting default light parameters\n");
+
+        /* We always use HEAP_ZERO_MEMORY when allocating the light_parameters
+           array, so we only have to setup anything that shoud not be zero. */
+        lpdestlight->dltType = D3DLIGHT_DIRECTIONAL;
+        lpdestlight->dcvDiffuse.u1.r = 1.f;
+        lpdestlight->dcvDiffuse.u2.g = 1.f;
+        lpdestlight->dcvDiffuse.u3.b = 1.f;
+        lpdestlight->dvDirection.u3.z = 1.f;
+    }
+
+    /* Look for this light in the active lights array. */
+    for (i = 0; i < This->max_active_lights; i++)
+        if (This->active_lights[i] == dwLightIndex)
+        {
+            lightslot = i;
+            break;
+        }
+
+    /* If we didn't find it, let's find the first available slot, if any. */
+    if (lightslot == -1)
+        for (i = 0; i < This->max_active_lights; i++)
+            if (This->active_lights[i] == ~0)
+            {
+                lightslot = i;
+                break;
+            }
 
     ENTER_GL();
+
+
     if (bEnable) {
-        if (((0x00000001 << dwLightIndex) & This->set_lights) == 0) {
-	    /* Set the default parameters.. */
-	    TRACE(" setting default light parameters...\n");
-	    GL_IDirect3DDeviceImpl_7_SetLight(iface, dwLightIndex,
&(This->light_parameters[dwLightIndex]));
-	}
-	glEnable(GL_LIGHT0 + dwLightIndex);
-	if ((This->active_lights & (0x00000001 << dwLightIndex)) == 0) {
+        if (lightslot == -1)
+        {
+            /* This means that the app is trying to enable more lights than
+               the maximum possible indicated in the caps.
+
+               Windows actually let you do this, and disable one of the
+               previously enabled lights to let you enable this one.
+
+               It's not documented and I'm not sure how windows pick
which light
+               to disable to make room for this one. */
+            FIXME("Enabling more light than the maximum is not
supported yet.");
+            return D3D_OK;
+        }
+
+        glEnable(GL_LIGHT0 + lightslot);
+
+
+        if (This->active_lights[lightslot] == ~0)
+        {
 	    /* This light gets active... Need to update its parameters to GL
before the next drawing */
 	    IDirect3DDeviceGLImpl *glThis = (IDirect3DDeviceGLImpl *) This;
-	    
-	    This->active_lights |= 0x00000001 << dwLightIndex;
+
+            This->active_lights[lightslot] = dwLightIndex;
 	    glThis->transform_state = GL_TRANSFORM_NONE;
 	}
     } else {
-        glDisable(GL_LIGHT0 + dwLightIndex);
-	This->active_lights &= ~(0x00000001 << dwLightIndex);
+        glDisable(GL_LIGHT0 + lightslot);
+        This->active_lights[lightslot] = ~0;
     }
-    LEAVE_GL();
 
+    LEAVE_GL();
     return DD_OK;
 }
 
@@ -3421,23 +3505,27 @@ d3ddevice_set_matrices(IDirect3DDeviceIm
 	}
 	if (This->state_block.render_state[D3DRENDERSTATE_LIGHTING - 1] != FALSE) {
 	    GLint i;
-	    DWORD runner;
 
-	    for (i = 0, runner = 0x00000001; i < MAX_LIGHTS; i++, runner <<= 1) {
-	        if (runner & This->active_lights) {
-		    switch (This->light_parameters[i].dltType) {
+            for (i = 0; i < This->max_active_lights; i++ )
+            {
+                DWORD dwLightIndex = This->active_lights[i];
+                if (dwLightIndex != ~0)
+                {
+                    LPD3DLIGHT7 pLight = &This->light_parameters[dwLightIndex];
+                    switch (pLight->dltType)
+                    {
 		        case D3DLIGHT_DIRECTIONAL: {
 			    float direction[4];
 			    float cut_off = 180.0;
 			    
-			    glLightfv(GL_LIGHT0 + i, GL_AMBIENT,  (float *)
&(This->light_parameters[i].dcvAmbient));
-			    glLightfv(GL_LIGHT0 + i, GL_DIFFUSE,  (float *)
&(This->light_parameters[i].dcvDiffuse));
-			    glLightfv(GL_LIGHT0 + i, GL_SPECULAR, (float *)
&(This->light_parameters[i].dcvSpecular));
+			    glLightfv(GL_LIGHT0 + i, GL_AMBIENT,  (float *) &pLight->dcvAmbient);
+			    glLightfv(GL_LIGHT0 + i, GL_DIFFUSE,  (float *) &pLight->dcvDiffuse);
+			    glLightfv(GL_LIGHT0 + i, GL_SPECULAR, (float *) &pLight->dcvSpecular);
 			    glLightfv(GL_LIGHT0 + i, GL_SPOT_CUTOFF, &cut_off);
 			    
-			    direction[0] = This->light_parameters[i].dvDirection.u1.x;
-			    direction[1] = This->light_parameters[i].dvDirection.u2.y;
-			    direction[2] = This->light_parameters[i].dvDirection.u3.z;
+			    direction[0] = pLight->dvDirection.u1.x;
+			    direction[1] = pLight->dvDirection.u2.y;
+			    direction[2] = pLight->dvDirection.u3.z;
 			    direction[3] = 0.0;
 			    glLightfv(GL_LIGHT0 + i, GL_POSITION, (float *) direction);
 			} break;
@@ -3446,17 +3534,17 @@ d3ddevice_set_matrices(IDirect3DDeviceIm
 			    float position[4];
 			    float cut_off = 180.0;
 			    
-			    glLightfv(GL_LIGHT0 + i, GL_AMBIENT,  (float *)
&(This->light_parameters[i].dcvAmbient));
-			    glLightfv(GL_LIGHT0 + i, GL_DIFFUSE,  (float *)
&(This->light_parameters[i].dcvDiffuse));
-			    glLightfv(GL_LIGHT0 + i, GL_SPECULAR, (float *)
&(This->light_parameters[i].dcvSpecular));
-			    position[0] = This->light_parameters[i].dvPosition.u1.x;
-			    position[1] = This->light_parameters[i].dvPosition.u2.y;
-			    position[2] = This->light_parameters[i].dvPosition.u3.z;
+			    glLightfv(GL_LIGHT0 + i, GL_AMBIENT,  (float *) &pLight->dcvAmbient);
+			    glLightfv(GL_LIGHT0 + i, GL_DIFFUSE,  (float *) &pLight->dcvDiffuse);
+			    glLightfv(GL_LIGHT0 + i, GL_SPECULAR, (float *) &pLight->dcvSpecular);
+			    position[0] = pLight->dvPosition.u1.x;
+			    position[1] = pLight->dvPosition.u2.y;
+			    position[2] = pLight->dvPosition.u3.z;
 			    position[3] = 1.0;
 			    glLightfv(GL_LIGHT0 + i, GL_POSITION, (float *) position);
-			    glLightfv(GL_LIGHT0 + i, GL_CONSTANT_ATTENUATION,
&(This->light_parameters[i].dvAttenuation0));
-			    glLightfv(GL_LIGHT0 + i, GL_LINEAR_ATTENUATION,
&(This->light_parameters[i].dvAttenuation1));
-			    glLightfv(GL_LIGHT0 + i, GL_QUADRATIC_ATTENUATION,
&(This->light_parameters[i].dvAttenuation2));
+			    glLightfv(GL_LIGHT0 + i, GL_CONSTANT_ATTENUATION,
&pLight->dvAttenuation0);
+			    glLightfv(GL_LIGHT0 + i, GL_LINEAR_ATTENUATION,
&pLight->dvAttenuation1);
+			    glLightfv(GL_LIGHT0 + i, GL_QUADRATIC_ATTENUATION,
&pLight->dvAttenuation2);
 			    glLightfv(GL_LIGHT0 + i, GL_SPOT_CUTOFF, &cut_off);
 			} break;
 
@@ -3465,25 +3553,25 @@ d3ddevice_set_matrices(IDirect3DDeviceIm
 			    float position[4];
 			    float cut_off = 90.0 * (This->light_parameters[i].dvPhi / M_PI);
 			    
-			    glLightfv(GL_LIGHT0 + i, GL_AMBIENT,  (float *)
&(This->light_parameters[i].dcvAmbient));
-			    glLightfv(GL_LIGHT0 + i, GL_DIFFUSE,  (float *)
&(This->light_parameters[i].dcvDiffuse));
-			    glLightfv(GL_LIGHT0 + i, GL_SPECULAR, (float *)
&(This->light_parameters[i].dcvSpecular));
+			    glLightfv(GL_LIGHT0 + i, GL_AMBIENT,  (float *) &pLight->dcvAmbient);
+			    glLightfv(GL_LIGHT0 + i, GL_DIFFUSE,  (float *) &pLight->dcvDiffuse);
+			    glLightfv(GL_LIGHT0 + i, GL_SPECULAR, (float *) &pLight->dcvSpecular);
 			    
-			    direction[0] = This->light_parameters[i].dvDirection.u1.x;
-			    direction[1] = This->light_parameters[i].dvDirection.u2.y;
-			    direction[2] = This->light_parameters[i].dvDirection.u3.z;
+			    direction[0] = pLight->dvDirection.u1.x;
+			    direction[1] = pLight->dvDirection.u2.y;
+			    direction[2] = pLight->dvDirection.u3.z;
 			    direction[3] = 0.0;
 			    glLightfv(GL_LIGHT0 + i, GL_SPOT_DIRECTION, (float *) direction);
-			    position[0] = This->light_parameters[i].dvPosition.u1.x;
-			    position[1] = This->light_parameters[i].dvPosition.u2.y;
-			    position[2] = This->light_parameters[i].dvPosition.u3.z;
+			    position[0] = pLight->dvPosition.u1.x;
+			    position[1] = pLight->dvPosition.u2.y;
+			    position[2] = pLight->dvPosition.u3.z;
 			    position[3] = 1.0;
 			    glLightfv(GL_LIGHT0 + i, GL_POSITION, (float *) position);
-			    glLightfv(GL_LIGHT0 + i, GL_CONSTANT_ATTENUATION,
&(This->light_parameters[i].dvAttenuation0));
-			    glLightfv(GL_LIGHT0 + i, GL_LINEAR_ATTENUATION,
&(This->light_parameters[i].dvAttenuation1));
-			    glLightfv(GL_LIGHT0 + i, GL_QUADRATIC_ATTENUATION,
&(This->light_parameters[i].dvAttenuation2));
+			    glLightfv(GL_LIGHT0 + i, GL_CONSTANT_ATTENUATION,
&pLight->dvAttenuation0);
+			    glLightfv(GL_LIGHT0 + i, GL_LINEAR_ATTENUATION,
&pLight->dvAttenuation1);
+			    glLightfv(GL_LIGHT0 + i, GL_QUADRATIC_ATTENUATION,
&pLight->dvAttenuation2);
 			    glLightfv(GL_LIGHT0 + i, GL_SPOT_CUTOFF, &cut_off);
-			    glLightfv(GL_LIGHT0 + i, GL_SPOT_EXPONENT,
&(This->light_parameters[i].dvFalloff));
+			    glLightfv(GL_LIGHT0 + i, GL_SPOT_EXPONENT, &pLight->dvFalloff);
 			} break;
 
 			default:
@@ -3492,7 +3580,7 @@ d3ddevice_set_matrices(IDirect3DDeviceIm
 		    }
 		}
 	    }
-	}
+        }
 	
 	glMultMatrixf((float *) world_mat);
     }
@@ -3985,14 +4073,16 @@ d3ddevice_create(IDirect3DDeviceImpl **o
     }
 
     /* Set the various light parameters */
-    for (light = 0; light < MAX_LIGHTS; light++) {
-        /* Only set the fields that are not zero-created */
-        object->light_parameters[light].dltType = D3DLIGHT_DIRECTIONAL;
-	object->light_parameters[light].dcvDiffuse.u1.r = 1.0;
-	object->light_parameters[light].dcvDiffuse.u2.g = 1.0;
-	object->light_parameters[light].dcvDiffuse.u3.b = 1.0;
-	object->light_parameters[light].dvDirection.u3.z = 1.0;
-    }
+    object->num_set_lights = 0;
+    object->max_active_lights = opengl_device_caps.dwMaxActiveLights;
+    object->light_parameters = NULL;
+    object->active_lights = HeapAlloc(GetProcessHeap(), 0,
+        object->max_active_lights * sizeof(BOOLEAN));
+    /* Fill the active light array with ~0, which is used to indicate an
+       invalid light index. We don't use 0, because it's a valid
light index. */
+    for (light=0; light < object->max_active_lights; light++)
+        object->active_lights[light] = ~0;
+
     
     /* Allocate memory for the matrices */
     object->world_mat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
16 * sizeof(float));




More information about the wine-patches mailing list