From 62b9c2b02656f4dbf8209c36cd89c319712a5c0a Mon Sep 17 00:00:00 2001 From: wuyize Date: Tue, 14 Mar 2023 22:44:23 +0800 Subject: [PATCH] =?UTF-8?q?FIX:=20style=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../res/Shaders/element.comp | 98 +- .../res/Shaders/painting.comp | 2026 +++++++++-------- .../src/Editor/util/PainterPathUtil.cpp | 8 +- .../src/Editor/util/PainterPathUtil.h | 4 +- .../src/Renderer/Model.cpp | 54 +- .../Renderer/Painting/MaterialStyleStroke.cpp | 6 +- .../Renderer/Painting/MaterialStyleStroke.h | 2 +- .../src/Renderer/RendererWidget.cpp | 6 +- UnitTest/ElementRendererTest.cpp | 21 + 9 files changed, 1232 insertions(+), 993 deletions(-) diff --git a/ArchitectureColoredPainting/res/Shaders/element.comp b/ArchitectureColoredPainting/res/Shaders/element.comp index 36f4220..93c0166 100644 --- a/ArchitectureColoredPainting/res/Shaders/element.comp +++ b/ArchitectureColoredPainting/res/Shaders/element.comp @@ -969,7 +969,7 @@ void drawLine(in float d, inout uint styleIndex, out vec4 elementColor, out vec2 } } -bool shouldFillBeginCap(vec2 localUV, bool onVeryBegin, int endType, vec2 p3, vec2 tangentBegin, vec2 tangentEndLast) +bool shouldFillBeginCap(vec2 localUV, bool onVeryBegin, int endType, vec2 p0, vec2 tangentBegin, vec2 tangentEndLast) { vec2 normal; if (onVeryBegin) @@ -985,17 +985,26 @@ bool shouldFillBeginCap(vec2 localUV, bool onVeryBegin, int endType, vec2 p3, ve vec2 normalNow = normalize(mat2(0, 1, -1, 0) * (-tangentBegin)); normal = normalLast + normalNow; } - return angleLargeThanPi(normal, localUV - p3); + return angleLargeThanPi(normal, localUV - p0); } -bool shouldFillEndCap(vec2 localUV, int endType, vec2 p0, vec2 tangentEnd) +bool shouldFillEndCap(vec2 localUV, bool onVeryEnd, int endType, vec2 p3, vec2 tangentEnd, vec2 tangentBeginNext) { vec2 normal; - if (endType == 0) - return true; - else if (endType == 1) - normal = normalize(mat2(0, 1, -1, 0) * tangentEnd); - return angleLargeThanPi(localUV - p0, normal); + if (onVeryEnd) + { + if (endType == 0) + return true; + else if (endType == 1) + normal = normalize(mat2(0, 1, -1, 0) * tangentEnd); + } + else + { + vec2 normalLast = normalize(mat2(0, 1, -1, 0) * tangentEnd); + vec2 normalNow = normalize(mat2(0, 1, -1, 0) * (-tangentBeginNext)); + normal = normalLast + normalNow; + } + return angleLargeThanPi(localUV - p3, normal); } void main() @@ -1055,21 +1064,25 @@ void main() float lineType = floor(styleHead.b * 10); // float lineType = 2; int endType = int(round(styleHead.b * 100)) % 10; - //endType = 1; + // endType = 1; int debugBegin = 0; bool onVeryBegin = false; + bool onVeryEnd = false; vec2 tangentEndLast; + uint lastHitIndex = 0; + bool lastHitElement = false; + hitElement = false; for (uint pathIndex = 0; pathIndex < pathSize; pathIndex++) - // for (uint pathIndex = 0; pathIndex < 4; pathIndex++) + //for (uint pathIndex = 0; pathIndex < 46; pathIndex++) { vec2 pTemp = path[pathIndex]; if (isinf(pTemp.x)) { // TODO: 检测是否封闭并处理 - if (hitElement && distance(localUV, p3Last) <= strokeWidth) - { - hitElement = shouldFillEndCap(localUV, endType, p3Last, tangentEndLast); - } + if (hitElement && distance(localUV, p3Last) <= strokeWidth) + { + // hitElement = shouldFillEndCap(localUV, true, endType, p3Last, tangentEndLast); + } pBegin = path[++pathIndex]; p3Last = pBegin; @@ -1079,10 +1092,30 @@ void main() } mat4x2 p = mat4x2(p3Last, pTemp, path[++pathIndex], path[++pathIndex]); + vec2 tangentBeginNext; + if (pathIndex + 1 < pathSize) + { + vec2 pTemp = path[pathIndex + 1]; + if (isinf(pTemp.x)) + { + onVeryEnd = true; + } + else + { + onVeryEnd = false; + vec2 pNext[3] = {p[3], pTemp, path[pathIndex + 2]}; + if (pNext[0] != pNext[1]) + tangentBeginNext = normalize(pNext[0] - pNext[1]); + else + tangentBeginNext = normalize(pNext[0] - pNext[2]); + } + } + float d = cubic_bezier_dis(localUV, p[0], p[1], p[2], p[3], true); if (d <= strokeWidth) { - bool onBegin = distance(localUV, p[0]) <= strokeWidth && p3Last == p[0]; + bool onBegin = distance(localUV, p[0]) <= strokeWidth; + bool onEnd = distance(localUV, p[3]) <= strokeWidth; vec2 tangentBegin; vec2 tangentEnd; if (p[0] != p[1]) @@ -1094,11 +1127,19 @@ void main() else tangentEnd = normalize(p[3] - p[1]); - if (onBegin ? shouldFillBeginCap(localUV, onVeryBegin, endType, p[0], tangentBegin, p3Last - p2Last) - : d < minDistance) + // if (onBegin ? shouldFillBeginCap(localUV, onVeryBegin, endType, p[0], tangentBegin, + // tangentEndLast) + // : (onEnd ? /*shouldFillEndCap(localUV, onVeryEnd, endType, p[3], tangentEnd, + // tangentBeginNext)*/ false + // : d < minDistance)) + bool hit = d < minDistance; + if (onBegin) + hit = hit && + shouldFillBeginCap(localUV, onVeryBegin, endType, p[0], tangentBegin, tangentEndLast); + if (onEnd) + hit = hit && shouldFillEndCap(localUV, onVeryEnd, endType, p[3], tangentEnd, tangentBeginNext); + if (hit) { - minDistance = min(minDistance, d); - bool reverse = p[3].y - p[0].y < 0.; if (tangentBegin.y == 0.) @@ -1111,13 +1152,26 @@ void main() if (lineType == 2 || (intTest % 2 == int(lineType))) { + minDistance = min(minDistance, d); + lastHitElement = hitElement; + lastHitIndex = pathIndex; hitElement = true; // elementColor = vec4(1, 1, 0, 1); vec2 metallicRoughness; drawLine(minDistance / strokeWidth, styleIndex, elementColor, metallicRoughness); } - else if (p3Last == p[0]) - hitElement = false; + // else if (lastHitIndex == pathIndex - 3) + // { + // hitElement = lastHitElement; + // lastHitElement = false; + // // if(lastHitElement ==false) + // //{ + // // hitElement = true; + // // elementColor = vec4(1, 1, 0, 1); + // //} + + // minDistance = 1e38; + // } } tangentEndLast = tangentEnd; } @@ -1127,7 +1181,7 @@ void main() } if (hitElement && distance(localUV, p3Last) <= strokeWidth) { - hitElement = shouldFillEndCap(localUV, endType, p3Last, tangentEndLast); + // hitElement = shouldFillEndCap(localUV, true, endType, p3Last, tangentEndLast); } } if (hitElement) diff --git a/ArchitectureColoredPainting/res/Shaders/painting.comp b/ArchitectureColoredPainting/res/Shaders/painting.comp index e7280a0..9ba19a9 100644 --- a/ArchitectureColoredPainting/res/Shaders/painting.comp +++ b/ArchitectureColoredPainting/res/Shaders/painting.comp @@ -1,6 +1,6 @@ #version 450 core -layout (local_size_x = 8, local_size_y = 8) in; +layout(local_size_x = 8, local_size_y = 8) in; layout(location = 0) uniform ivec2 pixelOffset; @@ -9,97 +9,95 @@ layout(rg8, binding = 1) uniform image2D gMetallicRoughness; layout(std430, binding = 0) buffer bvhBuffer { - uvec2 bvhChildren[]; + uvec2 bvhChildren[]; }; layout(std430, binding = 1) buffer bvhBoundBuffer { - vec4 bvhBound[]; + vec4 bvhBound[]; }; layout(std430, binding = 2) buffer elementOffsetBuffer { - /** - * @[0] elementBvhRoot - * @[1] styleOffset - * @[2] pointsOffset - * @[3] linesOffset + /** + * @[0] elementBvhRoot + * @[1] styleOffset + * @[2] pointsOffset + * @[3] linesOffset - */ - uint elementOffset[][5]; + */ + uint elementOffset[][5]; }; layout(std430, binding = 3) buffer elementIndexBuffer { - uint elementIndexs[]; //线和面 + uint elementIndexs[]; // 线和面 }; layout(std430, binding = 4) buffer elementDataBuffer { - float elementData[]; //点和Style + float elementData[]; // 点和Style }; const float PI = 3.14159265358979; - const uint STACK_SIZE = 10; struct Stack { - uint top; - uint data[STACK_SIZE]; + uint top; + uint data[STACK_SIZE]; - bool empty() - { - return top == 0; - } - bool full() - { - return top == STACK_SIZE; - } - bool getTop(out uint x) - { - if (empty()) - return false; - x = data[top - 1]; - return true; - } - bool pop() - { - if (empty()) - return false; - top--; - return true; - } - bool push(in uint x) - { - if (full()) - return false; - data[top] = x; - top++; - return true; - } + bool empty() + { + return top == 0; + } + bool full() + { + return top == STACK_SIZE; + } + bool getTop(out uint x) + { + if (empty()) + return false; + x = data[top - 1]; + return true; + } + bool pop() + { + if (empty()) + return false; + top--; + return true; + } + bool push(in uint x) + { + if (full()) + return false; + data[top] = x; + top++; + return true; + } } stack, elementStack; - // Modified from http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c // Credits to Doublefresh for hinting there int solve_quadric(vec2 coeffs, inout vec2 roots) { - // normal form: x^2 + px + q = 0 - float p = coeffs[1] / 2.; - float q = coeffs[0]; + // normal form: x^2 + px + q = 0 + float p = coeffs[1] / 2.; + float q = coeffs[0]; - float D = p * p - q; + float D = p * p - q; - if (D < 0.) - { - return 0; - } - else - { - roots[0] = -sqrt(D) - p; - roots[1] = sqrt(D) - p; + if (D < 0.) + { + return 0; + } + else + { + roots[0] = -sqrt(D) - p; + roots[1] = sqrt(D) - p; - return 2; - } + return 2; + } } // From Trisomie21 @@ -107,490 +105,520 @@ int solve_quadric(vec2 coeffs, inout vec2 roots) int solve_cubic(vec3 coeffs, inout vec3 r) { - float a = coeffs[2]; - float b = coeffs[1]; - float c = coeffs[0]; + float a = coeffs[2]; + float b = coeffs[1]; + float c = coeffs[0]; - float p = b - a * a / 3.0; - float q = a * (2.0 * a * a - 9.0 * b) / 27.0 + c; - float p3 = p * p * p; - float d = q * q + 4.0 * p3 / 27.0; - float offset = -a / 3.0; - if (d >= 0.0) - { // Single solution - float z = sqrt(d); - float u = (-q + z) / 2.0; - float v = (-q - z) / 2.0; - u = sign(u) * pow(abs(u), 1.0 / 3.0); - v = sign(v) * pow(abs(v), 1.0 / 3.0); - r[0] = offset + u + v; + float p = b - a * a / 3.0; + float q = a * (2.0 * a * a - 9.0 * b) / 27.0 + c; + float p3 = p * p * p; + float d = q * q + 4.0 * p3 / 27.0; + float offset = -a / 3.0; + if (d >= 0.0) + { // Single solution + float z = sqrt(d); + float u = (-q + z) / 2.0; + float v = (-q - z) / 2.0; + u = sign(u) * pow(abs(u), 1.0 / 3.0); + v = sign(v) * pow(abs(v), 1.0 / 3.0); + r[0] = offset + u + v; - // Single newton iteration to account for cancellation - float f = ((r[0] + a) * r[0] + b) * r[0] + c; - float f1 = (3. * r[0] + 2. * a) * r[0] + b; + // Single newton iteration to account for cancellation + float f = ((r[0] + a) * r[0] + b) * r[0] + c; + float f1 = (3. * r[0] + 2. * a) * r[0] + b; - r[0] -= f / f1; + r[0] -= f / f1; - return 1; - } - float u = sqrt(-p / 3.0); - float v = acos(-sqrt(-27.0 / p3) * q / 2.0) / 3.0; - float m = cos(v), n = sin(v) * 1.732050808; + return 1; + } + float u = sqrt(-p / 3.0); + float v = acos(-sqrt(-27.0 / p3) * q / 2.0) / 3.0; + float m = cos(v), n = sin(v) * 1.732050808; - // Single newton iteration to account for cancellation - //(once for every root) - r[0] = offset + u * (m + m); - r[1] = offset - u * (n + m); - r[2] = offset + u * (n - m); + // Single newton iteration to account for cancellation + //(once for every root) + r[0] = offset + u * (m + m); + r[1] = offset - u * (n + m); + r[2] = offset + u * (n - m); - vec3 f = ((r + a) * r + b) * r + c; - vec3 f1 = (3. * r + 2. * a) * r + b; + vec3 f = ((r + a) * r + b) * r + c; + vec3 f1 = (3. * r + 2. * a) * r + b; - r -= f / f1; + r -= f / f1; - return 3; + return 3; } - int segment_int_test(vec2 uv, vec2 p0, vec2 p1) { - p0 -= uv; - p1 -= uv; + p0 -= uv; + p1 -= uv; - int ret; + int ret; - if (p0.y * p1.y < 0.) - { - vec2 nor = p0 - p1; - nor = vec2(nor.y, -nor.x); + if (p0.y * p1.y < 0.) + { + vec2 nor = p0 - p1; + nor = vec2(nor.y, -nor.x); - float sgn; + float sgn; - if (p0.y > p1.y) - { - sgn = 1.; - } - else - { - sgn = -1.; - } + if (p0.y > p1.y) + { + sgn = 1.; + } + else + { + sgn = -1.; + } - if (dot(nor, p0) * sgn < 0.) - { - ret = 0; - } - else - { - ret = 1; - } - } - else - { - ret = 0; - } + if (dot(nor, p0) * sgn < 0.) + { + ret = 0; + } + else + { + ret = 1; + } + } + else + { + ret = 0; + } - return ret; + return ret; } int cubic_bezier_int_test(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3) { - float cu = (-p0.y + 3. * p1.y - 3. * p2.y + p3.y); - float qu = (3. * p0.y - 6. * p1.y + 3. * p2.y); - float li = (-3. * p0.y + 3. * p1.y); - float co = p0.y - uv.y; + float cu = (-p0.y + 3. * p1.y - 3. * p2.y + p3.y); + float qu = (3. * p0.y - 6. * p1.y + 3. * p2.y); + float li = (-3. * p0.y + 3. * p1.y); + float co = p0.y - uv.y; - vec3 roots = vec3(1e38); - int n_roots; + vec3 roots = vec3(1e38); + int n_roots; - int n_ints = 0; + int n_ints = 0; - if (uv.x < min(min(p0.x, p1.x), min(p2.x, p3.x))) - { - if (uv.y >= min(p0.y, p3.y) && uv.y <= max(p0.y, p3.y)) - { - n_ints = 1; - } - } - else - { + if (uv.x < min(min(p0.x, p1.x), min(p2.x, p3.x))) + { + if (uv.y >= min(p0.y, p3.y) && uv.y <= max(p0.y, p3.y)) + { + n_ints = 1; + } + } + else + { - if (abs(cu) < .0001) - { - n_roots = solve_quadric(vec2(co / qu, li / qu), roots.xy); - } - else - { - n_roots = solve_cubic(vec3(co / cu, li / cu, qu / cu), roots); - } + if (abs(cu) < .0001) + { + n_roots = solve_quadric(vec2(co / qu, li / qu), roots.xy); + } + else + { + n_roots = solve_cubic(vec3(co / cu, li / cu, qu / cu), roots); + } - for (int i = 0; i < n_roots; i++) - { - if (roots[i] >= 0. && roots[i] <= 1.) - { - float x_pos = -p0.x + 3. * p1.x - 3. * p2.x + p3.x; - x_pos = x_pos * roots[i] + 3. * p0.x - 6. * p1.x + 3. * p2.x; - x_pos = x_pos * roots[i] + -3. * p0.x + 3. * p1.x; - x_pos = x_pos * roots[i] + p0.x; + for (int i = 0; i < n_roots; i++) + { + if (roots[i] >= 0. && roots[i] <= 1.) + { + float x_pos = -p0.x + 3. * p1.x - 3. * p2.x + p3.x; + x_pos = x_pos * roots[i] + 3. * p0.x - 6. * p1.x + 3. * p2.x; + x_pos = x_pos * roots[i] + -3. * p0.x + 3. * p1.x; + x_pos = x_pos * roots[i] + p0.x; - if (x_pos > uv.x) - { - n_ints++; - } - } - } - } + if (x_pos > uv.x) + { + n_ints++; + } + } + } + } - return n_ints; + return n_ints; } bvec3 segment_sign_test(vec2 uv, vec2 p0, vec2 p1) { - p0 -= uv; - p1 -= uv; - bvec3 ret; - vec2 nor = p0 - p1; - nor = vec2(nor.y, -nor.x); + p0 -= uv; + p1 -= uv; + bvec3 ret; + vec2 nor = p0 - p1; + nor = vec2(nor.y, -nor.x); - float sgn; + float sgn; - if (p0.y > p1.y) - { - sgn = 1.; - } - else - { - sgn = -1.; - } + if (p0.y > p1.y) + { + sgn = 1.; + } + else + { + sgn = -1.; + } - if (dot(nor, p0) * sgn < 0.) - { - if (p0.y * p1.y < 0.) - ret.y = false; - else - ret.y = false; - ret.xz = bvec2(false); - } - else - { - if (p0.y * p1.y < 0.) - ret.y = true; - else - ret.y = false; - ret.xz = bvec2(true); - } - return ret; + if (dot(nor, p0) * sgn < 0.) + { + if (p0.y * p1.y < 0.) + ret.y = false; + else + ret.y = false; + ret.xz = bvec2(false); + } + else + { + if (p0.y * p1.y < 0.) + ret.y = true; + else + ret.y = false; + ret.xz = bvec2(true); + } + return ret; } bvec3 cubic_bezier_sign_test(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3) { -// if(abs(p3.y-p0.y)< 1e-4) -// { -// return segment_sign_test(uv, p0,p3); -// } + // if(abs(p3.y-p0.y)< 1e-4) + // { + // return segment_sign_test(uv, p0,p3); + // } - float cu = (-p0.y + 3. * p1.y - 3. * p2.y + p3.y); - float qu = (3. * p0.y - 6. * p1.y + 3. * p2.y); - float li = (-3. * p0.y + 3. * p1.y); - float co = p0.y - uv.y; + float cu = (-p0.y + 3. * p1.y - 3. * p2.y + p3.y); + float qu = (3. * p0.y - 6. * p1.y + 3. * p2.y); + float li = (-3. * p0.y + 3. * p1.y); + float co = p0.y - uv.y; - vec3 roots = vec3(1e38); - int n_roots = solve_cubic(vec3(co/cu,li/cu,qu/cu),roots); + vec3 roots = vec3(1e38); + int n_roots = solve_cubic(vec3(co / cu, li / cu, qu / cu), roots); - //int n_ints = 0; - bvec3 result = bvec3(false); - for(int i=0;i<3;i++){ - if(i < n_roots){ - if(roots[i] >= 0. && roots[i] <= 1.){ - float x_pos = -p0.x + 3. * p1.x - 3. * p2.x + p3.x; - x_pos = x_pos * roots[i] + 3. * p0.x - 6. * p1.x + 3. * p2.x; - x_pos = x_pos * roots[i] + -3. * p0.x + 3. * p1.x; - x_pos = x_pos * roots[i] + p0.x; + // int n_ints = 0; + bvec3 result = bvec3(false); + for (int i = 0; i < 3; i++) + { + if (i < n_roots) + { + if (roots[i] >= 0. && roots[i] <= 1.) + { + float x_pos = -p0.x + 3. * p1.x - 3. * p2.x + p3.x; + x_pos = x_pos * roots[i] + 3. * p0.x - 6. * p1.x + 3. * p2.x; + x_pos = x_pos * roots[i] + -3. * p0.x + 3. * p1.x; + x_pos = x_pos * roots[i] + p0.x; - if(x_pos > uv.x){ - result[1] = !result[1]; - } - } - } - } + if (x_pos > uv.x) + { + result[1] = !result[1]; + } + } + } + } - vec2 tang1 = p0.xy - p1.xy; - vec2 tang2 = p2.xy - p3.xy; + vec2 tang1 = p0.xy - p1.xy; + vec2 tang2 = p2.xy - p3.xy; - vec2 nor1 = vec2(tang1.y,-tang1.x); - vec2 nor2 = vec2(tang2.y,-tang2.x); + vec2 nor1 = vec2(tang1.y, -tang1.x); + vec2 nor2 = vec2(tang2.y, -tang2.x); - if(p0.y < p1.y){ - if((uv.y<=p0.y) && (dot(uv-p0.xy,nor1)>0.)){ - result[0]=!result[0]; - } - } - else{ - if(!(uv.y<=p0.y) && !(dot(uv-p0.xy,nor1)>0.)){ - result[0]=!result[0]; - } - } + if (p0.y < p1.y) + { + if ((uv.y <= p0.y) && (dot(uv - p0.xy, nor1) > 0.)) + { + result[0] = !result[0]; + } + } + else + { + if (!(uv.y <= p0.y) && !(dot(uv - p0.xy, nor1) > 0.)) + { + result[0] = !result[0]; + } + } - if(p2.y0.){ - result[2]=!result[2]; - } - } - else{ - if((uv.y<=p3.y) && !(dot(uv-p3.xy,nor2)>0.)){ - result[2]=!result[2]; - } - } - - return result; - // if(n_ints==0 || n_ints==2 || n_ints==4){ - // return 1; - // } - // else{ - // return 0; - // } + if (p2.y < p3.y) + { + if (!(uv.y <= p3.y) && dot(uv - p3.xy, nor2) > 0.) + { + result[2] = !result[2]; + } + } + else + { + if ((uv.y <= p3.y) && !(dot(uv - p3.xy, nor2) > 0.)) + { + result[2] = !result[2]; + } + } + + return result; + // if(n_ints==0 || n_ints==2 || n_ints==4){ + // return 1; + // } + // else{ + // return 0; + // } } const float eps = .000005; const int halley_iterations = 8; -//lagrange positive real root upper bound -//see for example: https://doi.org/10.1016/j.jsc.2014.09.038 -float upper_bound_lagrange5(float a0, float a1, float a2, float a3, float a4){ +// lagrange positive real root upper bound +// see for example: https://doi.org/10.1016/j.jsc.2014.09.038 +float upper_bound_lagrange5(float a0, float a1, float a2, float a3, float a4) +{ - vec4 coeffs1 = vec4(a0,a1,a2,a3); + vec4 coeffs1 = vec4(a0, a1, a2, a3); - vec4 neg1 = max(-coeffs1,vec4(0)); - float neg2 = max(-a4,0.); + vec4 neg1 = max(-coeffs1, vec4(0)); + float neg2 = max(-a4, 0.); - const vec4 indizes1 = vec4(0,1,2,3); - const float indizes2 = 4.; + const vec4 indizes1 = vec4(0, 1, 2, 3); + const float indizes2 = 4.; - vec4 bounds1 = pow(neg1,1./(5.-indizes1)); - float bounds2 = pow(neg2,1./(5.-indizes2)); + vec4 bounds1 = pow(neg1, 1. / (5. - indizes1)); + float bounds2 = pow(neg2, 1. / (5. - indizes2)); - vec2 min1_2 = min(bounds1.xz,bounds1.yw); - vec2 max1_2 = max(bounds1.xz,bounds1.yw); + vec2 min1_2 = min(bounds1.xz, bounds1.yw); + vec2 max1_2 = max(bounds1.xz, bounds1.yw); - float maxmin = max(min1_2.x,min1_2.y); - float minmax = min(max1_2.x,max1_2.y); + float maxmin = max(min1_2.x, min1_2.y); + float minmax = min(max1_2.x, max1_2.y); - float max3 = max(max1_2.x,max1_2.y); + float max3 = max(max1_2.x, max1_2.y); - float max_max = max(max3,bounds2); - float max_max2 = max(min(max3,bounds2),max(minmax,maxmin)); + float max_max = max(max3, bounds2); + float max_max2 = max(min(max3, bounds2), max(minmax, maxmin)); - return max_max + max_max2; + return max_max + max_max2; } -//lagrange upper bound applied to f(-x) to get lower bound -float lower_bound_lagrange5(float a0, float a1, float a2, float a3, float a4){ +// lagrange upper bound applied to f(-x) to get lower bound +float lower_bound_lagrange5(float a0, float a1, float a2, float a3, float a4) +{ - vec4 coeffs1 = vec4(-a0,a1,-a2,a3); + vec4 coeffs1 = vec4(-a0, a1, -a2, a3); - vec4 neg1 = max(-coeffs1,vec4(0)); - float neg2 = max(-a4,0.); + vec4 neg1 = max(-coeffs1, vec4(0)); + float neg2 = max(-a4, 0.); - const vec4 indizes1 = vec4(0,1,2,3); - const float indizes2 = 4.; + const vec4 indizes1 = vec4(0, 1, 2, 3); + const float indizes2 = 4.; - vec4 bounds1 = pow(neg1,1./(5.-indizes1)); - float bounds2 = pow(neg2,1./(5.-indizes2)); + vec4 bounds1 = pow(neg1, 1. / (5. - indizes1)); + float bounds2 = pow(neg2, 1. / (5. - indizes2)); - vec2 min1_2 = min(bounds1.xz,bounds1.yw); - vec2 max1_2 = max(bounds1.xz,bounds1.yw); + vec2 min1_2 = min(bounds1.xz, bounds1.yw); + vec2 max1_2 = max(bounds1.xz, bounds1.yw); - float maxmin = max(min1_2.x,min1_2.y); - float minmax = min(max1_2.x,max1_2.y); + float maxmin = max(min1_2.x, min1_2.y); + float minmax = min(max1_2.x, max1_2.y); - float max3 = max(max1_2.x,max1_2.y); + float max3 = max(max1_2.x, max1_2.y); - float max_max = max(max3,bounds2); - float max_max2 = max(min(max3,bounds2),max(minmax,maxmin)); + float max_max = max(max3, bounds2); + float max_max2 = max(min(max3, bounds2), max(minmax, maxmin)); - return -max_max - max_max2; + return -max_max - max_max2; } -vec2 parametric_cub_bezier(float t, vec2 p0, vec2 p1, vec2 p2, vec2 p3){ - vec2 a0 = (-p0 + 3. * p1 - 3. * p2 + p3); - vec2 a1 = (3. * p0 -6. * p1 + 3. * p2); - vec2 a2 = (-3. * p0 + 3. * p1); - vec2 a3 = p0; +vec2 parametric_cub_bezier(float t, vec2 p0, vec2 p1, vec2 p2, vec2 p3) +{ + vec2 a0 = (-p0 + 3. * p1 - 3. * p2 + p3); + vec2 a1 = (3. * p0 - 6. * p1 + 3. * p2); + vec2 a2 = (-3. * p0 + 3. * p1); + vec2 a3 = p0; - return (((a0 * t) + a1) * t + a2) * t + a3; + return (((a0 * t) + a1) * t + a2) * t + a3; } -void sort_roots3(inout vec3 roots){ - vec3 tmp; +void sort_roots3(inout vec3 roots) +{ + vec3 tmp; - tmp[0] = min(roots[0],min(roots[1],roots[2])); - tmp[1] = max(roots[0],min(roots[1],roots[2])); - tmp[2] = max(roots[0],max(roots[1],roots[2])); + tmp[0] = min(roots[0], min(roots[1], roots[2])); + tmp[1] = max(roots[0], min(roots[1], roots[2])); + tmp[2] = max(roots[0], max(roots[1], roots[2])); - roots=tmp; + roots = tmp; } -void sort_roots4(inout vec4 roots){ - vec4 tmp; +void sort_roots4(inout vec4 roots) +{ + vec4 tmp; - vec2 min1_2 = min(roots.xz,roots.yw); - vec2 max1_2 = max(roots.xz,roots.yw); + vec2 min1_2 = min(roots.xz, roots.yw); + vec2 max1_2 = max(roots.xz, roots.yw); - float maxmin = max(min1_2.x,min1_2.y); - float minmax = min(max1_2.x,max1_2.y); + float maxmin = max(min1_2.x, min1_2.y); + float minmax = min(max1_2.x, max1_2.y); - tmp[0] = min(min1_2.x,min1_2.y); - tmp[1] = min(maxmin,minmax); - tmp[2] = max(minmax,maxmin); - tmp[3] = max(max1_2.x,max1_2.y); + tmp[0] = min(min1_2.x, min1_2.y); + tmp[1] = min(maxmin, minmax); + tmp[2] = max(minmax, maxmin); + tmp[3] = max(max1_2.x, max1_2.y); - roots = tmp; + roots = tmp; } -float eval_poly5(float a0, float a1, float a2, float a3, float a4, float x){ +float eval_poly5(float a0, float a1, float a2, float a3, float a4, float x) +{ - float f = ((((x + a4) * x + a3) * x + a2) * x + a1) * x + a0; + float f = ((((x + a4) * x + a3) * x + a2) * x + a1) * x + a0; - return f; + return f; } -//halley's method -//basically a variant of newton raphson which converges quicker and has bigger basins of convergence -//see http://mathworld.wolfram.com/HalleysMethod.html -//or https://en.wikipedia.org/wiki/Halley%27s_method -float halley_iteration5(float a0, float a1, float a2, float a3, float a4, float x){ +// halley's method +// basically a variant of newton raphson which converges quicker and has bigger basins of convergence +// see http://mathworld.wolfram.com/HalleysMethod.html +// or https://en.wikipedia.org/wiki/Halley%27s_method +float halley_iteration5(float a0, float a1, float a2, float a3, float a4, float x) +{ - float f = ((((x + a4) * x + a3) * x + a2) * x + a1) * x + a0; - float f1 = (((5. * x + 4. * a4) * x + 3. * a3) * x + 2. * a2) * x + a1; - float f2 = ((20. * x + 12. * a4) * x + 6. * a3) * x + 2. * a2; + float f = ((((x + a4) * x + a3) * x + a2) * x + a1) * x + a0; + float f1 = (((5. * x + 4. * a4) * x + 3. * a3) * x + 2. * a2) * x + a1; + float f2 = ((20. * x + 12. * a4) * x + 6. * a3) * x + 2. * a2; - return x - (2. * f * f1) / (2. * f1 * f1 - f * f2); + return x - (2. * f * f1) / (2. * f1 * f1 - f * f2); } -float halley_iteration4(vec4 coeffs, float x){ +float halley_iteration4(vec4 coeffs, float x) +{ - float f = (((x + coeffs[3]) * x + coeffs[2]) * x + coeffs[1]) * x + coeffs[0]; - float f1 = ((4. * x + 3. * coeffs[3]) * x + 2. * coeffs[2]) * x + coeffs[1]; - float f2 = (12. * x + 6. * coeffs[3]) * x + 2. * coeffs[2]; + float f = (((x + coeffs[3]) * x + coeffs[2]) * x + coeffs[1]) * x + coeffs[0]; + float f1 = ((4. * x + 3. * coeffs[3]) * x + 2. * coeffs[2]) * x + coeffs[1]; + float f2 = (12. * x + 6. * coeffs[3]) * x + 2. * coeffs[2]; - return x - (2. * f * f1) / (2. * f1 * f1 - f * f2); + return x - (2. * f * f1) / (2. * f1 * f1 - f * f2); } // Modified from http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c // Credits to Doublefresh for hinting there -int solve_quartic(vec4 coeffs, inout vec4 s){ +int solve_quartic(vec4 coeffs, inout vec4 s) +{ - float a = coeffs[3]; - float b = coeffs[2]; - float c = coeffs[1]; - float d = coeffs[0]; + float a = coeffs[3]; + float b = coeffs[2]; + float c = coeffs[1]; + float d = coeffs[0]; - /* substitute x = y - A/4 to eliminate cubic term: - x^4 + px^2 + qx + r = 0 */ + /* substitute x = y - A/4 to eliminate cubic term: + x^4 + px^2 + qx + r = 0 */ - float sq_a = a * a; - float p = - 3./8. * sq_a + b; - float q = 1./8. * sq_a * a - 1./2. * a * b + c; - float r = - 3./256.*sq_a*sq_a + 1./16.*sq_a*b - 1./4.*a*c + d; + float sq_a = a * a; + float p = -3. / 8. * sq_a + b; + float q = 1. / 8. * sq_a * a - 1. / 2. * a * b + c; + float r = -3. / 256. * sq_a * sq_a + 1. / 16. * sq_a * b - 1. / 4. * a * c + d; - int num; + int num; - /* doesn't seem to happen for me */ - //if(abs(r) -eps){ - u = sqrt(abs(u)); - } - else{ - return 0; - } + if (u > -eps) + { + u = sqrt(abs(u)); + } + else + { + return 0; + } - if(v > -eps){ - v = sqrt(abs(v)); - } - else{ - return 0; - } + if (v > -eps) + { + v = sqrt(abs(v)); + } + else + { + return 0; + } - vec2 quad_coeffs; + vec2 quad_coeffs; - quad_coeffs[0] = z - u; - quad_coeffs[1] = q < 0. ? -v : v; + quad_coeffs[0] = z - u; + quad_coeffs[1] = q < 0. ? -v : v; - num = solve_quadric(quad_coeffs, s.xy); + num = solve_quadric(quad_coeffs, s.xy); - quad_coeffs[0]= z + u; - quad_coeffs[1] = q < 0. ? v : -v; + quad_coeffs[0] = z + u; + quad_coeffs[1] = q < 0. ? v : -v; - vec2 tmp=vec2(1e38); - int old_num=num; + vec2 tmp = vec2(1e38); + int old_num = num; - num += solve_quadric(quad_coeffs, tmp); - if(old_num!=num){ - if(old_num == 0){ - s[0] = tmp[0]; - s[1] = tmp[1]; - } - else{//old_num == 2 - s[2] = tmp[0]; - s[3] = tmp[1]; - } - } - } + num += solve_quadric(quad_coeffs, tmp); + if (old_num != num) + { + if (old_num == 0) + { + s[0] = tmp[0]; + s[1] = tmp[1]; + } + else + { // old_num == 2 + s[2] = tmp[0]; + s[3] = tmp[1]; + } + } + } - /* resubstitute */ + /* resubstitute */ - float sub = 1./4. * a; + float sub = 1. / 4. * a; - /* single halley iteration to fix cancellation */ - for(int i=0;i<4;i+=2){ - if(i < num){ - s[i] -= sub; - s[i] = halley_iteration4(coeffs,s[i]); + /* single halley iteration to fix cancellation */ + for (int i = 0; i < 4; i += 2) + { + if (i < num) + { + s[i] -= sub; + s[i] = halley_iteration4(coeffs, s[i]); - s[i+1] -= sub; - s[i+1] = halley_iteration4(coeffs,s[i+1]); - } - } + s[i + 1] -= sub; + s[i + 1] = halley_iteration4(coeffs, s[i + 1]); + } + } - return num; + return num; } -float cubic_bezier_dis(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool roundEnd){ +float cubic_bezier_dis(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool roundEnd) +{ - //switch points when near to end point to minimize numerical error - //only needed when control point(s) very far away - #if 0 +// switch points when near to end point to minimize numerical error +// only needed when control point(s) very far away +#if 0 vec2 mid_curve = parametric_cub_bezier(.5,p0,p1,p2,p3); vec2 mid_points = (p0 + p3)/2.; @@ -606,322 +634,355 @@ float cubic_bezier_dis(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool roundEn p2 = p1; p1 = tmp; } - #endif +#endif - vec2 a3 = (-p0 + 3. * p1 - 3. * p2 + p3); - vec2 a2 = (3. * p0 - 6. * p1 + 3. * p2); - vec2 a1 = (-3. * p0 + 3. * p1); - vec2 a0 = p0 - uv; - - //compute polynomial describing distance to current pixel dependent on a parameter t - float bc6 = dot(a3,a3); - float bc5 = 2.*dot(a3,a2); - float bc4 = dot(a2,a2) + 2.*dot(a1,a3); - float bc3 = 2.*(dot(a1,a2) + dot(a0,a3)); - float bc2 = dot(a1,a1) + 2.*dot(a0,a2); - float bc1 = 2.*dot(a0,a1); - float bc0 = dot(a0,a0); + vec2 a3 = (-p0 + 3. * p1 - 3. * p2 + p3); + vec2 a2 = (3. * p0 - 6. * p1 + 3. * p2); + vec2 a1 = (-3. * p0 + 3. * p1); + vec2 a0 = p0 - uv; - bc5 /= bc6; - bc4 /= bc6; - bc3 /= bc6; - bc2 /= bc6; - bc1 /= bc6; - bc0 /= bc6; - - //compute derivatives of this polynomial + // compute polynomial describing distance to current pixel dependent on a parameter t + float bc6 = dot(a3, a3); + float bc5 = 2. * dot(a3, a2); + float bc4 = dot(a2, a2) + 2. * dot(a1, a3); + float bc3 = 2. * (dot(a1, a2) + dot(a0, a3)); + float bc2 = dot(a1, a1) + 2. * dot(a0, a2); + float bc1 = 2. * dot(a0, a1); + float bc0 = dot(a0, a0); - float b0 = bc1 / 6.; - float b1 = 2. * bc2 / 6.; - float b2 = 3. * bc3 / 6.; - float b3 = 4. * bc4 / 6.; - float b4 = 5. * bc5 / 6.; + bc5 /= bc6; + bc4 /= bc6; + bc3 /= bc6; + bc2 /= bc6; + bc1 /= bc6; + bc0 /= bc6; - vec4 c1 = vec4(b1,2.*b2,3.*b3,4.*b4)/5.; - vec3 c2 = vec3(c1[1],2.*c1[2],3.*c1[3])/4.; - vec2 c3 = vec2(c2[1],2.*c2[2])/3.; - float c4 = c3[1]/2.; + // compute derivatives of this polynomial - vec4 roots_drv = vec4(1e38); + float b0 = bc1 / 6.; + float b1 = 2. * bc2 / 6.; + float b2 = 3. * bc3 / 6.; + float b3 = 4. * bc4 / 6.; + float b4 = 5. * bc5 / 6.; - int num_roots_drv = solve_quartic(c1,roots_drv); - sort_roots4(roots_drv); + vec4 c1 = vec4(b1, 2. * b2, 3. * b3, 4. * b4) / 5.; + vec3 c2 = vec3(c1[1], 2. * c1[2], 3. * c1[3]) / 4.; + vec2 c3 = vec2(c2[1], 2. * c2[2]) / 3.; + float c4 = c3[1] / 2.; - float ub = upper_bound_lagrange5(b0,b1,b2,b3,b4); - float lb = lower_bound_lagrange5(b0,b1,b2,b3,b4); + vec4 roots_drv = vec4(1e38); - vec3 a = vec3(1e38); - vec3 b = vec3(1e38); + int num_roots_drv = solve_quartic(c1, roots_drv); + sort_roots4(roots_drv); - vec3 roots = vec3(1e38); + float ub = upper_bound_lagrange5(b0, b1, b2, b3, b4); + float lb = lower_bound_lagrange5(b0, b1, b2, b3, b4); - int num_roots = 0; - - //compute root isolating intervals by roots of derivative and outer root bounds - //only roots going form - to + considered, because only those result in a minimum - if(num_roots_drv==4){ - if(eval_poly5(b0,b1,b2,b3,b4,roots_drv[0]) > 0.){ - a[0]=lb; - b[0]=roots_drv[0]; - num_roots=1; - } + vec3 a = vec3(1e38); + vec3 b = vec3(1e38); - if(sign(eval_poly5(b0,b1,b2,b3,b4,roots_drv[1])) != sign(eval_poly5(b0,b1,b2,b3,b4,roots_drv[2]))){ - if(num_roots == 0){ - a[0]=roots_drv[1]; - b[0]=roots_drv[2]; - num_roots=1; - } - else{ - a[1]=roots_drv[1]; - b[1]=roots_drv[2]; - num_roots=2; - } - } + vec3 roots = vec3(1e38); - if(eval_poly5(b0,b1,b2,b3,b4,roots_drv[3]) < 0.){ - if(num_roots == 0){ - a[0]=roots_drv[3]; - b[0]=ub; - num_roots=1; - } - else if(num_roots == 1){ - a[1]=roots_drv[3]; - b[1]=ub; - num_roots=2; - } - else{ - a[2]=roots_drv[3]; - b[2]=ub; - num_roots=3; - } - } - } - else{ - if(num_roots_drv==2){ - if(eval_poly5(b0,b1,b2,b3,b4,roots_drv[0]) < 0.){ - num_roots=1; - a[0]=roots_drv[1]; - b[0]=ub; - } - else if(eval_poly5(b0,b1,b2,b3,b4,roots_drv[1]) > 0.){ - num_roots=1; - a[0]=lb; - b[0]=roots_drv[0]; - } - else{ - num_roots=2; + int num_roots = 0; - a[0]=lb; - b[0]=roots_drv[0]; + // compute root isolating intervals by roots of derivative and outer root bounds + // only roots going form - to + considered, because only those result in a minimum + if (num_roots_drv == 4) + { + if (eval_poly5(b0, b1, b2, b3, b4, roots_drv[0]) > 0.) + { + a[0] = lb; + b[0] = roots_drv[0]; + num_roots = 1; + } - a[1]=roots_drv[1]; - b[1]=ub; - } + if (sign(eval_poly5(b0, b1, b2, b3, b4, roots_drv[1])) != sign(eval_poly5(b0, b1, b2, b3, b4, roots_drv[2]))) + { + if (num_roots == 0) + { + a[0] = roots_drv[1]; + b[0] = roots_drv[2]; + num_roots = 1; + } + else + { + a[1] = roots_drv[1]; + b[1] = roots_drv[2]; + num_roots = 2; + } + } - } - else{//num_roots_drv==0 - vec3 roots_snd_drv=vec3(1e38); - int num_roots_snd_drv=solve_cubic(c2,roots_snd_drv); + if (eval_poly5(b0, b1, b2, b3, b4, roots_drv[3]) < 0.) + { + if (num_roots == 0) + { + a[0] = roots_drv[3]; + b[0] = ub; + num_roots = 1; + } + else if (num_roots == 1) + { + a[1] = roots_drv[3]; + b[1] = ub; + num_roots = 2; + } + else + { + a[2] = roots_drv[3]; + b[2] = ub; + num_roots = 3; + } + } + } + else + { + if (num_roots_drv == 2) + { + if (eval_poly5(b0, b1, b2, b3, b4, roots_drv[0]) < 0.) + { + num_roots = 1; + a[0] = roots_drv[1]; + b[0] = ub; + } + else if (eval_poly5(b0, b1, b2, b3, b4, roots_drv[1]) > 0.) + { + num_roots = 1; + a[0] = lb; + b[0] = roots_drv[0]; + } + else + { + num_roots = 2; - vec2 roots_trd_drv=vec2(1e38); - int num_roots_trd_drv=solve_quadric(c3,roots_trd_drv); - num_roots=1; + a[0] = lb; + b[0] = roots_drv[0]; - a[0]=lb; - b[0]=ub; - } - - //further subdivide intervals to guarantee convergence of halley's method - //by using roots of further derivatives - vec3 roots_snd_drv=vec3(1e38); - int num_roots_snd_drv=solve_cubic(c2,roots_snd_drv); - sort_roots3(roots_snd_drv); + a[1] = roots_drv[1]; + b[1] = ub; + } + } + else + { // num_roots_drv==0 + vec3 roots_snd_drv = vec3(1e38); + int num_roots_snd_drv = solve_cubic(c2, roots_snd_drv); - int num_roots_trd_drv=0; - vec2 roots_trd_drv=vec2(1e38); + vec2 roots_trd_drv = vec2(1e38); + int num_roots_trd_drv = solve_quadric(c3, roots_trd_drv); + num_roots = 1; - if(num_roots_snd_drv!=3){ - num_roots_trd_drv=solve_quadric(c3,roots_trd_drv); - } + a[0] = lb; + b[0] = ub; + } - for(int i=0;i<3;i++){ - if(i < num_roots){ - for(int j=0;j<3;j+=2){ - if(j < num_roots_snd_drv){ - if(a[i] < roots_snd_drv[j] && b[i] > roots_snd_drv[j]){ - if(eval_poly5(b0,b1,b2,b3,b4,roots_snd_drv[j]) > 0.){ - b[i]=roots_snd_drv[j]; - } - else{ - a[i]=roots_snd_drv[j]; - } - } - } - } - for(int j=0;j<2;j++){ - if(j < num_roots_trd_drv){ - if(a[i] < roots_trd_drv[j] && b[i] > roots_trd_drv[j]){ - if(eval_poly5(b0,b1,b2,b3,b4,roots_trd_drv[j]) > 0.){ - b[i]=roots_trd_drv[j]; - } - else{ - a[i]=roots_trd_drv[j]; - } - } - } - } - } - } - } + // further subdivide intervals to guarantee convergence of halley's method + // by using roots of further derivatives + vec3 roots_snd_drv = vec3(1e38); + int num_roots_snd_drv = solve_cubic(c2, roots_snd_drv); + sort_roots3(roots_snd_drv); - float d0 = 1e38; + int num_roots_trd_drv = 0; + vec2 roots_trd_drv = vec2(1e38); - //compute roots with halley's method - - for(int i=0;i<3;i++){ - if(i < num_roots){ - roots[i] = .5 * (a[i] + b[i]); + if (num_roots_snd_drv != 3) + { + num_roots_trd_drv = solve_quadric(c3, roots_trd_drv); + } - for(int j=0;j roots_snd_drv[j]) + { + if (eval_poly5(b0, b1, b2, b3, b4, roots_snd_drv[j]) > 0.) + { + b[i] = roots_snd_drv[j]; + } + else + { + a[i] = roots_snd_drv[j]; + } + } + } + } + for (int j = 0; j < 2; j++) + { + if (j < num_roots_trd_drv) + { + if (a[i] < roots_trd_drv[j] && b[i] > roots_trd_drv[j]) + { + if (eval_poly5(b0, b1, b2, b3, b4, roots_trd_drv[j]) > 0.) + { + b[i] = roots_trd_drv[j]; + } + else + { + a[i] = roots_trd_drv[j]; + } + } + } + } + } + } + } - //compute squared distance to nearest point on curve - if(roundEnd) - { - roots[i] = clamp(roots[i],0.,1.); - vec2 to_curve = uv - parametric_cub_bezier(roots[i],p0,p1,p2,p3); - d0 = min(d0,dot(to_curve,to_curve)); - } - else - { - if(roots[i]<0.||roots[i]>1.) d0=min(d0,1e38); - else - { - vec2 to_curve = uv - parametric_cub_bezier(roots[i],p0,p1,p2,p3); - d0 = min(d0,dot(to_curve,to_curve)); - } - } - - } - } + float d0 = 1e38; - return sqrt(d0); + // compute roots with halley's method + + for (int i = 0; i < 3; i++) + { + if (i < num_roots) + { + roots[i] = .5 * (a[i] + b[i]); + + for (int j = 0; j < halley_iterations; j++) + { + roots[i] = halley_iteration5(b0, b1, b2, b3, b4, roots[i]); + } + + // compute squared distance to nearest point on curve + if (roundEnd) + { + roots[i] = clamp(roots[i], 0., 1.); + vec2 to_curve = uv - parametric_cub_bezier(roots[i], p0, p1, p2, p3); + d0 = min(d0, dot(to_curve, to_curve)); + } + else + { + if (roots[i] < 0. || roots[i] > 1.) + d0 = min(d0, 1e38); + else + { + vec2 to_curve = uv - parametric_cub_bezier(roots[i], p0, p1, p2, p3); + d0 = min(d0, dot(to_curve, to_curve)); + } + } + } + } + + return sqrt(d0); } - int cubic_bezier_int_test2(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool reverse) { - float cu = (-p0.y + 3. * p1.y - 3. * p2.y + p3.y); - float qu = (3. * p0.y - 6. * p1.y + 3. * p2.y); - float li = (-3. * p0.y + 3. * p1.y); - float co = p0.y - uv.y; + float cu = (-p0.y + 3. * p1.y - 3. * p2.y + p3.y); + float qu = (3. * p0.y - 6. * p1.y + 3. * p2.y); + float li = (-3. * p0.y + 3. * p1.y); + float co = p0.y - uv.y; - vec3 roots = vec3(1e38); - int n_roots; + vec3 roots = vec3(1e38); + int n_roots; - int n_ints = 0; + int n_ints = 0; - if (reverse? uv.x > max(max(p0.x, p1.x), max(p2.x, p3.x)): uv.x < min(min(p0.x, p1.x), min(p2.x, p3.x))) - { - if (uv.y >= min(p0.y, p3.y) && uv.y <= max(p0.y, p3.y)) - n_ints = 1; - } - else - { - if (abs(cu) < .0001) n_roots = solve_quadric(vec2(co / qu, li / qu), roots.xy); - else n_roots = solve_cubic(vec3(co / cu, li / cu, qu / cu), roots); - - for (int i = 0; i < n_roots; i++) - { - if (roots[i] >= 0. && roots[i] <= 1.) - { - float x_pos = -p0.x + 3. * p1.x - 3. * p2.x + p3.x; - x_pos = x_pos * roots[i] + 3. * p0.x - 6. * p1.x + 3. * p2.x; - x_pos = x_pos * roots[i] + -3. * p0.x + 3. * p1.x; - x_pos = x_pos * roots[i] + p0.x; + if (reverse ? uv.x > max(max(p0.x, p1.x), max(p2.x, p3.x)) : uv.x < min(min(p0.x, p1.x), min(p2.x, p3.x))) + { + if (uv.y >= min(p0.y, p3.y) && uv.y <= max(p0.y, p3.y)) + n_ints = 1; + } + else + { + if (abs(cu) < .0001) + n_roots = solve_quadric(vec2(co / qu, li / qu), roots.xy); + else + n_roots = solve_cubic(vec3(co / cu, li / cu, qu / cu), roots); - if (reverse? x_pos < uv.x: x_pos > uv.x) n_ints++; - } - } - } - return n_ints; + for (int i = 0; i < n_roots; i++) + { + if (roots[i] >= 0. && roots[i] <= 1.) + { + float x_pos = -p0.x + 3. * p1.x - 3. * p2.x + p3.x; + x_pos = x_pos * roots[i] + 3. * p0.x - 6. * p1.x + 3. * p2.x; + x_pos = x_pos * roots[i] + -3. * p0.x + 3. * p1.x; + x_pos = x_pos * roots[i] + p0.x; + + if (reverse ? x_pos < uv.x : x_pos > uv.x) + n_ints++; + } + } + } + return n_ints; } int ray_int_test(vec2 uv, vec2 p0, vec2 direction, bool reverse) { - p0 -= uv; - if (-p0.y * direction.y > 0.) - { - vec2 nor = -direction; - nor = vec2(nor.y, -nor.x); - float sgn = p0.y > direction.y? 1.: -1.; - if(reverse) sgn = -sgn; - return dot(nor, p0) * sgn < 0.? 0: 1; - } - else return 0; + p0 -= uv; + if (-p0.y * direction.y > 0.) + { + vec2 nor = -direction; + nor = vec2(nor.y, -nor.x); + float sgn = p0.y > direction.y ? 1. : -1.; + if (reverse) + sgn = -sgn; + return dot(nor, p0) * sgn < 0. ? 0 : 1; + } + else + return 0; } vec2 bezierTangent(float t, vec2 p0, vec2 p1, vec2 p2, vec2 p3) { - float u = 1 - t; - float uu = u * u; - float tu = t * u; - float tt = t * t; + float u = 1 - t; + float uu = u * u; + float tu = t * u; + float tt = t * t; - vec2 P = p0 * 3 * uu * (-1.0); - P += p1 * 3 * (uu - 2 * tu); - P += p2 * 3 * (2 * tu - tt); - P += p3 * 3 * tt; + vec2 P = p0 * 3 * uu * (-1.0); + P += p1 * 3 * (uu - 2 * tu); + P += p2 * 3 * (2 * tu - tt); + P += p3 * 3 * tt; - //返回单位向量 - return normalize(P); + // 返回单位向量 + return normalize(P); } -//判断两向量夹角(向量A逆时针到向量B)是否大于180°,大于180°返回真,否则返回假 +// 判断两向量夹角(向量A逆时针到向量B)是否大于180°,大于180°返回真,否则返回假 bool angleLargeThanPi(vec2 a, vec2 b) { - return a.x * b.y - b.x * a.y < 0; + return a.x * b.y - b.x * a.y < 0; } void drawLine(in float d, in uint styleIndex, out vec4 elementColor, out vec2 metallicRoughness) { - elementColor = vec4(1); - metallicRoughness = vec2(0.8); - uint headUint = floatBitsToUint(elementData[styleIndex+1]); + elementColor = vec4(1); + metallicRoughness = vec2(0.8); + uint headUint = floatBitsToUint(elementData[styleIndex + 1]); vec4 head = unpackUnorm4x8(headUint); - switch (int(head.a*100)%10) - //switch (2) + switch (int(head.a * 100) % 10) + // switch (2) { /// Plain case 0: { metallicRoughness = head.rg; - elementColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex+2])).rgb, 1); + elementColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex + 2])).rgb, 1); break; } /// RadialGradient case 1: { - uint size = headUint%(1<<15); - bool gradual = (headUint&(1<<15))!=0; + uint size = headUint % (1 << 15); + bool gradual = (headUint & (1 << 15)) != 0; - uint lastData = floatBitsToUint(elementData[styleIndex+2+0*2]); + uint lastData = floatBitsToUint(elementData[styleIndex + 2 + 0 * 2]); float lastLevel = 0; vec2 lastMetallicRoughness = unpackUnorm4x8(lastData).rg; - vec4 lastColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex+3+0*2])).rgb, 1); + vec4 lastColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex + 3 + 0 * 2])).rgb, 1); - for(uint i = 0; i < size; i++) + for (uint i = 0; i < size; i++) { - uint data = floatBitsToUint(elementData[styleIndex+2+i*2]); + uint data = floatBitsToUint(elementData[styleIndex + 2 + i * 2]); float level = unpackUnorm2x16(data).y; - vec4 currentColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex+3+i*2])).rgb, 1); + vec4 currentColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex + 3 + i * 2])).rgb, 1); vec2 currentMetallicRoughness = unpackUnorm4x8(data).rg; if (d <= level) { - if(gradual) + if (gradual) { - float a = (d-lastLevel)/(level-lastLevel); + float a = (d - lastLevel) / (level - lastLevel); elementColor = mix(lastColor, currentColor, a); metallicRoughness = mix(lastMetallicRoughness, currentMetallicRoughness, a); } @@ -962,315 +1023,364 @@ void drawLine(in float d, in uint styleIndex, out vec4 elementColor, out vec2 me } } -bool drawElement(uint elementIndex, vec2 localUV, vec2 scale, out vec3 color, out vec2 metallicRoughness, inout vec3 debugBVH = vec3(0)) +bool shouldFillBeginCap(vec2 localUV, bool onVeryBegin, int endType, vec2 p3, vec2 tangentBegin, vec2 tangentEndLast) { - bool hitElement = false; - vec4 elementColor = vec4(-1); - metallicRoughness = vec2(0, 0.8); - - uint currentOffset[] = elementOffset[elementIndex]; - uint elementBvhRoot = currentOffset[0]; - uint styleIndex = currentOffset[1]; - uint elementBvhLength = 0x80000000; - uint pointsOffset = currentOffset[2]; - uint linesOffset = currentOffset[3]; - - elementStack.top = 0; - uint elementBvhIndex = 0; - while (elementBvhIndex < elementBvhLength || !elementStack.empty()) - { - - while (elementBvhIndex < elementBvhLength) - { - vec4 bound = bvhBound[elementBvhRoot + elementBvhIndex]; - uint leftChild = bvhChildren[elementBvhRoot + elementBvhIndex].x; - - if (all(lessThan(bound.xy, localUV)) && all(lessThan(localUV, bound.zw))) - { - if (leftChild >= elementBvhLength) - { - if (any(greaterThan(bound.xy+vec2(0.003), localUV)) || any(greaterThan(localUV, bound.zw-vec2(0.003)))) - { - debugBVH.g = 0; - debugBVH.r += 1; - } - //uint styleIndex = bvhChildren[elementBvhRoot + elementBvhIndex].y; - //uint elementType = bvhChildren[elementBvhRoot + elementBvhIndex].y; - //float elementType = elementData[styleIndex]; - bool isFillStyle = elementData[styleIndex]<=0; - // for(int i = 0; i<200;i++) - if (isFillStyle) //面 - { - uint contourIndex = linesOffset + leftChild - 0x80000000; - - uint num_its = 0; - - uint lineCount = elementIndexs[contourIndex]; - - for ( uint contourIterator = contourIndex + 1;contourIterator < contourIndex + 1 + lineCount; contourIterator++) - { - uint lineIndex = elementIndexs[contourIterator]; - uint pLocation = linesOffset + 2 * lineIndex; - uvec4 pxIndex = uvec4(pointsOffset)+2*uvec4(elementIndexs[pLocation]>>16, elementIndexs[pLocation]&0xFFFF, elementIndexs[pLocation+1]>>16, elementIndexs[pLocation+1]&0xFFFF); - uvec4 pyIndex = uvec4(1)+pxIndex; - mat4x2 p = mat4x2(elementData[pxIndex[0]], elementData[pyIndex[0]], - elementData[pxIndex[1]], elementData[pyIndex[1]], - elementData[pxIndex[2]],elementData[pyIndex[2]], - elementData[pxIndex[3]], elementData[pyIndex[3]]); -// vec2 p[4] = {vec2(elementData[pxIndex[0]], elementData[pyIndex[0]]), -// vec2(elementData[pxIndex[1]], elementData[pyIndex[1]]), -// vec2(elementData[pxIndex[2]],elementData[pyIndex[2]]), -// vec2(elementData[pxIndex[3]], elementData[pyIndex[3]])}; -// if( bound.z==p[0].x && distance(localUV, p[3])<0.01) -// { -// debugBVH = vec3(0,0,1); -// } -// else if(distance(localUV, p[0])<0.01) -// debugBVH = vec3(1,1,1); -// if (p0 == p1 && p2 == p3) -// { -// num_its += segment_int_test(localUV, p0, p3); -// } -// -// else - num_its += cubic_bezier_int_test(localUV, p[0], p[1], p[2], p[3]); - } - - if (num_its % 2 == 1 && elementColor.a<1) - { - hitElement = true; - elementColor = vec4(1,1,0,0); - - vec4 head = unpackUnorm4x8(floatBitsToUint(elementData[styleIndex])); - if(head.z==0) - { - elementColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex+1])).rgb,0); - metallicRoughness = head.xy; - } - - - } - } - else //线 - { - float strokeWidth = elementData[styleIndex]; - float widthHeightRatio = uintBitsToFloat(currentOffset[4]); - - vec2 size = normalize(vec2(widthHeightRatio, 1)) + vec2(2 * strokeWidth); - vec2 ratio = widthHeightRatio < 1 ? vec2(widthHeightRatio, 1) : vec2(1, 1 / widthHeightRatio); - localUV *= ratio; - - - uint contourIndex = linesOffset + leftChild - 0x80000000; - float minDistance = 1e38; - uint lineCount = elementIndexs[contourIndex]; - vec4 styleHead = unpackUnorm4x8(floatBitsToUint(elementData[styleIndex+1])); - float lineType = floor(styleHead.b*10); - //float lineType = 2; - vec2 p3Last = vec2(1e38); - vec2 p2Last = vec2(1e38); - int debugBegin = 0; - for ( uint contourIterator_ = contourIndex + 1;contourIterator_ <= contourIndex + 1 + lineCount; contourIterator_++) - { - uint contourIterator = contourIterator_; - if(contourIterator_==contourIndex + 1 + lineCount) - contourIterator = contourIndex + 1; - uint lineIndex = elementIndexs[contourIterator]; - uint pLocation = linesOffset + 3 * lineIndex; - uvec4 pxIndex = uvec4(pointsOffset)+2*uvec4(elementIndexs[pLocation]>>16, elementIndexs[pLocation]&0xFFFF, elementIndexs[pLocation+1]>>16, elementIndexs[pLocation+1]&0xFFFF); - uvec4 pyIndex = uvec4(1)+pxIndex; - - mat4x2 p = mat4x2(elementData[pxIndex[0]], elementData[pyIndex[0]], - elementData[pxIndex[1]], elementData[pyIndex[1]], - elementData[pxIndex[2]], elementData[pyIndex[2]], - elementData[pxIndex[3]], elementData[pyIndex[3]]); - - p[0] *= ratio; - p[1] *= ratio; - p[2] *= ratio; - p[3] *= ratio; - - if(p[0]==p[1]&&p[2]==p[3]) - { - p[1] = (p[0]+p[3])/2; - p[2] = p[1]; - } - - if(distance(localUV,p[0])<=0.001) - { - if(p3Last==p[0]) debugBegin = 2; - else debugBegin = 1; - } - - float d = cubic_bezier_dis(localUV, p[0], p[1], p[2], p[3], true); - if(d<=strokeWidth) - { - bool onBegin = distance(localUV,p[0])<=strokeWidth&&p3Last==p[0]; - bool fill = true; - if(onBegin) - { - vec2 normalLast = normalize(mat2(0,1,-1,0)*(p3Last-p2Last)); - vec2 normalNow = normalize(mat2(0,1,-1,0)*(p[1]-p[0])); - vec2 normal = normalLast+normalNow; - fill = angleLargeThanPi(normal, localUV-p[0]); - } - if(onBegin?fill:d> 16, elementIndexs[pLocation] & 0xFFFF, + elementIndexs[pLocation + 1] >> 16, elementIndexs[pLocation + 1] & 0xFFFF); + uvec4 pyIndex = uvec4(1) + pxIndex; + mat4x2 p = + mat4x2(elementData[pxIndex[0]], elementData[pyIndex[0]], elementData[pxIndex[1]], elementData[pyIndex[1]], + elementData[pxIndex[2]], elementData[pyIndex[2]], elementData[pxIndex[3]], elementData[pyIndex[3]]); + + // if (bound.z == p[0].x && distance(localUV, p[3]) < 0.01) + // { + // debugBVH = vec3(0, 0, 1); + // } + // else if (distance(localUV, p[0]) < 0.01) + // debugBVH = vec3(1, 1, 1); + // if (p0 == p1 && p2 == p3) + // { + // num_its += segment_int_test(localUV, p0, p3); + // } + // else + num_its += cubic_bezier_int_test(localUV, p[0], p[1], p[2], p[3]); + } + + if (num_its % 2 == 1 && elementColor.a < 1) + { + hitElement = true; + elementColor = vec4(1, 1, 0, 0); + + vec4 head = unpackUnorm4x8(floatBitsToUint(elementData[styleIndex])); + if (head.z == 0) + { + elementColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex + 1])).rgb, 0); + metallicRoughness = head.xy; + } + } + return hitElement; +} + +bool strokeElement(vec2 localUV, uint contourIndex, uint linesOffset, uint pointsOffset, uint styleIndex, float widthHeightRatio, + inout vec4 elementColor, inout vec2 metallicRoughness) +{ + bool hitElement = false; + float strokeWidth = elementData[styleIndex]; + + vec2 size = normalize(vec2(widthHeightRatio, 1)) + vec2(2 * strokeWidth); + vec2 ratio = widthHeightRatio < 1 ? vec2(widthHeightRatio, 1) : vec2(1, 1 / widthHeightRatio); + localUV *= ratio; + + float minDistance = 1e38; + uint lineCount = elementIndexs[contourIndex]; + vec4 styleHead = unpackUnorm4x8(floatBitsToUint(elementData[styleIndex + 1])); + float lineType = floor(styleHead.b * 10); + int endType = int(round(styleHead.b * 100)) % 10; + vec2 p3Last = vec2(1e38); + vec2 p2Last = vec2(1e38); + vec2 tangentEndLast; + int debugBegin = 0; + for (uint contourIterator_ = contourIndex + 1; contourIterator_ < contourIndex + 1 + lineCount; contourIterator_++) + { + uint contourIterator = contourIterator_; + if (contourIterator_ == contourIndex + 1 + lineCount) + contourIterator = contourIndex + 1; + uint lineIndex = elementIndexs[contourIterator]; + uint pLocation = linesOffset + 3 * lineIndex; + vec2 percent = unpackUnorm2x16(elementIndexs[pLocation + 2]); + uvec4 pxIndex = + uvec4(pointsOffset) + 2 * uvec4(elementIndexs[pLocation] >> 16, elementIndexs[pLocation] & 0xFFFF, + elementIndexs[pLocation + 1] >> 16, elementIndexs[pLocation + 1] & 0xFFFF); + uvec4 pyIndex = uvec4(1) + pxIndex; + + mat4x2 p = + mat4x2(elementData[pxIndex[0]], elementData[pyIndex[0]], elementData[pxIndex[1]], elementData[pyIndex[1]], + elementData[pxIndex[2]], elementData[pyIndex[2]], elementData[pxIndex[3]], elementData[pyIndex[3]]); + + p[0] *= ratio; + p[1] *= ratio; + p[2] *= ratio; + p[3] *= ratio; + + if (p[0] == p[1] && p[2] == p[3]) + { + p[1] = (p[0] + p[3]) / 2; + p[2] = p[1]; + } + + if (distance(localUV, p[0]) <= 0.001) + { + if (p3Last == p[0]) + debugBegin = 2; + else + debugBegin = 1; + } + + float d = cubic_bezier_dis(localUV, p[0], p[1], p[2], p[3], true); + if (d <= strokeWidth) + { + bool onBegin = distance(localUV, p[0]) <= strokeWidth && ( p3Last == p[0] || contourIterator==contourIndex + 1); + vec2 tangentBegin; + vec2 tangentEnd; + if (p[0] != p[1]) + tangentBegin = normalize(p[0] - p[1]); + else + tangentBegin = normalize(p[0] - p[2]); + if (p[3] != p[2]) + tangentEnd = normalize(p[3] - p[2]); + else + tangentEnd = normalize(p[3] - p[1]); + + if (onBegin ? shouldFillBeginCap(localUV, percent[0]<1e-5, endType, p[0], tangentBegin, p3Last - p2Last) + : d < minDistance) + { + minDistance = min(minDistance, d); + + bool reverse = p[3].y - p[0].y < 0.; + + if (tangentBegin.y == 0.) + tangentBegin.y = reverse ? eps : -eps; + if (tangentEnd.y == 0.) + tangentEnd.y = reverse ? -eps : eps; + int intTest = cubic_bezier_int_test2(localUV, p[0], p[1], p[2], p[3], reverse) + + ray_int_test(localUV, p[0], tangentBegin, reverse) + + ray_int_test(localUV, p[3], tangentEnd, reverse); + + if (lineType == 2 || (intTest % 2 == int(lineType))) + { + hitElement = true; + // elementColor = vec4(1, 1, 0, 1); + vec2 metallicRoughness; + drawLine(minDistance / strokeWidth, styleIndex, elementColor, metallicRoughness); + } + else if (p3Last == p[0]) + hitElement = false; + } + tangentEndLast = tangentEnd; + } + p3Last = p[3]; + p2Last = p[2]; + } + if (hitElement && distance(localUV, p3Last) <= strokeWidth) + { + hitElement = shouldFillEndCap(localUV, endType, p3Last, tangentEndLast); + } + + // if (minDistance <= 0.001) + // { + // hitElement = true; + // elementColor = vec4(0, 0, 0, 1); + // if (debugBegin == 1) + // elementColor = vec4(1, 1, 0, 1); + // else if (debugBegin == 2) + // elementColor = vec4(0, 1, 0, 1); + // } + return hitElement; +} + +bool drawElement(uint elementIndex, vec2 localUV, vec2 scale, out vec3 color, out vec2 metallicRoughness, + inout vec3 debugBVH = vec3(0)) +{ + bool hitElement = false; + vec4 elementColor = vec4(-1); + metallicRoughness = vec2(0, 0.8); + + uint currentOffset[] = elementOffset[elementIndex]; + uint elementBvhRoot = currentOffset[0]; + uint styleIndex = currentOffset[1]; + uint pointsOffset = currentOffset[2]; + uint linesOffset = currentOffset[3]; + float widthHeightRatio = uintBitsToFloat(currentOffset[4]); + + elementStack.top = 0; + uint elementBvhIndex = 0; + uint elementBvhLength = 0x80000000; + while (elementBvhIndex < elementBvhLength || !elementStack.empty()) + { + while (elementBvhIndex < elementBvhLength) + { + vec4 bound = bvhBound[elementBvhRoot + elementBvhIndex]; + uint leftChild = bvhChildren[elementBvhRoot + elementBvhIndex].x; + + if (all(lessThan(bound.xy, localUV)) && all(lessThan(localUV, bound.zw))) + { + if (leftChild >= elementBvhLength) + { + if (any(greaterThan(bound.xy + vec2(0.003), localUV)) || + any(greaterThan(localUV, bound.zw - vec2(0.003)))) + { + debugBVH.g = 0; + debugBVH.r += 1; + } + + uint contourIndex = linesOffset + leftChild - 0x80000000; + bool isFillStyle = elementData[styleIndex] <= 0; + if (isFillStyle) // 面 + { + hitElement = fillElement(localUV, contourIndex, linesOffset, pointsOffset, styleIndex, + elementColor, metallicRoughness); + } + else // 线 + { + hitElement = strokeElement(localUV, contourIndex, linesOffset, pointsOffset, styleIndex, widthHeightRatio, + elementColor, metallicRoughness); + } + + elementBvhIndex = elementBvhLength; + color = elementColor.xyz; + return hitElement; + } + else + { + // debugBVH.b += 0.2; + elementStack.push(elementBvhIndex); + elementBvhIndex = leftChild; + } + } + else + elementBvhIndex = elementBvhLength; + } + if (!elementStack.empty()) + { + elementStack.getTop(elementBvhIndex); + elementStack.pop(); + elementBvhIndex = bvhChildren[elementBvhRoot + elementBvhIndex].y; + } + } + color = elementColor.xyz; + return hitElement; +} void main() { - ivec2 pixelLocation = ivec2(pixelOffset + gl_GlobalInvocationID.xy); - vec2 uv = (pixelLocation + vec2(0.5)) / imageSize(gBaseColor); - //imageStore(gBaseColor, pixelLocation, vec4(uv,1,1)); - //imageStore(gMetallicRoughness, pixelLocation, vec4(uv,1,1)); - //return; - uv = uv*2-vec2(1); - //vec2 uv = imageLoad(gPaintingTexCoord, pixelLocation).rg; + ivec2 pixelLocation = ivec2(pixelOffset + gl_GlobalInvocationID.xy); + vec2 uv = (pixelLocation + vec2(0.5)) / imageSize(gBaseColor); + // imageStore(gBaseColor, pixelLocation, vec4(uv,1,1)); + // imageStore(gMetallicRoughness, pixelLocation, vec4(uv,1,1)); + // return; + uv = uv * 2 - vec2(1); + // vec2 uv = imageLoad(gPaintingTexCoord, pixelLocation).rg; - vec3 debugBVH = vec3(0); - //bool debugHit = false; - vec4 color = vec4(1,1,1,-1); - vec2 metallicRoughness = vec2(0, 0.8); - stack.top = 0; - uint index = 0, visitTime = 0; - //uint bvhLength = paintingOffsets[paintingIndex-1].y; - uint bvhLength = 0x80000000; - while (index < bvhLength || !stack.empty()) - { - while (index < bvhLength) - { - visitTime++; - vec4 bound = bvhBound[index]; - uint leftChild = bvhChildren[index].x; - if (leftChild >= bvhLength) - { - uint zIndex = bvhChildren[index].y >> 18; - bvec2 flip = bvec2(bvhChildren[index].y & (1<<16), bvhChildren[index].y & (1<<17)); - float angle = (float(bvhChildren[index].y&((1<<16)-1)) / 65535.0) * 2 * PI; - mat2 rotation = {{cos(angle), -sin(angle)}, {sin(angle), cos(angle)}}; - vec2 localUV = uv - (bound.xy + bound.zw) / 2; - localUV = rotation * localUV; - vec2 scale = (bound.zw - bound.xy) / 2; - localUV /= scale; - if (all(lessThan(vec2(-1), localUV)) && all(lessThan(localUV, vec2(1))) && zIndex>color.w) - { - //if (any(greaterThan(bound.xy+vec2(0.005), uv)) || any(greaterThan(uv, bound.zw-vec2(0.005)))) - if (any(greaterThan(vec2(-1)+vec2(0.005), localUV)) || any(greaterThan(localUV, vec2(1)-vec2(0.005)))) - debugBVH.g += 0.3; - //uint elementIndex = leftChild - bvhLength; - //debugBVH.bg += 0.5 * (localUV + vec2(1)); + vec3 debugBVH = vec3(0); + // bool debugHit = false; + vec4 color = vec4(0.76, 0.33, 0.15, -1); + //vec4 color = vec4(1,1,1, -1); + vec2 metallicRoughness = vec2(0, 0.8); + stack.top = 0; + uint index = 0, visitTime = 0; + // uint bvhLength = paintingOffsets[paintingIndex-1].y; + uint bvhLength = 0x80000000; + while (index < bvhLength || !stack.empty()) + { + while (index < bvhLength) + { + visitTime++; + vec4 bound = bvhBound[index]; + uint leftChild = bvhChildren[index].x; + if (leftChild >= bvhLength) + { + uint zIndex = bvhChildren[index].y >> 18; + bvec2 flip = bvec2(bvhChildren[index].y & (1 << 16), bvhChildren[index].y & (1 << 17)); + float angle = (float(bvhChildren[index].y & ((1 << 16) - 1)) / 65535.0) * 2 * PI; + mat2 rotation = {{cos(angle), -sin(angle)}, {sin(angle), cos(angle)}}; + vec2 localUV = uv - (bound.xy + bound.zw) / 2; + localUV = rotation * localUV; + vec2 scale = (bound.zw - bound.xy) / 2; + localUV /= scale; + if (all(lessThan(vec2(-1), localUV)) && all(lessThan(localUV, vec2(1))) && zIndex > color.w) + { + // if (any(greaterThan(bound.xy+vec2(0.005), uv)) || any(greaterThan(uv, bound.zw-vec2(0.005)))) + if (any(greaterThan(vec2(-1) + vec2(0.005), localUV)) || + any(greaterThan(localUV, vec2(1) - vec2(0.005)))) + debugBVH.g += 0.3; + // uint elementIndex = leftChild - bvhLength; + // debugBVH.bg += 0.5 * (localUV + vec2(1)); - //debugBVH = vec3(0); - if(flip.x) localUV.x = -localUV.x; - if(flip.y) localUV.y = -localUV.y; - vec3 elementColor; - vec2 elementMetallicRoughness; - if(drawElement(leftChild - 0x80000000, localUV, scale, elementColor, elementMetallicRoughness, debugBVH)) - { - color = vec4(elementColor, zIndex); - metallicRoughness = elementMetallicRoughness; - } - - } + // debugBVH = vec3(0); + if (flip.x) + localUV.x = -localUV.x; + if (flip.y) + localUV.y = -localUV.y; + vec3 elementColor; + vec2 elementMetallicRoughness; + if (drawElement(leftChild - 0x80000000, localUV, scale, elementColor, elementMetallicRoughness, + debugBVH)) + { + color = vec4(elementColor, zIndex); + metallicRoughness = elementMetallicRoughness; + } + } - index = bvhLength; - } - else if (all(lessThan(bound.xy, uv)) && all(lessThan(uv, bound.zw))) - { - if (any(greaterThan(bound.xy+vec2(0.005), uv)) || any(greaterThan(uv, bound.zw-vec2(0.005)))) - debugBVH.g += 0.3; - //debugBVH.r += 0.02; - stack.push(index); - index = leftChild; - } - else - index = bvhLength; - } - if (!stack.empty()) - { - stack.getTop(index); - stack.pop(); - index = bvhChildren[index].y; - } - } - - imageStore(gBaseColor, pixelLocation, vec4(color.rgb,1)); - imageStore(gMetallicRoughness, pixelLocation, vec4(metallicRoughness, 0, 1)); - return; - if (/*color.a!=-1&&*/debugBVH==vec3(0)) - { - //imageStore(gBaseColor, pixelLocation, vec4(vec3(1, 1, 0),1)); - } - else - { - imageStore(gBaseColor, pixelLocation, vec4(debugBVH,1)); - imageStore(gMetallicRoughness, pixelLocation, vec4(0,0.8, 0, 1)); - } + index = bvhLength; + } + else if (all(lessThan(bound.xy, uv)) && all(lessThan(uv, bound.zw))) + { + if (any(greaterThan(bound.xy + vec2(0.005), uv)) || any(greaterThan(uv, bound.zw - vec2(0.005)))) + debugBVH.g += 0.3; + // debugBVH.r += 0.02; + stack.push(index); + index = leftChild; + } + else + index = bvhLength; + } + if (!stack.empty()) + { + stack.getTop(index); + stack.pop(); + index = bvhChildren[index].y; + } + } + imageStore(gBaseColor, pixelLocation, vec4(color.rgb, 1)); + imageStore(gMetallicRoughness, pixelLocation, vec4(metallicRoughness, 0, 1)); + return; + if (/*color.a!=-1&&*/ debugBVH == vec3(0)) + { + // imageStore(gBaseColor, pixelLocation, vec4(vec3(1, 1, 0),1)); + } + else + { + imageStore(gBaseColor, pixelLocation, vec4(debugBVH, 1)); + imageStore(gMetallicRoughness, pixelLocation, vec4(0, 0.8, 0, 1)); + } } \ No newline at end of file diff --git a/ArchitectureColoredPainting/src/Editor/util/PainterPathUtil.cpp b/ArchitectureColoredPainting/src/Editor/util/PainterPathUtil.cpp index f679dc2..e7d3b49 100644 --- a/ArchitectureColoredPainting/src/Editor/util/PainterPathUtil.cpp +++ b/ArchitectureColoredPainting/src/Editor/util/PainterPathUtil.cpp @@ -62,15 +62,15 @@ QPainterPath PainterPathUtil::monotonization(QPainterPath& painterPath) { return resPath; } -std::pair PainterPathUtil::normalized(const QPainterPath& path) +std::pair PainterPathUtil::normalized(const QPainterPath& path, float width) { auto rect = path.boundingRect(); - return { (QTransform::fromTranslate(-rect.center().x(), -rect.center().y()) * QTransform::fromScale(1 / (rect.width() / 1.999999), -1 / (rect.height() / 1.999999))).map(path), + return { (QTransform::fromTranslate(-rect.center().x(), -rect.center().y()) * QTransform::fromScale(1 / (rect.width() / 1.999999), -1 / (rect.height() / 1.999999)) * QTransform::fromScale(1 / (1 + width),1 / (1 + width))).map(path), rect.width() / rect.height() }; } -std::pair>, float> PainterPathUtil::toNormalizedLines(const QPainterPath& path) +std::pair>, float> PainterPathUtil::toNormalizedLines(const QPainterPath& path, float width) { - auto [p, ratio] = normalized(path); + auto [p, ratio] = normalized(path, width); return { transformToLines(p), ratio }; } diff --git a/ArchitectureColoredPainting/src/Editor/util/PainterPathUtil.h b/ArchitectureColoredPainting/src/Editor/util/PainterPathUtil.h index 0e0d579..f6fabcb 100644 --- a/ArchitectureColoredPainting/src/Editor/util/PainterPathUtil.h +++ b/ArchitectureColoredPainting/src/Editor/util/PainterPathUtil.h @@ -8,7 +8,7 @@ class PainterPathUtil public: static std::vector> transformToLines(const QPainterPath& painterPath); static QPainterPath monotonization(QPainterPath& painterPath); - static std::pair normalized(const QPainterPath& path); - static std::pair>, float> toNormalizedLines(const QPainterPath& path); + static std::pair normalized(const QPainterPath& path, float width = 0); + static std::pair>, float> toNormalizedLines(const QPainterPath& path, float width = 0); }; diff --git a/ArchitectureColoredPainting/src/Renderer/Model.cpp b/ArchitectureColoredPainting/src/Renderer/Model.cpp index 644863d..fc9c585 100644 --- a/ArchitectureColoredPainting/src/Renderer/Model.cpp +++ b/ArchitectureColoredPainting/src/Renderer/Model.cpp @@ -14,6 +14,7 @@ #include "../Editor/util/PainterPathUtil.h" #include "../Editor/util/SvgFileLoader.h" #include +#include "Painting/MaterialStyleStroke.h" using namespace Renderer; using std::vector; @@ -156,14 +157,13 @@ std::unique_ptr Model::processMesh(aiMesh* mesh, const aiScene* scene, for (auto& v : vertices) { - //qDebug() << v.TexCoords.x << v.TexCoords.y; v.TexCoords = (v.TexCoords - leftBottom) / (rightTop - leftBottom); - qDebug() << v.TexCoords.x << v.TexCoords.y; + //qDebug() << v.TexCoords.x << v.TexCoords.y; } - + mesh->vertices = vertices; mesh->indices = indices; - + mesh->paintingId = loadPainting(paintingPath); auto& handle = vtManager->getPaintingHandle(mesh->paintingId); mesh->textureBasecolor = handle.baseColor; @@ -271,6 +271,52 @@ GLuint Renderer::Model::loadPainting(std::string path) painting.addElement(*element[1], ElementTransform{ glm::vec2(-0.45, 0.45), glm::vec2(0.5,0.5) / 2.f, 0, glm::bvec2(false), 0 }); painting.addElement(*element[2], ElementTransform{ glm::vec2(0.50,-0.45), glm::vec2(0.6,0.7) / 2.f, 0, glm::bvec2(false), 0 }); } + else if (path == "1.json") + { + float widths[] = { 0.43, 0.43 * 0.25 / 0.15, 0.13 * 0.25 / 0.15 }; + QPainterPath painterPaths[6]; + for (int i = 0; i < 6; i++) + if (!SvgFileLoader().loadSvgFile(QString(std::format("../svg/{}.svg", i + 1).c_str()), painterPaths[i])) + qCritical() << "load error"; + + vector, float>> contours; + for (int i = 0; i < 3; i++) + { + auto [contour, ratio] = PainterPathUtil::toNormalizedLines(painterPaths[i], widths[i]); + contours.emplace_back(std::make_shared(contour), ratio); + } + class StyleStrokeRadialGradient : public Renderer::ElementStyle + { + public: + float width; + StrokeType type; + StyleStrokeRadialGradient(float width, StrokeType type) :width(width), type(type) {}; + virtual std::vector toBaseStyles() const override + { + std::map materialMap = { + {0.09, Material{QColor(255,255,255),0,0.8}}, + {0.63, Material{QColor(165,176,207),0,0.8}}, + {1.00, Material{QColor(58,64,151),0,0.8}} + }; + return { BaseStyle(std::make_shared(), + std::make_shared(width, type, StrokeEndType::kFlat, + std::make_shared(materialMap, false))) }; + } + }; + vector> style = { + std::make_shared(widths[0], StrokeType::kLeftSide), + std::make_shared(widths[1], StrokeType::kRightSide), + std::make_shared(widths[2], StrokeType::kLeftSide), + }; + vector> element = { + std::make_shared(Element{ contours[0].first, style[0], contours[0].second}), + std::make_shared(Element{ contours[1].first, style[1], contours[1].second}), + std::make_shared(Element{ contours[2].first, style[2], contours[2].second}), + }; + painting.addElement(*element[0], ElementTransform{ glm::vec2(-0.45,0.45), glm::vec2(0.25), 0, glm::bvec2(false), 0 }); + painting.addElement(*element[1], ElementTransform{ glm::vec2(-0.535,0.33), glm::vec2(0.15), 0, glm::bvec2(false), 0 }); + painting.addElement(*element[2], ElementTransform{ glm::vec2(-0.535,0.23), glm::vec2(0.15), 0, glm::bvec2(false), 0 }); + } else { for (int i = 0; i < 1000; i++) diff --git a/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleStroke.cpp b/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleStroke.cpp index 340b767..8af376d 100644 --- a/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleStroke.cpp +++ b/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleStroke.cpp @@ -104,7 +104,11 @@ std::unique_ptr Renderer::MaterialStyleStroke::clone() const bool Renderer::MaterialStyleStroke::operator==(const MaterialStyle& m) const { - return type() == m.type() && *materialStroke == *static_cast(m).materialStroke; + return type() == m.type() + && halfWidth == static_cast(m).halfWidth + && strokeType == static_cast(m).strokeType + && endType == static_cast(m).endType + && *materialStroke == *static_cast(m).materialStroke; } float Renderer::MaterialStyleStroke::getHalfWidth() const diff --git a/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleStroke.h b/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleStroke.h index db5ddbf..95f5957 100644 --- a/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleStroke.h +++ b/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleStroke.h @@ -54,7 +54,7 @@ namespace Renderer virtual std::unique_ptr clone() const override; virtual bool operator==(const MaterialStyle&) const override; float getHalfWidth() const; - //protected: + float halfWidth; StrokeType strokeType; StrokeEndType endType; diff --git a/ArchitectureColoredPainting/src/Renderer/RendererWidget.cpp b/ArchitectureColoredPainting/src/Renderer/RendererWidget.cpp index 5e777a4..d0df771 100644 --- a/ArchitectureColoredPainting/src/Renderer/RendererWidget.cpp +++ b/ArchitectureColoredPainting/src/Renderer/RendererWidget.cpp @@ -12,9 +12,11 @@ Renderer::RendererWidget::RendererWidget(QWidget* parent) auto openAction = new QAction(QStringLiteral("打开"), menu); auto saveAction = new QAction(QStringLiteral("保存"), menu); auto testAction = new QAction(QStringLiteral("测试"), menu); + auto test2Action = new QAction(QStringLiteral("测试2"), menu); menu->addAction(openAction); menu->addAction(saveAction); menu->addAction(testAction); + menu->addAction(test2Action); ui.openButton->setHaloVisible(false); ui.openButton->setOverlayStyle(::Material::TintedOverlay); @@ -47,7 +49,9 @@ Renderer::RendererWidget::RendererWidget(QWidget* parent) QObject::connect(testAction, &QAction::triggered, [&] { ui.openGLWidget->setModel("Models/Sponza/Sponza.gltf"); }); - + QObject::connect(test2Action, &QAction::triggered, [&] { + ui.openGLWidget->setModel("E:\\3D Objects\\Gate\\Gate.gltf"); + }); ui.horizontalSlider->setValue(105); ui.horizontalSlider_2->setValue(80); ui.exposureSlider->setValue(60); diff --git a/UnitTest/ElementRendererTest.cpp b/UnitTest/ElementRendererTest.cpp index 2ff2e88..1d34130 100644 --- a/UnitTest/ElementRendererTest.cpp +++ b/UnitTest/ElementRendererTest.cpp @@ -108,6 +108,27 @@ namespace UnitTest w.show(); a.exec(); } + TEST_METHOD(TestRightSideFlat) + { + QApplication a(argc, argv); + class StyleStrokeRadialGradient : public Renderer::ElementStyle + { + virtual std::vector toBaseStyles() const override + { + std::map materialMap = { + {0.20, Material{QColor(255,255,255)}}, + {0.60, Material{QColor(165,176,207)}}, + {1.00, Material{QColor(58,64,151)}} + }; + return { BaseStyle(std::make_shared(), + std::make_shared(200, StrokeType::kRightSide, StrokeEndType::kFlat, + std::make_shared(materialMap, false))) }; + } + } style; + TestGLWidget w(style); + w.show(); + a.exec(); + } }; TEST_CLASS(ElementRendererStokeMaterialTest)