Armature: Add back Stick bone draw type.
authorClément Foucault <foucault.clem@gmail.com>
Tue, 8 May 2018 10:05:06 +0000 (12:05 +0200)
committerClément Foucault <foucault.clem@gmail.com>
Tue, 8 May 2018 10:18:35 +0000 (12:18 +0200)
The actual code is a bit convoluted but allows good and "pseudo efficient"
drawing. (pseudo efficient because rendering instances with that amount of
vertices is really inneficient. We should go full procedural but need to
have bufferTexture implemented first) But drawing speed is not a bottleneck
here and it's already a million time less crappy than the old (2.79) immediate
mode method.

Instead of drawing actual wires with different width we render a triangle
fan batch (containing 3 fans: bone, head, tail) which is then oriented in
screen space to the bone direction. We then interpolate a float value
accross vertices giving us a nice blend factor to blend the colors and
gives us really smooth interpolation inside the bone.

The outside edge still being geometry will be antialiased by MSAA if enabled.

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/shaders/armature_stick_frag.glsl [new file with mode: 0644]
source/blender/draw/modes/shaders/armature_stick_vert.glsl [new file with mode: 0644]

index 53d46575a627f80a5053eaaa892fc9994eeb4cb5..71285d7ba041a77aa4630d16ba993c93c7067d9e 100644 (file)
@@ -236,6 +236,8 @@ data_to_c_simple(modes/shaders/armature_shape_solid_vert.glsl SRC)
 data_to_c_simple(modes/shaders/armature_shape_solid_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/armature_stick_vert.glsl SRC)
+data_to_c_simple(modes/shaders/armature_stick_frag.glsl SRC)
 data_to_c_simple(modes/shaders/edit_mesh_overlay_frag.glsl SRC)
 data_to_c_simple(modes/shaders/edit_mesh_overlay_vert.glsl SRC)
 data_to_c_simple(modes/shaders/edit_mesh_overlay_geom_tri.glsl SRC)
index 1a2ff70624bbc6520b051f9e1337334e7fed1b75..efbf1143c16d567faaf97f22f8ce20e6b2d90dd2 100644 (file)
@@ -82,6 +82,7 @@ static struct {
        DRWShadingGroup *bone_box_wire;
        DRWShadingGroup *bone_box_outline;
        DRWShadingGroup *bone_wire_wire;
+       DRWShadingGroup *bone_stick;
        DRWShadingGroup *bone_envelope_solid;
        DRWShadingGroup *bone_envelope_distance;
        DRWShadingGroup *bone_envelope_wire;
@@ -181,6 +182,20 @@ static void drw_shgroup_bone_wire_wire(const float (*bone_mat)[4], const float c
        DRW_shgroup_call_dynamic_add(g_data.bone_wire_wire, final_bonemat, color);
 }
 
+static void drw_shgroup_bone_stick(
+        const float (*bone_mat)[4],
+        const float col_wire[4], const float col_bone[4], const float col_head[4], const float col_tail[4])
+{
+       if (g_data.bone_stick == NULL) {
+               g_data.bone_stick = shgroup_instance_bone_stick(g_data.passes.bone_wire);
+       }
+       float final_bonemat[4][4], tail[4];
+       mul_m4_m4m4(final_bonemat, g_data.ob->obmat, bone_mat);
+       add_v3_v3v3(tail, final_bonemat[3], final_bonemat[1]);
+       DRW_shgroup_call_dynamic_add(g_data.bone_stick, final_bonemat[3], tail, col_wire, col_bone, col_head, col_tail);
+}
+
+
 /* Envelope */
 static void drw_shgroup_bone_envelope_distance(
         const float (*bone_mat)[4],
@@ -1192,11 +1207,62 @@ static void draw_bone_envelope(
 }
 
 static void draw_bone_line(
-        EditBone *UNUSED(eBone), bPoseChannel *UNUSED(pchan), bArmature *UNUSED(arm),
-        const int UNUSED(boneflag), const short UNUSED(constflag),
-        const int UNUSED(select_id))
+        EditBone *eBone, bPoseChannel *pchan, bArmature *arm,
+        const int boneflag, const short constflag, const int select_id)
 {
-       /* work in progress  -- fclem */
+       const float *col_bone = get_bone_solid_with_consts_color(eBone, pchan, arm, boneflag, constflag);
+       const float *col_wire = get_bone_wire_color(eBone, pchan, arm, boneflag, constflag);
+       const float no_display[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+       const float *col_head = no_display;
+       const float *col_tail = col_bone;
+
+       if (eBone) {
+               if (eBone->flag & BONE_TIPSEL) {
+                       col_tail = g_theme.vertex_select_color;
+               }
+               if (boneflag & BONE_SELECTED) {
+                       col_bone = g_theme.edge_select_color;
+               }
+               col_wire = g_theme.wire_color;
+       }
+
+       /*      Draw root point if we are not connected and parent are not hidden */
+       if ((BONE_FLAG(eBone, pchan) & BONE_CONNECTED) == 0) {
+               if (eBone && !(eBone->parent && !EBONE_VISIBLE(arm, eBone->parent))) {
+                       col_head = (eBone->flag & BONE_ROOTSEL) ? g_theme.vertex_select_color : col_bone;
+               }
+               else if (pchan) {
+                       Bone *bone = pchan->bone;
+                       if (!(bone->parent && (bone->parent->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG)))) {
+                               col_head = col_bone;
+                       }
+               }
+       }
+
+       if (g_theme.const_color != NULL) {
+               col_wire = no_display; /* actually shrink the display. */
+               col_bone = col_head = col_tail = g_theme.const_color;
+       }
+
+       if (select_id == -1) {
+               /* Not in selection mode, draw everything at once. */
+               drw_shgroup_bone_stick(BONE_VAR(eBone, pchan, disp_mat), col_wire, col_bone, col_head, col_tail);
+       }
+       else {
+               /* In selection mode, draw bone, root and tip separatly. */
+               DRW_select_load_id(select_id | BONESEL_BONE);
+               drw_shgroup_bone_stick(BONE_VAR(eBone, pchan, disp_mat), col_wire, col_bone, no_display, no_display);
+
+               if (col_head[3] > 0.0f) {
+                       DRW_select_load_id(select_id | BONESEL_ROOT);
+                       drw_shgroup_bone_stick(BONE_VAR(eBone, pchan, disp_mat), col_wire, no_display, col_head, no_display);
+               }
+
+               DRW_select_load_id(select_id | BONESEL_TIP);
+               drw_shgroup_bone_stick(BONE_VAR(eBone, pchan, disp_mat), col_wire, no_display, no_display, col_tail);
+
+               DRW_select_load_id(-1);
+       }
 }
 
 static void draw_bone_wire(
index 1a4a9be00bd40d345c52d28396720ce058e1a8bd..2366fa556b943f6fe7106e11105e83d5fd1832ff 100644 (file)
@@ -88,6 +88,7 @@ static struct DRWShapeCache {
        Gwn_Batch *drw_bone_envelope_outline;
        Gwn_Batch *drw_bone_point;
        Gwn_Batch *drw_bone_point_wire;
+       Gwn_Batch *drw_bone_stick;
        Gwn_Batch *drw_bone_arrows;
        Gwn_Batch *drw_camera;
        Gwn_Batch *drw_camera_frame;
@@ -2104,6 +2105,82 @@ Gwn_Batch *DRW_cache_bone_point_wire_outline_get(void)
        return SHC.drw_bone_point_wire;
 }
 
+/* keep in sync with armature_stick_vert.glsl */
+#define COL_WIRE (1 << 0)
+#define COL_HEAD (1 << 1)
+#define COL_TAIL (1 << 2)
+#define COL_BONE (1 << 3)
+
+#define POS_HEAD (1 << 4)
+#define POS_TAIL (1 << 5)
+#define POS_BONE (1 << 6)
+
+Gwn_Batch *DRW_cache_bone_stick_get(void)
+{
+       if (!SHC.drw_bone_stick) {
+#define CIRCLE_RESOL 12
+               unsigned int v = 0;
+               unsigned int flag;
+               const float radius = 2.0f; /* head/tail radius */
+               float pos[2];
+
+               /* Position Only 2D format */
+               static Gwn_VertFormat format = { 0 };
+               static struct { uint pos, flag; } attr_id;
+               if (format.attrib_ct == 0) {
+                       attr_id.pos  = GWN_vertformat_attr_add(&format, "pos",  GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+                       attr_id.flag = GWN_vertformat_attr_add(&format, "flag", GWN_COMP_U32, 1, GWN_FETCH_INT);
+               }
+
+               const unsigned int vcount = (CIRCLE_RESOL + 1) * 2 + 6;
+
+               Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(&format);
+               GWN_vertbuf_data_alloc(vbo, vcount);
+
+               Gwn_IndexBufBuilder elb;
+               GWN_indexbuf_init_ex(&elb, GWN_PRIM_TRI_FAN, (CIRCLE_RESOL + 2) * 2 + 6 + 2, vcount, true);
+
+               /* head/tail points */
+               for (int i = 0; i < 2; ++i) {
+                       /* center vertex */
+                       copy_v2_fl(pos, 0.0f);
+                       flag  = (i == 0) ? POS_HEAD : POS_TAIL;
+                       flag |= (i == 0) ? COL_HEAD : COL_TAIL;
+                       GWN_vertbuf_attr_set(vbo, attr_id.pos,  v, pos);
+                       GWN_vertbuf_attr_set(vbo, attr_id.flag, v, &flag);
+                       GWN_indexbuf_add_generic_vert(&elb, v++);
+                       /* circle vertices */
+                       flag |= COL_WIRE;
+                       for (int a = 0; a < CIRCLE_RESOL; a++) {
+                               pos[0] = radius * sinf((2.0f * M_PI * a) / ((float)CIRCLE_RESOL));
+                               pos[1] = radius * cosf((2.0f * M_PI * a) / ((float)CIRCLE_RESOL));
+                               GWN_vertbuf_attr_set(vbo, attr_id.pos,  v, pos);
+                               GWN_vertbuf_attr_set(vbo, attr_id.flag, v, &flag);
+                               GWN_indexbuf_add_generic_vert(&elb, v++);
+                       }
+                       /* Close the circle */
+                       GWN_indexbuf_add_generic_vert(&elb, v - CIRCLE_RESOL);
+
+                       GWN_indexbuf_add_primitive_restart(&elb);
+               }
+
+               /* Bone rectangle */
+               pos[0] = 0.0f;
+               for (int i = 0; i < 6; ++i) {
+                       pos[1] = (i == 0 || i == 3) ? 0.0f : ((i < 3) ? 1.0f : -1.0f);
+                       flag   = ((i <  2 || i >  4) ? POS_HEAD : POS_TAIL) |
+                                ((i == 0 || i == 3) ? 0 : COL_WIRE) | COL_BONE | POS_BONE;
+                       GWN_vertbuf_attr_set(vbo, attr_id.pos,  v, pos);
+                       GWN_vertbuf_attr_set(vbo, attr_id.flag, v, &flag);
+                       GWN_indexbuf_add_generic_vert(&elb, v++);
+               }
+
+               SHC.drw_bone_stick = GWN_batch_create_ex(GWN_PRIM_TRI_FAN, vbo, GWN_indexbuf_build(&elb), GWN_BATCH_OWNS_VBO);
+#undef CIRCLE_RESOL
+       }
+       return SHC.drw_bone_stick;
+}
+
 static void set_bone_axis_vert(
         Gwn_VertBuf *vbo, uint axis, uint pos, uint col,
         unsigned int *v, const float *a, const float *p, const float *c)
index 22a5588599f4c53354077899b42909cbdfcdb7ff..18bff56920eb58266a229ea678878dd9d5aefe3d 100644 (file)
@@ -105,6 +105,7 @@ struct Gwn_Batch *DRW_cache_bone_envelope_outline_get(void);
 struct Gwn_Batch *DRW_cache_bone_envelope_head_wire_outline_get(void);
 struct Gwn_Batch *DRW_cache_bone_point_get(void);
 struct Gwn_Batch *DRW_cache_bone_point_wire_outline_get(void);
+struct Gwn_Batch *DRW_cache_bone_stick_get(void);
 struct Gwn_Batch *DRW_cache_bone_arrows_get(void);
 
 /* Meshes */
index 2c606aa57d7fdd2178a38d97f6a2e6d72f6b4b3a..ba5e04df512b0a2e63a50495dd7a08f82e1d8beb 100644 (file)
@@ -165,6 +165,8 @@ extern char datatoc_armature_shape_solid_vert_glsl[];
 extern char datatoc_armature_shape_solid_frag_glsl[];
 extern char datatoc_armature_shape_outline_vert_glsl[];
 extern char datatoc_armature_shape_outline_geom_glsl[];
+extern char datatoc_armature_stick_vert_glsl[];
+extern char datatoc_armature_stick_frag_glsl[];
 extern char datatoc_gpu_shader_flat_color_frag_glsl[];
 
 extern char datatoc_object_mball_handles_vert_glsl[];
@@ -178,6 +180,7 @@ static struct {
        struct GPUShader *bone_envelope_outline;
        struct GPUShader *bone_sphere;
        struct GPUShader *bone_sphere_outline;
+       struct GPUShader *bone_stick;
 
        struct GPUShader *mball_handles;
 } g_shaders = {NULL};
@@ -194,6 +197,7 @@ static struct {
        struct Gwn_VertFormat *instance_distance_lines;
        struct Gwn_VertFormat *instance_spot;
        struct Gwn_VertFormat *instance_bone;
+       struct Gwn_VertFormat *instance_bone_stick;
        struct Gwn_VertFormat *instance_bone_outline;
        struct Gwn_VertFormat *instance_bone_envelope;
        struct Gwn_VertFormat *instance_bone_envelope_distance;
@@ -638,6 +642,30 @@ DRWShadingGroup *shgroup_instance_bone_sphere_outline(DRWPass *pass)
        return grp;
 }
 
+DRWShadingGroup *shgroup_instance_bone_stick(DRWPass *pass)
+{
+       if (g_shaders.bone_stick == NULL) {
+               g_shaders.bone_stick = DRW_shader_create(
+                           datatoc_armature_stick_vert_glsl, NULL,
+                           datatoc_armature_stick_frag_glsl, NULL);
+       }
+
+       DRW_shgroup_instance_format(g_formats.instance_bone_stick, {
+               {"boneStart", DRW_ATTRIB_FLOAT, 3},
+               {"boneEnd"  , DRW_ATTRIB_FLOAT, 3},
+               {"wireColor", DRW_ATTRIB_FLOAT, 4}, /* TODO port theses to uchar color */
+               {"boneColor", DRW_ATTRIB_FLOAT, 4},
+               {"headColor", DRW_ATTRIB_FLOAT, 4},
+               {"tailColor", DRW_ATTRIB_FLOAT, 4}
+       });
+
+       DRWShadingGroup *grp = DRW_shgroup_instance_create(g_shaders.bone_stick,
+                                                          pass, DRW_cache_bone_stick_get(),
+                                                          g_formats.instance_bone_stick);
+       DRW_shgroup_uniform_vec2(grp, "viewportSize", DRW_viewport_size_get(), 1);
+
+       return grp;
+}
 
 /* ******************************************** COLOR UTILS *********************************************** */
 
index 8730be54d93193b5f0fb3a2c93fb8e9de7612c9b..dca92188ca7e1087690094f209a3afce0c27f478 100644 (file)
@@ -124,6 +124,7 @@ struct DRWShadingGroup *shgroup_instance_bone_shape_outline(struct DRWPass *pass
 struct DRWShadingGroup *shgroup_instance_bone_shape_solid(struct DRWPass *pass, struct Gwn_Batch *geom);
 struct DRWShadingGroup *shgroup_instance_bone_sphere_outline(struct DRWPass *pass);
 struct DRWShadingGroup *shgroup_instance_bone_sphere_solid(struct DRWPass *pass);
+struct DRWShadingGroup *shgroup_instance_bone_stick(struct DRWPass *pass);
 
 int DRW_object_wire_theme_get(
         struct Object *ob, struct ViewLayer *view_layer, float **r_color);
diff --git a/source/blender/draw/modes/shaders/armature_stick_frag.glsl b/source/blender/draw/modes/shaders/armature_stick_frag.glsl
new file mode 100644 (file)
index 0000000..d03cf4c
--- /dev/null
@@ -0,0 +1,13 @@
+
+noperspective in float colorFac;
+flat in vec4 finalWireColor;
+flat in vec4 finalInnerColor;
+
+out vec4 fragColor;
+
+void main()
+{
+       float fac = smoothstep(1.0, 0.2, colorFac);
+       fragColor.rgb = mix(finalInnerColor.rgb, finalWireColor.rgb, fac);
+       fragColor.a = 1.0;
+}
diff --git a/source/blender/draw/modes/shaders/armature_stick_vert.glsl b/source/blender/draw/modes/shaders/armature_stick_vert.glsl
new file mode 100644 (file)
index 0000000..eb14239
--- /dev/null
@@ -0,0 +1,88 @@
+
+uniform mat4 ProjectionMatrix;
+uniform mat4 ViewProjectionMatrix;
+uniform mat4 ViewMatrix;
+uniform vec2 viewportSize;
+
+/* ---- Instanciated Attribs ---- */
+in vec2 pos; /* bone aligned screen space */
+in uint flag;
+
+#define COL_WIRE (1u << 0u)
+#define COL_HEAD (1u << 1u)
+#define COL_TAIL (1u << 2u)
+#define COL_BONE (1u << 3u)
+
+#define POS_HEAD (1u << 4u)
+#define POS_TAIL (1u << 5u) /* UNUSED */
+#define POS_BONE (1u << 6u)
+
+/* ---- Per instance Attribs ---- */
+in vec3 boneStart;
+in vec3 boneEnd;
+in vec4 wireColor; /* alpha encode if we do wire. If 0.0 we dont. */
+in vec4 boneColor; /* alpha encode if we do bone. If 0.0 we dont. */
+in vec4 headColor; /* alpha encode if we do head. If 0.0 we dont. */
+in vec4 tailColor; /* alpha encode if we do tail. If 0.0 we dont. */
+
+#define do_wire (wireColor.a > 0.0)
+#define is_head ((flag & POS_HEAD) != 0u)
+#define is_bone ((flag & POS_BONE) != 0u)
+
+noperspective out float colorFac;
+flat out vec4 finalWireColor;
+flat out vec4 finalInnerColor;
+
+uniform float stickSize = 5.0; /* might be dependant on DPI setting in the future. */
+
+/* project to screen space */
+vec2 proj(vec4 pos)
+{
+       return (0.5 * (pos.xy / pos.w) + 0.5) * viewportSize;
+}
+
+void main()
+{
+       finalInnerColor = ((flag & COL_HEAD) != 0u) ? headColor : tailColor;
+       finalInnerColor = ((flag & COL_BONE) != 0u) ? boneColor : finalInnerColor;
+       finalWireColor = (do_wire) ? wireColor : finalInnerColor;
+       /* Make the color */
+       colorFac = ((flag & COL_WIRE) == 0u) ? ((flag & COL_BONE) != 0u) ? 1.0 : 2.0 : 0.0;
+
+       vec4 v0 = ViewMatrix * vec4(boneStart, 1.0);
+       vec4 v1 = ViewMatrix * vec4(boneEnd, 1.0);
+
+       /* Clip the bone to the camera origin plane (not the clip plane)
+        * to avoid glitches if one end is behind the camera origin (in persp). */
+       const float clip_dist = -1e-7; /* hardcoded, -1e-8 is giving gliches. */
+       vec3 bvec = v1.xyz - v0.xyz;
+       vec3 clip_pt = v0.xyz + bvec * ((v0.z - clip_dist) / -bvec.z);
+       if (v0.z > clip_dist) {
+               v0.xyz = clip_pt;
+       }
+       else if (v1.z > clip_dist) {
+               v1.xyz = clip_pt;
+       }
+
+       vec4 p0 = ProjectionMatrix * v0;
+       vec4 p1 = ProjectionMatrix * v1;
+
+       float h = (is_head) ? p0.w : p1.w;
+
+       vec2 x_screen_vec = normalize(proj(p1) - proj(p0) + 1e-8);
+       vec2 y_screen_vec = vec2(x_screen_vec.y, -x_screen_vec.x);
+
+       /* 2D screen aligned pos at the point */
+       vec2 vpos = pos.x * x_screen_vec + pos.y * y_screen_vec;
+       vpos *= (ProjectionMatrix[3][3] == 0.0) ? h : 1.0;
+       vpos *= (do_wire) ? 1.0 : 0.5;
+
+       if (finalInnerColor.a > 0.0) {
+               gl_Position = (is_head) ? p0 : p1;
+               gl_Position.xy += stickSize * (vpos / viewportSize);
+               gl_Position.z += (is_bone) ? 0.0 : 1e-6; /* Avoid Z fighting of head/tails. */
+       }
+       else {
+               gl_Position = vec4(0.0);
+       }
+}