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.
This commit is contained in:
Antoine Chavasse 2005-06-07 21:34:40 +00:00 committed by Alexandre Julliard
parent 20806c730b
commit b65e790bd3
3 changed files with 177 additions and 64 deletions

View File

@ -225,8 +225,10 @@ struct IDirect3DDeviceImpl
DWORD material; DWORD material;
/* Light parameters */ /* Light parameters */
DWORD active_lights, set_lights; DWORD num_set_lights;
D3DLIGHT7 light_parameters[MAX_LIGHTS]; DWORD max_active_lights;
LPD3DLIGHT7 light_parameters;
DWORD *active_lights;
/* clipping planes */ /* clipping planes */
DWORD max_clipping_planes; DWORD max_clipping_planes;

View File

@ -642,9 +642,17 @@ Main_IDirect3DDeviceImpl_7_GetLight(LPDIRECT3DDEVICE7 iface,
ICOM_THIS_FROM(IDirect3DDeviceImpl, IDirect3DDevice7, iface); ICOM_THIS_FROM(IDirect3DDeviceImpl, IDirect3DDevice7, iface);
TRACE("(%p/%p)->(%08lx,%p)\n", This, iface, dwLightIndex, lpLight); 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]; *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)) { if (TRACE_ON(ddraw)) {
TRACE(" returning light : \n"); TRACE(" returning light : \n");
dump_D3DLIGHT7(lpLight); dump_D3DLIGHT7(lpLight);
@ -950,12 +958,25 @@ Main_IDirect3DDeviceImpl_7_GetLightEnable(LPDIRECT3DDEVICE7 iface,
DWORD dwLightIndex, DWORD dwLightIndex,
BOOL* pbEnable) BOOL* pbEnable)
{ {
int i;
ICOM_THIS_FROM(IDirect3DDeviceImpl, IDirect3DDevice7, iface); ICOM_THIS_FROM(IDirect3DDeviceImpl, IDirect3DDevice7, iface);
TRACE("(%p/%p)->(%08lx,%p)\n", This, iface, dwLightIndex, pbEnable); TRACE("(%p/%p)->(%08lx,%p)\n", This, iface, dwLightIndex, pbEnable);
if (dwLightIndex > MAX_LIGHTS) *pbEnable = 0; *pbEnable = 0;
else *pbEnable = ((0x00000001 << dwLightIndex) & This->active_lights) != 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); TRACE(" returning %d.\n", *pbEnable);
return DD_OK; return DD_OK;

View File

@ -428,6 +428,11 @@ GL_IDirect3DDeviceImpl_7_3T_2T_1T_Release(LPDIRECT3DDEVICE7 iface)
/* And warn the D3D object that this device is no longer active... */ /* And warn the D3D object that this device is no longer active... */
This->d3d->d3d_removed_device(This->d3d, This); 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->world_mat);
HeapFree(GetProcessHeap(), 0, This->view_mat); HeapFree(GetProcessHeap(), 0, This->view_mat);
HeapFree(GetProcessHeap(), 0, This->proj_mat); HeapFree(GetProcessHeap(), 0, This->proj_mat);
@ -2430,6 +2435,30 @@ GL_IDirect3DDeviceImpl_7_SetMaterial(LPDIRECT3DDEVICE7 iface,
return DD_OK; 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 HRESULT WINAPI
GL_IDirect3DDeviceImpl_7_SetLight(LPDIRECT3DDEVICE7 iface, GL_IDirect3DDeviceImpl_7_SetLight(LPDIRECT3DDEVICE7 iface,
@ -2438,6 +2467,8 @@ GL_IDirect3DDeviceImpl_7_SetLight(LPDIRECT3DDEVICE7 iface,
{ {
ICOM_THIS_FROM(IDirect3DDeviceImpl, IDirect3DDevice7, iface); ICOM_THIS_FROM(IDirect3DDeviceImpl, IDirect3DDevice7, iface);
IDirect3DDeviceGLImpl *glThis = (IDirect3DDeviceGLImpl *) This; IDirect3DDeviceGLImpl *glThis = (IDirect3DDeviceGLImpl *) This;
LPD3DLIGHT7 lpdestlight = get_light( This, dwLightIndex );
TRACE("(%p/%p)->(%08lx,%p)\n", This, iface, dwLightIndex, lpLight); TRACE("(%p/%p)->(%08lx,%p)\n", This, iface, dwLightIndex, lpLight);
if (TRACE_ON(ddraw)) { if (TRACE_ON(ddraw)) {
@ -2445,9 +2476,13 @@ GL_IDirect3DDeviceImpl_7_SetLight(LPDIRECT3DDEVICE7 iface,
dump_D3DLIGHT7(lpLight); dump_D3DLIGHT7(lpLight);
} }
if (dwLightIndex >= MAX_LIGHTS) return DDERR_INVALIDPARAMS; /* DirectX7 documentation states that this function can return
This->set_lights |= 0x00000001 << dwLightIndex; DDERR_OUTOFMEMORY, so we do just that in case of an allocation
This->light_parameters[dwLightIndex] = *lpLight; 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 :-) */ /* Some checks to print out nice warnings :-) */
switch (lpLight->dltType) { switch (lpLight->dltType) {
@ -2478,32 +2513,81 @@ GL_IDirect3DDeviceImpl_7_LightEnable(LPDIRECT3DDEVICE7 iface,
DWORD dwLightIndex, DWORD dwLightIndex,
BOOL bEnable) BOOL bEnable)
{ {
int lightslot = -1, i;
ICOM_THIS_FROM(IDirect3DDeviceImpl, IDirect3DDevice7, iface); ICOM_THIS_FROM(IDirect3DDeviceImpl, IDirect3DDevice7, iface);
LPD3DLIGHT7 lpdestlight = get_light(This, dwLightIndex);
TRACE("(%p/%p)->(%08lx,%d)\n", This, iface, dwLightIndex, bEnable); 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(); ENTER_GL();
if (bEnable) { if (bEnable) {
if (((0x00000001 << dwLightIndex) & This->set_lights) == 0) { if (lightslot == -1)
/* Set the default parameters.. */ {
TRACE(" setting default light parameters...\n"); /* This means that the app is trying to enable more lights than
GL_IDirect3DDeviceImpl_7_SetLight(iface, dwLightIndex, &(This->light_parameters[dwLightIndex])); the maximum possible indicated in the caps.
}
glEnable(GL_LIGHT0 + dwLightIndex); Windows actually let you do this, and disable one of the
if ((This->active_lights & (0x00000001 << dwLightIndex)) == 0) { 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 */ /* This light gets active... Need to update its parameters to GL before the next drawing */
IDirect3DDeviceGLImpl *glThis = (IDirect3DDeviceGLImpl *) This; IDirect3DDeviceGLImpl *glThis = (IDirect3DDeviceGLImpl *) This;
This->active_lights |= 0x00000001 << dwLightIndex; This->active_lights[lightslot] = dwLightIndex;
glThis->transform_state = GL_TRANSFORM_NONE; glThis->transform_state = GL_TRANSFORM_NONE;
} }
} else { } else {
glDisable(GL_LIGHT0 + dwLightIndex); glDisable(GL_LIGHT0 + lightslot);
This->active_lights &= ~(0x00000001 << dwLightIndex); This->active_lights[lightslot] = ~0;
} }
LEAVE_GL();
LEAVE_GL();
return DD_OK; return DD_OK;
} }
@ -3421,23 +3505,27 @@ d3ddevice_set_matrices(IDirect3DDeviceImpl *This, DWORD matrices,
} }
if (This->state_block.render_state[D3DRENDERSTATE_LIGHTING - 1] != FALSE) { if (This->state_block.render_state[D3DRENDERSTATE_LIGHTING - 1] != FALSE) {
GLint i; GLint i;
DWORD runner;
for (i = 0, runner = 0x00000001; i < MAX_LIGHTS; i++, runner <<= 1) { for (i = 0; i < This->max_active_lights; i++ )
if (runner & This->active_lights) { {
switch (This->light_parameters[i].dltType) { DWORD dwLightIndex = This->active_lights[i];
if (dwLightIndex != ~0)
{
LPD3DLIGHT7 pLight = &This->light_parameters[dwLightIndex];
switch (pLight->dltType)
{
case D3DLIGHT_DIRECTIONAL: { case D3DLIGHT_DIRECTIONAL: {
float direction[4]; float direction[4];
float cut_off = 180.0; float cut_off = 180.0;
glLightfv(GL_LIGHT0 + i, GL_AMBIENT, (float *) &(This->light_parameters[i].dcvAmbient)); glLightfv(GL_LIGHT0 + i, GL_AMBIENT, (float *) &pLight->dcvAmbient);
glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, (float *) &(This->light_parameters[i].dcvDiffuse)); glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, (float *) &pLight->dcvDiffuse);
glLightfv(GL_LIGHT0 + i, GL_SPECULAR, (float *) &(This->light_parameters[i].dcvSpecular)); glLightfv(GL_LIGHT0 + i, GL_SPECULAR, (float *) &pLight->dcvSpecular);
glLightfv(GL_LIGHT0 + i, GL_SPOT_CUTOFF, &cut_off); glLightfv(GL_LIGHT0 + i, GL_SPOT_CUTOFF, &cut_off);
direction[0] = This->light_parameters[i].dvDirection.u1.x; direction[0] = pLight->dvDirection.u1.x;
direction[1] = This->light_parameters[i].dvDirection.u2.y; direction[1] = pLight->dvDirection.u2.y;
direction[2] = This->light_parameters[i].dvDirection.u3.z; direction[2] = pLight->dvDirection.u3.z;
direction[3] = 0.0; direction[3] = 0.0;
glLightfv(GL_LIGHT0 + i, GL_POSITION, (float *) direction); glLightfv(GL_LIGHT0 + i, GL_POSITION, (float *) direction);
} break; } break;
@ -3446,17 +3534,17 @@ d3ddevice_set_matrices(IDirect3DDeviceImpl *This, DWORD matrices,
float position[4]; float position[4];
float cut_off = 180.0; float cut_off = 180.0;
glLightfv(GL_LIGHT0 + i, GL_AMBIENT, (float *) &(This->light_parameters[i].dcvAmbient)); glLightfv(GL_LIGHT0 + i, GL_AMBIENT, (float *) &pLight->dcvAmbient);
glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, (float *) &(This->light_parameters[i].dcvDiffuse)); glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, (float *) &pLight->dcvDiffuse);
glLightfv(GL_LIGHT0 + i, GL_SPECULAR, (float *) &(This->light_parameters[i].dcvSpecular)); glLightfv(GL_LIGHT0 + i, GL_SPECULAR, (float *) &pLight->dcvSpecular);
position[0] = This->light_parameters[i].dvPosition.u1.x; position[0] = pLight->dvPosition.u1.x;
position[1] = This->light_parameters[i].dvPosition.u2.y; position[1] = pLight->dvPosition.u2.y;
position[2] = This->light_parameters[i].dvPosition.u3.z; position[2] = pLight->dvPosition.u3.z;
position[3] = 1.0; position[3] = 1.0;
glLightfv(GL_LIGHT0 + i, GL_POSITION, (float *) position); 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_CONSTANT_ATTENUATION, &pLight->dvAttenuation0);
glLightfv(GL_LIGHT0 + i, GL_LINEAR_ATTENUATION, &(This->light_parameters[i].dvAttenuation1)); glLightfv(GL_LIGHT0 + i, GL_LINEAR_ATTENUATION, &pLight->dvAttenuation1);
glLightfv(GL_LIGHT0 + i, GL_QUADRATIC_ATTENUATION, &(This->light_parameters[i].dvAttenuation2)); glLightfv(GL_LIGHT0 + i, GL_QUADRATIC_ATTENUATION, &pLight->dvAttenuation2);
glLightfv(GL_LIGHT0 + i, GL_SPOT_CUTOFF, &cut_off); glLightfv(GL_LIGHT0 + i, GL_SPOT_CUTOFF, &cut_off);
} break; } break;
@ -3465,25 +3553,25 @@ d3ddevice_set_matrices(IDirect3DDeviceImpl *This, DWORD matrices,
float position[4]; float position[4];
float cut_off = 90.0 * (This->light_parameters[i].dvPhi / M_PI); 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_AMBIENT, (float *) &pLight->dcvAmbient);
glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, (float *) &(This->light_parameters[i].dcvDiffuse)); glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, (float *) &pLight->dcvDiffuse);
glLightfv(GL_LIGHT0 + i, GL_SPECULAR, (float *) &(This->light_parameters[i].dcvSpecular)); glLightfv(GL_LIGHT0 + i, GL_SPECULAR, (float *) &pLight->dcvSpecular);
direction[0] = This->light_parameters[i].dvDirection.u1.x; direction[0] = pLight->dvDirection.u1.x;
direction[1] = This->light_parameters[i].dvDirection.u2.y; direction[1] = pLight->dvDirection.u2.y;
direction[2] = This->light_parameters[i].dvDirection.u3.z; direction[2] = pLight->dvDirection.u3.z;
direction[3] = 0.0; direction[3] = 0.0;
glLightfv(GL_LIGHT0 + i, GL_SPOT_DIRECTION, (float *) direction); glLightfv(GL_LIGHT0 + i, GL_SPOT_DIRECTION, (float *) direction);
position[0] = This->light_parameters[i].dvPosition.u1.x; position[0] = pLight->dvPosition.u1.x;
position[1] = This->light_parameters[i].dvPosition.u2.y; position[1] = pLight->dvPosition.u2.y;
position[2] = This->light_parameters[i].dvPosition.u3.z; position[2] = pLight->dvPosition.u3.z;
position[3] = 1.0; position[3] = 1.0;
glLightfv(GL_LIGHT0 + i, GL_POSITION, (float *) position); 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_CONSTANT_ATTENUATION, &pLight->dvAttenuation0);
glLightfv(GL_LIGHT0 + i, GL_LINEAR_ATTENUATION, &(This->light_parameters[i].dvAttenuation1)); glLightfv(GL_LIGHT0 + i, GL_LINEAR_ATTENUATION, &pLight->dvAttenuation1);
glLightfv(GL_LIGHT0 + i, GL_QUADRATIC_ATTENUATION, &(This->light_parameters[i].dvAttenuation2)); glLightfv(GL_LIGHT0 + i, GL_QUADRATIC_ATTENUATION, &pLight->dvAttenuation2);
glLightfv(GL_LIGHT0 + i, GL_SPOT_CUTOFF, &cut_off); 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; } break;
default: default:
@ -3492,7 +3580,7 @@ d3ddevice_set_matrices(IDirect3DDeviceImpl *This, DWORD matrices,
} }
} }
} }
} }
glMultMatrixf((float *) world_mat); glMultMatrixf((float *) world_mat);
} }
@ -3985,14 +4073,16 @@ d3ddevice_create(IDirect3DDeviceImpl **obj, IDirectDrawImpl *d3d, IDirectDrawSur
} }
/* Set the various light parameters */ /* Set the various light parameters */
for (light = 0; light < MAX_LIGHTS; light++) { object->num_set_lights = 0;
/* Only set the fields that are not zero-created */ object->max_active_lights = opengl_device_caps.dwMaxActiveLights;
object->light_parameters[light].dltType = D3DLIGHT_DIRECTIONAL; object->light_parameters = NULL;
object->light_parameters[light].dcvDiffuse.u1.r = 1.0; object->active_lights = HeapAlloc(GetProcessHeap(), 0,
object->light_parameters[light].dcvDiffuse.u2.g = 1.0; object->max_active_lights * sizeof(BOOLEAN));
object->light_parameters[light].dcvDiffuse.u3.b = 1.0; /* Fill the active light array with ~0, which is used to indicate an
object->light_parameters[light].dvDirection.u3.z = 1.0; 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 */ /* Allocate memory for the matrices */
object->world_mat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 16 * sizeof(float)); object->world_mat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 16 * sizeof(float));