Armature: Envelope: Revisit envelope drawing again.
authorClément Foucault <foucault.clem@gmail.com>
Wed, 2 May 2018 06:44:24 +0000 (08:44 +0200)
committerClément Foucault <foucault.clem@gmail.com>
Wed, 2 May 2018 18:49:38 +0000 (20:49 +0200)
Past shader was too slow and had bad artifacts. This method is much simpler
and eficient and only exhibit some popping when the raidus of the head/tail
is changed.

source/blender/draw/intern/draw_armature.c
source/blender/draw/intern/draw_cache.c
source/blender/draw/modes/shaders/armature_envelope_vert.glsl

index 5a3c03d5be281f6f05ab5c06c5669a572891140e..ec2751d661be494de9499864a58af6b1c028c08d 100644 (file)
@@ -189,12 +189,6 @@ static void drw_shgroup_bone_envelope_distance(
                head_sphere[3] += *distance;
                tail_sphere[3]  = *radius_tail;
                tail_sphere[3] += *distance;
-
-               /* Shader transform is nicer if tail is the biggest. */
-               if (*radius_head > *radius_tail) {
-                       swap_v4_v4(head_sphere, tail_sphere);
-               }
-
                DRW_shgroup_call_dynamic_add(g_data.bone_envelope_distance, head_sphere, tail_sphere, color, final_bonemat[0]);
        }
 }
@@ -232,12 +226,6 @@ static void drw_shgroup_bone_envelope(
                /* Draw Body */
                float tmp_sphere[4];
                float len = len_v3v3(tail_sphere, head_sphere);
-
-               /* Shader transform is nicer if tail is the biggest. */
-               if (*radius_head > *radius_tail) {
-                       swap_v4_v4(head_sphere, tail_sphere);
-               }
-
                float fac_head = (len - head_sphere[3]) / len;
                float fac_tail = (len - tail_sphere[3]) / len;
 
index aa21bb7214a44f541caf1ca524a448df8afcc0f6..be4c1783affe71093e6e1998352a172d04d5466c 100644 (file)
@@ -85,7 +85,6 @@ static struct DRWShapeCache {
        Gwn_Batch *drw_bone_box_wire;
        Gwn_Batch *drw_bone_wire_wire;
        Gwn_Batch *drw_bone_envelope;
-       Gwn_Batch *drw_bone_envelope_distance;
        Gwn_Batch *drw_bone_envelope_outline;
        Gwn_Batch *drw_bone_point;
        Gwn_Batch *drw_bone_point_wire;
@@ -1881,20 +1880,18 @@ Gwn_Batch *DRW_cache_bone_wire_wire_outline_get(void)
  * Note that here we only encode head/tail in forth component of the vector. */
 static void benv_lat_lon_to_co(const float lat, const float lon, float r_nor[3])
 {
-       /* Poles are along Y axis. */
        r_nor[0] = sinf(lat) * cosf(lon);
-       r_nor[1] = -cosf(lat);
-       r_nor[2] = sinf(lat) * sinf(lon);
+       r_nor[1] = sinf(lat) * sinf(lon);
+       r_nor[2] = cosf(lat);
 }
 
 Gwn_Batch *DRW_cache_bone_envelope_solid_get(void)
 {
        if (!SHC.drw_bone_envelope) {
                const int lon_res = 24;
-               const int lat_res = 16;
+               const int lat_res = 24;
                const float lon_inc = 2.0f * M_PI / lon_res;
                const float lat_inc = M_PI / lat_res;
-               const float eps = 0.02f;
                unsigned int v_idx = 0;
 
                static Gwn_VertFormat format = { 0 };
@@ -1905,14 +1902,16 @@ Gwn_Batch *DRW_cache_bone_envelope_solid_get(void)
 
                /* Vertices */
                Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(&format);
-               GWN_vertbuf_data_alloc(vbo, ((lat_res + 1) * 2) * lon_res * 2);
+               GWN_vertbuf_data_alloc(vbo, ((lat_res + 1) * 2) * lon_res * 1);
 
-               float lon = lon_inc;
+               float lon = 0.0f;
                for (int i = 0; i < lon_res; i++, lon += lon_inc) {
                        float lat = 0.0f;
                        float co1[4], co2[4];
                        co1[3] = co2[3] = 0.0f;
 
+                       /* Note: the poles are duplicated on purpose, to restart the strip. */
+
                        /* 1st sphere */
                        for (int j = 0; j < lat_res; j++, lat += lat_inc) {
                                benv_lat_lon_to_co(lat, lon,           co1);
@@ -1921,36 +1920,13 @@ Gwn_Batch *DRW_cache_bone_envelope_solid_get(void)
                                GWN_vertbuf_attr_set(vbo, attr_id.pos, v_idx++, co1);
                                GWN_vertbuf_attr_set(vbo, attr_id.pos, v_idx++, co2);
                        }
-                       /* Need to close the sphere, but add a small gap to be able
-                        * to distinguish the verts in the vertex shader. */
-                       benv_lat_lon_to_co(M_PI - eps, lon,           co1);
-                       benv_lat_lon_to_co(M_PI - eps, lon + lon_inc, co2);
-                       GWN_vertbuf_attr_set(vbo, attr_id.pos, v_idx++, co1);
-                       GWN_vertbuf_attr_set(vbo, attr_id.pos, v_idx++, co2);
-
-                       /* Add some precision to the middle part */
-                       // co1[3] = co2[3] = 0.5f;
-                       // co1[1] = co2[1] = 0.0f;
-                       // GWN_vertbuf_attr_set(vbo, attr_id.pos, v_idx++, co1);
-                       // GWN_vertbuf_attr_set(vbo, attr_id.pos, v_idx++, co2);
 
-                       /* 2nd sphere */
-                       co1[3] = co2[3] = 1.0f;
+                       /* Closing the loop */
+                       benv_lat_lon_to_co(M_PI, lon,           co1);
+                       benv_lat_lon_to_co(M_PI, lon + lon_inc, co2);
 
-                       /* Need to open the sphere */
-                       benv_lat_lon_to_co(eps, lon,           co1);
-                       benv_lat_lon_to_co(eps, lon + lon_inc, co2);
                        GWN_vertbuf_attr_set(vbo, attr_id.pos, v_idx++, co1);
                        GWN_vertbuf_attr_set(vbo, attr_id.pos, v_idx++, co2);
-
-                       lat = lat_inc;
-                       for (int j = 1; j < lat_res + 1; j++, lat += lat_inc) {
-                               benv_lat_lon_to_co(lat, lon,           co1);
-                               benv_lat_lon_to_co(lat, lon + lon_inc, co2);
-
-                               GWN_vertbuf_attr_set(vbo, attr_id.pos, v_idx++, co1);
-                               GWN_vertbuf_attr_set(vbo, attr_id.pos, v_idx++, co2);
-                       }
                }
 
                SHC.drw_bone_envelope = GWN_batch_create_ex(GWN_PRIM_TRI_STRIP, vbo, NULL, GWN_BATCH_OWNS_VBO);
index fbce8ca69dda5e79f99da2af1742753864f1446b..6de842fdcb23df0c6abbaf20065f3af38dd5377b 100644 (file)
@@ -1,4 +1,5 @@
 
+uniform mat4 ViewMatrix;
 uniform mat4 ViewMatrixInverse;
 uniform mat4 ViewProjectionMatrix;
 
@@ -15,80 +16,33 @@ in vec3 xAxis;
 flat out vec4 finalColor;
 out vec3 normalView;
 
-struct Bone { vec3 p1, vec; float r1, rdif, vec_rsq, h_bias, h_scale; };
-
-float sdf_bone(vec3 p, Bone b)
-{
-       /* Simple capsule sdf with a minor touch and optimisations. */
-       vec3 pa = p - b.p1;
-       float h = dot(pa, b.vec) * b.vec_rsq;
-       h = h * b.h_scale + b.h_bias; /* comment this line for sharp transition. */
-       h = clamp(h, 0.0, 1.0);
-       return length(pa - b.vec * h) - (b.r1 + b.rdif * h);
-}
-
 void main()
 {
-       /* Raytracing against cone need a parametric definition of the cone
-        * using axis, angle and apex. But if both sphere are nearly the same
-        * size, the apex become ill defined, and so does the angle.
-        * So to circumvent this, we use the numerical solution: raymarching.
-        * But due to the the cost of raymarching per pixel, we choose to use it
-        * to position vertices from a VBO with the distance field.
-        * The nice thing is that we start the raymarching really near the surface
-        * so we actually need very few iterations to get a good result. */
+       vec3 bone_vec = tailSphere.xyz - headSphere.xyz;
+       float bone_len = max(1e-8, sqrt(dot(bone_vec, bone_vec)));
+       float bone_lenrcp = 1.0 / bone_len;
+       float sinb = (tailSphere.w - headSphere.w) * bone_lenrcp;
 
-       Bone b;
-       /* Precompute everything we can to speedup iterations. */
-       b.p1 = headSphere.xyz;
-       b.r1 = headSphere.w;
-       b.rdif = tailSphere.w - headSphere.w;
-       b.vec = tailSphere.xyz - headSphere.xyz;
-       float vec_lsq = max(1e-8, dot(b.vec, b.vec));
-       b.vec_rsq = 1.0 / vec_lsq;
-       float sinb = (tailSphere.w - headSphere.w) * b.vec_rsq;
-       float ofs1 = sinb * headSphere.w;
-       float ofs2 = sinb * tailSphere.w;
-       b.h_scale = 1.0 - ofs1 + ofs2;
-       b.h_bias = ofs1 * b.h_scale;
-
-       /* Radius for the initial position */
-       float rad = b.r1 + b.rdif * pos.w;
-
-       float vec_len = sqrt(vec_lsq);
-       vec3 y_axis = b.vec / max(1e-8, vec_len);
+       vec3 y_axis = bone_vec * bone_lenrcp;
        vec3 z_axis = normalize(cross(xAxis, -y_axis));
-       vec3 x_axis = cross(y_axis, -z_axis); /* cannot trust xAxis to be orthogonal. */
-
-       vec3 sp = pos.xyz * rad;
-       if (pos.w == 1.0) {
-               /* Prevent tail sphere to cover the head sphere if head is really big. */
-               sp.y = max(sp.y, -(vec_len - headSphere.w));
-               sp.y += vec_len; /* Position tail on the Bone axis. */
-       }
+       vec3 x_axis = cross(y_axis, z_axis); /* cannot trust xAxis to be orthogonal. */
 
-       /* p is vertex position in world space */
-       vec3 p = mat3(x_axis, y_axis, z_axis) * sp;
-       p += headSphere.xyz;
+       vec3 sp, nor;
+       nor = sp = pos.xyz;
 
-       /* push vert towards the capsule boundary */
-       vec3 dir = vec3(normalize(pos.xz + 1e-8), 0.0);
-       dir = mat3(x_axis, y_axis, z_axis) * dir.xzy;
+       /* In bone space */
+       bool is_head = (pos.z < -sinb);
+       sp   *= (is_head) ? headSphere.w : tailSphere.w;
+       sp.z += (is_head) ? 0.0 : bone_len;
 
-       /* Signed distance field gives us the distance to the surface.
-        * Use a few iteration for precision. */
-       p = p - dir * sdf_bone(p, b);
-       p = p - dir * sdf_bone(p, b);
-       p = p - dir * sdf_bone(p, b);
-       p = p - dir * sdf_bone(p, b);
-       p = p - dir * sdf_bone(p, b);
+       /* Convert to world space */
+       mat3 bone_mat = mat3(x_axis, y_axis, z_axis);
+       sp = bone_mat * sp.xzy + headSphere.xyz;
+       nor = bone_mat * nor.xzy;
 
-       const float eps = 0.0005;
-       normalView.x = sdf_bone(p + ViewMatrixInverse[0].xyz * eps, b);
-       normalView.y = sdf_bone(p + ViewMatrixInverse[1].xyz * eps, b);
-       normalView.z = sdf_bone(p + ViewMatrixInverse[2].xyz * eps, b);
+       normalView = mat3(ViewMatrix) * nor;
 
-       gl_Position = ViewProjectionMatrix * vec4(p, 1.0);
+       gl_Position = ViewProjectionMatrix * vec4(sp, 1.0);
 
        finalColor = color;
 }