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.
diff --git a/dlls/ddraw/device_opengl.c b/dlls/ddraw/device_opengl.c
index c514ec8..ca88442 100644
--- a/dlls/ddraw/device_opengl.c
+++ b/dlls/ddraw/device_opengl.c
@@ -428,6 +428,11 @@
/* 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 @@
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,
@@ -2438,6 +2467,8 @@
{
ICOM_THIS_FROM(IDirect3DDeviceImpl, IDirect3DDevice7, iface);
IDirect3DDeviceGLImpl *glThis = (IDirect3DDeviceGLImpl *) This;
+ LPD3DLIGHT7 lpdestlight = get_light( This, dwLightIndex );
+
TRACE("(%p/%p)->(%08lx,%p)\n", This, iface, dwLightIndex, lpLight);
if (TRACE_ON(ddraw)) {
@@ -2445,9 +2476,13 @@
dump_D3DLIGHT7(lpLight);
}
- if (dwLightIndex >= MAX_LIGHTS) return DDERR_INVALIDPARAMS;
- This->set_lights |= 0x00000001 << dwLightIndex;
- This->light_parameters[dwLightIndex] = *lpLight;
+ /* 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 +2513,81 @@
DWORD dwLightIndex,
BOOL bEnable)
{
+ int lightslot = -1, i;
ICOM_THIS_FROM(IDirect3DDeviceImpl, IDirect3DDevice7, iface);
+ LPD3DLIGHT7 lpdestlight = get_light(This, dwLightIndex);
+
TRACE("(%p/%p)->(%08lx,%d)\n", This, iface, dwLightIndex, bEnable);
-
- if (dwLightIndex >= MAX_LIGHTS) return DDERR_INVALIDPARAMS;
+
+ /* 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 @@
}
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 @@
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 @@
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 @@
}
}
}
- }
+ }
glMultMatrixf((float *) world_mat);
}
@@ -3985,14 +4073,16 @@
}
/* 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));