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;
/* 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;

View File

@ -642,9 +642,17 @@ Main_IDirect3DDeviceImpl_7_GetLight(LPDIRECT3DDEVICE7 iface,
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_GetLightEnable(LPDIRECT3DDEVICE7 iface,
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;

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... */
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(LPDIRECT3DDEVICE7 iface,
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 @@ GL_IDirect3DDeviceImpl_7_SetLight(LPDIRECT3DDEVICE7 iface,
{
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 @@ GL_IDirect3DDeviceImpl_7_SetLight(LPDIRECT3DDEVICE7 iface,
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 @@ GL_IDirect3DDeviceImpl_7_LightEnable(LPDIRECT3DDEVICE7 iface,
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 @@ d3ddevice_set_matrices(IDirect3DDeviceImpl *This, DWORD matrices,
}
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(IDirect3DDeviceImpl *This, DWORD matrices,
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(IDirect3DDeviceImpl *This, DWORD matrices,
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(IDirect3DDeviceImpl *This, DWORD matrices,
}
}
}
}
}
glMultMatrixf((float *) world_mat);
}
@ -3985,14 +4073,16 @@ d3ddevice_create(IDirect3DDeviceImpl **obj, IDirectDrawImpl *d3d, IDirectDrawSur
}
/* 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));