Clay Engine: Edit mesh overlays

Based on the previous overlay shader from merwin.
This shader takes care of clipped vertex cases and do all edit mode face info in one pass (except face centers).
As the shading is done one the triangle itself the visual can't go beyond the surface of the mesh. That leads to half displayed edges on the outline of the mesh.
This problem can be fixed by a second pass.

This is work in progress.
This commit is contained in:
Clément Foucault 2017-02-26 21:07:37 +01:00
parent fabde06b37
commit 712530eb93
8 changed files with 546 additions and 10 deletions

View File

@ -100,6 +100,13 @@ typedef struct MeshRenderData {
MLoop *mloop;
MPoly *mpoly;
BMVert *eve_act;
BMEdge *eed_act;
BMFace *efa_act;
int crease_ofs;
int bweight_ofs;
/* Data created on-demand (usually not for bmesh-based data). */
EdgeHash *ehash;
EdgeAdjacentPolys *edges_adjacent_polys;
@ -116,6 +123,9 @@ enum {
MR_DATATYPE_LOOPTRI = 1 << 2,
MR_DATATYPE_LOOP = 1 << 3,
MR_DATATYPE_POLY = 1 << 4,
MR_DATATYPE_ACTIVE = 1 << 5,
MR_DATATYPE_CREASE = 1 << 6,
MR_DATATYPE_BWEIGHT = 1 << 7,
};
static MeshRenderData *mesh_render_data_create(Mesh *me, const int types)
@ -150,6 +160,17 @@ static MeshRenderData *mesh_render_data_create(Mesh *me, const int types)
mrdata->totpoly = bm->totface;
bm_ensure_types |= BM_FACE;
}
if (types & MR_DATATYPE_ACTIVE) {
mrdata->efa_act = BM_mesh_active_face_get(bm, false, true);
mrdata->eed_act = BM_mesh_active_edge_get(bm);
mrdata->eve_act = BM_mesh_active_vert_get(bm);
}
if (types & MR_DATATYPE_CREASE) {
mrdata->crease_ofs = CustomData_get_offset(&bm->edata, CD_CREASE);
}
if (types & MR_DATATYPE_BWEIGHT) {
mrdata->bweight_ofs = CustomData_get_offset(&bm->edata, CD_BWEIGHT);
}
BM_mesh_elem_index_ensure(bm, bm_ensure_types);
BM_mesh_elem_table_ensure(bm, bm_ensure_types & ~BM_LOOP);
}
@ -820,23 +841,123 @@ Batch *BKE_mesh_batch_cache_get_fancy_edges(Mesh *me)
return cache->fancy_edges;
}
enum {
VFLAG_VERTEX_ACTIVE = 1 << 0,
VFLAG_VERTEX_SELECTED = 1 << 1,
VFLAG_FACE_ACTIVE = 1 << 2,
VFLAG_FACE_SELECTED = 1 << 3,
};
enum {
VFLAG_EDGE_EXISTS = 1 << 0,
VFLAG_EDGE_ACTIVE = 1 << 1,
VFLAG_EDGE_SELECTED = 1 << 2,
VFLAG_EDGE_SEAM = 1 << 3,
VFLAG_EDGE_SHARP = 1 << 4,
};
static unsigned char *get_vertex_flag(MeshRenderData *mrdata, const int v1, const int v2, const int v3, const int tri_idx)
{
/* This is really not efficient
* some operation are repeated accross all vertices
* but are constant overt the triangle / entire mesh */
/* First 2 bytes are bit flags
* 3rd is for sharp edges
* 4rd is for creased edges */
static unsigned char vflag[4];
memset(vflag, 0, sizeof(unsigned char) * 4);
/* if edit mode */
if (mrdata->edit_bmesh) {
BMesh *bm = mrdata->edit_bmesh->bm;
BMVert *bv1 = BM_vert_at_index(bm, v1);
BMVert *bv2 = BM_vert_at_index(bm, v2);
BMVert *bv3 = BM_vert_at_index(bm, v3);
/* Current vertex */
if (bv1 == mrdata->eve_act)
vflag[0] |= VFLAG_VERTEX_ACTIVE;
if (BM_elem_flag_test(bv1, BM_ELEM_SELECT))
vflag[0] |= VFLAG_VERTEX_SELECTED;
/* Current face */
BMFace *face = mrdata->edit_bmesh->looptris[tri_idx][0]->f;
if (face == mrdata->efa_act)
vflag[0] |= VFLAG_FACE_ACTIVE;
if (BM_elem_flag_test(face, BM_ELEM_SELECT))
vflag[0] |= VFLAG_FACE_SELECTED;
/* Oposite edge */
BMEdge *edge = BM_edge_exists(bv2, bv3);
/* if edge exists */
if (edge != NULL) {
vflag[1] |= VFLAG_EDGE_EXISTS;
if (edge == mrdata->eed_act)
vflag[1] |= VFLAG_EDGE_ACTIVE;
if (BM_elem_flag_test(edge, BM_ELEM_SELECT))
vflag[1] |= VFLAG_EDGE_SELECTED;
if (BM_elem_flag_test(edge, BM_ELEM_SEAM))
vflag[1] |= VFLAG_EDGE_SEAM;
if (!BM_elem_flag_test(edge, BM_ELEM_SMOOTH))
vflag[1] |= VFLAG_EDGE_SHARP;
/* Use a byte for value range */
if (mrdata->crease_ofs != -1) {
float crease = BM_ELEM_CD_GET_FLOAT(edge, mrdata->crease_ofs);
if (crease > 0) {
vflag[2] = (char)(crease * 255.0f);
}
}
/* Use a byte for value range */
if (mrdata->bweight_ofs != -1) {
float bweight = BM_ELEM_CD_GET_FLOAT(edge, mrdata->bweight_ofs);
if (bweight > 0) {
vflag[2] = (char)(bweight * 255.0f);
}
}
}
}
/* Object mode */
else {
/* Oposite edge */
if (mesh_render_data_edge_exists(mrdata, v2, v3)) {
vflag[1] |= VFLAG_EDGE_EXISTS;
}
}
return vflag;
}
static void add_overlay_tri(
MeshRenderData *mrdata, VertexBuffer *vbo, const unsigned int pos_id, const unsigned int edgeMod_id,
const int v1, const int v2, const int v3, const int base_vert_idx)
const int v1, const int v2, const int v3, const int tri_idx, const int base_vert_idx)
{
const float edgeMods[2] = { 0.0f, 1.0f };
const float *pos = mesh_render_data_vert_co(mrdata, v1);
unsigned char *vflag = get_vertex_flag(mrdata, v1, v2, v3, tri_idx);
setAttrib(vbo, pos_id, base_vert_idx + 0, pos);
setAttrib(vbo, edgeMod_id, base_vert_idx + 0, edgeMods + (mesh_render_data_edge_exists(mrdata, v2, v3) ? 1 : 0));
setAttrib(vbo, edgeMod_id, base_vert_idx + 0, vflag);
pos = mesh_render_data_vert_co(mrdata, v2);
vflag = get_vertex_flag(mrdata, v2, v1, v3, tri_idx);
setAttrib(vbo, pos_id, base_vert_idx + 1, pos);
setAttrib(vbo, edgeMod_id, base_vert_idx + 1, edgeMods + (mesh_render_data_edge_exists(mrdata, v3, v1) ? 1 : 0));
setAttrib(vbo, edgeMod_id, base_vert_idx + 1, vflag);
pos = mesh_render_data_vert_co(mrdata, v3);
vflag = get_vertex_flag(mrdata, v3, v2, v1, tri_idx);
setAttrib(vbo, pos_id, base_vert_idx + 2, pos);
setAttrib(vbo, edgeMod_id, base_vert_idx + 2, edgeMods + (mesh_render_data_edge_exists(mrdata, v1, v2) ? 1 : 0));
setAttrib(vbo, edgeMod_id, base_vert_idx + 2, vflag);
}
Batch *BKE_mesh_batch_cache_get_overlay_edges(Mesh *me)
@ -851,11 +972,13 @@ Batch *BKE_mesh_batch_cache_get_overlay_edges(Mesh *me)
if (format.attrib_ct == 0) {
/* initialize vertex format */
pos_id = add_attrib(&format, "pos", GL_FLOAT, 3, KEEP_FLOAT);
edgeMod_id = add_attrib(&format, "edgeWidthModulator", GL_FLOAT, 1, KEEP_FLOAT);
edgeMod_id = add_attrib(&format, "data", GL_UNSIGNED_BYTE, 4, KEEP_INT);
}
VertexBuffer *vbo = VertexBuffer_create_with_format(&format);
MeshRenderData *mrdata = mesh_render_data_create(me, MR_DATATYPE_VERT | MR_DATATYPE_EDGE | MR_DATATYPE_LOOPTRI);
MeshRenderData *mrdata = mesh_render_data_create(me, MR_DATATYPE_VERT | MR_DATATYPE_EDGE |
MR_DATATYPE_LOOPTRI | MR_DATATYPE_ACTIVE |
MR_DATATYPE_CREASE | MR_DATATYPE_BWEIGHT);
const int tri_ct = mesh_render_data_looptri_num_get(mrdata);
VertexBuffer_allocate_data(vbo, tri_ct * 3);
@ -864,7 +987,7 @@ Batch *BKE_mesh_batch_cache_get_overlay_edges(Mesh *me)
for (int i = 0; i < tri_ct; ++i) {
int tri_vert_idx[3];
mesh_render_data_looptri_verts_indices_get(mrdata, i, tri_vert_idx);
add_overlay_tri(mrdata, vbo, pos_id, edgeMod_id, tri_vert_idx[0], tri_vert_idx[1], tri_vert_idx[2], gpu_vert_idx);
add_overlay_tri(mrdata, vbo, pos_id, edgeMod_id, tri_vert_idx[0], tri_vert_idx[1], tri_vert_idx[2], i, gpu_vert_idx);
gpu_vert_idx += 3;
}

View File

@ -26,6 +26,8 @@
#include "DRW_engine.h"
#include "DRW_render.h"
#include "GPU_shader.h"
#include "draw_mode_pass.h"
#include "edit_mesh_mode.h"
@ -36,22 +38,33 @@ typedef struct EDIT_MESH_PassList {
struct DRWPass *ob_center_pass;
struct DRWPass *wire_outline_pass;
struct DRWPass *depth_pass_hidden_wire;
struct DRWPass *edit_face_overlay_pass;
} EDIT_MESH_PassList;
static DRWShadingGroup *depth_shgrp_hidden_wire;
static DRWShadingGroup *face_overlay_shgrp;
void EDIT_MESH_cache_init(void)
{
EDIT_MESH_PassList *psl = DRW_mode_pass_list_get();
static struct GPUShader *depth_sh;
static struct GPUShader *overlay_sh;
if (!depth_sh) {
depth_sh = DRW_shader_create_3D_depth_only();
}
if (!overlay_sh) {
overlay_sh = GPU_shader_get_builtin_shader(GPU_SHADER_EDGES_OVERLAY_EDIT);
}
psl->depth_pass_hidden_wire = DRW_pass_create("Depth Pass Hidden Wire", DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_CULL_BACK);
depth_shgrp_hidden_wire = DRW_shgroup_create(depth_sh, psl->depth_pass_hidden_wire);
psl->edit_face_overlay_pass = DRW_pass_create("Edit Mesh Face Overlay Pass", DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS | DRW_STATE_BLEND);
face_overlay_shgrp = DRW_shgroup_create(overlay_sh, psl->edit_face_overlay_pass);
DRW_shgroup_uniform_vec2(face_overlay_shgrp, "viewportSize", DRW_viewport_size_get(), 1);
DRW_mode_passes_setup(NULL,
NULL,
&psl->wire_outline_pass,
@ -63,7 +76,10 @@ void EDIT_MESH_cache_init(void)
void EDIT_MESH_cache_populate(Object *ob)
{
struct Batch *geom;
struct Batch *geom, *geom_overlay;
const struct bContext *C = DRW_get_context();
Scene *scene = CTX_data_scene(C);
Object *obedit = scene->obedit;
CollectionEngineSettings *ces_mode_ed = BKE_object_collection_engine_get(ob, COLLECTION_MODE_EDIT, "");
bool do_occlude_wire = BKE_collection_engine_property_value_get_bool(ces_mode_ed, "show_occlude_wire");
@ -71,6 +87,10 @@ void EDIT_MESH_cache_populate(Object *ob)
switch (ob->type) {
case OB_MESH:
geom = DRW_cache_surface_get(ob);
if (ob == obedit) {
geom_overlay = DRW_cache_wire_overlay_get(ob);
DRW_shgroup_call_add(face_overlay_shgrp, geom_overlay, ob->obmat);
}
if (do_occlude_wire) {
DRW_shgroup_call_add(depth_shgrp_hidden_wire, geom, ob->obmat);
DRW_shgroup_wire_outline(ob, true, false, true);
@ -107,6 +127,7 @@ void EDIT_MESH_draw(void)
EDIT_MESH_PassList *psl = DRW_mode_pass_list_get();
DRW_draw_pass(psl->depth_pass_hidden_wire);
DRW_draw_pass(psl->edit_face_overlay_pass);
DRW_draw_pass(psl->wire_outline_pass);
DRW_draw_pass(psl->non_meshes_pass);
DRW_draw_pass(psl->ob_center_pass);

View File

@ -191,6 +191,9 @@ data_to_c_simple(shaders/gpu_shader_edges_overlay_vert.glsl SRC)
data_to_c_simple(shaders/gpu_shader_edges_overlay_geom.glsl SRC)
data_to_c_simple(shaders/gpu_shader_edges_overlay_simple_geom.glsl SRC)
data_to_c_simple(shaders/gpu_shader_edges_overlay_frag.glsl SRC)
data_to_c_simple(shaders/gpu_shader_edit_overlay_frag.glsl SRC)
data_to_c_simple(shaders/gpu_shader_edit_overlay_geom.glsl SRC)
data_to_c_simple(shaders/gpu_shader_edit_overlay_vert.glsl SRC)
data_to_c_simple(shaders/gpu_shader_text_vert.glsl SRC)
data_to_c_simple(shaders/gpu_shader_text_frag.glsl SRC)
data_to_c_simple(shaders/gpu_shader_keyframe_diamond_vert.glsl SRC)

View File

@ -100,6 +100,7 @@ typedef enum GPUBuiltinShader {
GPU_SHADER_EDGES_FRONT_BACK_PERSP,
GPU_SHADER_EDGES_FRONT_BACK_ORTHO,
GPU_SHADER_EDGES_OVERLAY_SIMPLE,
GPU_SHADER_EDGES_OVERLAY_EDIT,
GPU_SHADER_EDGES_OVERLAY,
GPU_SHADER_KEYFRAME_DIAMOND,
GPU_SHADER_SIMPLE_LIGHTING,

View File

@ -109,6 +109,9 @@ extern char datatoc_gpu_shader_edges_overlay_vert_glsl[];
extern char datatoc_gpu_shader_edges_overlay_geom_glsl[];
extern char datatoc_gpu_shader_edges_overlay_simple_geom_glsl[];
extern char datatoc_gpu_shader_edges_overlay_frag_glsl[];
extern char datatoc_gpu_shader_edit_overlay_frag_glsl[];
extern char datatoc_gpu_shader_edit_overlay_geom_glsl[];
extern char datatoc_gpu_shader_edit_overlay_vert_glsl[];
extern char datatoc_gpu_shader_text_vert_glsl[];
extern char datatoc_gpu_shader_text_frag_glsl[];
extern char datatoc_gpu_shader_keyframe_diamond_vert_glsl[];
@ -667,6 +670,9 @@ GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader)
datatoc_gpu_shader_flat_color_frag_glsl },
[GPU_SHADER_EDGES_OVERLAY_SIMPLE] = { datatoc_gpu_shader_3D_vert_glsl, datatoc_gpu_shader_edges_overlay_frag_glsl,
datatoc_gpu_shader_edges_overlay_simple_geom_glsl },
[GPU_SHADER_EDGES_OVERLAY_EDIT] = { datatoc_gpu_shader_edit_overlay_vert_glsl,
datatoc_gpu_shader_edit_overlay_frag_glsl,
datatoc_gpu_shader_edit_overlay_geom_glsl },
[GPU_SHADER_EDGES_OVERLAY] = { datatoc_gpu_shader_edges_overlay_vert_glsl,
datatoc_gpu_shader_edges_overlay_frag_glsl,
datatoc_gpu_shader_edges_overlay_geom_glsl },

View File

@ -0,0 +1,191 @@
/* Solid Wirefram implementation
* Mike Erwin, Clément Foucault */
/* This shader follows the principles of
* http://developer.download.nvidia.com/SDK/10/direct3d/Source/SolidWireframe/Doc/SolidWireframe.pdf */
flat in vec3 edgesCrease;
flat in vec3 edgesBweight;
flat in ivec3 flag;
flat in vec4 faceColor;
flat in int clipCase;
// #ifdef VERTEX_SELECTION
smooth in vec3 vertexColor;
// #endif
/* We use a vec4[2] interface to pass edge data
* (without fragmenting memory accesses)
*
* There is 2 cases :
*
* - Simple case : geometry shader return edge distances
* in the first 2 components of the first vec4.
* This needs noperspective interpolation.
* The rest is filled with vertex screen positions.
*
* - Hard case : two 2d edge corner are described by each
* vec4 as origin and direction. This is constant over
* the triangle and use to detect the correct case. */
noperspective in vec4 eData1;
flat in vec4 eData2;
out vec4 FragColor;
#define EDGE_EXISTS (1 << 0)
#define EDGE_ACTIVE (1 << 1)
#define EDGE_SELECTED (1 << 2)
#define EDGE_SEAM (1 << 3)
#define EDGE_SHARP (1 << 4)
/* Vertex flag is shifted and combined with the edge flag */
#define VERTEX_ACTIVE (1 << (0 + 8))
#define VERTEX_SELECTED (1 << (1 + 8))
/* Style Parameters in pixel */
const float transitionWidth = 1.0;
/* Width : minimum central size
* Outline : additional info */
const float edgeWidth = 0.2;
const float edgeOutlineWidth = 1.5;
const float vertexWidth = 2.0;
const float vertexOutline = 2.0;
/* Array to retreive vert/edge indices */
const ivec3 clipEdgeIdx[6] = ivec3[6](
ivec3(1, 0, 2),
ivec3(2, 0, 1),
ivec3(2, 1, 0),
ivec3(2, 1, 0),
ivec3(2, 0, 1),
ivec3(1, 0, 2)
);
const ivec3 clipPointIdx[6] = ivec3[6](
ivec3(0, 1, 2),
ivec3(0, 2, 1),
ivec3(0, 2, 1),
ivec3(1, 2, 0),
ivec3(1, 2, 0),
ivec3(2, 1, 0)
);
float getEdgeSize(int v)
{
float size = 0.0;
size += ((flag[v] & EDGE_EXISTS) != 0) ? edgeWidth : 0;
size += ((flag[v] & EDGE_SEAM) != 0) ? edgeOutlineWidth : 0;
size += ((flag[v] & EDGE_SHARP) != 0) ? edgeOutlineWidth : 0;
size += (edgesCrease[v] > 0.0) ? edgeOutlineWidth : 0;
size += (edgesBweight[v] > 0.0) ? edgeOutlineWidth : 0;
return size;
}
float getVertexSize(int v)
{
float size = vertexWidth;
size += ((flag[v] & VERTEX_ACTIVE) != 0) ? vertexOutline : 0;
return size;
}
void colorDist(vec4 color, float width, inout float dist)
{
FragColor = mix(color, FragColor, smoothstep(0.0, transitionWidth, dist));
dist += width;
}
float distToEdge(vec2 o, vec2 dir)
{
vec2 af = gl_FragCoord.xy - o;
float daf = dot(dir, af);
return sqrt(abs(dot(af, af) - daf * daf));
}
void main()
{
vec3 e, p;
/* Step 1 : Computing Distances */
if (clipCase == 0) {
e.xy = eData1.xy;
/* computing missing distance */
vec2 dir = normalize(eData1.zw - eData2.xy);
e.z = distToEdge(eData1.zw, dir);
p.x = distance(eData1.zw, gl_FragCoord.xy);
p.y = distance(eData2.xy, gl_FragCoord.xy);
p.z = distance(eData2.zw, gl_FragCoord.xy);
}
else {
ivec3 eidxs = clipEdgeIdx[clipCase - 1];
ivec3 pidxs = clipPointIdx[clipCase - 1];
e[eidxs.x] = distToEdge(eData1.xy, eData1.zw);
e[eidxs.y] = distToEdge(eData2.xy, eData2.zw);
/* Three edges visible cases */
if (clipCase == 1 || clipCase == 2 || clipCase == 4) {
e[eidxs.z] = distToEdge(eData1.xy, normalize(eData2.xy - eData1.xy));
p[pidxs.y] = distance(eData2.xy, gl_FragCoord.xy);
}
else {
e[eidxs.z] = 1e10; /* off screen */
p[pidxs.y] = 1e10; /* off screen */
}
p[pidxs.x] = distance(eData1.xy, gl_FragCoord.xy);
p[pidxs.z] = 1e10; /* off screen */
}
/* Step 2 : coloring (order dependant) */
/* First */
FragColor = faceColor;
/* Edges */
for (int v = 0; v < 3; ++v) {
float edgeness = e[v] - getEdgeSize(v);
/* Ordered from outer to inner */
if (edgesBweight[v] > 0.0)
colorDist(vec4(0.0, 0.4, 1.0, edgesBweight[v]), edgeOutlineWidth, edgeness);
if (edgesCrease[v] > 0.0)
colorDist(vec4(0.0, 1.0, 1.0, edgesCrease[v]), edgeOutlineWidth, edgeness);
if ((flag[v] & EDGE_SEAM) != 0)
colorDist(vec4(1.0, 0.0, 0.0, 1.0), edgeOutlineWidth, edgeness);
if ((flag[v] & EDGE_SHARP) != 0)
colorDist(vec4(0.0, 0.5, 1.0, 1.0), edgeOutlineWidth, edgeness);
if ((flag[v] & EDGE_ACTIVE) != 0)
colorDist(vec4(1.0, 1.0, 0.0, 1.0), edgeWidth, edgeness);
else if ((flag[v] & EDGE_SELECTED) != 0)
colorDist(vec4(0.0, 1.0, 0.0, 1.0), edgeWidth, edgeness);
else if ((flag[v] & EDGE_EXISTS) != 0)
colorDist(vec4(0.0, 0.0, 0.0, 1.0), edgeWidth, edgeness);
}
/* Points */
for (int v = 0; v < 3; ++v) {
float size = p[v] - getVertexSize(v);
/* Ordered from outer to inner */
if ((flag[v] & VERTEX_ACTIVE) != 0)
colorDist(vec4(1.0, 0.0, 0.0, 1.0), vertexOutline, size);
if ((flag[v] & VERTEX_SELECTED) != 0)
colorDist(vec4(0.0, 1.0, 0.0, 1.0), vertexWidth, size);
else
colorDist(vec4(0.0, 0.0, 0.0, 1.0), vertexWidth, size);
}
}

View File

@ -0,0 +1,168 @@
/* Solid Wirefram implementation
* Mike Erwin, Clément Foucault */
/* This shader follows the principles of
* http://developer.download.nvidia.com/SDK/10/direct3d/Source/SolidWireframe/Doc/SolidWireframe.pdf */
layout(triangles) in;
layout(triangle_strip, max_vertices=3) out;
uniform mat4 ProjectionMatrix;
uniform vec2 viewportSize;
in vec4 vPos[];
in vec4 pPos[];
in ivec4 vData[];
/* these are the same for all vertices
* and does not need interpolation */
flat out vec3 edgesCrease;
flat out vec3 edgesSharp;
flat out ivec3 flag;
flat out vec4 faceColor;
flat out int clipCase;
// #ifdef VERTEX_SELECTION
smooth out vec3 vertexColor;
// #endif
/* See fragment shader */
noperspective out vec4 eData1;
flat out vec4 eData2;
#define VERTEX_ACTIVE (1 << 0)
#define VERTEX_SELECTED (1 << 1)
#define FACE_ACTIVE (1 << 2)
#define FACE_SELECTED (1 << 3)
/* Table 1. Triangle Projection Cases */
const ivec4 clipPointsIdx[6] = ivec4[6](
ivec4(0, 1, 2, 2),
ivec4(0, 2, 1, 1),
ivec4(0, 0, 1, 2),
ivec4(1, 2, 0, 0),
ivec4(1, 1, 0, 2),
ivec4(2, 2, 0, 1)
);
/* project to screen space */
vec2 proj(int v)
{
vec4 pos = pPos[v];
return (0.5 * (pos.xy / pos.w) + 0.5) * viewportSize;
}
float dist(vec2 pos[3], int v)
{
/* current vertex position */
vec2 vpos = pos[v];
/* endpoints of opposite edge */
vec2 e1 = pos[(v + 1) % 3];
vec2 e2 = pos[(v + 2) % 3];
/* Edge normalized vector */
vec2 dir = normalize(e2 - e1);
/* perpendicular to dir */
vec2 orthogonal = vec2(-dir.y, dir.x);
return abs(dot(vpos - e1, orthogonal));
}
vec3 getVertexColor(int v)
{
if ((vData[v].x & VERTEX_ACTIVE) != 0)
return vec3(0.0, 1.0, 0.0);
else if ((vData[v].x & VERTEX_SELECTED) != 0)
return vec3(1.0, 0.0, 0.0);
else
return vec3(0.0, 0.0, 0.0);
}
vec4 getClipData(vec2 pos[3], ivec2 vidx)
{
vec2 A = pos[vidx.x];
vec2 Adir = normalize(A - pos[vidx.y]);
return vec4(A, Adir);
}
void doVertex(int v)
{
#ifdef VERTEX_SELECTION
vertexColor = getVertexColor(v);
#endif
gl_Position = pPos[v];
EmitVertex();
}
void main()
{
/* First we detect which case we are in */
clipCase = 0;
/* if perspective */
if (ProjectionMatrix[3][3] == 0.0) {
/* See Table 1. Triangle Projection Cases */
clipCase += int(pPos[0].z / pPos[0].w < -1 || vPos[0].z > 0.0) * 4;
clipCase += int(pPos[1].z / pPos[1].w < -1 || vPos[1].z > 0.0) * 2;
clipCase += int(pPos[2].z / pPos[2].w < -1 || vPos[2].z > 0.0) * 1;
}
/* If triangle is behind nearplane, early out */
if (clipCase == 7)
return;
/* Edge */
for (int v = 0; v < 3; ++v) {
flag[v] = vData[v].y;// | (vData[v].x << 8);
edgesCrease[v] = vData[v].z / 255.0;
edgesSharp[v] = vData[v].w / 255.0;
}
/* Face */
if ((vData[0].x & FACE_ACTIVE) != 0) {
faceColor = vec4(0.1, 1.0, 0.0, 0.2);
}
else if ((vData[0].x & FACE_SELECTED) != 0) {
faceColor = vec4(1.0, 0.2, 0.0, 0.2);
}
else {
faceColor = vec4(0.0, 0.0, 0.0, 0.2);
}
/* Vertex */
vec2 pos[3] = vec2[3](proj(0), proj(1), proj(2));
/* Simple case : compute edge distances in geometry shader */
if (clipCase == 0) {
/* Packing screen positions and 2 distances */
eData1 = vec4(0.0, 0.0, pos[0]);
eData2 = vec4(pos[1], pos[2]);
/* Only pass the first 2 distances */
for (int v = 0; v < 2; ++v) {
eData1[v] = dist(pos, v);
doVertex(v);
eData1[v] = 0.0;
}
/* and the last vertex
doVertex(2);
}
/* Harder case : compute visible edges vectors */
else {
ivec4 vindices = clipPointsIdx[clipCase - 1];
eData1 = getClipData(pos, vindices.xz);
eData2 = getClipData(pos, vindices.yw);
for (int v = 0; v < 3; ++v)
doVertex(v);
}
EndPrimitive();
}

View File

@ -0,0 +1,23 @@
/* Solid Wirefram implementation
* Mike Erwin, Clément Foucault */
/* This shader follows the principles of
* http://developer.download.nvidia.com/SDK/10/direct3d/Source/SolidWireframe/Doc/SolidWireframe.pdf */
uniform mat4 ModelViewMatrix;
uniform mat4 ModelViewProjectionMatrix;
in vec3 pos;
in ivec4 data;
out vec4 vPos;
out vec4 pPos;
out ivec4 vData;
void main()
{
vPos = ModelViewMatrix * vec4(pos, 1.0);
pPos = ModelViewProjectionMatrix * vec4(pos, 1.0);
vData = data;
}