Armature: Envelope Bones: Change drawing method.
authorClément Foucault <foucault.clem@gmail.com>
Sun, 29 Apr 2018 17:39:44 +0000 (19:39 +0200)
committerClément Foucault <foucault.clem@gmail.com>
Wed, 2 May 2018 18:49:38 +0000 (20:49 +0200)
We now use a more pleasant and efficient way to display enveloppe bones
and their radius.

For this we use a capsule geometry that is displaced (in the vertex shader)
to a signed distance field that represents the bone shape.

The bone distance radius are now drawn in 3D using a "pseudo-fresnel" effect.
This gives a better understanding of what is inside the radius of influence.

When capsules are not needed, we switch to default raytraced points.
The capsules are not distorded by the bone's matrix (same as their actual
influence radius) and are correctly displayed even with complex scaled
parents hierarchy.

source/blender/draw/CMakeLists.txt
source/blender/draw/intern/draw_armature.c
source/blender/draw/intern/draw_cache.c
source/blender/draw/intern/draw_cache.h
source/blender/draw/intern/draw_common.c
source/blender/draw/intern/draw_common.h
source/blender/draw/modes/edit_armature_mode.c
source/blender/draw/modes/object_mode.c
source/blender/draw/modes/pose_mode.c
source/blender/draw/modes/shaders/armature_envelope_frag.glsl [new file with mode: 0644]
source/blender/draw/modes/shaders/armature_envelope_vert.glsl [new file with mode: 0644]

index 3ab0ea409906ff1eb6ff059c31f899f582df7187..3ccbf967b87ba5968d0fc5e7e64baec846d75bfe 100644 (file)
@@ -224,6 +224,8 @@ data_to_c_simple(modes/shaders/common_fullscreen_vert.glsl SRC)
 data_to_c_simple(modes/shaders/armature_sphere_vert.glsl SRC)
 data_to_c_simple(modes/shaders/armature_sphere_frag.glsl SRC)
 data_to_c_simple(modes/shaders/armature_sphere_outline_vert.glsl SRC)
+data_to_c_simple(modes/shaders/armature_envelope_vert.glsl SRC)
+data_to_c_simple(modes/shaders/armature_envelope_frag.glsl SRC)
 data_to_c_simple(modes/shaders/armature_shape_outline_vert.glsl SRC)
 data_to_c_simple(modes/shaders/armature_shape_outline_geom.glsl SRC)
 data_to_c_simple(modes/shaders/edit_mesh_overlay_frag.glsl SRC)
index 756cc3e6992d384e2407d6a49f20c908fd5a8b1a..6b685f07de1a3d7f819043b732818262e26b29f9 100644 (file)
@@ -95,6 +95,9 @@ static struct {
        DRWPass *pass_bone_envelope;
 } g_data = {NULL};
 
+/* Prototype */
+static void drw_shgroup_bone_point_solid(const float (*bone_mat)[4], const float color[4]);
+
 /* -------------------------------------------------------------------- */
 
 /** \name Shader Groups (DRW_shgroup)
@@ -171,13 +174,22 @@ static void drw_shgroup_bone_envelope_distance(
 {
        if (g_data.pass_bone_envelope != NULL) {
                if (g_data.bone_envelope_distance == NULL) {
-                       struct Gwn_Batch *geom = DRW_cache_bone_envelope_distance_outline_get();
-                       /* Note: bone_wire draw pass is not really working, think we need another one here? */
-                       g_data.bone_envelope_distance = shgroup_instance_bone_envelope_wire(g_data.pass_bone_envelope, geom);
+                       g_data.bone_envelope_distance = shgroup_instance_bone_envelope_solid(g_data.pass_bone_envelope);
+                       /* pass_bone_envelope should have the DRW_STATE_CULL_FRONT state enabled. */
                }
+               float head_sphere[4] = {0.0f, 0.0f, 0.0f, 1.0f}, tail_sphere[4] = {0.0f, 1.0f, 0.0f, 1.0f};
                float final_bonemat[4][4];
                mul_m4_m4m4(final_bonemat, g_data.ob->obmat, bone_mat);
-               DRW_shgroup_call_dynamic_add(g_data.bone_envelope_distance, final_bonemat, color, radius_head, radius_tail, distance);
+               /* We need matrix mul because we need shear applied. */
+               /* NOTE: could be done in shader if that becomes a bottleneck. */
+               mul_m4_v4(final_bonemat, head_sphere);
+               mul_m4_v4(final_bonemat, tail_sphere);
+               head_sphere[3]  = *radius_head;
+               head_sphere[3] += *distance;
+               tail_sphere[3]  = *radius_tail;
+               tail_sphere[3] += *distance;
+
+               DRW_shgroup_call_dynamic_add(g_data.bone_envelope_distance, head_sphere, tail_sphere, color, final_bonemat[0]);
        }
 }
 
@@ -186,12 +198,63 @@ static void drw_shgroup_bone_envelope_solid(
         const float *radius_head, const float *radius_tail)
 {
        if (g_data.bone_envelope_solid == NULL) {
-               struct Gwn_Batch *geom = DRW_cache_bone_envelope_solid_get();
-               g_data.bone_envelope_solid = shgroup_instance_bone_envelope_solid(g_data.pass_bone_solid, geom);
+               g_data.bone_envelope_solid = shgroup_instance_bone_envelope_solid(g_data.pass_bone_solid);
+               /* We can have a lot of overdraw if we don't do this. Also envelope are not subject to
+                * inverted matrix. */
+               DRW_shgroup_state_enable(g_data.bone_envelope_solid, DRW_STATE_CULL_BACK);
+       }
+       if (g_data.bone_point_solid == NULL) {
+               g_data.bone_point_solid = shgroup_instance_armature_sphere(g_data.pass_bone_solid);
        }
+
+       float head_sphere[4] = {0.0f, 0.0f, 0.0f, 1.0f}, tail_sphere[4] = {0.0f, 1.0f, 0.0f, 1.0f};
        float final_bonemat[4][4];
        mul_m4_m4m4(final_bonemat, g_data.ob->obmat, bone_mat);
-       DRW_shgroup_call_dynamic_add(g_data.bone_envelope_solid, final_bonemat, color, radius_head, radius_tail);
+       mul_m4_v4(final_bonemat, head_sphere);
+       mul_m4_v4(final_bonemat, tail_sphere);
+       head_sphere[3] = *radius_head;
+       tail_sphere[3] = *radius_tail;
+
+       if (head_sphere[3] < 0.0f) {
+               /* Draw Tail only */
+               float tmp[4][4] = {{0.0f}};
+               tmp[0][0] = tmp[1][1] = tmp[2][2] = tail_sphere[3] / 0.05f;
+               tmp[3][3] = 1.0f;
+               copy_v3_v3(tmp[3], tail_sphere);
+               DRW_shgroup_call_dynamic_add(g_data.bone_point_solid, tmp, color);
+       }
+       else if (tail_sphere[3] < 0.0f) {
+               /* Draw Head only */
+               float tmp[4][4] = {{0.0f}};
+               tmp[0][0] = tmp[1][1] = tmp[2][2] = head_sphere[3] / 0.05f;
+               tmp[3][3] = 1.0f;
+               copy_v3_v3(tmp[3], head_sphere);
+               DRW_shgroup_call_dynamic_add(g_data.bone_point_solid, tmp, color);
+       }
+       else {
+               /* Draw Body */
+               float tmp_sphere[4];
+               float len = len_v3v3(tail_sphere, head_sphere);
+               float fac_head = (len - head_sphere[3]) / len;
+               float fac_tail = (len - tail_sphere[3]) / len;
+
+               /* Small epsilon to avoid problem with float precison in shader. */
+               if (len > (tail_sphere[3] + head_sphere[3]) + 1e-8f) {
+                       copy_v4_v4(tmp_sphere, head_sphere);
+                       interp_v4_v4v4(head_sphere, tail_sphere, head_sphere, fac_head);
+                       interp_v4_v4v4(tail_sphere, tmp_sphere,  tail_sphere, fac_tail);
+                       DRW_shgroup_call_dynamic_add(g_data.bone_envelope_solid, head_sphere, tail_sphere, color, final_bonemat[0]);
+               }
+               else {
+                       float tmp[4][4] = {{0.0f}};
+                       float fac = max_ff(fac_head, 1.0f - fac_tail);
+                       interp_v4_v4v4(tmp_sphere, tail_sphere, head_sphere, clamp_f(fac, 0.0f, 1.0f));
+                       tmp[0][0] = tmp[1][1] = tmp[2][2] = tmp_sphere[3] / 0.05f;
+                       tmp[3][3] = 1.0f;
+                       copy_v3_v3(tmp[3], tmp_sphere);
+                       DRW_shgroup_call_dynamic_add(g_data.bone_point_solid, tmp, color);
+               }
+       }
 }
 
 static void drw_shgroup_bone_envelope_wire(
index 1dd588afc9f0af7e807caacb1e4808669c9d8e11..1b41ff311a8f59fb66193e38253ec866753e29e1 100644 (file)
@@ -1878,43 +1878,24 @@ Gwn_Batch *DRW_cache_bone_wire_wire_outline_get(void)
        return SHC.drw_bone_wire_wire;
 }
 
-
 /* Helpers for envelope bone's solid sphere-with-hidden-equatorial-cylinder.
  * 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[1] = -cosf(lat);
        r_nor[2] = sinf(lat) * sinf(lon);
 }
 
-static void benv_add_tri(Gwn_VertBuf *vbo, uint pos_id, uint *v_idx, float *co1, float *co2, float *co3)
-{
-       /* Given tri and its seven other mirrors along X/Y/Z axes. */
-       for (int x = -1; x <= 1; x += 2) {
-               for (int y = -1; y <= 1; y += 2) {
-                       const float head_tail = (y == -1) ? 0.0f : 1.0f;
-                       for (int z = -1; z <= 1; z += 2) {
-                               GWN_vertbuf_attr_set(vbo, pos_id, (*v_idx)++,
-                                                    (const float[4]){co1[0] * x, co1[1] * y, co1[2] * z, head_tail});
-                               GWN_vertbuf_attr_set(vbo, pos_id, (*v_idx)++,
-                                                    (const float[4]){co2[0] * x, co2[1] * y, co2[2] * z, head_tail});
-                               GWN_vertbuf_attr_set(vbo, pos_id, (*v_idx)++,
-                                                    (const float[4]){co3[0] * x, co3[1] * y, co3[2] * z, head_tail});
-                       }
-               }
-       }
-}
-
 Gwn_Batch *DRW_cache_bone_envelope_solid_get(void)
 {
-#define CIRCLE_RESOL 32  /* Must be multiple of 4 */
        if (!SHC.drw_bone_envelope) {
-               const int lon_res = CIRCLE_RESOL / 4;
-               const int lat_res = CIRCLE_RESOL / 4;
-               const float lon_inc = M_PI_2 / lon_res;
-               const float lat_inc = M_PI_2 / lat_res;
+               const int lon_res = 24;
+               const int lat_res = 16;
+               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 };
@@ -1925,92 +1906,59 @@ 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 * lon_res * 8 * 6);
+               GWN_vertbuf_data_alloc(vbo, ((lat_res + 1) * 2) * lon_res * 2);
 
-               float lon = 0.0f;
+               float lon = lon_inc;
                for (int i = 0; i < lon_res; i++, lon += lon_inc) {
                        float lat = 0.0f;
-                       float co1[3], co2[3], co3[3], co4[3];
+                       float co1[4], co2[4];
+                       co1[3] = co2[3] = 0.0f;
 
+                       /* 1st sphere */
                        for (int j = 0; j < lat_res; j++, lat += lat_inc) {
-                               benv_lat_lon_to_co(lat,           lon,           co1);
-                               benv_lat_lon_to_co(lat,           lon + lon_inc, co2);
-                               benv_lat_lon_to_co(lat + lat_inc, lon + lon_inc, co3);
-                               benv_lat_lon_to_co(lat + lat_inc, lon,           co4);
+                               benv_lat_lon_to_co(lat, lon,           co1);
+                               benv_lat_lon_to_co(lat, lon + lon_inc, co2);
 
-                               if (j != 0) {  /* At pole, n1 and n2 are identical. */
-                                       benv_add_tri(vbo, attr_id.pos, &v_idx, co1, co2, co3);
-                               }
-                               benv_add_tri(vbo, attr_id.pos, &v_idx, co1, co3, co4);
+                               GWN_vertbuf_attr_set(vbo, attr_id.pos, v_idx++, co1);
+                               GWN_vertbuf_attr_set(vbo, attr_id.pos, v_idx++, co2);
                        }
-
-                       /* lat is at equator (i.e. lat == pi / 2). */
-                       /* We need to add 'cylinder' part between the equators (along XZ plane). */
-                       for (int x = -1; x <= 1; x += 2) {
-                               for (int z = -1; z <= 1; z += 2) {
-                                       GWN_vertbuf_attr_set(vbo, attr_id.pos, v_idx++,
-                                                            (const float[4]){co3[0] * x, co3[1], co3[2] * z, 0.0f});
-                                       GWN_vertbuf_attr_set(vbo, attr_id.pos, v_idx++,
-                                                            (const float[4]){co4[0] * x, co4[1], co4[2] * z, 0.0f});
-                                       GWN_vertbuf_attr_set(vbo, attr_id.pos, v_idx++,
-                                                            (const float[4]){co4[0] * x, co4[1], co4[2] * z, 1.0f});
-
-                                       GWN_vertbuf_attr_set(vbo, attr_id.pos, v_idx++,
-                                                            (const float[4]){co3[0] * x, co3[1], co3[2] * z, 0.0f});
-                                       GWN_vertbuf_attr_set(vbo, attr_id.pos, v_idx++,
-                                                            (const float[4]){co4[0] * x, co4[1], co4[2] * z, 1.0f});
-                                       GWN_vertbuf_attr_set(vbo, attr_id.pos, v_idx++,
-                                                            (const float[4]){co3[0] * x, co3[1], co3[2] * z, 1.0f});
-                               }
+                       /* 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;
+
+                       /* 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_TRIS, vbo, NULL, GWN_BATCH_OWNS_VBO);
+               SHC.drw_bone_envelope = GWN_batch_create_ex(GWN_PRIM_TRI_STRIP, vbo, NULL, GWN_BATCH_OWNS_VBO);
        }
        return SHC.drw_bone_envelope;
 }
 
-
-Gwn_Batch *DRW_cache_bone_envelope_distance_outline_get(void)
-{
-#define CIRCLE_RESOL 32  /* Must be multiple of 2 */
-       if (!SHC.drw_bone_envelope_distance) {
-               unsigned int v_idx = 0;
-
-               static Gwn_VertFormat format = { 0 };
-               static unsigned int pos_id;
-               if (format.attrib_ct == 0) {
-                       pos_id = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 4, GWN_FETCH_FLOAT);
-               }
-
-               /* Vertices */
-               Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(&format);
-               GWN_vertbuf_data_alloc(vbo, CIRCLE_RESOL * 2 + 6);
-
-               /* Encoded triangle strip, vertex shader gives them final correct value. */
-               for (int i = 0; i < CIRCLE_RESOL + 1; i++) {
-                       const bool is_headtail_transition = ELEM(i, CIRCLE_RESOL / 2, CIRCLE_RESOL);
-                       const float head_tail = (i > CIRCLE_RESOL / 2) ? 1.0f : 0.0f;
-                       const float alpha = 2.0f * M_PI * i / CIRCLE_RESOL;
-                       const float x = cosf(alpha);
-                       const float y = -sinf(alpha);
-
-                       /*                                                        { X, Y, head/tail, inner/outer border } */
-                       GWN_vertbuf_attr_set(vbo, pos_id, v_idx++, (const float[4]){x, y, head_tail, 0.0f});
-                       GWN_vertbuf_attr_set(vbo, pos_id, v_idx++, (const float[4]){x, y, head_tail, 1.0f});
-                       if (is_headtail_transition) {
-                               GWN_vertbuf_attr_set(vbo, pos_id, v_idx++, (const float[4]){x, y, 1.0f - head_tail, 0.0f});
-                               GWN_vertbuf_attr_set(vbo, pos_id, v_idx++, (const float[4]){x, y, 1.0f - head_tail, 1.0f});
-                       }
-               }
-
-               SHC.drw_bone_envelope_distance = GWN_batch_create_ex(GWN_PRIM_TRI_STRIP, vbo, NULL, GWN_BATCH_OWNS_VBO);
-       }
-       return SHC.drw_bone_envelope_distance;
-#undef CIRCLE_RESOL
-}
-
-
 /* Bone body. */
 Gwn_Batch *DRW_cache_bone_envelope_wire_outline_get(void)
 {
index b56f2c0218b20b75171b2d03a306caa4fd3d3783..b2270ba57c7c6bce76203d1ffffb95849db8baad 100644 (file)
@@ -101,7 +101,6 @@ struct Gwn_Batch *DRW_cache_bone_box_get(void);
 struct Gwn_Batch *DRW_cache_bone_box_wire_outline_get(void);
 struct Gwn_Batch *DRW_cache_bone_wire_wire_outline_get(void);
 struct Gwn_Batch *DRW_cache_bone_envelope_solid_get(void);
-struct Gwn_Batch *DRW_cache_bone_envelope_distance_outline_get(void);
 struct Gwn_Batch *DRW_cache_bone_envelope_wire_outline_get(void);
 struct Gwn_Batch *DRW_cache_bone_envelope_head_wire_outline_get(void);
 struct Gwn_Batch *DRW_cache_bone_point_get(void);
index 6d684b1c9fbef4581d7a9ab08034d838b35924df..8697d4c814ee697987c597fdde7f98bd345972f5 100644 (file)
@@ -158,6 +158,8 @@ void DRW_globals_update(void)
 
 extern char datatoc_armature_sphere_vert_glsl[];
 extern char datatoc_armature_sphere_frag_glsl[];
+extern char datatoc_armature_envelope_vert_glsl[];
+extern char datatoc_armature_envelope_frag_glsl[];
 extern char datatoc_armature_sphere_outline_vert_glsl[];
 extern char datatoc_armature_shape_outline_vert_glsl[];
 extern char datatoc_armature_shape_outline_geom_glsl[];
@@ -165,6 +167,8 @@ extern char datatoc_gpu_shader_flat_color_frag_glsl[];
 
 static struct {
        struct GPUShader *shape_outline;
+       struct GPUShader *bone_envelope;
+       struct GPUShader *bone_envelope_outline;
        struct GPUShader *bone_sphere;
        struct GPUShader *bone_sphere_outline;
 } g_armature_shaders = {NULL};
@@ -438,20 +442,24 @@ DRWShadingGroup *shgroup_instance_bone_envelope_wire(DRWPass *pass, struct Gwn_B
        return grp;
 }
 
-DRWShadingGroup *shgroup_instance_bone_envelope_solid(DRWPass *pass, struct Gwn_Batch *geom)
+DRWShadingGroup *shgroup_instance_bone_envelope_solid(DRWPass *pass)
 {
-       static float light[3] = {0.0f, 0.0f, 1.0f};
-       GPUShader *sh = GPU_shader_get_builtin_shader(GPU_SHADER_3D_INSTANCE_BONE_ENVELOPE_SOLID);
+       if (g_armature_shaders.bone_envelope == NULL) {
+               g_armature_shaders.bone_envelope = DRW_shader_create(
+                           datatoc_armature_envelope_vert_glsl, NULL,
+                           datatoc_armature_envelope_frag_glsl, NULL);
+       }
 
        DRW_shgroup_instance_format(g_formats.instance_bone_envelope_solid, {
-               {"InstanceModelMatrix" , DRW_ATTRIB_FLOAT, 16},
+               {"headSphere"          , DRW_ATTRIB_FLOAT, 4},
+               {"tailSphere"          , DRW_ATTRIB_FLOAT, 4},
                {"color"               , DRW_ATTRIB_FLOAT, 4},
-               {"radius_head"         , DRW_ATTRIB_FLOAT, 1},
-               {"radius_tail"         , DRW_ATTRIB_FLOAT, 1}
+               {"xAxis"               , DRW_ATTRIB_FLOAT, 3}
        });
 
-       DRWShadingGroup *grp = DRW_shgroup_instance_create(sh, pass, geom, g_formats.instance_bone_envelope_solid);
-       DRW_shgroup_uniform_vec3(grp, "light", light, 1);
+       DRWShadingGroup *grp = DRW_shgroup_instance_create(g_armature_shaders.bone_envelope,
+                                                          pass, DRW_cache_bone_envelope_solid_get(),
+                                                          g_formats.instance_bone_envelope_solid);
 
        return grp;
 }
index ad6ab2d89fd2220f2ffc6f085cb1d732f25db9bb..8197474928254611c16f470ac9a9fc1e7d124dc9 100644 (file)
@@ -119,7 +119,7 @@ struct DRWShadingGroup *shgroup_camera_instance(struct DRWPass *pass, struct Gwn
 struct DRWShadingGroup *shgroup_distance_lines_instance(struct DRWPass *pass, struct Gwn_Batch *geom);
 struct DRWShadingGroup *shgroup_spot_instance(struct DRWPass *pass, struct Gwn_Batch *geom);
 struct DRWShadingGroup *shgroup_instance_bone_envelope_wire(struct DRWPass *pass, struct Gwn_Batch *geom);
-struct DRWShadingGroup *shgroup_instance_bone_envelope_solid(struct DRWPass *pass, struct Gwn_Batch *geom);
+struct DRWShadingGroup *shgroup_instance_bone_envelope_solid(struct DRWPass *pass);
 struct DRWShadingGroup *shgroup_instance_mball_handles(struct DRWPass *pass, struct Gwn_Batch *geom);
 struct DRWShadingGroup *shgroup_instance_armature_shape_outline(struct DRWPass *pass, struct Gwn_Batch *geom);
 struct DRWShadingGroup *shgroup_instance_armature_sphere(struct DRWPass *pass);
index 292e62f090cd7a868d19d4d9456bb11920da3a22..bc992c8741a48a8ad80c13ef995538150d987a5b 100644 (file)
@@ -93,7 +93,7 @@ static void EDIT_ARMATURE_cache_init(void *vedata)
 
        {
                /* distance outline around envelope bones */
-               DRWState state = DRW_STATE_ADDITIVE | DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS | DRW_STATE_BLEND;
+               DRWState state = DRW_STATE_ADDITIVE | DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS | DRW_STATE_CULL_FRONT;
                psl->bone_envelope = DRW_pass_create("Bone Envelope Outline Pass", state);
        }
 
index d48c1bbc0ba99ce285a9132fe830eb1961e90756..8b86fe8c9a238f49aa282f0bd90fdac2cdd0d074 100644 (file)
@@ -1052,7 +1052,7 @@ static void OBJECT_cache_init(void *vedata)
 
        {
                /* distance outline around envelope bones */
-               DRWState state = DRW_STATE_ADDITIVE | DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS | DRW_STATE_BLEND;
+               DRWState state = DRW_STATE_ADDITIVE | DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS | DRW_STATE_CULL_FRONT;
                psl->bone_envelope = DRW_pass_create("Bone Envelope Outline Pass", state);
        }
 
index 5a3ed791b84f6652dd43d341b55e2e363601976f..9c3d6a655a585f76f50716561bd11ae0986327c6 100644 (file)
@@ -102,7 +102,7 @@ static void POSE_cache_init(void *vedata)
 
        {
                /* distance outline around envelope bones */
-               DRWState state = DRW_STATE_ADDITIVE | DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS | DRW_STATE_BLEND;
+               DRWState state = DRW_STATE_ADDITIVE | DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS | DRW_STATE_CULL_FRONT;
                psl->bone_envelope = DRW_pass_create("Bone Envelope Outline Pass", state);
        }
 
diff --git a/source/blender/draw/modes/shaders/armature_envelope_frag.glsl b/source/blender/draw/modes/shaders/armature_envelope_frag.glsl
new file mode 100644 (file)
index 0000000..468a04d
--- /dev/null
@@ -0,0 +1,15 @@
+
+flat in vec4 finalColor;
+in vec3 normalView;
+
+out vec4 fragColor;
+
+void main()
+{
+       float n = normalize(normalView).z;
+       n = gl_FrontFacing ? n : -n;
+       n = clamp(n, 0.0, 1.0);
+       n = gl_FrontFacing ? n : 1.0 - n;
+       fragColor = finalColor;
+       fragColor.rgb *= n;
+}
diff --git a/source/blender/draw/modes/shaders/armature_envelope_vert.glsl b/source/blender/draw/modes/shaders/armature_envelope_vert.glsl
new file mode 100644 (file)
index 0000000..a67b9c3
--- /dev/null
@@ -0,0 +1,96 @@
+
+uniform mat4 ViewMatrixInverse;
+uniform mat4 ViewProjectionMatrix;
+uniform mat4 ModelViewProjectionMatrix;
+uniform mat4 ProjectionMatrix;
+
+/* ---- Instanciated Attribs ---- */
+in vec4 pos;  /* w encodes head (== 0.0f), tail (== 1.0f). */
+
+/* ---- Per instance Attribs ---- */
+/* Assumed to be in world coordinate already. */
+in vec4 headSphere;
+in vec4 tailSphere;
+in vec4 color;
+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. */
+
+       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 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. */
+       }
+
+       /* p is vertex position in world space */
+       vec3 p = mat3(x_axis, y_axis, z_axis) * sp;
+       p += headSphere.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;
+
+       /* 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);
+
+       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);
+
+       gl_Position = ViewProjectionMatrix * vec4(p, 1.0);
+
+       finalColor = color;
+}