2025-07-01

This commit is contained in:
2026-03-17 14:30:01 -06:00
parent f9a22056dd
commit 62b5978595
4579 changed files with 1257472 additions and 0 deletions
@@ -0,0 +1,93 @@
/*
Copyright (C) 2023 CG Cookie
http://cgcookie.com
hello@cgcookie.com
Created by Jonathan Denning
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
struct Options {
mat4 uMVPMatrix;
float uInOut;
}
uniform Options options;
in vec4 vPos;
in vec4 vFrom;
in vec4 vInColor;
in vec4 vOutColor;
/////////////////////////////////////////////////////////////////////////
// vertex shader
#version 330
out float aRot;
out vec4 aInColor;
out vec4 aOutColor;
float angle(vec2 d) { return atan(d.y, d.x); }
void main() {
vec4 p0 = options.uMVPMatrix * vFrom;
vec4 p1 = options.uMVPMatrix * vPos;
gl_Position = p1;
aRot = angle((p1.xy / p1.w) - (p0.xy / p0.w));
aInColor = vInColor;
aOutColor = vOutColor;
}
/////////////////////////////////////////////////////////////////////////
// fragment shader
#version 330
in float aRot;
in vec4 aInColor;
in vec4 aOutColor;
out vec4 outColor;
float alpha(vec2 dir) {
vec2 d0 = dir - vec2(1,1);
vec2 d1 = dir - vec2(1,-1);
float d0v = -d0.x/2.0 - d0.y;
float d1v = -d1.x/2.0 + d1.y;
float dv0 = length(dir);
float dv1 = distance(dir, vec2(-2,0));
if(d0v < 1.0 || d1v < 1.0) return -1.0;
// if(dv0 > 1.0) return -1.0;
if(dv1 < 1.3) return -1.0;
if(d0v - 1.0 < (1.0 - options.uInOut) || d1v - 1.0 < (1.0 - options.uInOut)) return 0.0;
//if(dv0 > options.uInOut) return 0.0;
if(dv1 - 1.3 < (1.0 - options.uInOut)) return 0.0;
return 1.0;
}
void main() {
vec2 d = 2.0 * (gl_PointCoord - vec2(0.5, 0.5));
vec2 dr = vec2(cos(aRot)*d.x - sin(aRot)*d.y, sin(aRot)*d.x + cos(aRot)*d.y);
float a = alpha(dr);
if(a < 0.0) { discard; return; }
outColor = mix(aOutColor, aInColor, a);
// https://wiki.blender.org/wiki/Reference/Release_Notes/2.83/Python_API
outColor = blender_srgb_to_framebuffer_space(outColor);
}
@@ -0,0 +1,211 @@
/*
Copyright (C) 2023 CG Cookie
http://cgcookie.com
hello@cgcookie.com
Created by Jonathan Denning
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "bmesh_render_prefix.glsl"
/////////////////////////////////////////////////////////////////////////
// vertex shader
in vec4 vert_pos0; // position wrt model
in vec4 vert_pos1; // position wrt model
in vec2 vert_offset;
in vec4 vert_norm; // normal wrt model
in float selected; // is edge selected? 0=no; 1=yes
in float warning; // is edge warning? 0=no; 1=yes
in float pinned; // is edge pinned? 0=no; 1=yes
in float seam; // is edge on seam? 0=no; 1=yes
out vec4 vPPosition; // final position (projected)
out vec4 vCPosition; // position wrt camera
out vec4 vWPosition; // position wrt world
out vec4 vMPosition; // position wrt model
out vec4 vTPosition; // position wrt target
out vec4 vWTPosition_x; // position wrt target world
out vec4 vWTPosition_y; // position wrt target world
out vec4 vWTPosition_z; // position wrt target world
out vec4 vCTPosition_x; // position wrt target camera
out vec4 vCTPosition_y; // position wrt target camera
out vec4 vCTPosition_z; // position wrt target camera
out vec4 vPTPosition_x; // position wrt target projected
out vec4 vPTPosition_y; // position wrt target projected
out vec4 vPTPosition_z; // position wrt target projected
out vec3 vCNormal; // normal wrt camera
out vec3 vWNormal; // normal wrt world
out vec3 vMNormal; // normal wrt model
out vec3 vTNormal; // normal wrt target
out vec4 vColorIn; // color of geometry inside
out vec4 vColorOut; // color of geometry outside (considers selection)
out vec2 vPCPosition;
bool is_warning() { return use_warning() && warning > 0.5; }
bool is_pinned() { return use_pinned() && pinned > 0.5; }
bool is_seam() { return use_seam() && seam > 0.5; }
bool is_selection() { return use_selection() && selected > 0.5; }
void main() {
vec4 pos0 = get_pos(vec3(vert_pos0));
vec4 pos1 = get_pos(vec3(vert_pos1));
vec2 ppos0 = xyz4(options.matrix_p * options.matrix_v * options.matrix_m * pos0).xy;
vec2 ppos1 = xyz4(options.matrix_p * options.matrix_v * options.matrix_m * pos1).xy;
vec2 pdir0 = normalize(ppos1 - ppos0);
vec2 pdir1 = vec2(-pdir0.y, pdir0.x);
vec4 off = vec4((options.radius.x + options.radius.y + 2.0) * pdir1 * 2.0 * (vert_offset.y-0.5) / options.screen_size.xy, 0, 0);
vec4 pos = pos0 + vert_offset.x * (pos1 - pos0);
vec3 norm = normalize(vec3(vert_norm) * vec3(options.vert_scale));
vec4 wpos = push_pos(options.matrix_m * pos);
vec3 wnorm = normalize(mat3(options.matrix_mn) * norm);
vec4 tpos = options.matrix_ti * wpos;
vec3 tnorm = vec3(
dot(wnorm, vec3(options.mirror_x)),
dot(wnorm, vec3(options.mirror_y)),
dot(wnorm, vec3(options.mirror_z)));
vMPosition = pos;
vWPosition = wpos;
vCPosition = options.matrix_v * wpos;
vPPosition = off + xyz4(options.matrix_p * options.matrix_v * wpos);
vPCPosition = xyz4(options.matrix_p * options.matrix_v * wpos).xy;
vMNormal = norm;
vWNormal = wnorm;
vCNormal = normalize(mat3(options.matrix_vn) * wnorm);
vTPosition = tpos;
vWTPosition_x = options.matrix_t * vec4(0.0, tpos.y, tpos.z, 1.0);
vWTPosition_y = options.matrix_t * vec4(tpos.x, 0.0, tpos.z, 1.0);
vWTPosition_z = options.matrix_t * vec4(tpos.x, tpos.y, 0.0, 1.0);
vCTPosition_x = options.matrix_v * vWTPosition_x;
vCTPosition_y = options.matrix_v * vWTPosition_y;
vCTPosition_z = options.matrix_v * vWTPosition_z;
vPTPosition_x = options.matrix_p * vCTPosition_x;
vPTPosition_y = options.matrix_p * vCTPosition_y;
vPTPosition_z = options.matrix_p * vCTPosition_z;
vTNormal = tnorm;
gl_Position = vPPosition;
vColorIn = options.color_normal;
vColorOut = vec4(options.color_normal.rgb, 0.0);
if(is_selection()) {
vColorIn = color_over(options.color_selected, vColorIn);
vColorOut = vec4(options.color_selected.rgb, 0.0);
}
if(is_warning()) vColorOut = color_over(options.color_warning, vColorOut);
if(is_pinned()) vColorOut = color_over(options.color_pinned, vColorOut);
if(is_seam()) vColorOut = color_over(options.color_seam, vColorOut);
vColorIn.a *= 1.0 - options.hidden.x;
vColorOut.a *= 1.0 - options.hidden.x;
if(debug_invert_backfacing && vCNormal.z < 0.0) {
vColorIn = vec4(vec3(1,1,1) - vColorIn.rgb, vColorIn.a);
vColorOut = vec4(vec3(1,1,1) - vColorOut.rgb, vColorOut.a);
}
}
/////////////////////////////////////////////////////////////////////////
// fragment shader
in vec4 vPPosition; // final position (projected)
in vec4 vCPosition; // position wrt camera
in vec4 vWPosition; // position wrt world
in vec4 vMPosition; // position wrt model
in vec4 vTPosition; // position wrt target
in vec4 vWTPosition_x; // position wrt target world
in vec4 vWTPosition_y; // position wrt target world
in vec4 vWTPosition_z; // position wrt target world
in vec4 vCTPosition_x; // position wrt target camera
in vec4 vCTPosition_y; // position wrt target camera
in vec4 vCTPosition_z; // position wrt target camera
in vec4 vPTPosition_x; // position wrt target projected
in vec4 vPTPosition_y; // position wrt target projected
in vec4 vPTPosition_z; // position wrt target projected
in vec3 vCNormal; // normal wrt camera
in vec3 vWNormal; // normal wrt world
in vec3 vMNormal; // normal wrt model
in vec3 vTNormal; // normal wrt target
in vec4 vColorIn; // color of geometry inside (considers selection)
in vec4 vColorOut; // color of geometry outside
in vec2 vPCPosition;
out vec4 outColor;
out float gl_FragDepth;
void main() {
float clip = options.clip[1] - options.clip[0];
float focus = (view_distance() - options.clip[0]) / clip + 0.04;
float dist_from_center = length(options.screen_size.xy * (vPCPosition - vPPosition.xy));
float alpha_mult = 1.0 - (dist_from_center - (options.radius.x + options.radius.y));
if(alpha_mult <= 0) {
discard;
return;
}
float mix_in_out = clamp(dist_from_center - options.radius.x, 0.0, 1.0);
vec4 vColor = mix(vColorIn, vColorOut, mix_in_out);
vec3 rgb = vColor.rgb;
float alpha = vColor.a * min(1.0, alpha_mult);
if(is_view_perspective()) {
// perspective projection
vec3 v = xyz3(vCPosition);
float l = length(v);
float l_clip = (l - options.clip[0]) / clip;
float d = -dot(vCNormal, v) / l;
if(d <= 0.0) {
if(cull_backfaces()) {
alpha = 0.0;
discard;
return;
} else {
alpha *= min(1.0, alpha_backface());
}
}
} else {
// orthographic projection
vec3 v = vec3(0, 0, clip * 0.5); // + vCPosition.xyz / vCPosition.w;
float l = length(v);
float l_clip = (l - options.clip[0]) / clip;
float d = dot(vCNormal, v) / l;
if(d <= 0.0) {
if(cull_backfaces()) {
alpha = 0.0;
discard;
return;
} else {
alpha *= min(1.0, alpha_backface());
}
}
}
alpha *= min(1.0, pow(max(vCNormal.z, 0.01), 0.25));
outColor = coloring(vec4(rgb, alpha));
// https://wiki.blender.org/wiki/Reference/Release_Notes/2.83/Python_API
outColor = blender_srgb_to_framebuffer_space(outColor);
}
@@ -0,0 +1,153 @@
/*
Copyright (C) 2023 CG Cookie
http://cgcookie.com
hello@cgcookie.com
Created by Jonathan Denning
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define BMESH_FACE
#include "bmesh_render_prefix.glsl"
/////////////////////////////////////////////////////////////////////////
// vertex shader
in vec4 vert_pos; // position wrt model
in vec4 vert_norm; // normal wrt model
in float selected; // is face selected? 0=no; 1=yes
in float pinned; // is face pinned? 0=no; 1=yes
out vec4 vPPosition; // final position (projected)
out vec4 vCPosition; // position wrt camera
out vec4 vTPosition; // position wrt target
out vec4 vCTPosition_x; // position wrt target camera
out vec4 vCTPosition_y; // position wrt target camera
out vec4 vCTPosition_z; // position wrt target camera
out vec4 vPTPosition_x; // position wrt target projected
out vec4 vPTPosition_y; // position wrt target projected
out vec4 vPTPosition_z; // position wrt target projected
out vec3 vCNormal; // normal wrt camera
out vec4 vColor; // color of geometry (considers selection)
void main() {
//vec4 off = vec4(radius * (vert_dir0 * vert_offset.x + vert_dir1 * vert_offset.y) / screen_size, 0, 0);
vec4 pos = get_pos(vec3(vert_pos));
vec3 norm = normalize(vec3(vert_norm) * vec3(options.vert_scale));
vec4 wpos = push_pos(options.matrix_m * pos);
vec3 wnorm = normalize(mat3(options.matrix_mn) * norm);
vec4 tpos = options.matrix_ti * wpos;
vec3 tnorm = vec3(
dot(wnorm, vec3(options.mirror_x)),
dot(wnorm, vec3(options.mirror_y)),
dot(wnorm, vec3(options.mirror_z)));
vCPosition = options.matrix_v * wpos;
vPPosition = xyz4(options.matrix_p * options.matrix_v * wpos);
vCNormal = normalize(mat3(options.matrix_vn) * wnorm);
vTPosition = tpos;
vCTPosition_x = options.matrix_v * options.matrix_t * vec4(0.0, tpos.y, tpos.z, 1.0);
vCTPosition_y = options.matrix_v * options.matrix_t * vec4(tpos.x, 0.0, tpos.z, 1.0);
vCTPosition_z = options.matrix_v * options.matrix_t * vec4(tpos.x, tpos.y, 0.0, 1.0);
vPTPosition_x = options.matrix_p * vCTPosition_x;
vPTPosition_y = options.matrix_p * vCTPosition_y;
vPTPosition_z = options.matrix_p * vCTPosition_z;
gl_Position = vPPosition;
vColor = options.color_normal;
if(use_selection() && selected > 0.5) vColor = mix(vColor, options.color_selected, 0.75);
if(use_pinned() && pinned > 0.5) vColor = mix(vColor, options.color_pinned, 0.75);
vColor.a *= 1.0 - options.hidden.x;
if(debug_invert_backfacing && vCNormal.z < 0.0) {
vColor = vec4(vec3(1,1,1) - vColor.rgb, vColor.a);
}
}
/////////////////////////////////////////////////////////////////////////
// fragment shader
in vec4 vPPosition; // final position (projected)
in vec4 vCPosition; // position wrt camera
in vec4 vTPosition; // position wrt target
in vec4 vCTPosition_x; // position wrt target camera
in vec4 vCTPosition_y; // position wrt target camera
in vec4 vCTPosition_z; // position wrt target camera
in vec4 vPTPosition_x; // position wrt target projected
in vec4 vPTPosition_y; // position wrt target projected
in vec4 vPTPosition_z; // position wrt target projected
in vec3 vCNormal; // normal wrt camera
in vec4 vColor; // color of geometry (considers selection)
out vec4 outColor;
out float gl_FragDepth;
void main() {
float clip = options.clip[1] - options.clip[0];
float focus = (view_distance() - options.clip[0]) / clip + 0.04;
vec3 rgb = vColor.rgb;
float alpha = vColor.a;
if(vCNormal.z < 0) { discard; return; }
if(is_view_perspective()) {
// perspective projection
vec3 v = xyz3(vCPosition);
float l = length(v);
float l_clip = (l - options.clip[0]) / clip;
float d = -dot(vCNormal, v) / l;
if(d <= 0.0) {
if(cull_backfaces()) {
alpha = 0.0;
discard;
return;
} else {
alpha *= min(1.0, alpha_backface());
}
}
} else {
// orthographic projection
vec3 v = vec3(0, 0, clip * 0.5); // + vCPosition.xyz / vCPosition.w;
float l = length(v);
float l_clip = (l - options.clip[0]) / clip;
float d = dot(vCNormal, v) / l;
if(d <= 0.0) {
if(cull_backfaces()) {
alpha = 0.0;
discard;
return;
} else {
alpha *= min(1.0, alpha_backface());
}
}
}
alpha *= min(1.0, pow(max(vCNormal.z, 0.01), 0.25));
outColor = coloring(vec4(rgb, alpha));
// https://wiki.blender.org/wiki/Reference/Release_Notes/2.83/Python_API
outColor = blender_srgb_to_framebuffer_space(outColor);
}
@@ -0,0 +1,210 @@
/*
Copyright (C) 2023 CG Cookie
http://cgcookie.com
hello@cgcookie.com
Created by Jonathan Denning
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/////////////////////////////////////////////////////////////////////////
// common shader
struct Options {
mat4 matrix_m; // model xform matrix
mat4 matrix_mn; // model xform matrix for normal (inv transpose of matrix_m)
mat4 matrix_t; // target xform matrix
mat4 matrix_ti; // target xform matrix inverse
mat4 matrix_v; // view xform matrix
mat4 matrix_vn; // view xform matrix for normal
mat4 matrix_p; // projection matrix
vec4 clip;
vec4 screen_size;
vec4 view_settings0; // [ view_distance, perspective, focus_mult, alpha_backface ]
vec4 view_settings1; // [ cull_backfaces, unit_scaling_factor, normal_offset (how far to push geo along normal), constrain_offset (should constrain by focus) ]
vec4 view_settings2; // [ view push, xxx, xxx, xxx ]
vec4 view_position;
vec4 color_normal; // color of geometry if not selected
vec4 color_selected; // color of geometry if selected
vec4 color_warning; // color of geometry if warning
vec4 color_pinned; // color of geometry if pinned
vec4 color_seam; // color of geometry if seam
vec4 use_settings0; // [ selection, warning, pinned, seam ]
vec4 use_settings1; // [ rounding, xxx, xxx, xxx ]
vec4 mirror_settings; // [ view (0=none; 1=edge at plane; 2=color faces on far side of plane), effect (0=no effect, 1=full), xxx, xxx ]
vec4 mirroring; // mirror along axis: 0=false, 1=true
vec4 mirror_o; // mirroring origin wrt world
vec4 mirror_x; // mirroring x-axis wrt world
vec4 mirror_y; // mirroring y-axis wrt world
vec4 mirror_z; // mirroring z-axis wrt world
vec4 vert_scale; // used for mirroring
vec4 hidden; // affects alpha for geometry below surface. 0=opaque, 1=transparent
vec4 offset;
vec4 dotoffset;
vec4 radius;
};
uniform Options options;
const bool srgbTarget = true;
const bool debug_invert_backfacing = false;
int mirror_view() {
float v = options.mirror_settings[0];
if(v > 1.5) return 2;
if(v > 0.5) return 1;
return 0;
}
float mirror_effect() { return options.mirror_settings[1]; }
float view_distance() { return options.view_settings0[0]; }
bool is_view_perspective() { return options.view_settings0[1] > 0.5; }
float focus_mult() { return options.view_settings0[2]; }
float alpha_backface() { return options.view_settings0[3]; }
bool cull_backfaces() { return options.view_settings1[0] > 0.5; }
float unit_scaling_factor() { return options.view_settings1[1]; }
float normal_offset() { return options.view_settings1[2]; }
bool constrain_offset() { return options.view_settings1[3] > 0.5; }
float view_push() { return options.view_settings2[0]; }
vec4 view_position() { return options.view_position; }
float clip_near() { return options.clip[0]; }
float clip_far() { return options.clip[1]; }
bool use_selection() { return options.use_settings0[0] > 0.5; }
bool use_warning() { return options.use_settings0[1] > 0.5; }
bool use_pinned() { return options.use_settings0[2] > 0.5; }
bool use_seam() { return options.use_settings0[3] > 0.5; }
bool use_rounding() { return options.use_settings1[0] > 0.5; }
float magic_offset() { return options.offset.x; }
float magic_dotoffset() { return options.dotoffset.x; }
vec4 color_over(vec4 top, vec4 bottom) {
float a = top.a + (1.0 - top.a) * bottom.a;
vec3 c = (top.rgb * top.a + (1.0 - top.a) * bottom.a * bottom.rgb) / a;
return vec4(c, a);
}
/////////////////////////////////////////////////////////////////////////
// vertex shader
vec4 get_pos(vec3 p) {
float mult = 1.0;
if(constrain_offset()) {
mult = 1.0;
} else {
float clip_dist = clip_far() - clip_near();
float focus = (view_distance() - clip_near()) / clip_dist + 0.04;
mult = focus;
}
vec3 norm_offset = vec3(vert_norm) * normal_offset() * mult;
vec3 mirror = vec3(options.vert_scale);
return vec4((p + norm_offset) * mirror, 1.0);
}
vec4 push_pos(vec4 p) {
float clip_dist = clip_far() - clip_near();
float focus = (1.0 - clamp((view_distance() - clip_near()) / clip_dist, 0.0, 1.0)) * 0.1;
return vec4( mix(view_position().xyz, p.xyz, view_push()), p.w);
}
vec4 xyz4(vec4 v) { return vec4(v.xyz / abs(v.w), sign(v.w)); }
/////////////////////////////////////////////////////////////////////////
// fragment shader
vec3 xyz3(vec4 v) { return v.xyz / v.w; }
// adjusts color based on mirroring settings and fragment position
vec4 coloring(vec4 orig) {
vec4 mixer = vec4(0.6, 0.6, 0.6, 0.0);
if(mirror_view() == 0) {
// NO SYMMETRY VIEW
} else if(mirror_view() == 1) {
// EDGE VIEW
float edge_width = 5.0 / options.screen_size.y;
vec3 viewdir;
if(is_view_perspective()) {
viewdir = normalize(xyz3(vCPosition));
} else {
viewdir = vec3(0,0,1);
}
vec3 diffc_x = xyz3(vCTPosition_x) - xyz3(vCPosition);
vec3 diffc_y = xyz3(vCTPosition_y) - xyz3(vCPosition);
vec3 diffc_z = xyz3(vCTPosition_z) - xyz3(vCPosition);
vec3 dirc_x = normalize(diffc_x);
vec3 dirc_y = normalize(diffc_y);
vec3 dirc_z = normalize(diffc_z);
vec3 diffp_x = xyz3(vPTPosition_x) - xyz3(vPPosition);
vec3 diffp_y = xyz3(vPTPosition_y) - xyz3(vPPosition);
vec3 diffp_z = xyz3(vPTPosition_z) - xyz3(vPPosition);
vec3 aspect = vec3(1.0, options.screen_size.y / options.screen_size.x, 0.0);
float s = 0.0;
if(options.mirroring.x > 0.5 && length(diffp_x * aspect) < edge_width * (1.0 - pow(abs(dot(viewdir,dirc_x)), 10.0))) {
mixer.r = 1.0;
s = max(s, (vTPosition.x < 0.0) ? 1.0 : 0.1);
}
if(options.mirroring.y > 0.5 && length(diffp_y * aspect) < edge_width * (1.0 - pow(abs(dot(viewdir,dirc_y)), 10.0))) {
mixer.g = 1.0;
s = max(s, (vTPosition.y > 0.0) ? 1.0 : 0.1);
}
if(options.mirroring.z > 0.5 && length(diffp_z * aspect) < edge_width * (1.0 - pow(abs(dot(viewdir,dirc_z)), 10.0))) {
mixer.b = 1.0;
s = max(s, (vTPosition.z < 0.0) ? 1.0 : 0.1);
}
mixer.a = mirror_effect() * s + mixer.a * (1.0 - s);
} else if(mirror_view() == 2) {
// FACE VIEW
if(options.mirroring.x > 0.5 && vTPosition.x < 0.0) {
mixer.r = 1.0;
mixer.a = mirror_effect();
}
if(options.mirroring.y > 0.5 && vTPosition.y > 0.0) {
mixer.g = 1.0;
mixer.a = mirror_effect();
}
if(options.mirroring.z > 0.5 && vTPosition.z < 0.0) {
mixer.b = 1.0;
mixer.a = mirror_effect();
}
}
float m0 = mixer.a, m1 = 1.0 - mixer.a;
#ifdef BMESH_FACE
return vec4(mixer.rgb * m0 + orig.rgb * orig.a * m1, m0 + orig.a * m1);
#else
return vec4(mixer.rgb * m0 + orig.rgb * m1, m0 + orig.a * m1);
#endif
}
vec4 blender_srgb_to_framebuffer_space(vec4 in_color)
{
if (srgbTarget) {
vec3 c = max(in_color.rgb, vec3(0.0));
vec3 c1 = c * (1.0 / 12.92);
vec3 c2 = pow((c + 0.055) * (1.0 / 1.055), vec3(2.4));
in_color.rgb = mix(c1, c2, step(vec3(0.04045), c));
}
return in_color;
}
@@ -0,0 +1,168 @@
/*
Copyright (C) 2023 CG Cookie
http://cgcookie.com
hello@cgcookie.com
Created by Jonathan Denning
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "bmesh_render_prefix.glsl"
/////////////////////////////////////////////////////////////////////////
// vertex shader
in vec4 vert_pos; // position wrt model
in vec2 vert_offset;
in vec4 vert_norm; // normal wrt model
in float selected; // is vertex selected? 0=no; 1=yes
in float warning; // is vertex warning? 0=no; 1=yes
in float pinned; // is vertex pinned? 0=no; 1=yes
in float seam; // is vertex along seam? 0=no; 1=yes
out vec4 vPPosition; // final position (projected)
out vec4 vCPosition; // position wrt camera
out vec4 vTPosition; // position wrt target
out vec4 vCTPosition_x; // position wrt target camera
out vec4 vCTPosition_y; // position wrt target camera
out vec4 vCTPosition_z; // position wrt target camera
out vec4 vPTPosition_x; // position wrt target projected
out vec4 vPTPosition_y; // position wrt target projected
out vec4 vPTPosition_z; // position wrt target projected
out vec3 vCNormal; // normal wrt camera
out vec4 vColor; // color of geometry (considers selection)
out vec2 vPCPosition;
void main() {
vec2 vo = vert_offset * 2 - vec2(1, 1);
vec4 off = vec4((options.radius.x + 2) * vo / options.screen_size.xy, 0, 0);
vec4 pos = get_pos(vec3(vert_pos));
vec3 norm = normalize(vec3(vert_norm) * vec3(options.vert_scale));
vec4 wpos = push_pos(options.matrix_m * pos);
vec3 wnorm = normalize(mat3(options.matrix_mn) * norm);
vec4 tpos = options.matrix_ti * wpos;
vec3 tnorm = vec3(
dot(wnorm, vec3(options.mirror_x)),
dot(wnorm, vec3(options.mirror_y)),
dot(wnorm, vec3(options.mirror_z)));
vCPosition = options.matrix_v * wpos;
vPPosition = off + xyz4(options.matrix_p * options.matrix_v * wpos);
vPCPosition = xyz4(options.matrix_p * options.matrix_v * wpos).xy;
vCNormal = normalize(mat3(options.matrix_vn) * wnorm);
vTPosition = tpos;
vCTPosition_x = options.matrix_v * options.matrix_t * vec4(0.0, tpos.y, tpos.z, 1.0);
vCTPosition_y = options.matrix_v * options.matrix_t * vec4(tpos.x, 0.0, tpos.z, 1.0);
vCTPosition_z = options.matrix_v * options.matrix_t * vec4(tpos.x, tpos.y, 0.0, 1.0);
vPTPosition_x = options.matrix_p * vCTPosition_x;
vPTPosition_y = options.matrix_p * vCTPosition_y;
vPTPosition_z = options.matrix_p * vCTPosition_z;
gl_Position = vPPosition;
vColor = options.color_normal;
if(use_warning() && warning > 0.5) vColor = mix(vColor, options.color_warning, 0.75);
if(use_selection() && selected > 0.5) vColor = mix(vColor, options.color_selected, 0.75);
if(use_pinned() && pinned > 0.5) vColor = mix(vColor, options.color_pinned, 0.75);
if(use_seam() && seam > 0.5) vColor = mix(vColor, options.color_seam, 0.75);
vColor.a *= 1.0 - options.hidden.x;
if(debug_invert_backfacing && vCNormal.z < 0.0) {
vColor = vec4(vec3(1,1,1) - vColor.rgb, vColor.a);
}
}
/////////////////////////////////////////////////////////////////////////
// fragment shader
in vec4 vPPosition; // final position (projected)
in vec4 vCPosition; // position wrt camera
in vec4 vTPosition; // position wrt target
in vec4 vCTPosition_x; // position wrt target camera
in vec4 vCTPosition_y; // position wrt target camera
in vec4 vCTPosition_z; // position wrt target camera
in vec4 vPTPosition_x; // position wrt target projected
in vec4 vPTPosition_y; // position wrt target projected
in vec4 vPTPosition_z; // position wrt target projected
in vec3 vCNormal; // normal wrt camera
in vec4 vColor; // color of geometry (considers selection)
in vec2 vPCPosition;
out vec4 outColor;
out float gl_FragDepth;
void main() {
float clip = options.clip[1] - options.clip[0];
float focus = (view_distance() - options.clip[0]) / clip + 0.04;
vec3 rgb = vColor.rgb;
float alpha = vColor.a;
if(use_rounding()) {
float dist_from_center = length(options.screen_size.xy * (vPCPosition - vPPosition.xy));
float alpha_mult = 1.0 - (dist_from_center - options.radius.x);
if(alpha_mult <= 0) {
discard;
return;
}
alpha *= min(1.0, alpha_mult);
}
if(is_view_perspective()) {
// perspective projection
vec3 v = xyz3(vCPosition);
float l = length(v);
float l_clip = (l - options.clip[0]) / clip;
float d = -dot(vCNormal, v) / l;
if(d <= 0.0) {
if(cull_backfaces()) {
alpha = 0.0;
discard;
return;
} else {
alpha *= min(1.0, alpha_backface());
}
}
} else {
// orthographic projection
vec3 v = vec3(0, 0, clip * 0.5); // + vCPosition.xyz / vCPosition.w;
float l = length(v);
float l_clip = (l - options.clip[0]) / clip;
float d = dot(vCNormal, v) / l;
if(d <= 0.0) {
if(cull_backfaces()) {
alpha = 0.0;
discard;
return;
} else {
alpha *= min(1.0, alpha_backface());
}
}
}
alpha *= min(1.0, pow(max(vCNormal.z, 0.01), 0.25));
outColor = coloring(vec4(rgb, alpha));
// https://wiki.blender.org/wiki/Reference/Release_Notes/2.83/Python_API
outColor = blender_srgb_to_framebuffer_space(outColor);
}
@@ -0,0 +1,121 @@
/*
Copyright (C) 2023 CG Cookie
http://cgcookie.com
hello@cgcookie.com
Created by Jonathan Denning
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
draws an antialiased, stippled circle
ex: stipple [3,2] color0 '=' color1 '-'
produces '===--===--===--===-' (just wrapped as a circle!)
*/
struct Options {
mat4 MVPMatrix; // pixel matrix
vec4 screensize; // width,height of screen (for antialiasing)
vec4 center; // center of circle
vec4 color0; // color of on stipple
vec4 color1; // color of off stipple
vec4 radius_width; // radius of circle, line width (perp to line)
vec4 stipple_data; // stipple lengths, offset
};
uniform Options options;
const bool srgbTarget = true;
const float TAU = 6.28318530718;
float radius() { return options.radius_width.x; }
float width() { return options.radius_width.y; }
vec2 stipple_lengths() { return options.stipple_data.xy; }
float stipple_offset() { return options.stipple_data.z; }
/////////////////////////////////////////////////////////////////////////
// vertex shader
in vec2 pos; // x: [0,1], ratio of circumference. y: [0,1], inner/outer radius (width)
noperspective out vec2 vpos; // position scaled by screensize
noperspective out vec2 cpos; // center of line, scaled by screensize
noperspective out float offset; // stipple offset of individual fragment
void main() {
float circumference = TAU * radius();
float ang = TAU * pos.x;
float r = radius() + (pos.y - 0.5) * (width() + 2.0);
vec2 v = vec2(cos(ang), sin(ang));
vec2 p = options.center.xy + vec2(0.5,0.5) + r * v;
vec2 cp = options.center.xy + vec2(0.5,0.5) + radius() * v;
vec4 pcp = options.MVPMatrix * vec4(cp, 0.0, 1.0);
gl_Position = options.MVPMatrix * vec4(p, 0.0, 1.0);
vpos = vec2(gl_Position.x * options.screensize.x, gl_Position.y * options.screensize.y);
cpos = vec2(pcp.x * options.screensize.x, pcp.y * options.screensize.y);
offset = circumference * pos.x + stipple_offset();
}
/////////////////////////////////////////////////////////////////////////
// fragment shader
noperspective in vec2 vpos;
noperspective in vec2 cpos;
noperspective in float offset;
out vec4 outColor;
vec4 blender_srgb_to_framebuffer_space(vec4 in_color)
{
if (srgbTarget) {
vec3 c = max(in_color.rgb, vec3(0.0));
vec3 c1 = c * (1.0 / 12.92);
vec3 c2 = pow((c + 0.055) * (1.0 / 1.055), vec3(2.4));
in_color.rgb = mix(c1, c2, step(vec3(0.04045), c));
}
return in_color;
}
void main() {
// stipple
if(stipple_lengths().y <= 0) { // stipple disabled
outColor = options.color0;
} else {
float t = stipple_lengths().x + stipple_lengths().y;
float s = mod(offset, t);
float sd = s - stipple_lengths().x;
if(s <= 0.5 || s >= t - 0.5) {
outColor = mix(options.color1, options.color0, mod(s + 0.5, t));
} else if(s >= stipple_lengths().x - 0.5 && s <= stipple_lengths().x + 0.5) {
outColor = mix(options.color0, options.color1, s - (stipple_lengths().x - 0.5));
} else if(s < stipple_lengths().x) {
outColor = options.color0;
} else {
outColor = options.color1;
}
}
// antialias along edge of line
float cdist = length(cpos - vpos);
if(cdist > width()) {
outColor.a *= clamp(1.0 - (cdist - width()), 0.0, 1.0);
}
// https://wiki.blender.org/wiki/Reference/Release_Notes/2.83/Python_API
outColor = blender_srgb_to_framebuffer_space(outColor);
}
@@ -0,0 +1,97 @@
/*
Copyright (C) 2023 CG Cookie
http://cgcookie.com
hello@cgcookie.com
Created by Jonathan Denning
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
struct Options {
mat4 MVPMatrix; // pixel matrix
vec4 screensize; // [ width, height, _, _ ] of screen (for antialiasing)
vec4 center; // center of circle
vec4 color; // color of circle
vec4 plane_x; // x direction in plane the circle lies in
vec4 plane_y; // y direction in plane the circle lies in
vec4 settings; // [ radius, line width (perp to line in plane), depth range near for drawover, depth range far ]
};
uniform Options options;
const float TAU = 6.28318530718;
const bool srgbTarget = true;
float radius() { return options.settings[0]; }
float width() { return options.settings[1]; }
float depth_near() { return options.settings[2]; }
float depth_far() { return options.settings[3]; }
/////////////////////////////////////////////////////////////////////////
// vertex shader
in vec2 pos; // x: [0,1], ratio of circumference. y: [0,1], inner/outer radius (width)
noperspective out vec2 vpos; // position scaled by screensize
noperspective out vec2 cpos; // center of line, scaled by screensize
void main() {
float ang = TAU * pos.x;
float r = radius() + (pos.y - 0.5) * width();
vec3 v = options.plane_x.xyz * cos(ang) + options.plane_y.xyz * sin(ang);
vec3 p = options.center.xyz + r * v;
vec3 cp = options.center.xyz + radius() * v;
vec4 pcp = options.MVPMatrix * vec4(cp, 1.0);
gl_Position = options.MVPMatrix * vec4(p, 1.0);
vpos = vec2(gl_Position.x * options.screensize.x, gl_Position.y * options.screensize.y);
cpos = vec2(pcp.x * options.screensize.x, pcp.y * options.screensize.y);
}
/////////////////////////////////////////////////////////////////////////
// fragment shader
noperspective in vec2 vpos;
noperspective in vec2 cpos;
out vec4 outColor;
out float gl_FragDepth;
vec4 blender_srgb_to_framebuffer_space(vec4 in_color)
{
if (srgbTarget) {
vec3 c = max(in_color.rgb, vec3(0.0));
vec3 c1 = c * (1.0 / 12.92);
vec3 c2 = pow((c + 0.055) * (1.0 / 1.055), vec3(2.4));
in_color.rgb = mix(c1, c2, step(vec3(0.04045), c));
}
return in_color;
}
void main() {
outColor = options.color;
// antialias along edge of line.... NOT WORKING!
float cdist = length(cpos - vpos);
if(cdist > width()) {
outColor.a *= clamp(1.0 - (cdist - width()), 1.0, 1.0);
}
// https://wiki.blender.org/wiki/Reference/Release_Notes/2.83/Python_API
outColor = blender_srgb_to_framebuffer_space(outColor);
gl_FragDepth = mix(depth_near(), depth_far(), gl_FragCoord.z);
}
@@ -0,0 +1,114 @@
/*
Copyright (C) 2023 CG Cookie
http://cgcookie.com
hello@cgcookie.com
Created by Jonathan Denning
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
draws an antialiased, stippled line
ex: stipple [3,2] color0 '=' color1 '-'
produces '===--===--===--===-'
| |
\_pos0 pos1_/
*/
struct Options {
mat4 MVPMatrix; // pixel matrix
vec4 screensize; // width,height of screen (for antialiasing)
vec4 pos0; // front end of line
vec4 pos1; // back end of line
vec4 color0; // color of on stipple
vec4 color1; // color of off stipple
vec4 stipple_width; // lengths for stipple (x: color0, y: color1, z: initial shift) and line width (perp to line)
};
uniform Options options;
const bool srgbTarget = true;
/////////////////////////////////////////////////////////////////////////
// vertex shader
in vec2 pos; // which corner of line ([0,0], [0,1], [1,1], [1,0])
noperspective out vec2 vpos; // position scaled by screensize
noperspective out vec2 cpos; // center of line, scaled by screensize
noperspective out float offset; // stipple offset of individual fragment
void main() {
vec2 v01 = options.pos1.xy - options.pos0.xy;
vec2 d01 = normalize(v01);
vec2 perp = vec2(-d01.y, d01.x);
vec2 cp = options.pos0.xy + vec2(0.5,0.5) + (pos.x * v01);
vec2 p = cp + ((options.stipple_width.w + 2.0) * (pos.y - 0.5) * perp);
vec4 pcp = options.MVPMatrix * vec4(cp, 0.0, 1.0);
gl_Position = options.MVPMatrix * vec4(p, 0.0, 1.0);
offset = length(v01) * pos.x + options.stipple_width.z;
vpos = vec2(gl_Position.x * options.screensize.x, gl_Position.y * options.screensize.y);
cpos = vec2(pcp.x * options.screensize.x, pcp.y * options.screensize.y);
}
/////////////////////////////////////////////////////////////////////////
// fragment shader
noperspective in vec2 vpos;
noperspective in vec2 cpos;
noperspective in float offset;
out vec4 outColor;
vec4 blender_srgb_to_framebuffer_space(vec4 in_color)
{
if (srgbTarget) {
vec3 c = max(in_color.rgb, vec3(0.0));
vec3 c1 = c * (1.0 / 12.92);
vec3 c2 = pow((c + 0.055) * (1.0 / 1.055), vec3(2.4));
in_color.rgb = mix(c1, c2, step(vec3(0.04045), c));
}
return in_color;
}
void main() {
// stipple
if(options.stipple_width.y <= 0) { // stipple disabled
outColor = options.color0;
} else {
float t = options.stipple_width.x + options.stipple_width.y;
float s = mod(offset, t);
float sd = s - options.stipple_width.x;
vec4 colors = options.color1;
if(colors.a < (1.0/255.0)) colors.rgb = options.color0.rgb;
if(s <= 0.5 || s >= t - 0.5) {
outColor = mix(colors, options.color0, mod(s + 0.5, t));
} else if(s >= options.stipple_width.x - 0.5 && s <= options.stipple_width.x + 0.5) {
outColor = mix(options.color0, colors, s - (options.stipple_width.x - 0.5));
} else if(s < options.stipple_width.x) {
outColor = options.color0;
} else {
outColor = colors;
}
}
// antialias along edge of line
float cdist = length(cpos - vpos);
if(cdist > options.stipple_width.w) {
outColor.a *= clamp(1.0 - (cdist - options.stipple_width.w), 0.0, 1.0);
}
// https://wiki.blender.org/wiki/Reference/Release_Notes/2.83/Python_API
outColor = blender_srgb_to_framebuffer_space(outColor);
}
@@ -0,0 +1,85 @@
/*
Copyright (C) 2023 CG Cookie
http://cgcookie.com
hello@cgcookie.com
Created by Jonathan Denning
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
struct Options {
mat4 mvpmatrix; // pixel matrix
vec4 screensize; // width,height of screen (for antialiasing)
vec4 center; // center of point
vec4 radius_border;
vec4 color; // color point
vec4 colorBorder; // color of border
};
uniform Options options;
const bool srgbTarget = true;
/////////////////////////////////////////////////////////////////////////
// vertex shader
in vec2 pos; // four corners of point ([0,0], [0,1], [1,1], [1,0])
noperspective out vec2 vpos; // position scaled by screensize
void main() {
float radius_border = options.radius_border.x + options.radius_border.y;
vec2 p = options.center.xy + (pos - vec2(0.5, 0.5)) * radius_border;
gl_Position = options.mvpmatrix * vec4(p, 0.0, 1.0);
vpos = gl_Position.xy * options.screensize.xy; // just p?
}
/////////////////////////////////////////////////////////////////////////
// fragment shader
noperspective in vec2 vpos;
out vec4 outColor;
vec4 blender_srgb_to_framebuffer_space(vec4 in_color)
{
if (srgbTarget) {
vec3 c = max(in_color.rgb, vec3(0.0));
vec3 c1 = c * (1.0 / 12.92);
vec3 c2 = pow((c + 0.055) * (1.0 / 1.055), vec3(2.4));
in_color.rgb = mix(c1, c2, step(vec3(0.04045), c));
}
return in_color;
}
void main() {
float radius_border = options.radius_border.x + options.radius_border.y;
vec4 colorb = options.colorBorder;
if(colorb.a < (1.0/255.0)) colorb.rgb = options.color.rgb;
vec2 ctr = (options.mvpmatrix * vec4(options.center.xy, 0.0, 1.0)).xy;
float d = distance(vpos, ctr.xy * options.screensize.xy);
if(d > radius_border) { discard; return; }
if(d <= options.radius_border.x) {
float d2 = options.radius_border.x - d;
outColor = mix(colorb, options.color, clamp(d2 - options.radius_border.y/2.0, 0.0, 1.0));
} else {
float d2 = d - options.radius_border.x;
outColor = mix(colorb, vec4(colorb.rgb,0), clamp(d2 - options.radius_border.y/2.0, 0.0, 1.0));
}
// https://wiki.blender.org/wiki/Reference/Release_Notes/2.83/Python_API
outColor = blender_srgb_to_framebuffer_space(outColor);
}
@@ -0,0 +1,77 @@
/*
Copyright (C) 2023 CG Cookie
http://cgcookie.com
hello@cgcookie.com
Created by Jonathan Denning
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
struct Options {
mat4 MVPMatrix; // pixel matrix
vec4 pos0;
vec4 color0;
vec4 pos1;
vec4 color1;
vec4 pos2;
vec4 color2;
};
uniform Options options;
const bool srgbTarget = true;
/////////////////////////////////////////////////////////////////////////
// vertex shader
in vec2 pos; // x: [0,1], alpha. y: [0,1], beta
out vec4 color;
void main() {
float a = clamp(pos.x, 0.0, 1.0);
float b = clamp(pos.y, 0.0, 1.0);
float c = 1.0 - a - b;
vec2 p = (options.pos0 * a + options.pos1 * b + options.pos2 * c).xy;
gl_Position = options.MVPMatrix * vec4(p, 0.0, 1.0);
color = options.color0 * a + options.color1 * b + options.color2 * c;
}
/////////////////////////////////////////////////////////////////////////
// fragment shader
in vec4 color;
out vec4 outColor;
vec4 blender_srgb_to_framebuffer_space(vec4 in_color)
{
if (srgbTarget) {
vec3 c = max(in_color.rgb, vec3(0.0));
vec3 c1 = c * (1.0 / 12.92);
vec3 c2 = pow((c + 0.055) * (1.0 / 1.055), vec3(2.4));
in_color.rgb = mix(c1, c2, step(vec3(0.04045), c));
}
return in_color;
}
void main() {
outColor = color;
// https://wiki.blender.org/wiki/Reference/Release_Notes/2.83/Python_API
outColor = blender_srgb_to_framebuffer_space(outColor);
}
@@ -0,0 +1,78 @@
/*
Copyright (C) 2023 CG Cookie
http://cgcookie.com
hello@cgcookie.com
Created by Jonathan Denning
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
struct Options {
mat4 MVPMatrix; // view matrix
vec4 pos0;
vec4 color0;
vec4 pos1;
vec4 color1;
vec4 pos2;
vec4 color2;
};
uniform Options options;
const bool srgbTarget = true;
/////////////////////////////////////////////////////////////////////////
// vertex shader
in vec2 pos; // x: [0,1], alpha. y: [0,1], beta
out vec4 color;
void main() {
float a = clamp(pos.x, 0.0, 1.0);
float b = clamp(pos.y, 0.0, 1.0);
float c = 1.0 - a - b;
vec3 p = vec3(options.pos0) * a + vec3(options.pos1) * b + vec3(options.pos2) * c;
gl_Position = options.MVPMatrix * vec4(p, 1.0);
color = options.color0 * a + options.color1 * b + options.color2 * c;
}
/////////////////////////////////////////////////////////////////////////
// fragment shader
in vec4 color;
out vec4 outColor;
vec4 blender_srgb_to_framebuffer_space(vec4 in_color)
{
if (srgbTarget) {
vec3 c = max(in_color.rgb, vec3(0.0));
vec3 c1 = c * (1.0 / 12.92);
vec3 c2 = pow((c + 0.055) * (1.0 / 1.055), vec3(2.4));
in_color.rgb = mix(c1, c2, step(vec3(0.04045), c));
}
return in_color;
}
void main() {
outColor = color;
// https://wiki.blender.org/wiki/Reference/Release_Notes/2.83/Python_API
outColor = blender_srgb_to_framebuffer_space(outColor);
}
@@ -0,0 +1,440 @@
/*
Copyright (C) 2023 CG Cookie
http://cgcookie.com
hello@cgcookie.com
Created by Jonathan Denning
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#version 330
// // the following two lines are an attempt to solve issues #1025, #879, #753
// precision mediump float;
// precision lowp int; // only used to represent enum or bool
struct Options {
mat4 uMVPMatrix;
vec4 lrtb;
vec4 wh;
vec4 depth;
vec4 margin_lrtb;
vec4 padding_lrtb;
vec4 border_width_radius;
vec4 border_left_color;
vec4 border_right_color;
vec4 border_top_color;
vec4 border_bottom_color;
vec4 background_color;
// see IMAGE_SCALE_XXX values below
ivec4 image_settings;
};
uniform Options options;
uniform sampler2D image;
const bool srgbTarget = true;
bool image_use() { return options.image_settings[0] != 0; }
int image_fit() { return options.image_settings[1]; }
float pos_l() { return options.lrtb[0]; }
float pos_r() { return options.lrtb[1]; }
float pos_t() { return options.lrtb[2]; }
float pos_b() { return options.lrtb[3]; }
float size_w() { return options.wh[0]; }
float size_h() { return options.wh[1]; }
float depth() { return options.depth[0]; }
float margin_l() { return options.margin_lrtb[0]; }
float margin_r() { return options.margin_lrtb[1]; }
float margin_t() { return options.margin_lrtb[2]; }
float margin_b() { return options.margin_lrtb[3]; }
float padding_l() { return options.padding_lrtb[0]; }
float padding_r() { return options.padding_lrtb[1]; }
float padding_t() { return options.padding_lrtb[2]; }
float padding_b() { return options.padding_lrtb[3]; }
float border_width() { return options.border_width_radius[0]; }
float border_radius() { return options.border_width_radius[1]; }
vec4 border_left_color() { return options.border_left_color; }
vec4 border_right_color() { return options.border_right_color; }
vec4 border_top_color() { return options.border_top_color; }
vec4 border_bottom_color() { return options.border_bottom_color; }
vec4 background_color() { return options.background_color; }
////////////////////////////////////////
// vertex shader
in vec2 pos;
out vec2 screen_pos;
void main() {
// set vertex to bottom-left, top-left, top-right, or bottom-right location, depending on pos
vec2 p = vec2(
(pos.x < 0.5) ? (pos_l() - 1.0) : (pos_r() + 1.0),
(pos.y < 0.5) ? (pos_b() - 1.0) : (pos_t() + 1.0)
);
// convert depth to z-order
float zorder = 1.0 - depth() / 1000.0;
screen_pos = p;
gl_Position = options.uMVPMatrix * vec4(p, zorder, 1);
}
////////////////////////////////////////
// fragment shader
in vec2 screen_pos;
out vec4 outColor;
out float gl_FragDepth;
float sqr(float s) { return s * s; }
float sumsqr(float a, float b) { return sqr(a) + sqr(b); }
float min4(float a, float b, float c, float d) { return min(min(min(a, b), c) ,d); }
vec4 mix_over(vec4 above, vec4 below) {
vec3 a_ = above.rgb * above.a;
vec3 b_ = below.rgb * below.a;
float alpha = above.a + (1.0 - above.a) * below.a;
return vec4((a_ + b_ * (1.0 - above.a)) / alpha, alpha);
}
int get_margin_region(float dist_left, float dist_right, float dist_top, float dist_bottom) {
float dist_min = min4(dist_left, dist_right, dist_top, dist_bottom);
if(dist_min == dist_left) return REGION_MARGIN_LEFT;
if(dist_min == dist_right) return REGION_MARGIN_RIGHT;
if(dist_min == dist_top) return REGION_MARGIN_TOP;
if(dist_min == dist_bottom) return REGION_MARGIN_BOTTOM;
return REGION_ERROR; // this should never happen
}
int get_region() {
/* this function determines which region the fragment is in wrt properties of UI element,
specifically: position, size, border width, border radius, margins
v top-left
+-----------------+
| \ / | <- margin regions
| +---------+ |
| |\ /| | <- border regions
| | +-----+ | |
| | | | | | <- inside border region (content area + padding)
| | +-----+ | |
| |/ \| |
| +---------+ |
| / \ |
+-----------------+
^ bottom-right
- margin regions
- broken into top, right, bottom, left
- each TRBL margin size can be different size
- border regions
- broken into top, right, bottom, left
- each can have different colors, but all same size (TODO!)
- inside border region
- where content is drawn (image)
- NOTE: padding takes up this space
- ERROR region _should_ never happen, but can be returned from this fn if something goes wrong
*/
float dist_left = screen_pos.x - (pos_l() + margin_l());
float dist_right = (pos_r() - margin_r() + 1.0) - screen_pos.x;
float dist_bottom = screen_pos.y - (pos_b() + margin_b() - 1.0);
float dist_top = (pos_t() - margin_t()) - screen_pos.y;
float radwid = max(border_radius(), border_width());
float rad = max(0.0, border_radius() - border_width());
float radwid2 = sqr(radwid);
float rad2 = sqr(rad);
if(dist_left < 0 || dist_right < 0 || dist_top < 0 || dist_bottom < 0) return REGION_OUTSIDE;
// margin
int margin_region = get_margin_region(dist_left, dist_right, dist_top, dist_bottom);
// within top and bottom, might be left or right side
if(dist_bottom > radwid && dist_top > radwid) {
if(dist_left > border_width() && dist_right > border_width()) return REGION_BACKGROUND;
if(dist_left < dist_right) return REGION_BORDER_LEFT;
return REGION_BORDER_RIGHT;
}
// within left and right, might be bottom or top
if(dist_left > radwid && dist_right > radwid) {
if(dist_bottom > border_width() && dist_top > border_width()) return REGION_BACKGROUND;
if(dist_bottom < dist_top) return REGION_BORDER_BOTTOM;
return REGION_BORDER_TOP;
}
// top-left
if(dist_top <= radwid && dist_left <= radwid) {
float r2 = sumsqr(dist_left - radwid, dist_top - radwid);
if(r2 > radwid2) return margin_region;
if(r2 < rad2) return REGION_BACKGROUND;
if(dist_left < dist_top) return REGION_BORDER_LEFT;
return REGION_BORDER_TOP;
}
// top-right
if(dist_top <= radwid && dist_right <= radwid) {
float r2 = sumsqr(dist_right - radwid, dist_top - radwid);
if(r2 > radwid2) return margin_region;
if(r2 < rad2) return REGION_BACKGROUND;
if(dist_right < dist_top) return REGION_BORDER_RIGHT;
return REGION_BORDER_TOP;
}
// bottom-left
if(dist_bottom <= radwid && dist_left <= radwid) {
float r2 = sumsqr(dist_left - radwid, dist_bottom - radwid);
if(r2 > radwid2) return margin_region;
if(r2 < rad2) return REGION_BACKGROUND;
if(dist_left < dist_bottom) return REGION_BORDER_LEFT;
return REGION_BORDER_BOTTOM;
}
// bottom-right
if(dist_bottom <= radwid && dist_right <= radwid) {
float r2 = sumsqr(dist_right - radwid, dist_bottom - radwid);
if(r2 > radwid2) return margin_region;
if(r2 < rad2) return REGION_BACKGROUND;
if(dist_right < dist_bottom) return REGION_BORDER_RIGHT;
return REGION_BORDER_BOTTOM;
}
// something bad happened
return REGION_ERROR;
}
vec4 mix_image(vec4 bg) {
vec4 c = bg;
// drawing space
float dw = size_w() - (margin_l() + border_width() + padding_l() + padding_r() + border_width() + margin_r());
float dh = size_h() - (margin_t() + border_width() + padding_t() + padding_b() + border_width() + margin_b());
float dx = screen_pos.x - (pos_l() + (margin_l() + border_width() + padding_l()));
float dy = -(screen_pos.y - (pos_t() - (margin_t() + border_width() + padding_t())));
float dsx = (dx + 0.5) / dw;
float dsy = (dy + 0.5) / dh;
// texture
vec2 tsz = vec2(textureSize(image, 0));
float tw = tsz.x, th = tsz.y;
float tx, ty;
switch(image_fit()) {
case IMAGE_SCALE_FILL:
// object-fit: fill = stretch / squash to fill entire drawing space (non-uniform scale)
// do nothing here
tx = tw * dx / dw;
ty = th * dy / dh;
break;
case IMAGE_SCALE_CONTAIN: {
// object-fit: contain = uniformly scale texture to fit entirely in drawing space (will be letterboxed)
// find smaller scaled dimension, and use that
float _tw, _th;
if(dw / dh < tw / th) {
// scaling by height is too big, so scale by width
_tw = tw;
_th = tw * dh / dw;
} else {
_tw = th * dw / dh;
_th = th;
}
tx = dsx * _tw - (_tw - tw) / 2.0;
ty = dsy * _th - (_th - th) / 2.0;
break; }
case IMAGE_SCALE_COVER: {
// object-fit: cover = uniformly scale texture to fill entire drawing space (will be cropped)
// find larger scaled dimension, and use that
float _tw, _th;
if(dw / dh > tw / th) {
// scaling by height is too big, so scale by width
_tw = tw;
_th = tw * dh / dw;
} else {
_tw = th * dw / dh;
_th = th;
}
tx = dsx * _tw - (_tw - tw) / 2.0;
ty = dsy * _th - (_th - th) / 2.0;
break; }
case IMAGE_SCALE_DOWN:
// object-fit: scale-down = either none or contain, whichever is smaller
if(dw >= tw && dh >= th) {
// none
tx = dx + (tw - dw) / 2.0;
ty = dy + (th - dh) / 2.0;
} else {
float _tw, _th;
if(dw / dh < tw / th) {
// scaling by height is too big, so scale by width
_tw = tw;
_th = tw * dh / dw;
} else {
_tw = th * dw / dh;
_th = th;
}
tx = dsx * _tw - (_tw - tw) / 2.0;
ty = dsy * _th - (_th - th) / 2.0;
}
break;
case IMAGE_SCALE_NONE:
// object-fit: none (no resizing)
tx = dx + (tw - dw) / 2.0;
ty = dy + (th - dh) / 2.0;
break;
default: // error!
tx = tw / 2.0;
ty = th / 2.0;
break;
}
vec2 texcoord = vec2(tx / tw, 1 - ty / th);
bool inside = 0.0 <= texcoord.x && texcoord.x <= 1.0 && 0.0 <= texcoord.y && texcoord.y <= 1.0;
if(inside) {
vec4 t = texture(image, texcoord) + COLOR_DEBUG_IMAGE;
c = mix_over(t, c);
}
#ifdef DEBUG_IMAGE_CHECKER
if(inside) {
// generate checker pattern to test scaling
switch((int(32.0 * texcoord.x) + 4 * int(32.0 * texcoord.y)) % 16) {
case 0: c = COLOR_CHECKER_00; break;
case 1: c = COLOR_CHECKER_01; break;
case 2: c = COLOR_CHECKER_02; break;
case 3: c = COLOR_CHECKER_03; break;
case 4: c = COLOR_CHECKER_04; break;
case 5: c = COLOR_CHECKER_05; break;
case 6: c = COLOR_CHECKER_06; break;
case 7: c = COLOR_CHECKER_07; break;
case 8: c = COLOR_CHECKER_08; break;
case 9: c = COLOR_CHECKER_09; break;
case 10: c = COLOR_CHECKER_10; break;
case 11: c = COLOR_CHECKER_11; break;
case 12: c = COLOR_CHECKER_12; break;
case 13: c = COLOR_CHECKER_13; break;
case 14: c = COLOR_CHECKER_14; break;
case 15: c = COLOR_CHECKER_15; break;
}
}
#endif
#ifdef DEBUG_IMAGE_OUTSIDE
if(!inside) {
c = vec4(
1.0 - (1.0 - c.r) * 0.5,
1.0 - (1.0 - c.g) * 0.5,
1.0 - (1.0 - c.b) * 0.5,
c.a
);
}
#endif
return c;
}
vec4 blender_srgb_to_framebuffer_space(vec4 in_color)
{
if (srgbTarget) {
vec3 c = max(in_color.rgb, vec3(0.0));
vec3 c1 = c * (1.0 / 12.92);
vec3 c2 = pow((c + 0.055) * (1.0 / 1.055), vec3(2.4));
in_color.rgb = mix(c1, c2, step(vec3(0.04045), c));
}
return in_color;
}
void main() {
vec4 c = vec4(0,0,0,0);
int region = get_region();
// workaround switched-discard (issue #1042)
#ifndef DEBUG_DONT_DISCARD
#ifndef DEBUG_COLOR_REGIONS
#ifndef DEBUG_COLOR_MARGINS
if(region == REGION_MARGIN_TOP) { discard; return; }
if(region == REGION_MARGIN_RIGHT) { discard; return; }
if(region == REGION_MARGIN_BOTTOM) { discard; return; }
if(region == REGION_MARGIN_LEFT) { discard; return; }
#endif
if(region == REGION_OUTSIDE) { discard; return; }
#endif
#endif
switch(region) {
case REGION_BORDER_TOP: c = border_top_color(); break;
case REGION_BORDER_RIGHT: c = border_right_color(); break;
case REGION_BORDER_BOTTOM: c = border_bottom_color(); break;
case REGION_BORDER_LEFT: c = border_left_color(); break;
case REGION_BACKGROUND: c = background_color(); break;
// following colors show only if DEBUG settings allow or something really unexpected happens
case REGION_MARGIN_TOP: c = COLOR_MARGIN_TOP; break;
case REGION_MARGIN_RIGHT: c = COLOR_MARGIN_RIGHT; break;
case REGION_MARGIN_BOTTOM: c = COLOR_MARGIN_BOTTOM; break;
case REGION_MARGIN_LEFT: c = COLOR_MARGIN_LEFT; break;
case REGION_OUTSIDE: c = COLOR_OUTSIDE; break; // keep transparent
case REGION_ERROR: c = COLOR_ERROR; break; // should never hit here
default: c = COLOR_ERROR_NEVER; // should **really** never hit here
}
// DEBUG_COLOR_REGIONS will mix over other colors
#ifdef DEBUG_COLOR_REGIONS
switch(region) {
case REGION_BORDER_TOP: c = mix_over(COLOR_BORDER_TOP, c); break;
case REGION_BORDER_RIGHT: c = mix_over(COLOR_BORDER_RIGHT, c); break;
case REGION_BORDER_BOTTOM: c = mix_over(COLOR_BORDER_BOTTOM, c); break;
case REGION_BORDER_LEFT: c = mix_over(COLOR_BORDER_LEFT, c); break;
case REGION_BACKGROUND: c = mix_over(COLOR_BACKGROUND, c); break;
}
#endif
// apply image if used
if(image_use()) c = mix_image(c);
c = vec4(c.rgb * c.a, c.a);
// https://wiki.blender.org/wiki/Reference/Release_Notes/2.83/Python_API
c = blender_srgb_to_framebuffer_space(c);
#ifdef DEBUG_SNAP_ALPHA
if(c.a < 0.25) {
c.a = 0.0;
#ifndef DEBUG_DONT_DISCARD
discard; return;
#endif
}
else c.a = 1.0;
#endif
outColor = c;
//gl_FragDepth = gl_FragDepth * 0.999999;
gl_FragDepth = gl_FragCoord.z * 0.999999; // fix for issue #915?
}