DwM: Armature: Add distance outline and wire drawing of envelope bones.

This is not complete, it does not implement 3D solid drawing of
envelope bones. 2D wire is hence always drawn for now.

Some notes:

I did not try to implement the 'capsule' approach suggested by @fclem, because:
  1. I spent enough time on this already, and finally got something working.
  2. I managed to get rid of geometry shader completely.
  3. Current approach allows us to use same shader for
     distance outline and envelope wire.

It's working fine, except for one glitch - superpositions of envelope
outlines do not work as expected, not sure what's wrong here, tried to
disable zbuff, enable GL_BLEND, no luck so far...
I think we need our own 'background' drawpass to get them working (also
to avoid them drawing over the wire lines).
This commit is contained in:
Bastien Montagne 2017-05-17 12:34:06 +02:00
parent 11c167ff76
commit 3c799ba801
9 changed files with 364 additions and 12 deletions

View File

@ -78,6 +78,9 @@ static struct {
DRWShadingGroup *bone_box_solid;
DRWShadingGroup *bone_box_wire;
DRWShadingGroup *bone_wire_wire;
DRWShadingGroup *bone_envelope_distance;
DRWShadingGroup *bone_envelope_wire;
DRWShadingGroup *bone_envelope_head_wire;
DRWShadingGroup *bone_point_solid;
DRWShadingGroup *bone_point_wire;
DRWShadingGroup *bone_axes;
@ -145,6 +148,44 @@ 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, bone_mat, color);
}
/* Envelope */
static void DRW_shgroup_bone_envelope_distance(
const float (*bone_mat)[4], const float color[4],
const float *radius_head, const float *radius_tail, const float *distance)
{
if (g_data.bone_envelope_distance == NULL) {
struct 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(g_data.bone_wire, geom, g_data.ob->obmat);
}
DRW_shgroup_call_dynamic_add(g_data.bone_envelope_distance, bone_mat, color, radius_head, radius_tail, distance);
}
static void DRW_shgroup_bone_envelope_wire(
const float (*bone_mat)[4], const float color[4],
const float *radius_head, const float *radius_tail, const float *distance)
{
if (g_data.bone_envelope_wire == NULL) {
struct Batch *geom = DRW_cache_bone_envelope_wire_outline_get();
g_data.bone_envelope_wire = shgroup_instance_bone_envelope(g_data.bone_wire, geom, g_data.ob->obmat);
}
DRW_shgroup_call_dynamic_add(g_data.bone_envelope_wire, bone_mat, color, radius_head, radius_tail, distance);
}
static void DRW_shgroup_bone_envelope_head_wire(
const float (*bone_mat)[4], const float color[4],
const float *radius_head, const float *radius_tail, const float *distance)
{
if (g_data.bone_envelope_head_wire == NULL) {
struct Batch *geom = DRW_cache_bone_envelope_head_wire_outline_get();
g_data.bone_envelope_head_wire = shgroup_instance_bone_envelope(g_data.bone_wire, geom, g_data.ob->obmat);
}
DRW_shgroup_call_dynamic_add(g_data.bone_envelope_head_wire, bone_mat, color, radius_head, radius_tail, distance);
}
/* Custom (geometry) */
static void DRW_shgroup_bone_custom_solid(const float (*bone_mat)[4], const float color[4], Object *custom)
@ -837,7 +878,7 @@ static void draw_axes(EditBone *eBone, bPoseChannel *pchan)
const float *col = (g_theme.const_color) ? g_theme.const_color :
(BONE_FLAG(eBone, pchan) & BONE_SELECTED) ? g_theme.text_hi_color : g_theme.text_color;
DRW_shgroup_bone_axes(BONE_VAR(eBone, pchan, disp_tail_mat), col);
DRW_shgroup_bone_axes(BONE_VAR(eBone, pchan, disp_mat), col);
}
static void draw_points(
@ -850,6 +891,8 @@ static void draw_points(
const float *col_wire_root = (g_theme.const_color) ? g_theme.const_color : g_theme.vertex_color;
const float *col_wire_tail = (g_theme.const_color) ? g_theme.const_color : g_theme.vertex_color;
const bool is_envelope_draw = (arm->drawtype == ARM_ENVELOPE);
/* Edit bone points can be selected */
if (eBone) {
if (eBone->flag & BONE_ROOTSEL) {
@ -878,15 +921,27 @@ static void draw_points(
if (eBone) {
if (!((eBone->parent) && !EBONE_VISIBLE(arm, eBone->parent))) {
DRW_shgroup_bone_point_solid(eBone->disp_mat, col_solid_root);
DRW_shgroup_bone_point_wire(eBone->disp_mat, col_wire_root);
if (is_envelope_draw) {
DRW_shgroup_bone_envelope_head_wire(eBone->disp_mat, col_wire_root,
&eBone->rad_head, &eBone->rad_tail, &eBone->dist);
}
else {
DRW_shgroup_bone_point_solid(eBone->disp_mat, col_solid_root);
DRW_shgroup_bone_point_wire(eBone->disp_mat, col_wire_root);
}
}
}
else {
Bone *bone = pchan->bone;
if (!((bone->parent) && (bone->parent->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG)))) {
DRW_shgroup_bone_point_solid(pchan->disp_mat, col_solid_root);
DRW_shgroup_bone_point_wire(pchan->disp_mat, col_wire_root);
if (is_envelope_draw) {
DRW_shgroup_bone_envelope_head_wire(pchan->disp_mat, col_wire_root,
&bone->rad_head, &bone->rad_tail, &bone->dist);
}
else {
DRW_shgroup_bone_point_solid(pchan->disp_mat, col_solid_root);
DRW_shgroup_bone_point_wire(pchan->disp_mat, col_wire_root);
}
}
}
}
@ -895,8 +950,24 @@ static void draw_points(
if (select_id != -1) {
DRW_select_load_id(select_id | BONESEL_TIP);
}
DRW_shgroup_bone_point_solid(BONE_VAR(eBone, pchan, disp_tail_mat), col_solid_tail);
DRW_shgroup_bone_point_wire(BONE_VAR(eBone, pchan, disp_tail_mat), col_wire_tail);
if (is_envelope_draw) {
const float *rad_tail, *dist;
if (eBone) {
rad_tail = &eBone->rad_tail;
dist = &eBone->dist;
}
else {
rad_tail = &pchan->bone->rad_tail;
dist = &pchan->bone->dist;
}
DRW_shgroup_bone_envelope_head_wire(
BONE_VAR(eBone, pchan, disp_tail_mat), col_wire_root, rad_tail, rad_tail, dist);
}
else {
DRW_shgroup_bone_point_solid(BONE_VAR(eBone, pchan, disp_tail_mat), col_solid_tail);
DRW_shgroup_bone_point_wire(BONE_VAR(eBone, pchan, disp_tail_mat), col_wire_tail);
}
if (select_id != -1) {
DRW_select_load_id(-1);
@ -933,11 +1004,56 @@ static void draw_bone_custom_shape(
}
static void draw_bone_envelope(
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 DRWContextState *draw_ctx = DRW_context_state_get();
View3D *v3d = draw_ctx->v3d;
// const float *col_solid = get_bone_solid_color(eBone, pchan, arm, boneflag, constflag);
const float *col_wire = get_bone_wire_color(eBone, pchan, arm, boneflag, constflag);
static const float col_white[4] = {1.0f, 1.0f, 1.0f, 0.2f};
float *rad_head, *rad_tail, *distance;
if (eBone) {
rad_tail = &eBone->rad_tail;
distance = &eBone->dist;
rad_head = (eBone->parent && (boneflag & BONE_CONNECTED)) ? &eBone->parent->rad_tail : &eBone->rad_head;
}
else {
rad_tail = &pchan->bone->rad_tail;
distance = &pchan->bone->dist;
rad_head = (pchan->parent && (boneflag & BONE_CONNECTED)) ? &pchan->parent->bone->rad_tail : &pchan->bone->rad_head;
}
/* Not working! Probably needs its own drawpass... */
glEnable(GL_BLEND);
if (v3d->zbuf) {
glDisable(GL_DEPTH_TEST);
}
if (boneflag & BONE_SELECTED) {
DRW_shgroup_bone_envelope_distance(BONE_VAR(eBone, pchan, disp_mat), col_white, rad_head, rad_tail, distance);
}
if (v3d->zbuf) {
glEnable(GL_DEPTH_TEST);
}
glDisable(GL_BLEND);
if (select_id != -1) {
DRW_select_load_id(select_id | BONESEL_BONE);
}
DRW_shgroup_bone_envelope_wire(BONE_VAR(eBone, pchan, disp_mat), col_wire, rad_head, rad_tail, distance);
if (select_id != -1) {
DRW_select_load_id(-1);
}
draw_points(eBone, pchan, arm, boneflag, constflag, select_id);
}
static void draw_bone_line(

View File

@ -70,6 +70,9 @@ static struct DRWShapeCache {
Batch *drw_bone_box;
Batch *drw_bone_box_wire;
Batch *drw_bone_wire_wire;
Batch *drw_bone_envelope_distance;
Batch *drw_bone_envelope_wire;
Batch *drw_bone_envelope_head_wire;
Batch *drw_bone_point;
Batch *drw_bone_point_wire;
Batch *drw_bone_arrows;
@ -110,6 +113,9 @@ void DRW_shape_cache_free(void)
BATCH_DISCARD_ALL_SAFE(SHC.drw_bone_box);
BATCH_DISCARD_ALL_SAFE(SHC.drw_bone_box_wire);
BATCH_DISCARD_ALL_SAFE(SHC.drw_bone_wire_wire);
BATCH_DISCARD_ALL_SAFE(SHC.drw_bone_envelope_distance);
BATCH_DISCARD_ALL_SAFE(SHC.drw_bone_envelope_wire);
BATCH_DISCARD_ALL_SAFE(SHC.drw_bone_envelope_head_wire);
BATCH_DISCARD_ALL_SAFE(SHC.drw_bone_point);
BATCH_DISCARD_ALL_SAFE(SHC.drw_bone_point_wire);
BATCH_DISCARD_ALL_SAFE(SHC.drw_bone_arrows);
@ -1524,6 +1530,111 @@ Batch *DRW_cache_bone_wire_wire_outline_get(void)
}
Batch *DRW_cache_bone_envelope_distance_outline_get(void)
{
#define CIRCLE_RESOL 32
if (!SHC.drw_bone_envelope_distance) {
unsigned int v_idx = 0;
static VertexFormat format = { 0 };
static unsigned int pos_id;
if (format.attrib_ct == 0) {
pos_id = VertexFormat_add_attrib(&format, "pos", COMP_F32, 4, KEEP_FLOAT);
}
/* Vertices */
VertexBuffer *vbo = VertexBuffer_create_with_format(&format);
VertexBuffer_allocate_data(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 } */
VertexBuffer_set_attrib(vbo, pos_id, v_idx++, (const float[4]){x, y, head_tail, 0.0f});
VertexBuffer_set_attrib(vbo, pos_id, v_idx++, (const float[4]){x, y, head_tail, 1.0f});
if (is_headtail_transition) {
VertexBuffer_set_attrib(vbo, pos_id, v_idx++, (const float[4]){x, y, 1.0f - head_tail, 0.0f});
VertexBuffer_set_attrib(vbo, pos_id, v_idx++, (const float[4]){x, y, 1.0f - head_tail, 1.0f});
}
}
SHC.drw_bone_envelope_distance = Batch_create(PRIM_TRIANGLE_STRIP, vbo, NULL);
}
return SHC.drw_bone_envelope_distance;
#undef CIRCLE_RESOL
}
/* Bone body and tail. */
Batch *DRW_cache_bone_envelope_wire_outline_get(void)
{
if (!SHC.drw_bone_envelope_wire) {
unsigned int v_idx = 0;
static VertexFormat format = { 0 };
static unsigned int pos_id;
if (format.attrib_ct == 0) {
pos_id = VertexFormat_add_attrib(&format, "pos", COMP_F32, 4, KEEP_FLOAT);
}
/* Vertices */
VertexBuffer *vbo = VertexBuffer_create_with_format(&format);
VertexBuffer_allocate_data(vbo, 4);
/* Two lines between head and tail circles. */
/* Encoded lines, vertex shader gives them final correct value. */
/* { X, Y, head/tail, inner/outer border } */
VertexBuffer_set_attrib(vbo, pos_id, v_idx++, (const float[4]){ 1.0f, 0.0f, 0.0f, 0.0f});
VertexBuffer_set_attrib(vbo, pos_id, v_idx++, (const float[4]){ 1.0f, 0.0f, 1.0f, 0.0f});
VertexBuffer_set_attrib(vbo, pos_id, v_idx++, (const float[4]){-1.0f, 0.0f, 0.0f, 0.0f});
VertexBuffer_set_attrib(vbo, pos_id, v_idx++, (const float[4]){-1.0f, 0.0f, 1.0f, 0.0f});
SHC.drw_bone_envelope_wire = Batch_create(PRIM_LINES, vbo, NULL);
}
return SHC.drw_bone_envelope_wire;
}
/* Bone head. */
Batch *DRW_cache_bone_envelope_head_wire_outline_get(void)
{
#define CIRCLE_RESOL 32
if (!SHC.drw_bone_envelope_head_wire) {
unsigned int v_idx = 0;
static VertexFormat format = { 0 };
static unsigned int pos_id;
if (format.attrib_ct == 0) {
pos_id = VertexFormat_add_attrib(&format, "pos", COMP_F32, 4, KEEP_FLOAT);
}
/* Vertices */
VertexBuffer *vbo = VertexBuffer_create_with_format(&format);
VertexBuffer_allocate_data(vbo, CIRCLE_RESOL);
/* Encoded lines, vertex shader gives them final correct value. */
/* Only head circle (tail is drawn in disp_tail_mat space as a head one by draw_armature.c's draw_point()). */
for (int i = 0; i < CIRCLE_RESOL; i++) {
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 } */
VertexBuffer_set_attrib(vbo, pos_id, v_idx++, (const float[4]){ x, y, 0.0f, 0.0f});
}
SHC.drw_bone_envelope_head_wire = Batch_create(PRIM_LINE_LOOP, vbo, NULL);
}
return SHC.drw_bone_envelope_head_wire;
#undef CIRCLE_RESOL
}
Batch *DRW_cache_bone_point_get(void)
{
if (!SHC.drw_bone_point) {

View File

@ -82,6 +82,9 @@ struct Batch *DRW_cache_bone_octahedral_wire_outline_get(void);
struct Batch *DRW_cache_bone_box_get(void);
struct Batch *DRW_cache_bone_box_wire_outline_get(void);
struct Batch *DRW_cache_bone_wire_wire_outline_get(void);
struct Batch *DRW_cache_bone_envelope_distance_outline_get(void);
struct Batch *DRW_cache_bone_envelope_wire_outline_get(void);
struct Batch *DRW_cache_bone_envelope_head_wire_outline_get(void);
struct Batch *DRW_cache_bone_point_get(void);
struct Batch *DRW_cache_bone_point_wire_outline_get(void);
struct Batch *DRW_cache_bone_arrows_get(void);

View File

@ -293,6 +293,22 @@ DRWShadingGroup *shgroup_spot_instance(DRWPass *pass, struct Batch *geom)
return grp;
}
DRWShadingGroup *shgroup_instance_bone_envelope(DRWPass *pass, struct Batch *geom, float (*obmat)[4])
{
GPUShader *sh = GPU_shader_get_builtin_shader(GPU_SHADER_3D_INSTANCE_BONE_ENVELOPE);
DRWShadingGroup *grp = DRW_shgroup_instance_create(sh, pass, geom);
DRW_shgroup_attrib_float(grp, "InstanceModelMatrix", 16);
DRW_shgroup_attrib_float(grp, "color", 4);
DRW_shgroup_attrib_float(grp, "radius_head", 1);
DRW_shgroup_attrib_float(grp, "radius_tail", 1);
DRW_shgroup_attrib_float(grp, "distance", 1);
DRW_shgroup_uniform_mat4(grp, "ObjectModelMatrix", (float *)obmat);
return grp;
}
/* ******************************************** COLOR UTILS *********************************************** */
/* TODO FINISH */
@ -387,4 +403,4 @@ float *DRW_color_background_blend_get(int theme_id)
UI_GetThemeColorBlendShade4fv(theme_id, TH_BACK, 0.5, 0, ret);
return ret;
}
}

View File

@ -101,6 +101,7 @@ struct DRWShadingGroup *shgroup_instance(struct DRWPass *pass, struct Batch *geo
struct DRWShadingGroup *shgroup_camera_instance(struct DRWPass *pass, struct Batch *geom);
struct DRWShadingGroup *shgroup_distance_lines_instance(struct DRWPass *pass, struct Batch *geom);
struct DRWShadingGroup *shgroup_spot_instance(struct DRWPass *pass, struct Batch *geom);
struct DRWShadingGroup *shgroup_instance_bone_envelope(struct DRWPass *pass, struct Batch *geom, float (*obmat)[4]);
int DRW_object_wire_theme_get(struct Object *ob, struct SceneLayer *sl, float **r_color);
float *DRW_color_background_blend_get(int theme_id);

View File

@ -171,6 +171,7 @@ data_to_c_simple(shaders/gpu_shader_instance_camera_vert.glsl SRC)
data_to_c_simple(shaders/gpu_shader_instance_distance_line_vert.glsl SRC)
data_to_c_simple(shaders/gpu_shader_instance_edges_variying_color_geom.glsl SRC)
data_to_c_simple(shaders/gpu_shader_instance_edges_variying_color_vert.glsl SRC)
data_to_c_simple(shaders/gpu_shader_instance_bone_envelope_vert.glsl SRC)
data_to_c_simple(shaders/gpu_shader_3D_groundline_geom.glsl SRC)
data_to_c_simple(shaders/gpu_shader_3D_groundpoint_vert.glsl SRC)

View File

@ -168,6 +168,8 @@ typedef enum GPUBuiltinShader {
GPU_SHADER_INSTANCE_VARIYING_COLOR_VARIYING_SCALE,
GPU_SHADER_INSTANCE_EDGES_VARIYING_COLOR,
GPU_SHADER_3D_INSTANCE_BONE_ENVELOPE,
GPU_NUM_BUILTIN_SHADERS /* (not an actual shader) */
} GPUBuiltinShader;

View File

@ -92,6 +92,7 @@ extern char datatoc_gpu_shader_instance_camera_vert_glsl[];
extern char datatoc_gpu_shader_instance_distance_line_vert_glsl[];
extern char datatoc_gpu_shader_instance_edges_variying_color_geom_glsl[];
extern char datatoc_gpu_shader_instance_edges_variying_color_vert_glsl[];
extern char datatoc_gpu_shader_instance_bone_envelope_vert_glsl[];
extern char datatoc_gpu_shader_3D_groundpoint_vert_glsl[];
extern char datatoc_gpu_shader_3D_groundline_geom_glsl[];
@ -786,6 +787,9 @@ GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader)
[GPU_SHADER_INSTANCE_EDGES_VARIYING_COLOR] = { datatoc_gpu_shader_instance_edges_variying_color_vert_glsl,
datatoc_gpu_shader_flat_color_frag_glsl,
datatoc_gpu_shader_instance_edges_variying_color_geom_glsl},
[GPU_SHADER_3D_INSTANCE_BONE_ENVELOPE] = { datatoc_gpu_shader_instance_bone_envelope_vert_glsl,
datatoc_gpu_shader_flat_color_frag_glsl },
};
if (builtin_shaders[shader] == NULL) {

View File

@ -0,0 +1,98 @@
/* This shader takes a 2D shape, puts it in 3D Object space such that is stays aligned with view and bone,
* and scales head/tail/distance according to per-instance attributes
* (and 'role' of current vertex, encoded in zw input, head or tail, and inner or outer for distance outline).
* It is used for both the distance outline drawing, and the wire version of envelope bone. */
uniform mat4 ViewMatrix;
uniform mat4 ProjectionMatrix;
uniform mat4 ObjectModelMatrix;
/* ---- Instanciated Attribs ---- */
in vec4 pos; /* z encodes head (== 0.0f), tail (== 1.0f) or in-between; w encodes inner (0.0f) or outer border. */
/* ---- Per instance Attribs ---- */
in mat4 InstanceModelMatrix;
in vec4 color;
in float radius_head;
in float radius_tail;
in float distance;
flat out vec4 finalColor;
void main()
{
/* We get head/tail in object space. */
mat4 bone_mat = InstanceModelMatrix;
vec4 head = bone_mat * vec4(0.0f, 0.0f, 0.0f, 1.0f);
vec4 tail = bone_mat * vec4(0.0f, 1.0f, 0.0f, 1.0f);
/* We generate our XY axes in object space, Y axis being aligned with bone in view space. */
mat4 obview_mat = ViewMatrix * ObjectModelMatrix;
mat4 iobview_mat = inverse(obview_mat);
vec4 view_bone_vec = obview_mat * normalize(tail - head);
view_bone_vec.z = 0.0f;
if (length(view_bone_vec.xy) <= 1e-5f) {
/* A bit weak, but will do the job for now.
* Ideally we could compute head/tail radius in view space, and take larger one... */
if (view_bone_vec.x > view_bone_vec.y) {
view_bone_vec.x = 1e-5f;
}
else {
view_bone_vec.y = 1e-5f;
}
}
vec3 bone_axis_y = normalize((iobview_mat * view_bone_vec).xyz);
vec3 bone_axis_x = normalize(cross(bone_axis_y, iobview_mat[2].xyz));
/* Where does this comes from???? Don't know why, but is mandatory anyway... :/ */
float size = 2.0f;
head.xyz *= size;
tail.xyz *= size;
float head_fac = pos.z; // == 0: head; == 1: tail; in-between: along bone.
bool do_distance_offset = (pos.w != 0.0f);
vec2 xy_pos = pos.xy;
vec4 ob_pos;
vec4 ob_bone_origin;
float radius;
/* head */
if (head_fac <= 0.0f) {
radius = radius_head;
ob_bone_origin = head;
}
/* tail */
else if (head_fac >= 1.0f) {
radius = radius_tail;
ob_bone_origin = tail;
}
/* Body of the bone */
#if 0 /* Note: not used currently! */
else {
float tail_fac = 1.0f - head_fac;
radius = radius_head * head_fac + radius_tail * tail_fac;
ob_bone_origin = head * head_fac + tail * tail_fac;
}
#endif
if (do_distance_offset) {
radius += distance;
}
xy_pos = xy_pos * radius * size;
ob_pos = ob_bone_origin + vec4(bone_axis_x * xy_pos.x + bone_axis_y * xy_pos.y, 1.0f);
gl_Position = ProjectionMatrix * obview_mat * ob_pos;
finalColor = color;
}