From d35922a2b4eb5af4301f7c0db9ebc79ddf04abf3 Mon Sep 17 00:00:00 2001
From: David Adam <david.adam.cnrs@gmail.com>
Date: Mon, 17 Nov 2008 07:35:53 +0100
Subject: A new patch for ComputeSphereVisibility

---
 dlls/ddraw/device.c    |  134 +++++++++++++++++++++++++++++++++++++++---------
 dlls/ddraw/tests/d3d.c |  131 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 241 insertions(+), 24 deletions(-)

diff --git a/dlls/ddraw/device.c b/dlls/ddraw/device.c
index 7efc66f..7310478 100644
--- a/dlls/ddraw/device.c
+++ b/dlls/ddraw/device.c
@@ -46,6 +46,7 @@
 
 #include "ddraw.h"
 #include "d3d.h"
+#include "d3drmdef.h"
 
 #include "ddraw_private.h"
 #include "wine/debug.h"
@@ -4722,6 +4723,39 @@ Thunk_IDirect3DDeviceImpl_3_DrawIndexedPrimitiveVB(IDirect3DDevice3 *iface,
  *  is singular)
  *
  *****************************************************************************/
+
+static DWORD* in_plane(DWORD* partial_visibility, const unsigned int decay, const D3DVECTOR* normal, const D3DVALUE origin_plane, 
+    const D3DVECTOR* point, const D3DVALUE radius)
+{
+    float distance;
+
+    distance = origin_plane + normal->u1.x * point->u1.x + normal->u2.y * point->u2.y + normal->u3.z * point->u3.z;
+    if  ( (distance > -radius) && (distance < radius) ) *partial_visibility  = 1 << decay;
+    if  (distance <= -radius) *partial_visibility  = 2 << decay;
+    if  (distance >= radius) *partial_visibility  = 0;
+    return partial_visibility;
+}
+
+static D3DVALUE determinant(const D3DMATRIX *pm)
+{
+    D3DVALUE det, t[6], tmp[4];
+
+    t[0] = pm->_33 * pm->_44 - pm->_43 * pm->_34;
+    t[1] = pm->_32 * pm->_43 - pm->_42 * pm->_34;
+    t[2] = pm->_32 * pm->_43 - pm->_42 * pm->_33;
+    t[3] = pm->_31 * pm->_44 - pm->_41 * pm->_34;
+    t[4] = pm->_31 * pm->_43 - pm->_41 * pm->_33;
+    t[5] = pm->_31 * pm->_42 - pm->_41 * pm->_32;
+ 
+    tmp[0] = pm->_22 * t[0] - pm->_23 * t[1] + pm->_24 * t[2];
+    tmp[1] = pm->_21 * t[0] - pm->_23 * t[3] + pm->_24 * t[4];
+    tmp[2] = pm->_21 * t[1] - pm->_22 * t[3] + pm->_24 * t[5];
+    tmp[3] = pm->_21 * t[2] - pm->_22 * t[4] + pm->_23 * t[5];
+ 
+    det = pm->_11 * tmp[0] - pm->_12 * tmp[1] + pm->_13 * tmp[2] - pm->_14 * tmp[3]; 
+    return det;
+}
+
 static HRESULT WINAPI
 IDirect3DDeviceImpl_7_ComputeSphereVisibility(IDirect3DDevice7 *iface,
                                               D3DVECTOR *Centers,
@@ -4730,30 +4764,82 @@ IDirect3DDeviceImpl_7_ComputeSphereVisibility(IDirect3DDevice7 *iface,
                                               DWORD Flags,
                                               DWORD *ReturnValues)
 {
-    ICOM_THIS_FROM(IDirect3DDeviceImpl, IDirect3DDevice7, iface);
-    FIXME("(%p)->(%p,%p,%08x,%08x,%p): stub!\n", This, Centers, Radii, NumSpheres, Flags, ReturnValues);
-
-    /* the DirectX 7 sdk says that the visibility is computed by
-     * back-transforming the viewing frustum to model space
-     * using the inverse of the combined world, view and projection
-     * matrix. If the matrix can't be reversed, D3DERR_INVALIDMATRIX
-     * is returned.
-     *
-     * Basic implementation idea:
-     * 1) Check if the center is in the viewing frustum
-     * 2) Cut the sphere with the planes of the viewing
-     *    frustum
-     *
-     * ->Center inside the frustum, no intersections:
-     *    Fully visible
-     * ->Center outside the frustum, no intersections:
-     *    Not visible
-     * ->Some intersections: Partially visible
-     *
-     * Implement this call in WineD3D. Either implement the
-     * matrix and vector stuff in WineD3D, or use some external
-     * math library.
-     */
+    D3DMATRIX m, temp;
+    D3DVECTOR vec;
+    DWORD partial_visibility, visibility;
+    D3DVALUE origin_plane;
+    unsigned int i;
+
+    if (!Centers || !Radii || !ReturnValues ) return DDERR_INVALIDPARAMS;
+
+    IDirect3DDeviceImpl_7_GetTransform(iface, D3DTRANSFORMSTATE_WORLD, &m);
+
+    IDirect3DDeviceImpl_7_GetTransform(iface, D3DTRANSFORMSTATE_VIEW, &temp);
+    multiply_matrix(&m, &m, &temp);
+
+    IDirect3DDeviceImpl_7_GetTransform(iface, D3DTRANSFORMSTATE_PROJECTION, &temp);
+    multiply_matrix(&m, &m, &temp);
+
+    if (abs(determinant(&m)) < 0.0001f) return D3DERR_INVALIDMATRIX;
+
+    for(i=0; i<NumSpheres; i++)
+    {
+        visibility = D3DVIS_INSIDE_FRUSTUM;
+
+/* Left plane */
+        vec.u1.x = m._14 + m._11;
+        vec.u2.y = m._24 + m._21;
+        vec.u3.z = m._34 + m._31;
+        origin_plane = m._44 + m._41;
+
+        in_plane(&partial_visibility, 2, &vec, origin_plane, &Centers[i], Radii[i]);
+            visibility = visibility | partial_visibility;
+
+/* Right plane */
+        vec.u1.x = m._14 - m._11;
+        vec.u2.y = m._24 - m._21;
+        vec.u3.z = m._34 - m._31;
+        origin_plane = m._44 - m._41;
+
+        in_plane(&partial_visibility, 4, &vec, origin_plane, &Centers[i], Radii[i]);
+            visibility = visibility | partial_visibility;
+
+/* Bottom plane */
+        vec.u1.x = m._14 + m._12;
+        vec.u2.y = m._24 + m._22;
+        vec.u3.z = m._34 + m._32;
+        origin_plane = m._44 + m._42;
+
+        in_plane(&partial_visibility, 8, &vec, origin_plane, &Centers[i], Radii[i]);
+            visibility = visibility | partial_visibility;
+
+/* Top plane */
+        vec.u1.x = m._14 - m._12;
+        vec.u2.y = m._24 - m._22;
+        vec.u3.z = m._34 - m._32;
+        origin_plane = m._44 - m._42;
+
+        in_plane(&partial_visibility, 6, &vec, origin_plane, &Centers[i], Radii[i]);
+            visibility = visibility | partial_visibility;
+
+/* Near plane */
+        vec.u1.x = m._13;
+        vec.u2.y = m._23;
+        vec.u3.z = m._33;
+        origin_plane = m._43;
+
+        in_plane(&partial_visibility, 10, &vec, origin_plane, &Centers[i], Radii[i]);
+            visibility = visibility | partial_visibility;
+
+/* Far plane*/
+        vec.u1.x = m._14 - m._13;
+        vec.u2.y = m._24 - m._23;
+        vec.u3.z = m._34 - m._33;
+        origin_plane = m._44 - m._43;
+
+        in_plane(&partial_visibility, 12, &vec, origin_plane, &Centers[i], Radii[i]);
+            ReturnValues[i] = visibility | partial_visibility;
+    }
 
     return D3D_OK;
 }
diff --git a/dlls/ddraw/tests/d3d.c b/dlls/ddraw/tests/d3d.c
index 94a0dfe..814f993 100644
--- a/dlls/ddraw/tests/d3d.c
+++ b/dlls/ddraw/tests/d3d.c
@@ -21,6 +21,7 @@
  */
 
 #include <assert.h>
+#include <stdio.h>
 #include "wine/test.h"
 #include "initguid.h"
 #include "ddraw.h"
@@ -2743,6 +2744,135 @@ static void SetMaterialTest(void)
     ok(rc == DDERR_INVALIDPARAMS, "Expected DDERR_INVALIDPARAMS, got %x\n", rc);
 }
 
+static void ComputeSphereVisibility(void)
+{
+    D3DMATRIX proj, view, world;
+    D3DVALUE radius;
+    D3DVECTOR center;
+    DWORD result[1];
+    HRESULT rc;
+
+    world._11=1.0; world._12=0.0; world._13=0.0; world._14=0.0;
+    world._21=0.0; world._22=1.0; world._13=0.0; world._14=0.0;
+    world._31=0.0; world._32=0.0; world._33=1.0; world._34=0.0;
+    world._41=0.0; world._42=0.0; world._43=0.0; world._44=1.0;
+
+    view._11=1.000000; view._12=0.000000; view._13=0.000000; view._14=0.000000;
+    view._21=0.000000; view._22=0.768221; view._23=-0.640185; view._24=0.000000;
+    view._31=-0.000000; view._32=0.640185; view._33=0.768221; view._34=0.000000;
+    view._41=-14.852037; view._42=9.857489; view._43=11.600972; view._44=1.000000;
+
+    proj._11=1.810660; proj._12=0.000000; proj._13=0.00000; proj._14=0.000000;
+    proj._21=0.000000; proj._22=2.414213; proj._23=0.000000, proj._24=0.000000;
+    proj._31=0.000000; proj._32=0.000000; proj._33=1.020408, proj._34=1.000000;
+    proj._41=0.000000; proj._42=0.000000; proj._43=-0.102041; proj._44=0.000000;
+
+    center.x=11.461533; 
+    center.y=-4.761727;
+    center.z=-1.171646;
+
+    radius=38.252632;
+
+    IDirect3DDevice7_SetTransform(lpD3DDevice, D3DTRANSFORMSTATE_WORLD, &world);
+    IDirect3DDevice7_SetTransform(lpD3DDevice, D3DTRANSFORMSTATE_VIEW , &view);
+    IDirect3DDevice7_SetTransform(lpD3DDevice, D3DTRANSFORMSTATE_PROJECTION, &proj);
+
+    rc = IDirect3DDevice7_ComputeSphereVisibility(lpD3DDevice, &center, &radius, 1, 0, result);
+
+    ok(rc == D3D_OK, "Expected D3D_OK, received %x\n", rc);
+    ok(result[0] == 0x0000003f, "Expected 0x3f, got %x\n\n\n\n\n\n\n", result[0]);
+
+    world._11=1.0; world._12=0.0; world._13=0.0; world._14=0.0;
+    world._21=0.0; world._22=1.0; world._13=0.0; world._14=0.0;
+    world._31=0.0; world._32=0.0; world._33=1.0; world._34=0.0;
+    world._41=0.0; world._42=0.0; world._43=0.0; world._44=1.0;
+
+    view._11=1.0; view._12=0.0; view._13=0.0; view._14=0.0;
+    view._21=0.0; view._22=1.0; view._23=0.0; view._24=0.0;
+    view._31=0.0; view._32=0.0; view._33=1.0; view._34=0.0;
+    view._41=0.0; view._42=0.0; view._43=0.0; view._44=1.0;
+
+    proj._11=10.0; proj._12=0.0; proj._13=0.0; proj._14=0.0;
+    proj._21=0.0; proj._22=10.0; proj._23=0.0, proj._24=0.0;
+    proj._31=0.0; proj._32=0.0; proj._33=10.0, proj._34=0.0;
+    proj._41=0.0; proj._42=0.0; proj._43=0.0; proj._44=1.0;
+
+    center.x=0.0; 
+    center.y=0.0;
+    center.z=0.05;
+
+    radius=0.04;
+
+    IDirect3DDevice7_SetTransform(lpD3DDevice, D3DTRANSFORMSTATE_WORLD, &world);
+    IDirect3DDevice7_SetTransform(lpD3DDevice, D3DTRANSFORMSTATE_VIEW , &view);
+    IDirect3DDevice7_SetTransform(lpD3DDevice, D3DTRANSFORMSTATE_PROJECTION, &proj);
+
+    rc = IDirect3DDevice7_ComputeSphereVisibility(lpD3DDevice, &center, &radius, 1, 0, result);
+
+    ok(rc == D3D_OK, "Expected D3D_OK, received %x\n", rc);
+    ok(result[0] == 0x00000000, "Expected 0x0, got %x\n\n\n\n\n\n\n", result[0]);
+
+    world._11=1.0; world._12=0.0; world._13=0.0; world._14=0.0;
+    world._21=0.0; world._22=1.0; world._13=0.0; world._14=0.0;
+    world._31=0.0; world._32=0.0; world._33=1.0; world._34=0.0;
+    world._41=0.0; world._42=0.0; world._43=0.0; world._44=1.0;
+
+    view._11=1.0; view._12=0.0; view._13=0.0; view._14=0.0;
+    view._21=0.0; view._22=1.0; view._23=0.0; view._24=0.0;
+    view._31=0.0; view._32=0.0; view._33=1.0; view._34=0.0;
+    view._41=0.0; view._42=0.0; view._43=0.0; view._44=1.0;
+
+    proj._11=1.0; proj._12=0.0; proj._13=0.0; proj._14=0.0;
+    proj._21=0.0; proj._22=1.0; proj._23=0.0, proj._24=0.0;
+    proj._31=0.0; proj._32=0.0; proj._33=1.0, proj._34=0.0;
+    proj._41=0.0; proj._42=0.0; proj._43=0.0; proj._44=1.0;
+
+    center.x=0.0; 
+    center.y=0.0;
+    center.z=0.5;
+
+    radius=0.5;
+
+    IDirect3DDevice7_SetTransform(lpD3DDevice, D3DTRANSFORMSTATE_WORLD, &world);
+    IDirect3DDevice7_SetTransform(lpD3DDevice, D3DTRANSFORMSTATE_VIEW , &view);
+    IDirect3DDevice7_SetTransform(lpD3DDevice, D3DTRANSFORMSTATE_PROJECTION, &proj);
+
+    rc = IDirect3DDevice7_ComputeSphereVisibility(lpD3DDevice, &center, &radius, 1, 0, result);
+
+    ok(rc == D3D_OK, "Expected D3D_OK, received %x\n", rc);
+    ok(result[0] == 0x00000010, "Expected 0x10, got %x\n\n\n\n\n\n\n", result[0]);
+
+    world._11=1.0; world._12=0.0; world._13=0.0; world._14=0.0;
+    world._21=0.0; world._22=1.0; world._13=0.0; world._14=0.0;
+    world._31=0.0; world._32=0.0; world._33=1.0; world._34=0.0;
+    world._41=0.0; world._42=0.0; world._43=0.0; world._44=1.0;
+
+    view._11=1.0; view._12=0.0; view._13=0.0; view._14=0.0;
+    view._21=0.0; view._22=1.0; view._23=0.0; view._24=0.0;
+    view._31=0.0; view._32=0.0; view._33=1.0; view._34=0.0;
+    view._41=0.0; view._42=0.0; view._43=0.0; view._44=1.0;
+
+    proj._11=1.0; proj._12=0.0; proj._13=0.0; proj._14=0.0;
+    proj._21=0.0; proj._22=1.0; proj._23=0.0, proj._24=0.0;
+    proj._31=0.0; proj._32=0.0; proj._33=1.0, proj._34=0.0;
+    proj._41=0.0; proj._42=0.0; proj._43=0.0; proj._44=1.0;
+
+    center.x=0.0; 
+    center.y=0.0;
+    center.z=0.0;
+
+    radius=0.0;
+
+    IDirect3DDevice7_SetTransform(lpD3DDevice, D3DTRANSFORMSTATE_WORLD, &world);
+    IDirect3DDevice7_SetTransform(lpD3DDevice, D3DTRANSFORMSTATE_VIEW , &view);
+    IDirect3DDevice7_SetTransform(lpD3DDevice, D3DTRANSFORMSTATE_PROJECTION, &proj);
+
+    rc = IDirect3DDevice7_ComputeSphereVisibility(lpD3DDevice, &center, &radius, 1, 0, result);
+
+    ok(rc == D3D_OK, "Expected D3D_OK, received %x\n", rc);
+    ok(result[0] == 0x00000000, "Expected 0x00, got %x\n\n\n\n\n\n\n", result[0]);
+}
+
 START_TEST(d3d)
 {
     init_function_pointers();
@@ -2761,6 +2891,7 @@ START_TEST(d3d)
         LimitTest();
         D3D7EnumTest();
         SetMaterialTest();
+        ComputeSphereVisibility();
         CapsTest();
         VertexBufferDescTest();
         D3D7_OldRenderStateTest();
-- 
1.6.0.2

