diff --git a/dlls/ddraw/tests/d3d.c b/dlls/ddraw/tests/d3d.c index bb37f084bc8..1125875fe98 100644 --- a/dlls/ddraw/tests/d3d.c +++ b/dlls/ddraw/tests/d3d.c @@ -218,6 +218,65 @@ static void LightTest(void) /* Light 23 has not been set */ rc = IDirect3DDevice7_GetLightEnable(lpD3DDevice, 23, &bEnabled ); ok(rc==DDERR_INVALIDPARAMS, "GetLightEnable returned: %x\n", rc); + + /* Set some lights with invalid parameters */ + memset(&light, 0, sizeof(D3DLIGHT7)); + light.dltType = 0; + U1(light.dcvDiffuse).r = 1.f; + U2(light.dcvDiffuse).g = 1.f; + U3(light.dcvDiffuse).b = 1.f; + U3(light.dvDirection).z = 1.f; + rc = IDirect3DDevice7_SetLight(lpD3DDevice, 100, &light); + ok(rc==DDERR_INVALIDPARAMS, "SetLight returned: %x\n", rc); + + memset(&light, 0, sizeof(D3DLIGHT7)); + light.dltType = 12345; + U1(light.dcvDiffuse).r = 1.f; + U2(light.dcvDiffuse).g = 1.f; + U3(light.dcvDiffuse).b = 1.f; + U3(light.dvDirection).z = 1.f; + rc = IDirect3DDevice7_SetLight(lpD3DDevice, 101, &light); + ok(rc==DDERR_INVALIDPARAMS, "SetLight returned: %x\n", rc); + + rc = IDirect3DDevice7_SetLight(lpD3DDevice, 102, NULL); + ok(rc==DDERR_INVALIDPARAMS, "SetLight returned: %x\n", rc); + + memset(&light, 0, sizeof(D3DLIGHT7)); + light.dltType = D3DLIGHT_SPOT; + U1(light.dcvDiffuse).r = 1.f; + U2(light.dcvDiffuse).g = 1.f; + U3(light.dcvDiffuse).b = 1.f; + U3(light.dvDirection).z = 1.f; + + U3(light.dvAttenuation0) = -1.0 / 0.0; /* -INFINITY */ + rc = IDirect3DDevice7_SetLight(lpD3DDevice, 103, &light); + ok(rc==DDERR_INVALIDPARAMS, "SetLight returned: %x\n", rc); + + U3(light.dvAttenuation0) = -1.0; + rc = IDirect3DDevice7_SetLight(lpD3DDevice, 103, &light); + ok(rc==DDERR_INVALIDPARAMS, "SetLight returned: %x\n", rc); + + U3(light.dvAttenuation0) = 0.0; + rc = IDirect3DDevice7_SetLight(lpD3DDevice, 103, &light); + ok(rc==D3D_OK, "SetLight returned: %x\n", rc); + + U3(light.dvAttenuation0) = 1.0; + rc = IDirect3DDevice7_SetLight(lpD3DDevice, 103, &light); + ok(rc==D3D_OK, "SetLight returned: %x\n", rc); + + U3(light.dvAttenuation0) = 1.0 / 0.0; /* +INFINITY */ + rc = IDirect3DDevice7_SetLight(lpD3DDevice, 103, &light); + ok(rc==D3D_OK, "SetLight returned: %x\n", rc); + + U3(light.dvAttenuation0) = 0.0 / 0.0; /* NaN */ + rc = IDirect3DDevice7_SetLight(lpD3DDevice, 103, &light); + ok(rc==D3D_OK, "SetLight returned: %x\n", rc); + + /* Directional light ignores attenuation */ + light.dltType = D3DLIGHT_DIRECTIONAL; + U3(light.dvAttenuation0) = -1.0; + rc = IDirect3DDevice7_SetLight(lpD3DDevice, 103, &light); + ok(rc==D3D_OK, "SetLight returned: %x\n", rc); } static void ProcessVerticesTest(void) diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c index af0e96a2338..05b48525cc3 100644 --- a/dlls/wined3d/device.c +++ b/dlls/wined3d/device.c @@ -2199,6 +2199,37 @@ static HRESULT WINAPI IWineD3DDeviceImpl_SetLight(IWineD3DDevice *iface, DWORD I IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface; TRACE("(%p) : Idx(%d), pLight(%p). Hash index is %d\n", This, Index, pLight, Hi); + /* Check the parameter range. Need for speed most wanted sets junk lights which confuse + * the gl driver. + */ + if(!pLight) { + WARN("Light pointer = NULL, returning WINED3DERR_INVALIDCALL\n"); + return WINED3DERR_INVALIDCALL; + } + + switch(pLight->Type) { + case WINED3DLIGHT_POINT: + case WINED3DLIGHT_SPOT: + case WINED3DLIGHT_PARALLELPOINT: + case WINED3DLIGHT_GLSPOT: + /* Incorrect attenuation values can cause the gl driver to crash. Happens with Need for speed + * most wanted + */ + if(pLight->Attenuation0 < 0.0 || pLight->Attenuation1 < 0.0 || pLight->Attenuation2 < 0.0) { + WARN("Attenuation is negative, returning WINED3DERR_INVALIDCALL\n"); + return WINED3DERR_INVALIDCALL; + } + break; + + case WINED3DLIGHT_DIRECTIONAL: + /* Ignores attenuation */ + break; + + default: + WARN("Light type out of range, returning WINED3DERR_INVALIDCALL\n"); + return WINED3DERR_INVALIDCALL; + } + LIST_FOR_EACH(e, &This->updateStateBlock->lightMap[Hi]) { object = LIST_ENTRY(e, PLIGHTINFOEL, entry); if(object->OriginalIndex == Index) break; diff --git a/dlls/wined3d/state.c b/dlls/wined3d/state.c index 1a73b57c361..2b76dff930e 100644 --- a/dlls/wined3d/state.c +++ b/dlls/wined3d/state.c @@ -2981,21 +2981,16 @@ static void light(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3DContex 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"); - if ((lightInfo->OriginalParms.Range *lightInfo->OriginalParms.Range) >= FLT_MIN) { quad_att = 1.4/(lightInfo->OriginalParms.Range *lightInfo->OriginalParms.Range); } else { quad_att = 0; /* 0 or MAX? (0 seems to be ok) */ } - if (quad_att < lightInfo->OriginalParms.Attenuation2) quad_att = lightInfo->OriginalParms.Attenuation2; - glLightf(GL_LIGHT0 + Index, GL_QUADRATIC_ATTENUATION, quad_att); - checkGLcall("glLightf"); + /* Do not assign attenuation values for lights that do not use them. D3D apps are free to pass any junk, + * but gl drivers use them and may crash due to bad Attenuation values. Need for Speed most wanted sets + * Attenuation0 to NaN and crashes in the gl lib + */ switch (lightInfo->OriginalParms.Type) { case WINED3DLIGHT_POINT: @@ -3004,6 +2999,14 @@ static void light(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3DContex checkGLcall("glLightfv"); glLightf(GL_LIGHT0 + Index, GL_SPOT_CUTOFF, lightInfo->cutoff); checkGLcall("glLightf"); + /* 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"); + if (quad_att < lightInfo->OriginalParms.Attenuation2) quad_att = lightInfo->OriginalParms.Attenuation2; + glLightf(GL_LIGHT0 + Index, GL_QUADRATIC_ATTENUATION, quad_att); + checkGLcall("glLightf"); /* FIXME: Range */ break; @@ -3018,6 +3021,14 @@ static void light(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3DContex checkGLcall("glLightf"); glLightf(GL_LIGHT0 + Index, GL_SPOT_CUTOFF, lightInfo->cutoff); checkGLcall("glLightf"); + /* 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"); + if (quad_att < lightInfo->OriginalParms.Attenuation2) quad_att = lightInfo->OriginalParms.Attenuation2; + glLightf(GL_LIGHT0 + Index, GL_QUADRATIC_ATTENUATION, quad_att); + checkGLcall("glLightf"); /* FIXME: Range */ break;