[PATCH v3 4/8] wined3d: Implement lighting with directional lights in process_vertices_strided().

Paul Gofman gofmanp at gmail.com
Mon May 20 04:56:39 CDT 2019


Signed-off-by: Paul Gofman <gofmanp at gmail.com>
---
v2:
    - rename dot_vec3 to wined3d_vec3_dot();
    - use wined3d_vec3_dot() in normalize_vec3();
    - use functions with explicit types instead of madd_vec();
    - rename multiply_vector3_matrix() to multiply_vector3_matrix3();
    - introduce 'struct wined3d_matrix3' and use it in multiply_vector3_matrix3().
    - increase bit field size for light count.
v3:
    - use wined3d_ prefix for vector math functions;
    - set .a component to 1.0 for output specular color regardless of _FOGENABLE.

 dlls/ddraw/tests/ddraw1.c      |   4 +-
 dlls/wined3d/device.c          | 302 ++++++++++++++++++++++++++++++---
 dlls/wined3d/wined3d_private.h |   1 +
 3 files changed, 283 insertions(+), 24 deletions(-)

diff --git a/dlls/ddraw/tests/ddraw1.c b/dlls/ddraw/tests/ddraw1.c
index 7dda6eba20..9525b2dc0c 100644
--- a/dlls/ddraw/tests/ddraw1.c
+++ b/dlls/ddraw/tests/ddraw1.c
@@ -5894,7 +5894,7 @@ static void test_material(void)
         ok(SUCCEEDED(hr), "Failed to end scene, hr %#x.\n", hr);
         color = get_surface_color(rt, 320, 240);
         if (test_data[i].material)
-            todo_wine ok(compare_color(color, test_data[i].expected_color, 1)
+            ok(compare_color(color, test_data[i].expected_color, 1)
                     /* The Windows 8 testbot appears to return undefined results. */
                     || broken(TRUE),
                     "Got unexpected color 0x%08x, test %u.\n", color, i);
@@ -6273,7 +6273,7 @@ static void test_lighting(void)
         ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
 
         color = get_surface_color(rt, 320, 240);
-        todo_wine ok(color == tests[i].expected, "%s has color 0x%08x.\n", tests[i].message, color);
+        ok(color == tests[i].expected, "%s has color 0x%08x.\n", tests[i].message, color);
     }
 
     IDirect3DExecuteBuffer_Release(execute_buffer);
diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c
index f1072f9cdb..e8835e5548 100644
--- a/dlls/wined3d/device.c
+++ b/dlls/wined3d/device.c
@@ -3176,6 +3176,11 @@ static void color_from_mcs(struct wined3d_color *color, enum wined3d_material_co
     wined3d_format_get_color(element->format, element->data.addr + index * element->stride, color);
 }
 
+static float wined3d_vec3_dot(const struct wined3d_vec3 *src1, const struct wined3d_vec3 *src2)
+{
+    return src1->x * src2->x + src1->y * src2->y + src1->z * src2->z;
+}
+
 static float wined3d_clamp(float value, float min_value, float max_value)
 {
     return value < min_value ? min_value : value > max_value ? max_value : value;
@@ -3190,6 +3195,206 @@ static void wined3d_color_clamp(struct wined3d_color *dst, const struct wined3d_
     dst->a = wined3d_clamp(src->a, min_value, max_value);
 }
 
+static void wined3d_vec3_normalize(struct wined3d_vec3 *dest, const struct wined3d_vec3 *src)
+{
+    float rnorm = 1.0f / sqrtf(wined3d_vec3_dot(src, src));
+
+    *dest = *src;
+    if (isfinite(rnorm))
+    {
+        dest->x *= rnorm;
+        dest->y *= rnorm;
+        dest->z *= rnorm;
+    }
+}
+
+static void wined3d_color_rgb_mul_add(struct wined3d_color *dst, const struct wined3d_color *src, float c)
+{
+    dst->r += src->r * c;
+    dst->g += src->g * c;
+    dst->b += src->b * c;
+}
+
+struct wined3d_matrix3
+{
+    float _11, _12, _13;
+    float _21, _22, _23;
+    float _31, _32, _33;
+};
+
+static void wined3d_vec3_m3x3(struct wined3d_vec3 *dest, const struct wined3d_vec3 *src1,
+        const struct wined3d_matrix3 *mat)
+{
+    struct wined3d_vec3 temp;
+
+    temp.x = (src1->x * mat->_11) + (src1->y * mat->_21) + (src1->z * mat->_31);
+    temp.y = (src1->x * mat->_12) + (src1->y * mat->_22) + (src1->z * mat->_32);
+    temp.z = (src1->x * mat->_13) + (src1->y * mat->_23) + (src1->z * mat->_33);
+
+    *dest = temp;
+}
+
+struct light_transformed
+{
+    struct wined3d_color diffuse, specular, ambient;
+    struct wined3d_vec4 position;
+    struct wined3d_vec3 direction;
+    float range, falloff, c_att, l_att, q_att, cos_htheta, cos_hphi;
+};
+
+struct lights_settings
+{
+    struct light_transformed lights[WINED3D_MAX_SOFTWARE_ACTIVE_LIGHTS];
+    struct wined3d_color ambient_light;
+    struct wined3d_matrix modelview_matrix;
+    struct wined3d_matrix3 normal_matrix;
+
+    DWORD point_light_count          : 8;
+    DWORD spot_light_count           : 8;
+    DWORD directional_light_count    : 8;
+    DWORD parallel_point_light_count : 8;
+    DWORD legacy_lighting            : 1;
+    DWORD normalize                  : 1;
+    DWORD localviewer                : 1;
+    DWORD padding                    : 29;
+};
+
+static void init_transformed_lights(struct lights_settings *ls, const struct wined3d_state *state,
+        BOOL legacy_lighting)
+{
+    const struct wined3d_light_info *lights[WINED3D_MAX_SOFTWARE_ACTIVE_LIGHTS];
+    const struct wined3d_light_info *light_info;
+    struct light_transformed *light;
+    unsigned int lights_count;
+    struct wined3d_vec4 vec4;
+    unsigned int i, index;
+
+    memset(ls, 0, sizeof(*ls));
+
+    wined3d_color_from_d3dcolor(&ls->ambient_light, state->render_states[WINED3D_RS_AMBIENT]);
+    ls->legacy_lighting = !!legacy_lighting;
+    ls->normalize = !!state->render_states[WINED3D_RS_NORMALIZENORMALS];
+    ls->localviewer = !!state->render_states[WINED3D_RS_LOCALVIEWER];
+
+    multiply_matrix(&ls->modelview_matrix, &state->transforms[WINED3D_TS_VIEW],
+            &state->transforms[WINED3D_TS_WORLD_MATRIX(0)]);
+    compute_normal_matrix(&ls->normal_matrix._11, legacy_lighting, &ls->modelview_matrix);
+
+    index = 0;
+    for (i = 0; i < LIGHTMAP_SIZE && index < WINED3D_MAX_SOFTWARE_ACTIVE_LIGHTS; ++i)
+    {
+        LIST_FOR_EACH_ENTRY(light_info, &state->light_state.light_map[i], struct wined3d_light_info, entry)
+        {
+            if (!light_info->enabled)
+                continue;
+
+            switch (light_info->OriginalParms.type)
+            {
+                case WINED3D_LIGHT_DIRECTIONAL:
+                    ++ls->directional_light_count;
+                    break;
+                default:
+                    FIXME("Unhandled light type %#x.\n", light_info->OriginalParms.type);
+                    continue;
+            }
+            lights[index] = light_info;
+            if (++index == WINED3D_MAX_SOFTWARE_ACTIVE_LIGHTS)
+                break;
+        }
+    }
+
+    lights_count = index;
+    index = 0;
+    for (i = 0; i < lights_count; ++i)
+    {
+        light_info = lights[i];
+        if (light_info->OriginalParms.type != WINED3D_LIGHT_DIRECTIONAL)
+            continue;
+
+        light = &ls->lights[index];
+        wined3d_vec4_mat(&vec4, &light_info->direction, &state->transforms[WINED3D_TS_VIEW]);
+        wined3d_vec3_normalize(&light->direction, (const struct wined3d_vec3 *)&vec4);
+
+        light->diffuse = light_info->OriginalParms.diffuse;
+        light->ambient = light_info->OriginalParms.ambient;
+        light->specular = light_info->OriginalParms.specular;
+        ++index;
+    }
+}
+
+static void update_light_diffuse_specular(struct wined3d_color *diffuse, struct wined3d_color *specular,
+        const struct wined3d_vec3 *dir, float att, float material_shininess,
+        const struct wined3d_vec3 *normal_transformed,
+        const struct wined3d_vec3 *position_transformed_normalized,
+        const struct light_transformed *light, const struct lights_settings *ls)
+{
+    struct wined3d_vec3 vec3;
+    float t, c;
+
+    c = wined3d_clamp(wined3d_vec3_dot(dir, normal_transformed), 0.0f, 1.0f);
+    wined3d_color_rgb_mul_add(diffuse, &light->diffuse, c * att);
+
+    if (ls->localviewer)
+    {
+        vec3.x = dir->x - position_transformed_normalized->x;
+        vec3.y = dir->y - position_transformed_normalized->y;
+        vec3.z = dir->z - position_transformed_normalized->z;
+    }
+    else
+    {
+        vec3.x = dir->x;
+        vec3.y = dir->y;
+        vec3.z = dir->z - 1.0f;
+    }
+    wined3d_vec3_normalize(&vec3, &vec3);
+    t = wined3d_vec3_dot(normal_transformed, &vec3);
+    if (t > 0.0f && (!ls->legacy_lighting || material_shininess > 0.0f)
+            && wined3d_vec3_dot(dir, normal_transformed) > 0.0f)
+        wined3d_color_rgb_mul_add(specular, &light->specular, att * powf(t, material_shininess));
+}
+
+static void compute_light(struct wined3d_color *ambient, struct wined3d_color *diffuse,
+        struct wined3d_color *specular, const struct lights_settings *ls, const struct wined3d_vec3 *normal,
+        const struct wined3d_vec4 *position, float material_shininess)
+{
+    struct wined3d_vec3 position_transformed_normalized;
+    struct wined3d_vec3 normal_transformed = {0.0f};
+    struct wined3d_vec4 position_transformed;
+    const struct light_transformed *light;
+    unsigned int i, index;
+    float rcp_w;
+
+    wined3d_vec4_mat(&position_transformed, position, &ls->modelview_matrix);
+    rcp_w = 1.0f / position_transformed.w;
+    position_transformed.x *= rcp_w;
+    position_transformed.y *= rcp_w;
+    position_transformed.z *= rcp_w;
+    position_transformed.w = 1.0f;
+    wined3d_vec3_normalize(&position_transformed_normalized, (const struct wined3d_vec3 *)&position_transformed);
+
+    if (normal)
+    {
+        wined3d_vec3_m3x3(&normal_transformed, normal, &ls->normal_matrix);
+        if (ls->normalize)
+            wined3d_vec3_normalize(&normal_transformed, &normal_transformed);
+    }
+
+    diffuse->r = diffuse->g = diffuse->b = diffuse->a = 0.0f;
+    *specular = *diffuse;
+    *ambient = ls->ambient_light;
+
+    index = 0;
+    for (i = 0; i < ls->directional_light_count; ++i, ++index)
+    {
+        light = &ls->lights[index];
+
+        wined3d_color_rgb_mul_add(ambient, &light->ambient, 1.0f);
+        if (normal)
+            update_light_diffuse_specular(diffuse, specular, &light->direction, 1.0f, material_shininess,
+                    &normal_transformed, &position_transformed_normalized, light, ls);
+    }
+}
+
 /* Context activation is done by the caller. */
 #define copy_and_next(dest, src, size) memcpy(dest, src, size); dest += (size)
 static HRESULT process_vertices_strided(const struct wined3d_device *device, DWORD dwDestIndex, DWORD dwCount,
@@ -3204,18 +3409,14 @@ static HRESULT process_vertices_strided(const struct wined3d_device *device, DWO
     struct wined3d_map_desc map_desc;
     struct wined3d_box box = {0};
     struct wined3d_viewport vp;
+    struct lights_settings ls;
     unsigned int vertex_size;
+    BOOL do_clip, lighting;
     DWORD numTextures;
     unsigned int i;
     BYTE *dest_ptr;
-    BOOL doClip;
     HRESULT hr;
 
-    if (stream_info->use_map & (1u << WINED3D_FFP_NORMAL))
-    {
-        WARN(" lighting state not saved yet... Some strange stuff may happen !\n");
-    }
-
     if (!(stream_info->use_map & (1u << WINED3D_FFP_POSITION)))
     {
         ERR("Source has no position mask.\n");
@@ -3231,16 +3432,16 @@ static HRESULT process_vertices_strided(const struct wined3d_device *device, DWO
          * so disable clipping for now.
          * (The graphics in Half-Life are broken, and my processvertices
          *  test crashes with IDirect3DDevice3)
-        doClip = TRUE;
+        do_clip = TRUE;
          */
-        doClip = FALSE;
+        do_clip = FALSE;
         if(!warned) {
            warned = TRUE;
            FIXME("Clipping is broken and disabled for now\n");
         }
     }
     else
-        doClip = FALSE;
+        do_clip = FALSE;
 
     vertex_size = wined3d_get_flexible_vertex_size(dst_fvf);
     box.left = dwDestIndex * vertex_size;
@@ -3284,21 +3485,27 @@ static HRESULT process_vertices_strided(const struct wined3d_device *device, DWO
 
     numTextures = (dst_fvf & WINED3DFVF_TEXCOUNT_MASK) >> WINED3DFVF_TEXCOUNT_SHIFT;
 
+    lighting = state->render_states[WINED3D_RS_LIGHTING]
+            && (dst_fvf & (WINED3DFVF_DIFFUSE | WINED3DFVF_SPECULAR));
     wined3d_get_mcs(&diffuse_source, &emissive_source, &ambient_source, &specular_source,
             state, stream_info);
-
     output_color_format = wined3d_get_format(device->adapter, WINED3DFMT_B8G8R8A8_UNORM, 0);
     material_specular_state_color = state->render_states[WINED3D_RS_SPECULARENABLE]
             ? &state->material.specular : &black;
+    if (lighting)
+        init_transformed_lights(&ls, state, device->adapter->d3d_info.wined3d_creation_flags
+                & WINED3D_LEGACY_FFP_LIGHTING);
 
     for (i = 0; i < dwCount; i+= 1) {
+        struct wined3d_color ambient, diffuse, specular;
+        const struct wined3d_stream_info_element *position_element
+                = &stream_info->elements[WINED3D_FFP_POSITION];
+        const float *p = (const float *)(position_element->data.addr + i * position_element->stride);
         unsigned int tex_index;
 
         if ( ((dst_fvf & WINED3DFVF_POSITION_MASK) == WINED3DFVF_XYZ ) ||
              ((dst_fvf & WINED3DFVF_POSITION_MASK) == WINED3DFVF_XYZRHW ) ) {
             /* The position first */
-            const struct wined3d_stream_info_element *element = &stream_info->elements[WINED3D_FFP_POSITION];
-            const float *p = (const float *)(element->data.addr + i * element->stride);
             float x, y, z, rhw;
             TRACE("In: ( %06.2f %06.2f %06.2f )\n", p[0], p[1], p[2]);
 
@@ -3327,10 +3534,9 @@ static HRESULT process_vertices_strided(const struct wined3d_device *device, DWO
              *
              */
 
-            if( !doClip ||
-                ( (-rhw -eps < x) && (-rhw -eps < y) && ( -eps < z) &&
-                  (x <= rhw + eps) && (y <= rhw + eps ) && (z <= rhw + eps) &&
-                  ( rhw > eps ) ) ) {
+            if(!do_clip || (-rhw - eps < x && -rhw - eps < y && -eps < z && x <= rhw + eps
+                    && y <= rhw + eps && z <= rhw + eps && rhw > eps))
+            {
 
                 /* "Normal" viewport transformation (not clipped)
                  * 1) The values are divided by rhw
@@ -3406,25 +3612,77 @@ static HRESULT process_vertices_strided(const struct wined3d_device *device, DWO
             copy_and_next(dest_ptr, normal, 3 * sizeof(float));
         }
 
+        if (lighting)
+        {
+            const struct wined3d_stream_info_element *element;
+            struct wined3d_vec4 position;
+            struct wined3d_vec3 *normal;
+
+            position.x = p[0];
+            position.y = p[1];
+            position.z = p[2];
+            position.w = 1.0f;
+
+            if (stream_info->use_map & (1u << WINED3D_FFP_NORMAL))
+            {
+                element = &stream_info->elements[WINED3D_FFP_NORMAL];
+                normal = (struct wined3d_vec3 *)(element->data.addr + i * element->stride);
+            }
+            else
+            {
+                normal = NULL;
+            }
+            compute_light(&ambient, &diffuse, &specular, &ls, normal, &position,
+                    state->render_states[WINED3D_RS_SPECULARENABLE] ? state->material.power : 0.0f);
+        }
+
         if (dst_fvf & WINED3DFVF_DIFFUSE)
         {
-            struct wined3d_color material_diffuse;
+            struct wined3d_color material_diffuse, material_ambient, material_emissive, diffuse_color;
 
             color_from_mcs(&material_diffuse, diffuse_source, &state->material.diffuse, i, stream_info);
 
-            wined3d_color_clamp(&material_diffuse, &material_diffuse, 0.0f, 1.0f);
-            *((DWORD *)dest_ptr) = wined3d_format_convert_from_float(output_color_format, &material_diffuse);
+            if (lighting)
+            {
+                color_from_mcs(&material_ambient, ambient_source, &state->material.ambient, i, stream_info);
+                color_from_mcs(&material_emissive, emissive_source, &state->material.emissive, i, stream_info);
+
+                diffuse_color.a = material_diffuse.a;
+                diffuse_color.r = ambient.r * material_ambient.r
+                        + diffuse.r * material_diffuse.r + material_emissive.r;
+                diffuse_color.g = ambient.g * material_ambient.g
+                        + diffuse.g * material_diffuse.g + material_emissive.g;
+                diffuse_color.b = ambient.b * material_ambient.b
+                        + diffuse.b * material_diffuse.b + material_emissive.b;
+            }
+            else
+            {
+                diffuse_color = material_diffuse;
+            }
+            wined3d_color_clamp(&diffuse_color, &diffuse_color, 0.0f, 1.0f);
+            *((DWORD *)dest_ptr) = wined3d_format_convert_from_float(output_color_format, &diffuse_color);
             dest_ptr += sizeof(DWORD);
         }
 
         if (dst_fvf & WINED3DFVF_SPECULAR)
         {
-            struct wined3d_color material_specular;
+            struct wined3d_color material_specular, specular_color;
 
             color_from_mcs(&material_specular, specular_source, material_specular_state_color, i, stream_info);
 
-            wined3d_color_clamp(&material_specular, &material_specular, 0.0f, 1.0f);
-            *((DWORD *)dest_ptr) = wined3d_format_convert_from_float(output_color_format, &material_specular);
+            if (lighting)
+            {
+                specular_color.r = specular.r * material_specular.r;
+                specular_color.g = specular.g * material_specular.g;
+                specular_color.b = specular.b * material_specular.b;
+                specular_color.a = 1.0f;
+            }
+            else
+            {
+                specular_color = material_specular;
+            }
+            wined3d_color_clamp(&specular_color, &specular_color, 0.0f, 1.0f);
+            *((DWORD *)dest_ptr) = wined3d_format_convert_from_float(output_color_format, &specular_color);
             dest_ptr += sizeof(DWORD);
         }
 
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index 57f5e69edc..a44e56c88f 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -270,6 +270,7 @@ static inline enum complex_fixup get_complex_fixup(struct color_fixup_desc fixup
 #define WINED3D_MAX_VERTEX_SAMPLERS 4
 #define WINED3D_MAX_COMBINED_SAMPLERS (WINED3D_MAX_FRAGMENT_SAMPLERS + WINED3D_MAX_VERTEX_SAMPLERS)
 #define WINED3D_MAX_ACTIVE_LIGHTS   8
+#define WINED3D_MAX_SOFTWARE_ACTIVE_LIGHTS 32
 #define WINED3D_MAX_CLIP_DISTANCES  8
 #define MAX_CONSTANT_BUFFERS        15
 #define MAX_SAMPLER_OBJECTS         16
-- 
2.21.0




More information about the wine-devel mailing list