3D View: support non-uniform scaled lamps
authorCampbell Barton <ideasman42@gmail.com>
Thu, 15 Oct 2015 11:36:31 +0000 (22:36 +1100)
committerCampbell Barton <ideasman42@gmail.com>
Thu, 15 Oct 2015 11:36:31 +0000 (22:36 +1100)
D1378 by @youle

Non-uniform scaled lamps now cast oval/rectangular shadows, viewport & BGE.

doc/python_api/rst/gpu.rst
source/blender/editors/space_view3d/drawobject.c
source/blender/gpu/GPU_material.h
source/blender/gpu/intern/gpu_material.c
source/blender/gpu/shaders/gpu_shader_material.glsl
source/blender/python/intern/gpu.c

index b56523c2206876397dcd3760076df5603a145337..5e7486f22dd21338a4d5079567985805775216c3 100644 (file)
@@ -277,6 +277,12 @@ GLSL Lamp Uniforms
 
    :type: float
 
+.. data:: GPU_DYNAMIC_LAMP_SPOTSCALE
+
+   Represents the SpotLamp local scale.
+
+   :type: float2
+
 
 GLSL Sampler Uniforms
 ^^^^^^^^^^^^^^^^^^^^^
index 8e664d744514326b092f3ccc17b7becf1ee5b520..7f3a50ae6e54b87b20761c8bc9996fcf2ecfb487 100644 (file)
@@ -1198,7 +1198,7 @@ static void drawlamp(View3D *v3d, RegionView3D *rv3d, Base *base,
        Object *ob = base->object;
        const float pixsize = ED_view3d_pixel_size(rv3d, ob->obmat[3]);
        Lamp *la = ob->data;
-       float vec[3], lvec[3], vvec[3], circrad, x, y, z;
+       float vec[3], lvec[3], vvec[3], circrad;
        float lampsize;
        float imat[4][4];
 
@@ -1345,7 +1345,7 @@ static void drawlamp(View3D *v3d, RegionView3D *rv3d, Base *base,
                /* skip drawing extra info */
        }
        else if ((la->type == LA_SPOT) || (la->type == LA_YF_PHOTON)) {
-
+               float x, y, z, z_abs;
                copy_v3_fl3(lvec, 0.0f, 0.0f, 1.0f);
                copy_v3_fl3(vvec, rv3d->persmat[0][2], rv3d->persmat[1][2], rv3d->persmat[2][2]);
                mul_transposed_mat3_m4_v3(ob->obmat, vvec);
@@ -1358,46 +1358,75 @@ static void drawlamp(View3D *v3d, RegionView3D *rv3d, Base *base,
                mul_v3_fl(lvec, x);
                mul_v3_fl(vvec, x);
 
-               /* draw the angled sides of the cone */
-               glBegin(GL_LINE_STRIP);
-               glVertex3fv(vvec);
-               glVertex3fv(vec);
-               glVertex3fv(lvec);
-               glEnd();
-               
                x *= y;
 
-               /* draw the circle/square at the end of the cone */
-               glTranslatef(0.0, 0.0, x);
+               z_abs = fabsf(z);
+
                if (la->mode & LA_SQUARE) {
-                       float tvec[3];
-                       float z_abs = fabsf(z);
-
-                       tvec[0] = tvec[1] = z_abs;
-                       tvec[2] = 0.0;
-
-                       glBegin(GL_LINE_LOOP);
-                       glVertex3fv(tvec);
-                       tvec[1] = -z_abs; /* neg */
-                       glVertex3fv(tvec);
-                       tvec[0] = -z_abs; /* neg */
-                       glVertex3fv(tvec);
-                       tvec[1] = z_abs; /* pos */
-                       glVertex3fv(tvec);
-                       glEnd();
+                       /* draw pyramid */
+                       const float vertices[5][3] = {
+                           /* 5 of vertex coords of pyramid */
+                           {0.0f, 0.0f, 0.0f},
+                           {z_abs, z_abs, x},
+                           {-z_abs, -z_abs, x},
+                           {z_abs, -z_abs, x},
+                           {-z_abs, z_abs, x},
+                       };
+                       const unsigned char indices[] = {
+                           0, 1, 3,
+                           0, 3, 2,
+                           0, 2, 4,
+                           0, 1, 4,
+                       };
+
+                       /* Draw call:
+                        * activate and specify pointer to vertex array */
+                       glEnableClientState(GL_VERTEX_ARRAY);
+                       glVertexPointer(3, GL_FLOAT, 0, vertices);
+                       /* draw the pyramid */
+                       glDrawElements(GL_LINE_STRIP, 12, GL_UNSIGNED_BYTE, indices);
+
+                       /* deactivate vertex arrays after drawing */
+                       glDisableClientState(GL_VERTEX_ARRAY);
+
+                       glTranslatef(0.0f, 0.0f, x);
+
+                       /* draw the square representing spotbl */
+                       if (la->type == LA_SPOT) {
+                               float blend = z_abs * (1.0f - pow2f(la->spotblend));
+
+                               /* hide line if it is zero size or overlaps with outer border,
+                                * previously it adjusted to always to show it but that seems
+                                * confusing because it doesn't show the actual blend size */
+                               if (blend != 0.0f && blend != z_abs) {
+                                       fdrawbox(blend, -blend, -blend, blend);
+                               }
+                       }
                }
                else {
-                       circ(0.0, 0.0, fabsf(z));
-               }
 
-               /* draw the circle/square representing spotbl */
-               if (la->type == LA_SPOT) {
-                       float spotblcirc = fabsf(z) * (1.0f - pow2f(la->spotblend));
-                       /* hide line if it is zero size or overlaps with outer border,
-                        * previously it adjusted to always to show it but that seems
-                        * confusing because it doesn't show the actual blend size */
-                       if (spotblcirc != 0 && spotblcirc != fabsf(z))
-                               circ(0.0, 0.0, spotblcirc);
+                       /* draw the angled sides of the cone */
+                       glBegin(GL_LINE_STRIP);
+                       glVertex3fv(vvec);
+                       glVertex3fv(vec);
+                       glVertex3fv(lvec);
+                       glEnd();
+
+                       /* draw the circle at the end of the cone */
+                       glTranslatef(0.0f, 0.0f, x);
+                       circ(0.0f, 0.0f, z_abs);
+
+                       /* draw the circle representing spotbl */
+                       if (la->type == LA_SPOT) {
+                               float blend = z_abs * (1.0f - pow2f(la->spotblend));
+
+                               /* hide line if it is zero size or overlaps with outer border,
+                               * previously it adjusted to always to show it but that seems
+                               * confusing because it doesn't show the actual blend size */
+                               if (blend != 0.0f && blend != z_abs) {
+                                       circ(0.0f, 0.0f, blend);
+                               }
+                       }
                }
 
                if (drawcone)
index 9a8507574968cc350acecf45956758ce0eb6787a..25a4f33b526df5d039cb4e9e4c51cc04bde59f67 100644 (file)
@@ -160,6 +160,7 @@ typedef enum GPUDynamicType {
        GPU_DYNAMIC_LAMP_ATT2            = 9  | GPU_DYNAMIC_GROUP_LAMP,
        GPU_DYNAMIC_LAMP_SPOTSIZE        = 10 | GPU_DYNAMIC_GROUP_LAMP,
        GPU_DYNAMIC_LAMP_SPOTBLEND       = 11 | GPU_DYNAMIC_GROUP_LAMP,
+       GPU_DYNAMIC_LAMP_SPOTSCALE       = 12 | GPU_DYNAMIC_GROUP_LAMP,
 
        GPU_DYNAMIC_SAMPLER_2DBUFFER     = 1  | GPU_DYNAMIC_GROUP_SAMPLER,
        GPU_DYNAMIC_SAMPLER_2DIMAGE      = 2  | GPU_DYNAMIC_GROUP_SAMPLER,
index 5b6472329345b03a9e1f3fbd141ad98e05aad577..82902f8d69c104dc69fa17bbc783b6809f612d42 100644 (file)
@@ -141,6 +141,7 @@ struct GPULamp {
        float dynimat[4][4];
 
        float spotsi, spotbl, k;
+       float spotvec[2];
        float dyndist, dynatt1, dynatt2;
        float dist, att1, att2;
        float shadow_color[3];
@@ -536,12 +537,15 @@ static GPUNodeLink *lamp_get_visibility(GPUMaterial *mat, GPULamp *lamp, GPUNode
 
                if (lamp->type == LA_SPOT) {
                        if (lamp->mode & LA_SQUARE) {
-                               mat->dynproperty |= DYN_LAMP_VEC|DYN_LAMP_IMAT;
-                               GPU_link(mat, "lamp_visibility_spot_square", GPU_dynamic_uniform(lamp->dynvec, GPU_DYNAMIC_LAMP_DYNVEC, lamp->ob), GPU_dynamic_uniform((float*)lamp->dynimat, GPU_DYNAMIC_LAMP_DYNIMAT, lamp->ob), *lv, &inpr);
+                               mat->dynproperty |= DYN_LAMP_VEC | DYN_LAMP_IMAT;
+                               GPU_link(mat, "lamp_visibility_spot_square", GPU_dynamic_uniform(lamp->dynvec, GPU_DYNAMIC_LAMP_DYNVEC, lamp->ob), GPU_dynamic_uniform((float*)lamp->dynimat, GPU_DYNAMIC_LAMP_DYNIMAT, lamp->ob),
+                               GPU_dynamic_uniform((float *)lamp->spotvec, GPU_DYNAMIC_LAMP_SPOTSCALE, lamp->ob), *lv, &inpr);
                        }
                        else {
-                               mat->dynproperty |= DYN_LAMP_VEC;
-                               GPU_link(mat, "lamp_visibility_spot_circle", GPU_dynamic_uniform(lamp->dynvec, GPU_DYNAMIC_LAMP_DYNVEC, lamp->ob), *lv, &inpr);
+                               mat->dynproperty |= DYN_LAMP_VEC | DYN_LAMP_IMAT;
+                               GPU_link(mat, "lamp_visibility_spot_circle", GPU_dynamic_uniform(lamp->dynvec, GPU_DYNAMIC_LAMP_DYNVEC, lamp->ob),
+                               GPU_dynamic_uniform((float *)lamp->dynimat, GPU_DYNAMIC_LAMP_DYNIMAT, lamp->ob),
+                               GPU_dynamic_uniform((float *)lamp->spotvec, GPU_DYNAMIC_LAMP_SPOTSCALE, lamp->ob), *lv, &inpr);
                        }
                        
                        GPU_link(mat, "lamp_visibility_spot", GPU_dynamic_uniform(&lamp->spotsi, GPU_DYNAMIC_LAMP_SPOTSIZE, lamp->ob), GPU_dynamic_uniform(&lamp->spotbl, GPU_DYNAMIC_LAMP_SPOTSIZE, lamp->ob), inpr, visifac, &visifac);
@@ -1854,24 +1858,41 @@ static void gpu_lamp_calc_winmat(GPULamp *lamp)
                temp = 0.5f * lamp->size * cosf(angle) / sinf(angle);
                pixsize = lamp->d / temp;
                wsize = pixsize * 0.5f * lamp->size;
-               perspective_m4(lamp->winmat, -wsize, wsize, -wsize, wsize, lamp->d, lamp->clipend);
+               if (lamp->type & LA_SPOT) {
+                       /* compute shadows according to X and Y scaling factors */
+                       perspective_m4(
+                               lamp->winmat,
+                               -wsize * lamp->spotvec[0], wsize * lamp->spotvec[0],
+                               -wsize * lamp->spotvec[1], wsize * lamp->spotvec[1],
+                               lamp->d, lamp->clipend);
+               }
+               else {
+                       perspective_m4(lamp->winmat, -wsize, wsize, -wsize, wsize, lamp->d, lamp->clipend);
+               }
        }
 }
 
 void GPU_lamp_update(GPULamp *lamp, int lay, int hide, float obmat[4][4])
 {
        float mat[4][4];
+       float obmat_scale[3];
 
        lamp->lay = lay;
        lamp->hide = hide;
 
-       copy_m4_m4(mat, obmat);
-       normalize_m4(mat);
+       normalize_m4_m4_ex(mat, obmat, obmat_scale);
 
        copy_v3_v3(lamp->vec, mat[2]);
        copy_v3_v3(lamp->co, mat[3]);
        copy_m4_m4(lamp->obmat, mat);
        invert_m4_m4(lamp->imat, mat);
+
+       /* update spotlamp scale on X and Y axis */
+       lamp->spotvec[0] = obmat_scale[0] / obmat_scale[2];
+       lamp->spotvec[1] = obmat_scale[1] / obmat_scale[2];
+
+       /* makeshadowbuf */
+       gpu_lamp_calc_winmat(lamp);
 }
 
 void GPU_lamp_update_colors(GPULamp *lamp, float r, float g, float b, float energy)
@@ -1895,8 +1916,6 @@ void GPU_lamp_update_spot(GPULamp *lamp, float spotsize, float spotblend)
 {
        lamp->spotsi = cosf(spotsize * 0.5f);
        lamp->spotbl = (1.0f - lamp->spotsi) * spotblend;
-
-       gpu_lamp_calc_winmat(lamp);
 }
 
 static void gpu_lamp_from_blender(Scene *scene, Object *ob, Object *par, Lamp *la, GPULamp *lamp)
@@ -1941,9 +1960,6 @@ static void gpu_lamp_from_blender(Scene *scene, Object *ob, Object *par, Lamp *l
 
        /* arbitrary correction for the fact we do no soft transition */
        lamp->bias *= 0.25f;
-
-       /* makeshadowbuf */
-       gpu_lamp_calc_winmat(lamp);
 }
 
 static void gpu_lamp_shadow_free(GPULamp *lamp)
index 311fcb8ead2be92105f8ed5964b8a41473b8d6d7..5b62739b0eb26be70d90ee1e0024b5fe7b1a8f3b 100644 (file)
@@ -1584,11 +1584,13 @@ void lamp_visibility_sphere(float lampdist, float dist, float visifac, out float
        outvisifac= visifac*max(t, 0.0)/lampdist;
 }
 
-void lamp_visibility_spot_square(vec3 lampvec, mat4 lampimat, vec3 lv, out float inpr)
+void lamp_visibility_spot_square(vec3 lampvec, mat4 lampimat, vec2 scale, vec3 lv, out float inpr)
 {
        if(dot(lv, lampvec) > 0.0) {
                vec3 lvrot = (lampimat*vec4(lv, 0.0)).xyz;
-               float x = max(abs(lvrot.x/lvrot.z), abs(lvrot.y/lvrot.z));
+               /* without clever non-uniform scale, we could do: */
+               // float x = max(abs(lvrot.x / lvrot.z), abs(lvrot.y / lvrot.z));
+               float x = max(abs((lvrot.x / scale.x) / lvrot.z), abs((lvrot.y / scale.y) / lvrot.z));
 
                inpr = 1.0/sqrt(1.0 + x*x);
        }
@@ -1596,9 +1598,21 @@ void lamp_visibility_spot_square(vec3 lampvec, mat4 lampimat, vec3 lv, out float
                inpr = 0.0;
 }
 
-void lamp_visibility_spot_circle(vec3 lampvec, vec3 lv, out float inpr)
+void lamp_visibility_spot_circle(vec3 lampvec, mat4 lampimat, vec2 scale, vec3 lv, out float inpr)
 {
-       inpr = dot(lv, lampvec);
+       /* without clever non-uniform scale, we could do: */
+       // inpr = dot(lv, lampvec);
+       if (dot(lv, lampvec) > 0.0) {
+               vec3 lvrot = (lampimat * vec4(lv, 0.0)).xyz;
+               float x = abs(lvrot.x / lvrot.z);
+               float y = abs(lvrot.y / lvrot.z);
+
+               float ellipse = abs((x * x) / (scale.x * scale.x) + (y * y) / (scale.y * scale.y));
+
+               inpr = 1.0 / sqrt(1.0 + ellipse);
+       }
+       else
+               inpr = 0.0;
 }
 
 void lamp_visibility_spot(float spotsi, float spotbl, float inpr, float visifac, out float outvisifac)
index a7ece10f06c9134b90401991a974afc7408010d2..bc15e109901e0226ee437946c00e7dbf09ecdd75 100644 (file)
@@ -116,6 +116,7 @@ static PyObject *PyInit_gpu(void)
        PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_LAMP_DISTANCE);
        PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_LAMP_SPOTSIZE);
        PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_LAMP_SPOTBLEND);
+       PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_LAMP_SPOTSCALE);
        /* GPU_DYNAMIC_GROUP_SAMPLER */
        PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_SAMPLER_2DBUFFER);
        PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_SAMPLER_2DIMAGE);