mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-16 13:04:48 +00:00
wg_engine: update mesh generations and render mechanics
[issues 1479: lottie](#1479) - optimaze stroking algorithm by prevent vector normalizations - using render meshes heaps to reduce webgpu instances re-creations - using single instance of pilylines for standard operations such as trim and split - "on-the-fly" strokes generations with dash pattern - "on-the-fly" mesh objects generation in a process of path decoding - merge strokes into single mesh by each shape
This commit is contained in:
parent
398d181e3f
commit
c778f451b1
7 changed files with 383 additions and 619 deletions
|
@ -32,11 +32,11 @@ void WgMath::initialize()
|
||||||
{
|
{
|
||||||
if (initialized) return;
|
if (initialized) return;
|
||||||
initialized = true;
|
initialized = true;
|
||||||
const uint32_t nPoints = 360;
|
constexpr uint32_t nPoints = 360;
|
||||||
sinus.reserve(360);
|
sinus.reserve(nPoints);
|
||||||
cosin.reserve(360);
|
cosin.reserve(nPoints);
|
||||||
for (uint32_t i = 0; i < nPoints; i++) {
|
for (uint32_t i = 0; i < nPoints; i++) {
|
||||||
float angle = i * (2 * M_PI) / nPoints;
|
float angle = i * (2 * MATH_PI) / nPoints;
|
||||||
sinus.push(sin(angle));
|
sinus.push(sin(angle));
|
||||||
cosin.push(cos(angle));
|
cosin.push(cos(angle));
|
||||||
}
|
}
|
||||||
|
@ -54,8 +54,9 @@ void WgMath::release() {
|
||||||
|
|
||||||
WgPolyline::WgPolyline()
|
WgPolyline::WgPolyline()
|
||||||
{
|
{
|
||||||
pts.reserve(1024);
|
constexpr uint32_t nPoints = 360;
|
||||||
dist.reserve(1014);
|
pts.reserve(nPoints);
|
||||||
|
dist.reserve(nPoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -179,123 +180,78 @@ void WgPolyline::getBBox(WgPoint& pmin, WgPoint& pmax) const
|
||||||
// WgGeometryData
|
// WgGeometryData
|
||||||
//***********************************************************************
|
//***********************************************************************
|
||||||
|
|
||||||
void WgGeometryData::computeTriFansIndexes()
|
WgGeometryData::WgGeometryData() {
|
||||||
{
|
constexpr uint32_t nPoints = 10240;
|
||||||
if (positions.count <= 2) return;
|
positions.pts.reserve(nPoints);
|
||||||
indexes.reserve((positions.count - 2) * 3);
|
texCoords.reserve(nPoints);
|
||||||
for (size_t i = 0; i < positions.count - 2; i++) {
|
indexes.reserve(nPoints);
|
||||||
indexes.push(0);
|
|
||||||
indexes.push(i + 1);
|
|
||||||
indexes.push(i + 2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void WgGeometryData::computeContour(WgGeometryData* data)
|
|
||||||
{
|
|
||||||
assert(data);
|
|
||||||
assert(data->positions.count > 1);
|
|
||||||
clear();
|
|
||||||
uint32_t istart = data->getIndexMinX(); // index start
|
|
||||||
uint32_t icnt = data->positions.count; // index count
|
|
||||||
|
|
||||||
uint32_t iprev = istart == 0 ? icnt - 1 : istart - 1;
|
|
||||||
uint32_t inext = (istart + 1) % icnt; // index current
|
|
||||||
WgPoint p0 = data->positions[istart]; // current segment start
|
|
||||||
bool isIntersected = false;
|
|
||||||
bool isClockWise = !isCW(data->positions[iprev], data->positions[istart], data->positions[inext]);
|
|
||||||
|
|
||||||
uint32_t ii{}; // intersected segment index
|
|
||||||
WgPoint pi{}; // intersection point
|
|
||||||
positions.push(p0);
|
|
||||||
while(!((inext == istart) && (!isIntersected))) {
|
|
||||||
if (data->getClosestIntersection(p0, data->positions[inext], pi, ii)) {
|
|
||||||
isIntersected = true;
|
|
||||||
p0 = pi;
|
|
||||||
// operate intersection point
|
|
||||||
if (isClockWise) // clock wise behavior
|
|
||||||
inext = isCW(p0, pi, data->positions[ii]) ? ii : ((ii + 1) % icnt);
|
|
||||||
else // contr-clock wise behavior
|
|
||||||
inext = isCW(p0, pi, data->positions[ii]) ? ((ii + 1) % icnt) : ii;
|
|
||||||
} else { // simple next point
|
|
||||||
isIntersected = false;
|
|
||||||
p0 = data->positions[inext];
|
|
||||||
inext = (inext + 1) % icnt;
|
|
||||||
}
|
|
||||||
positions.push(p0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgGeometryData::appendCubic(WgPoint p1, WgPoint p2, WgPoint p3)
|
void WgGeometryData::clear()
|
||||||
{
|
{
|
||||||
WgPoint p0 = positions.count > 0 ? positions.last() : WgPoint(0.0f, 0.0f);
|
indexes.clear();
|
||||||
const size_t segs = 16;
|
positions.clear();
|
||||||
for (size_t i = 1; i <= segs; i++) {
|
texCoords.clear();
|
||||||
float t = i / (float)segs;
|
}
|
||||||
// get cubic spline interpolation coefficients
|
|
||||||
float t0 = 1 * (1.0f - t) * (1.0f - t) * (1.0f - t);
|
|
||||||
float t1 = 3 * (1.0f - t) * (1.0f - t) * t;
|
|
||||||
float t2 = 3 * (1.0f - t) * t * t;
|
|
||||||
float t3 = 1 * t * t * t;
|
|
||||||
positions.push(p0 * t0 + p1 * t1 + p2 * t2 + p3 * t3);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void WgGeometryData::appendBox(WgPoint pmin, WgPoint pmax)
|
void WgGeometryData::appendBox(WgPoint pmin, WgPoint pmax)
|
||||||
{
|
{
|
||||||
appendRect(
|
appendRect(
|
||||||
{ pmin.x, pmin.y},
|
{ pmin.x, pmin.y },
|
||||||
{ pmax.x, pmin.y},
|
{ pmax.x, pmin.y },
|
||||||
{ pmin.x, pmax.y},
|
{ pmin.x, pmax.y },
|
||||||
{ pmax.x, pmax.y});
|
{ pmax.x, pmax.y });
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgGeometryData::appendRect(WgPoint p0, WgPoint p1, WgPoint p2, WgPoint p3)
|
void WgGeometryData::appendRect(WgPoint p0, WgPoint p1, WgPoint p2, WgPoint p3)
|
||||||
{
|
{
|
||||||
uint32_t index = positions.count;
|
uint32_t index = positions.pts.count;
|
||||||
positions.push(p0); // +0
|
positions.appendPoint(p0); // +0
|
||||||
positions.push(p1); // +1
|
positions.appendPoint(p1); // +1
|
||||||
positions.push(p2); // +2
|
positions.appendPoint(p2); // +2
|
||||||
positions.push(p3); // +3
|
positions.appendPoint(p3); // +3
|
||||||
indexes.push(index + 0);
|
indexes.push(index + 0);
|
||||||
indexes.push(index + 1);
|
indexes.push(index + 1);
|
||||||
indexes.push(index + 2);
|
indexes.push(index + 2);
|
||||||
indexes.push(index + 1);
|
indexes.push(index + 1);
|
||||||
indexes.push(index + 3);
|
indexes.push(index + 3);
|
||||||
indexes.push(index + 2);
|
indexes.push(index + 2);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: optimize vertex and index count
|
|
||||||
void WgGeometryData::appendCircle(WgPoint center, float radius)
|
void WgGeometryData::appendCircle(WgPoint center, float radius)
|
||||||
{
|
{
|
||||||
uint32_t index = positions.count;
|
uint32_t indexCenter = positions.pts.count;
|
||||||
uint32_t nSegments = std::trunc(radius);
|
positions.appendPoint(center);
|
||||||
for (uint32_t i = 0; i < nSegments; i++) {
|
uint32_t index = positions.pts.count;
|
||||||
float angle0 = (float)(i + 0) / nSegments * MATH_PI * 2.0f;
|
positions.appendPoint({ center.x + gMath->sinus[0] * radius, center.y + gMath->cosin[0] * radius });
|
||||||
float angle1 = (float)(i + 1) / nSegments * MATH_PI * 2.0f;
|
uint32_t nPoints = (uint32_t)(radius * 2.0f);
|
||||||
WgPoint p0 = center + WgPoint(sin(angle0) * radius, cos(angle0) * radius);
|
nPoints = nPoints < 8 ? 8 : nPoints;
|
||||||
WgPoint p1 = center + WgPoint(sin(angle1) * radius, cos(angle1) * radius);
|
const uint32_t step = gMath->sinus.count / nPoints;
|
||||||
positions.push(center); // +0
|
for (uint32_t i = step; i < gMath->sinus.count; i += step) {
|
||||||
positions.push(p0); // +1
|
positions.appendPoint({ center.x + gMath->sinus[i] * radius, center.y + gMath->cosin[i] * radius });
|
||||||
positions.push(p1); // +2
|
indexes.push(index); // prev point
|
||||||
indexes.push(index + 0);
|
indexes.push(indexCenter); // center point
|
||||||
indexes.push(index + 1);
|
indexes.push(index + 1); // curr point
|
||||||
indexes.push(index + 2);
|
index++;
|
||||||
index += 3;
|
|
||||||
}
|
}
|
||||||
};
|
positions.appendPoint({ center.x + gMath->sinus[0] * radius, center.y + gMath->cosin[0] * radius });
|
||||||
|
indexes.push(index); // prev point
|
||||||
|
indexes.push(indexCenter); // center point
|
||||||
|
indexes.push(index + 1); // curr point
|
||||||
|
positions.appendPoint(center);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgGeometryData::appendImageBox(float w, float h)
|
void WgGeometryData::appendImageBox(float w, float h)
|
||||||
{
|
{
|
||||||
positions.push({ 0.0f, 0.0f });
|
positions.appendPoint({ 0.0f, 0.0f });
|
||||||
positions.push({ w , 0.0f });
|
positions.appendPoint({ w , 0.0f });
|
||||||
positions.push({ w , h });
|
positions.appendPoint({ w , h });
|
||||||
positions.push({ 0.0f, h });
|
positions.appendPoint({ 0.0f, h });
|
||||||
texCoords.push({ 0.0f, 0.0f });
|
texCoords.push({ 0.0f, 0.0f });
|
||||||
texCoords.push({ 1.0f, 0.0f });
|
texCoords.push({ 1.0f, 0.0f });
|
||||||
texCoords.push({ 1.0f, 1.0f });
|
texCoords.push({ 1.0f, 1.0f });
|
||||||
|
@ -306,15 +262,15 @@ void WgGeometryData::appendImageBox(float w, float h)
|
||||||
indexes.push(0);
|
indexes.push(0);
|
||||||
indexes.push(2);
|
indexes.push(2);
|
||||||
indexes.push(3);
|
indexes.push(3);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgGeometryData::appendBlitBox()
|
void WgGeometryData::appendBlitBox()
|
||||||
{
|
{
|
||||||
positions.push({ -1.0f, +1.0f });
|
positions.appendPoint({ -1.0f, +1.0f });
|
||||||
positions.push({ +1.0f, +1.0f });
|
positions.appendPoint({ +1.0f, +1.0f });
|
||||||
positions.push({ +1.0f, -1.0f });
|
positions.appendPoint({ +1.0f, -1.0f });
|
||||||
positions.push({ -1.0f, -1.0f });
|
positions.appendPoint({ -1.0f, -1.0f });
|
||||||
texCoords.push({ 0.0f, 0.0f });
|
texCoords.push({ 0.0f, 0.0f });
|
||||||
texCoords.push({ 1.0f, 0.0f });
|
texCoords.push({ 1.0f, 0.0f });
|
||||||
texCoords.push({ 1.0f, 1.0f });
|
texCoords.push({ 1.0f, 1.0f });
|
||||||
|
@ -331,13 +287,13 @@ void WgGeometryData::appendBlitBox()
|
||||||
void WgGeometryData::appendMesh(const RenderMesh* rmesh)
|
void WgGeometryData::appendMesh(const RenderMesh* rmesh)
|
||||||
{
|
{
|
||||||
assert(rmesh);
|
assert(rmesh);
|
||||||
positions.reserve(rmesh->triangleCnt * 3);
|
positions.pts.reserve(rmesh->triangleCnt * 3);
|
||||||
texCoords.reserve(rmesh->triangleCnt * 3);
|
texCoords.reserve(rmesh->triangleCnt * 3);
|
||||||
indexes.reserve(rmesh->triangleCnt * 3);
|
indexes.reserve(rmesh->triangleCnt * 3);
|
||||||
for (uint32_t i = 0; i < rmesh->triangleCnt; i++) {
|
for (uint32_t i = 0; i < rmesh->triangleCnt; i++) {
|
||||||
positions.push(rmesh->triangles[i].vertex[0].pt);
|
positions.appendPoint(rmesh->triangles[i].vertex[0].pt);
|
||||||
positions.push(rmesh->triangles[i].vertex[1].pt);
|
positions.appendPoint(rmesh->triangles[i].vertex[1].pt);
|
||||||
positions.push(rmesh->triangles[i].vertex[2].pt);
|
positions.appendPoint(rmesh->triangles[i].vertex[2].pt);
|
||||||
texCoords.push(rmesh->triangles[i].vertex[0].uv);
|
texCoords.push(rmesh->triangles[i].vertex[0].uv);
|
||||||
texCoords.push(rmesh->triangles[i].vertex[1].uv);
|
texCoords.push(rmesh->triangles[i].vertex[1].uv);
|
||||||
texCoords.push(rmesh->triangles[i].vertex[2].uv);
|
texCoords.push(rmesh->triangles[i].vertex[2].uv);
|
||||||
|
@ -345,440 +301,185 @@ void WgGeometryData::appendMesh(const RenderMesh* rmesh)
|
||||||
indexes.push(i*3 + 1);
|
indexes.push(i*3 + 1);
|
||||||
indexes.push(i*3 + 2);
|
indexes.push(i*3 + 2);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
WgPoint WgGeometryData::interpolate(float t, uint32_t& index)
|
|
||||||
{
|
|
||||||
assert(t >= 0.0f);
|
|
||||||
assert(t <= 1.0f);
|
|
||||||
// get total length
|
|
||||||
float totalLength = 0.0f;
|
|
||||||
for (uint32_t i = 0; i < positions.count - 1; i++)
|
|
||||||
totalLength += positions[i].dist(positions[i+1]);
|
|
||||||
float targetLength = totalLength * t;
|
|
||||||
// find current segment index and interpolation factor
|
|
||||||
float currentFactor = 0.0f;
|
|
||||||
float currentLength = 0.0f;
|
|
||||||
for (index = 0; index < positions.count - 1; index++) {
|
|
||||||
float segmentLength = positions[index].dist(positions[index+1]);
|
|
||||||
currentLength += segmentLength;
|
|
||||||
if(currentLength >= targetLength) {
|
|
||||||
currentFactor = 1.0f -(currentLength - targetLength) / segmentLength;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// get interpolated position and segment index
|
|
||||||
return positions[index] * (1.0f - currentFactor) + positions[index + 1] * currentFactor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
float WgGeometryData::getLength()
|
void WgGeometryData::appendStrokeDashed(const WgPolyline* polyline, const RenderStroke *stroke)
|
||||||
{
|
|
||||||
float length = 0.0f;
|
|
||||||
for (uint32_t i = 0; i < positions.count - 1; i++)
|
|
||||||
length += positions[i].dist(positions[i+1]);
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool WgGeometryData::getClosestIntersection(WgPoint p1, WgPoint p2, WgPoint& pi, uint32_t& index)
|
|
||||||
{
|
|
||||||
bool finded = false;
|
|
||||||
pi = p2;
|
|
||||||
float dist = p1.dist(p2);
|
|
||||||
for (uint32_t i = 0; i < positions.count - 1; i++) {
|
|
||||||
WgPoint p3 = positions[i+0];
|
|
||||||
WgPoint p4 = positions[i+1];
|
|
||||||
float bot = (p1.x - p2.x)*(p3.y - p4.y) - (p1.y - p2.y)*(p3.x - p4.x);
|
|
||||||
if (bot != 0) {
|
|
||||||
float top0 = (p1.x - p3.x)*(p3.y - p4.y) - (p1.y - p3.y)*(p3.x - p4.x);
|
|
||||||
float top1 = (p1.x - p2.x)*(p1.y - p3.y) - (p1.y - p2.y)*(p1.x - p3.x);
|
|
||||||
float t0 = +top0 / bot;
|
|
||||||
float t1 = -top1 / bot;
|
|
||||||
if ((t0 > 0.0f) && (t0 < 1.0f) && (t1 > 0.0f) && (t1 < 1.0f)) {
|
|
||||||
WgPoint p = { p1.x + (p2.x - p1.x) * top0 / bot, p1.y + (p2.y - p1.y) * top0 / bot };
|
|
||||||
float d = p.dist(p1);
|
|
||||||
if (d < dist) {
|
|
||||||
pi = p;
|
|
||||||
index = i;
|
|
||||||
dist = d;
|
|
||||||
finded = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return finded;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WgGeometryData::isCW(WgPoint p1, WgPoint p2, WgPoint p3)
|
|
||||||
{
|
|
||||||
WgPoint v1 = p2 - p1;
|
|
||||||
WgPoint v2 = p3 - p1;
|
|
||||||
return (v1.x*v2.y - v1.y*v2.x) < 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
uint32_t WgGeometryData::getIndexMinX()
|
|
||||||
{
|
|
||||||
assert(positions.count > 0);
|
|
||||||
uint32_t index = 0;
|
|
||||||
for (uint32_t i = 1; i < positions.count; i++)
|
|
||||||
if (positions[index].x > positions[i].x) index = i;
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
uint32_t WgGeometryData::getIndexMaxX()
|
|
||||||
{
|
|
||||||
assert(positions.count > 0);
|
|
||||||
uint32_t index = 0;
|
|
||||||
for (uint32_t i = 1; i < positions.count; i++)
|
|
||||||
if (positions[index].x < positions[i].x) index = i;
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
uint32_t WgGeometryData::getIndexMinY()
|
|
||||||
{
|
|
||||||
assert(positions.count > 0);
|
|
||||||
uint32_t index = 0;
|
|
||||||
for (uint32_t i = 1; i < positions.count; i++)
|
|
||||||
if (positions[index].y > positions[i].y) index = i;
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
uint32_t WgGeometryData::getIndexMaxY()
|
|
||||||
{
|
|
||||||
assert(positions.count > 0);
|
|
||||||
uint32_t index = 0;
|
|
||||||
for (uint32_t i = 1; i < positions.count; i++)
|
|
||||||
if (positions[index].y < positions[i].y) index = i;
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void WgGeometryData::close()
|
|
||||||
{
|
|
||||||
if (positions.count > 1) {
|
|
||||||
positions.push(positions[0]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void WgGeometryData::clear()
|
|
||||||
{
|
|
||||||
indexes.clear();
|
|
||||||
positions.clear();
|
|
||||||
texCoords.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
//***********************************************************************
|
|
||||||
// WgGeometryDataGroup
|
|
||||||
//***********************************************************************
|
|
||||||
|
|
||||||
void WgGeometryDataGroup::getBBox(WgPoint& pmin, WgPoint& pmax) {
|
|
||||||
assert(geometries.count > 0);
|
|
||||||
assert(geometries[0]->positions.count > 0);
|
|
||||||
pmin = geometries[0]->positions[0];
|
|
||||||
pmax = geometries[0]->positions[0];
|
|
||||||
for (uint32_t i = 0; i < geometries.count; i++) {
|
|
||||||
for (uint32_t j = 0; j < geometries[i]->positions.count; j++) {
|
|
||||||
pmin.x = std::min(pmin.x, geometries[i]->positions[j].x);
|
|
||||||
pmin.y = std::min(pmin.y, geometries[i]->positions[j].y);
|
|
||||||
pmax.x = std::max(pmax.x, geometries[i]->positions[j].x);
|
|
||||||
pmax.y = std::max(pmax.y, geometries[i]->positions[j].y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void WgGeometryDataGroup::tesselate(const RenderShape& rshape)
|
|
||||||
{
|
|
||||||
// windiwg fill rule
|
|
||||||
if (rshape.rule == FillRule::Winding) {
|
|
||||||
WgGeometryDataGroup polylines{};
|
|
||||||
decodePath(rshape, &polylines);
|
|
||||||
contourPolyline(&polylines, this);
|
|
||||||
} else // even-odd fill rule
|
|
||||||
if (rshape.rule == FillRule::EvenOdd) {
|
|
||||||
decodePath(rshape, this);
|
|
||||||
}
|
|
||||||
// update triangle fans indexes
|
|
||||||
for (uint32_t i = 0; i < geometries.count; i++)
|
|
||||||
geometries[i]->computeTriFansIndexes();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void WgGeometryDataGroup::stroke(const RenderShape& rshape)
|
|
||||||
{
|
|
||||||
assert(rshape.stroke);
|
|
||||||
auto strokeData = new WgGeometryData();
|
|
||||||
// decode
|
|
||||||
WgGeometryDataGroup polylines{};
|
|
||||||
decodePath(rshape, &polylines);
|
|
||||||
// trim -> split -> stroke
|
|
||||||
if ((rshape.stroke->dashPattern) && ((rshape.stroke->trim.begin != 0.0f) || (rshape.stroke->trim.end != 1.0f))) {
|
|
||||||
WgGeometryDataGroup trimmed{};
|
|
||||||
WgGeometryDataGroup splitted{};
|
|
||||||
trimPolyline(&polylines, &trimmed, rshape.stroke);
|
|
||||||
splitPolyline(&trimmed, &splitted, rshape.stroke);
|
|
||||||
strokePolyline(&splitted, strokeData, rshape.stroke);
|
|
||||||
} else // trim -> stroke
|
|
||||||
if ((rshape.stroke->trim.begin != 0.0f) || (rshape.stroke->trim.end != 1.0f)) {
|
|
||||||
WgGeometryDataGroup trimmed{};
|
|
||||||
trimPolyline(&polylines, &trimmed, rshape.stroke);
|
|
||||||
strokePolyline(&trimmed, strokeData, rshape.stroke);
|
|
||||||
} else // split -> stroke
|
|
||||||
if (rshape.stroke->dashPattern) {
|
|
||||||
WgGeometryDataGroup splitted{};
|
|
||||||
splitPolyline(&polylines, &splitted, rshape.stroke);
|
|
||||||
strokePolyline(&splitted, strokeData, rshape.stroke);
|
|
||||||
} else { // stroke
|
|
||||||
strokePolyline(&polylines, strokeData, rshape.stroke);
|
|
||||||
}
|
|
||||||
// append stroke geometry
|
|
||||||
geometries.push(strokeData);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void WgGeometryDataGroup::release()
|
|
||||||
{
|
|
||||||
for (uint32_t i = 0; i < geometries.count; i++)
|
|
||||||
delete geometries[i];
|
|
||||||
geometries.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void WgGeometryDataGroup::decodePath(const RenderShape& rshape, WgGeometryDataGroup* polyline)
|
|
||||||
{
|
|
||||||
size_t pntIndex = 0;
|
|
||||||
for (uint32_t cmdIndex = 0; cmdIndex < rshape.path.cmds.count; cmdIndex++) {
|
|
||||||
PathCommand cmd = rshape.path.cmds[cmdIndex];
|
|
||||||
if (cmd == PathCommand::MoveTo) {
|
|
||||||
polyline->geometries.push(new WgGeometryData);
|
|
||||||
auto outline = polyline->geometries.last();
|
|
||||||
outline->positions.push(rshape.path.pts[pntIndex]);
|
|
||||||
pntIndex++;
|
|
||||||
} else if (cmd == PathCommand::LineTo) {
|
|
||||||
auto outline = polyline->geometries.last();
|
|
||||||
if (outline)
|
|
||||||
outline->positions.push(rshape.path.pts[pntIndex]);
|
|
||||||
pntIndex++;
|
|
||||||
} else if (cmd == PathCommand::Close) {
|
|
||||||
auto outline = polyline->geometries.last();
|
|
||||||
if ((outline) && (outline->positions.count > 0))
|
|
||||||
outline->positions.push(outline->positions[0]);
|
|
||||||
} else if (cmd == PathCommand::CubicTo) {
|
|
||||||
auto outline = polyline->geometries.last();
|
|
||||||
if ((outline) && (outline->positions.count > 0))
|
|
||||||
outline->appendCubic(
|
|
||||||
rshape.path.pts[pntIndex + 0],
|
|
||||||
rshape.path.pts[pntIndex + 1],
|
|
||||||
rshape.path.pts[pntIndex + 2]
|
|
||||||
);
|
|
||||||
pntIndex += 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void WgGeometryDataGroup::contourPolyline(WgGeometryDataGroup* polyline, WgGeometryDataGroup* contours)
|
|
||||||
{
|
{
|
||||||
|
assert(stroke);
|
||||||
assert(polyline);
|
assert(polyline);
|
||||||
assert(contours);
|
static WgPolyline dashed;
|
||||||
for (uint32_t i = 0 ; i < polyline->geometries.count; i++) {
|
dashed.clear();
|
||||||
WgGeometryData* geometry = new WgGeometryData();
|
// append single point polyline
|
||||||
geometry->computeContour(polyline->geometries[i]);
|
if (polyline->pts.count == 1)
|
||||||
contours->geometries.push(geometry);
|
appendStroke(polyline, stroke);
|
||||||
|
// append multy points dashed polyline
|
||||||
|
else if (polyline->pts.count >= 2) {
|
||||||
|
auto& pts = polyline->pts;
|
||||||
|
auto& dist = polyline->dist;
|
||||||
|
// starting state
|
||||||
|
uint32_t dashIndex = 0;
|
||||||
|
float currentLength = stroke->dashPattern[dashIndex];
|
||||||
|
// iterate by polyline points
|
||||||
|
for (uint32_t i = 0; i < pts.count - 1; i++) {
|
||||||
|
// append current polyline point
|
||||||
|
if (dashIndex % 2 == 0)
|
||||||
|
dashed.appendPoint(pts[i]);
|
||||||
|
// move inside polyline sergemnt
|
||||||
|
while(currentLength < dist[i+1]) {
|
||||||
|
// get current point
|
||||||
|
float t = currentLength / dist[i+1];
|
||||||
|
dashed.appendPoint(pts[i] + (pts[i+1] - pts[i]) * t);
|
||||||
|
// update current state
|
||||||
|
dashIndex = (dashIndex + 1) % stroke->dashCnt;
|
||||||
|
currentLength += stroke->dashPattern[dashIndex];
|
||||||
|
// append stroke if dash
|
||||||
|
if (dashIndex % 2 != 0) {
|
||||||
|
appendStroke(&dashed, stroke);
|
||||||
|
dashed.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// update current subline length
|
||||||
|
currentLength -= dist[i+1];
|
||||||
|
}
|
||||||
|
// draw last subline
|
||||||
|
if (dashIndex % 2 == 0) {
|
||||||
|
dashed.appendPoint(pts.last());
|
||||||
|
appendStroke(&dashed, stroke);
|
||||||
|
dashed.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgGeometryDataGroup::trimPolyline(WgGeometryDataGroup* polyline, WgGeometryDataGroup* trimmed, RenderStroke *stroke)
|
void WgGeometryData::appendStroke(const WgPolyline* polyline, const RenderStroke *stroke)
|
||||||
{
|
{
|
||||||
assert(stroke);
|
assert(stroke);
|
||||||
for (uint32_t i = 0; i < polyline->geometries.count; i++) {
|
assert(polyline);
|
||||||
auto segment = new WgGeometryData;
|
|
||||||
uint32_t is = 0;
|
|
||||||
uint32_t ie = 0;
|
|
||||||
WgPoint vs = polyline->geometries[i]->interpolate(stroke->trim.begin, is);
|
|
||||||
WgPoint ve = polyline->geometries[i]->interpolate(stroke->trim.end, ie);
|
|
||||||
segment->positions.push(vs);
|
|
||||||
for (uint32_t j = is+1; j <= ie; j++)
|
|
||||||
segment->positions.push(polyline->geometries[i]->positions[j]);
|
|
||||||
segment->positions.push(ve);
|
|
||||||
trimmed->geometries.push(segment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void WgGeometryDataGroup::splitPolyline(WgGeometryDataGroup* polyline, WgGeometryDataGroup* splitted, RenderStroke *stroke)
|
|
||||||
{
|
|
||||||
assert(stroke);
|
|
||||||
for (uint32_t i = 0; i < polyline->geometries.count; i++) {
|
|
||||||
auto& vlist = polyline->geometries[i]->positions;
|
|
||||||
|
|
||||||
// append single point segment
|
|
||||||
if (vlist.count == 1) {
|
|
||||||
auto segment = new WgGeometryData;
|
|
||||||
segment->positions.push(vlist.last());
|
|
||||||
splitted->geometries.push(segment);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vlist.count >= 2) {
|
|
||||||
uint32_t icurr = 1;
|
|
||||||
uint32_t ipatt = 0;
|
|
||||||
WgPoint vcurr = vlist[0];
|
|
||||||
while (icurr < vlist.count) {
|
|
||||||
if (ipatt % 2 == 0) {
|
|
||||||
splitted->geometries.push(new WgGeometryData);
|
|
||||||
splitted->geometries.last()->positions.push(vcurr);
|
|
||||||
}
|
|
||||||
float lcurr = stroke->dashPattern[ipatt];
|
|
||||||
while ((icurr < vlist.count) && (vlist[icurr].dist(vcurr) < lcurr)) {
|
|
||||||
lcurr -= vlist[icurr].dist(vcurr);
|
|
||||||
vcurr = vlist[icurr];
|
|
||||||
icurr++;
|
|
||||||
if (ipatt % 2 == 0) splitted->geometries.last()->positions.push(vcurr);
|
|
||||||
}
|
|
||||||
if (icurr < vlist.count) {
|
|
||||||
vcurr = vcurr + (vlist[icurr] - vlist[icurr-1]).normal() * lcurr;
|
|
||||||
if (ipatt % 2 == 0) splitted->geometries.last()->positions.push(vcurr);
|
|
||||||
}
|
|
||||||
ipatt = (ipatt + 1) % stroke->dashCnt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void WgGeometryDataGroup::strokePolyline(WgGeometryDataGroup* polyline, WgGeometryData* strokes, RenderStroke *stroke)
|
|
||||||
{
|
|
||||||
float wdt = stroke->width / 2;
|
float wdt = stroke->width / 2;
|
||||||
for (uint32_t i = 0; i < polyline->geometries.count; i++) {
|
|
||||||
auto outline = polyline->geometries[i];
|
|
||||||
|
|
||||||
// single point sub-path
|
// single point sub-path
|
||||||
if (outline->positions.count == 1) {
|
if (polyline->pts.count == 1) {
|
||||||
if (stroke->cap == StrokeCap::Round) {
|
if (stroke->cap == StrokeCap::Round) {
|
||||||
strokes->appendCircle(outline->positions[0], wdt);
|
appendCircle(polyline->pts[0], wdt);
|
||||||
} else if (stroke->cap == StrokeCap::Butt) {
|
} else if (stroke->cap == StrokeCap::Butt) {
|
||||||
// for zero length sub-paths no stroke is rendered
|
// for zero length sub-paths no stroke is rendered
|
||||||
} else if (stroke->cap == StrokeCap::Square) {
|
} else if (stroke->cap == StrokeCap::Square) {
|
||||||
strokes->appendRect(
|
appendRect(
|
||||||
outline->positions[0] + WgPoint(+wdt, +wdt),
|
polyline->pts[0] + WgPoint(+wdt, +wdt),
|
||||||
outline->positions[0] + WgPoint(+wdt, -wdt),
|
polyline->pts[0] + WgPoint(+wdt, -wdt),
|
||||||
outline->positions[0] + WgPoint(-wdt, +wdt),
|
polyline->pts[0] + WgPoint(-wdt, +wdt),
|
||||||
outline->positions[0] + WgPoint(-wdt, -wdt)
|
polyline->pts[0] + WgPoint(-wdt, -wdt)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
} else
|
||||||
|
|
||||||
// single line sub-path
|
// single line sub-path
|
||||||
if (outline->positions.count == 2) {
|
if (polyline->pts.count == 2) {
|
||||||
WgPoint v0 = outline->positions[0];
|
WgPoint v0 = polyline->pts[0];
|
||||||
WgPoint v1 = outline->positions[1];
|
WgPoint v1 = polyline->pts[1];
|
||||||
WgPoint dir0 = (v1 - v0).normal();
|
WgPoint dir0 = (v1 - v0) / polyline->dist[1];
|
||||||
WgPoint nrm0 = WgPoint{ -dir0.y, +dir0.x };
|
WgPoint nrm0 = WgPoint{ -dir0.y, +dir0.x };
|
||||||
if (stroke->cap == StrokeCap::Round) {
|
if (stroke->cap == StrokeCap::Round) {
|
||||||
strokes->appendRect(
|
appendRect(v0 - nrm0 * wdt, v0 + nrm0 * wdt, v1 - nrm0 * wdt, v1 + nrm0 * wdt);
|
||||||
v0 - nrm0 * wdt, v0 + nrm0 * wdt,
|
appendCircle(polyline->pts[0], wdt);
|
||||||
v1 - nrm0 * wdt, v1 + nrm0 * wdt);
|
appendCircle(polyline->pts[1], wdt);
|
||||||
strokes->appendCircle(outline->positions[0], wdt);
|
|
||||||
strokes->appendCircle(outline->positions[1], wdt);
|
|
||||||
} else if (stroke->cap == StrokeCap::Butt) {
|
} else if (stroke->cap == StrokeCap::Butt) {
|
||||||
strokes->appendRect(
|
appendRect(v0 - nrm0 * wdt, v0 + nrm0 * wdt, v1 - nrm0 * wdt, v1 + nrm0 * wdt);
|
||||||
v0 - nrm0 * wdt, v0 + nrm0 * wdt,
|
|
||||||
v1 - nrm0 * wdt, v1 + nrm0 * wdt
|
|
||||||
);
|
|
||||||
} else if (stroke->cap == StrokeCap::Square) {
|
} else if (stroke->cap == StrokeCap::Square) {
|
||||||
strokes->appendRect(
|
appendRect(
|
||||||
v0 - nrm0 * wdt - dir0 * wdt, v0 + nrm0 * wdt - dir0 * wdt,
|
v0 - nrm0 * wdt - dir0 * wdt, v0 + nrm0 * wdt - dir0 * wdt,
|
||||||
v1 - nrm0 * wdt + dir0 * wdt, v1 + nrm0 * wdt + dir0 * wdt
|
v1 - nrm0 * wdt + dir0 * wdt, v1 + nrm0 * wdt + dir0 * wdt
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
} else
|
||||||
|
|
||||||
// multi-lined sub-path
|
// multi-lined sub-path
|
||||||
if (outline->positions.count > 2) {
|
if (polyline->pts.count > 2) {
|
||||||
|
if (polyline->isClosed()) {
|
||||||
|
if (stroke->join == StrokeJoin::Round) {
|
||||||
|
appendCircle(polyline->pts[0], wdt);
|
||||||
|
} else {
|
||||||
|
float dist0 = polyline->dist.last();
|
||||||
|
float dist1 = polyline->dist[1];
|
||||||
|
WgPoint v0 = polyline->pts[polyline->pts.count - 2];
|
||||||
|
WgPoint v1 = polyline->pts[0];
|
||||||
|
WgPoint v2 = polyline->pts[1];
|
||||||
|
WgPoint dir0 = (v1 - v0) / dist0;
|
||||||
|
WgPoint dir1 = (v2 - v1) / dist1;
|
||||||
|
WgPoint nrm0 { +dir0.y, -dir0.x };
|
||||||
|
WgPoint nrm1 { +dir1.y, -dir1.x };
|
||||||
|
WgPoint offset0 = nrm0 * wdt;
|
||||||
|
WgPoint offset1 = nrm1 * wdt;
|
||||||
|
if (stroke->join == StrokeJoin::Bevel) {
|
||||||
|
appendRect(v1 - offset0, v1 + offset0, v1 - offset1, v1 + offset1);
|
||||||
|
} else if (stroke->join == StrokeJoin::Miter) {
|
||||||
|
WgPoint nrm = (nrm0 + nrm1).normal();
|
||||||
|
float cosine = nrm.dot(nrm0);
|
||||||
|
if ((cosine != 0.0f) && (abs(cosine) != 1.0f) && (abs(wdt / cosine) <= stroke->miterlimit * 2)) {
|
||||||
|
appendRect(v1 + nrm * (wdt / cosine), v1 + offset0, v1 + offset1, v1 - nrm * (wdt / cosine));
|
||||||
|
} else {
|
||||||
|
appendRect(v1 - offset0, v1 + offset0, v1 - offset1, v1 + offset1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// append first cap
|
// append first cap
|
||||||
WgPoint v0 = outline->positions[0];
|
WgPoint v0 = polyline->pts[0];
|
||||||
WgPoint v1 = outline->positions[1];
|
WgPoint v1 = polyline->pts[1];
|
||||||
WgPoint dir0 = (v1 - v0).normal();
|
WgPoint dir0 = (v1 - v0) / polyline->dist[1];
|
||||||
WgPoint nrm0 = WgPoint{ -dir0.y, +dir0.x };
|
WgPoint nrm0 = WgPoint{ -dir0.y, +dir0.x };
|
||||||
if (stroke->cap == StrokeCap::Round) {
|
if (stroke->cap == StrokeCap::Round) {
|
||||||
strokes->appendCircle(v0, wdt);
|
appendCircle(v0, wdt);
|
||||||
} else if (stroke->cap == StrokeCap::Butt) {
|
} else if (stroke->cap == StrokeCap::Butt) {
|
||||||
// no cap needed
|
// no cap needed
|
||||||
} else if (stroke->cap == StrokeCap::Square) {
|
} else if (stroke->cap == StrokeCap::Square) {
|
||||||
strokes->appendRect(
|
appendRect(v0 - nrm0 * wdt - dir0 * wdt, v0 + nrm0 * wdt - dir0 * wdt, v0 - nrm0 * wdt, v0 + nrm0 * wdt);
|
||||||
v0 - nrm0 * wdt - dir0 * wdt,
|
|
||||||
v0 + nrm0 * wdt - dir0 * wdt,
|
|
||||||
v0 - nrm0 * wdt,
|
|
||||||
v0 + nrm0 * wdt
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// append last cap
|
// append last cap
|
||||||
v0 = outline->positions[outline->positions.count - 2];
|
v0 = polyline->pts[polyline->pts.count - 2];
|
||||||
v1 = outline->positions[outline->positions.count - 1];
|
v1 = polyline->pts[polyline->pts.count - 1];
|
||||||
dir0 = (v1 - v0).normal();
|
dir0 = (v1 - v0) / polyline->dist[polyline->pts.count - 1];
|
||||||
nrm0 = WgPoint{ -dir0.y, +dir0.x };
|
nrm0 = WgPoint{ -dir0.y, +dir0.x };
|
||||||
if (stroke->cap == StrokeCap::Round) {
|
if (stroke->cap == StrokeCap::Round) {
|
||||||
strokes->appendCircle(v1, wdt);
|
appendCircle(v1, wdt);
|
||||||
} else if (stroke->cap == StrokeCap::Butt) {
|
} else if (stroke->cap == StrokeCap::Butt) {
|
||||||
// no cap needed
|
// no cap needed
|
||||||
} else if (stroke->cap == StrokeCap::Square) {
|
} else if (stroke->cap == StrokeCap::Square) {
|
||||||
strokes->appendRect(
|
appendRect(v1 - nrm0 * wdt, v1 + nrm0 * wdt, v1 - nrm0 * wdt + dir0 * wdt, v1 + nrm0 * wdt + dir0 * wdt);
|
||||||
v1 - nrm0 * wdt,
|
}
|
||||||
v1 + nrm0 * wdt,
|
|
||||||
v1 - nrm0 * wdt + dir0 * wdt,
|
|
||||||
v1 + nrm0 * wdt + dir0 * wdt
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// append sub-lines
|
// append sub-lines
|
||||||
for (uint32_t j = 0; j < outline->positions.count - 1; j++) {
|
for (uint32_t i = 0; i < polyline->pts.count - 1; i++) {
|
||||||
WgPoint v0 = outline->positions[j + 0];
|
float dist1 = polyline->dist[i + 1];
|
||||||
WgPoint v1 = outline->positions[j + 1];
|
WgPoint v1 = polyline->pts[i + 0];
|
||||||
WgPoint dir = (v1 - v0).normal();
|
WgPoint v2 = polyline->pts[i + 1];
|
||||||
WgPoint nrm { -dir.y, +dir.x };
|
WgPoint dir1 = (v2 - v1) / dist1;
|
||||||
strokes->appendRect(
|
|
||||||
v0 - nrm * wdt,
|
|
||||||
v0 + nrm * wdt,
|
|
||||||
v1 - nrm * wdt,
|
|
||||||
v1 + nrm * wdt
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// append joints (TODO: separate by joint types)
|
|
||||||
for (uint32_t j = 1; j < outline->positions.count - 1; j++) {
|
|
||||||
WgPoint v0 = outline->positions[j - 1];
|
|
||||||
WgPoint v1 = outline->positions[j + 0];
|
|
||||||
WgPoint v2 = outline->positions[j + 1];
|
|
||||||
WgPoint dir0 = (v1 - v0).normal();
|
|
||||||
WgPoint dir1 = (v1 - v2).normal();
|
|
||||||
WgPoint nrm0 { -dir0.y, +dir0.x };
|
|
||||||
WgPoint nrm1 { +dir1.y, -dir1.x };
|
WgPoint nrm1 { +dir1.y, -dir1.x };
|
||||||
|
WgPoint offset1 = nrm1 * wdt;
|
||||||
|
appendRect(v1 - offset1, v1 + offset1, v2 - offset1, v2 + offset1);
|
||||||
|
|
||||||
|
if (i > 0) {
|
||||||
if (stroke->join == StrokeJoin::Round) {
|
if (stroke->join == StrokeJoin::Round) {
|
||||||
strokes->appendCircle(v1, wdt);
|
appendCircle(v1, wdt);
|
||||||
} else if (stroke->join == StrokeJoin::Bevel) {
|
|
||||||
strokes->appendRect(
|
|
||||||
v1 - nrm0 * wdt, v1 - nrm1 * wdt,
|
|
||||||
v1 + nrm1 * wdt, v1 + nrm0 * wdt
|
|
||||||
);
|
|
||||||
} else if (stroke->join == StrokeJoin::Miter) {
|
|
||||||
WgPoint nrm = (dir0 + dir1).normal();
|
|
||||||
float cosine = nrm.dot(nrm0);
|
|
||||||
if ((cosine != 0.0f) && (abs(wdt / cosine) <= stroke->miterlimit * 2)) {
|
|
||||||
strokes->appendRect(v1 + nrm * (wdt / cosine), v1 + nrm0 * wdt, v1 + nrm1 * wdt, v1);
|
|
||||||
strokes->appendRect(v1 - nrm * (wdt / cosine), v1 - nrm0 * wdt, v1 - nrm1 * wdt, v1);
|
|
||||||
} else {
|
} else {
|
||||||
strokes->appendRect(
|
float dist0 = polyline->dist[i + 0];
|
||||||
v1 - nrm0 * wdt, v1 - nrm1 * wdt,
|
WgPoint v0 = polyline->pts[i - 1];
|
||||||
v1 + nrm1 * wdt, v1 + nrm0 * wdt);
|
WgPoint dir0 = (v1 - v0) / dist0;
|
||||||
|
WgPoint nrm0 { +dir0.y, -dir0.x };
|
||||||
|
WgPoint offset0 = nrm0 * wdt;
|
||||||
|
if (stroke->join == StrokeJoin::Bevel) {
|
||||||
|
appendRect(v1 - offset0, v1 + offset0, v1 - offset1, v1 + offset1);
|
||||||
|
} else if (stroke->join == StrokeJoin::Miter) {
|
||||||
|
WgPoint nrm = (nrm0 + nrm1).normal();
|
||||||
|
float cosine = nrm.dot(nrm0);
|
||||||
|
if ((cosine != 0.0f) && (abs(cosine) != 1.0f) && (abs(wdt / cosine) <= stroke->miterlimit * 2)) {
|
||||||
|
appendRect(v1 + nrm * (wdt / cosine), v1 + offset0, v1 + offset1, v1);
|
||||||
|
appendRect(v1 - nrm * (wdt / cosine), v1 - offset0, v1 - offset1, v1);
|
||||||
|
} else {
|
||||||
|
appendRect(v1 - offset0, v1 + offset0, v1 - offset1, v1 + offset1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,6 @@ public:
|
||||||
inline WgPoint lerp(const WgPoint& p, float t) const { return { x + (p.x - x) * t, y + (p.y - y) * t }; };
|
inline WgPoint lerp(const WgPoint& p, float t) const { return { x + (p.x - x) * t, y + (p.y - y) * t }; };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct WgMath
|
struct WgMath
|
||||||
{
|
{
|
||||||
Array<float> sinus;
|
Array<float> sinus;
|
||||||
|
@ -102,14 +101,12 @@ struct WgGeometryData
|
||||||
{
|
{
|
||||||
static WgMath* gMath;
|
static WgMath* gMath;
|
||||||
|
|
||||||
Array<WgPoint> positions{};
|
WgPolyline positions{};
|
||||||
Array<WgPoint> texCoords{};
|
Array<WgPoint> texCoords{};
|
||||||
Array<uint32_t> indexes{};
|
Array<uint32_t> indexes{};
|
||||||
|
|
||||||
// webgpu did not support triangle fans primitives type
|
WgGeometryData();
|
||||||
// so we can emulate triangle fans using indexing
|
void clear();
|
||||||
void computeTriFansIndexes();
|
|
||||||
void computeContour(WgGeometryData* data);
|
|
||||||
|
|
||||||
void appendCubic(WgPoint p1, WgPoint p2, WgPoint p3);
|
void appendCubic(WgPoint p1, WgPoint p2, WgPoint p3);
|
||||||
void appendBox(WgPoint pmin, WgPoint pmax);
|
void appendBox(WgPoint pmin, WgPoint pmax);
|
||||||
|
@ -118,37 +115,8 @@ struct WgGeometryData
|
||||||
void appendImageBox(float w, float h);
|
void appendImageBox(float w, float h);
|
||||||
void appendBlitBox();
|
void appendBlitBox();
|
||||||
void appendMesh(const RenderMesh* rmesh);
|
void appendMesh(const RenderMesh* rmesh);
|
||||||
|
void appendStrokeDashed(const WgPolyline* polyline, const RenderStroke *stroke);
|
||||||
WgPoint interpolate(float t, uint32_t& ind); // t = [0;1]
|
void appendStroke(const WgPolyline* polyline, const RenderStroke *stroke);
|
||||||
|
|
||||||
float getLength();
|
|
||||||
bool getClosestIntersection(WgPoint p1, WgPoint p2, WgPoint& pi, uint32_t& index);
|
|
||||||
bool isCW(WgPoint p1, WgPoint p2, WgPoint p3);
|
|
||||||
|
|
||||||
uint32_t getIndexMinX();
|
|
||||||
uint32_t getIndexMaxX();
|
|
||||||
uint32_t getIndexMinY();
|
|
||||||
uint32_t getIndexMaxY();
|
|
||||||
|
|
||||||
void close();
|
|
||||||
void clear();
|
|
||||||
};
|
|
||||||
|
|
||||||
struct WgGeometryDataGroup
|
|
||||||
{
|
|
||||||
Array<WgGeometryData*> geometries{};
|
|
||||||
virtual ~WgGeometryDataGroup() { release(); }
|
|
||||||
|
|
||||||
void getBBox(WgPoint& pmin, WgPoint& pmax);
|
|
||||||
void tesselate(const RenderShape& rshape);
|
|
||||||
void stroke(const RenderShape& rshape);
|
|
||||||
void release();
|
|
||||||
private:
|
|
||||||
static void decodePath(const RenderShape& rshape, WgGeometryDataGroup* polyline);
|
|
||||||
static void contourPolyline(WgGeometryDataGroup* polyline, WgGeometryDataGroup* contours);
|
|
||||||
static void trimPolyline(WgGeometryDataGroup* polyline, WgGeometryDataGroup* trimmed, RenderStroke *stroke);
|
|
||||||
static void splitPolyline(WgGeometryDataGroup* polyline, WgGeometryDataGroup* splitted, RenderStroke *stroke);
|
|
||||||
static void strokePolyline(WgGeometryDataGroup* polyline, WgGeometryData* strokes, RenderStroke *stroke);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _TVG_WG_GEOMETRY_H_
|
#endif // _TVG_WG_GEOMETRY_H_
|
|
@ -30,7 +30,7 @@
|
||||||
// WgMeshData
|
// WgMeshData
|
||||||
//***********************************************************************
|
//***********************************************************************
|
||||||
|
|
||||||
void WgMeshData::draw(WGPURenderPassEncoder renderPassEncoder)
|
void WgMeshData::draw(WgContext& context, WGPURenderPassEncoder renderPassEncoder)
|
||||||
{
|
{
|
||||||
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, bufferPosition, 0, vertexCount * sizeof(float) * 2);
|
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, bufferPosition, 0, vertexCount * sizeof(float) * 2);
|
||||||
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, bufferIndex, WGPUIndexFormat_Uint32, 0, indexCount * sizeof(uint32_t));
|
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, bufferIndex, WGPUIndexFormat_Uint32, 0, indexCount * sizeof(uint32_t));
|
||||||
|
@ -38,7 +38,15 @@ void WgMeshData::draw(WGPURenderPassEncoder renderPassEncoder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgMeshData::drawImage(WGPURenderPassEncoder renderPassEncoder)
|
void WgMeshData::drawFan(WgContext& context, WGPURenderPassEncoder renderPassEncoder)
|
||||||
|
{
|
||||||
|
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, bufferPosition, 0, vertexCount * sizeof(float) * 2);
|
||||||
|
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, context.indexBufferFan, WGPUIndexFormat_Uint32, 0, indexCount * sizeof(uint32_t));
|
||||||
|
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, indexCount, 1, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WgMeshData::drawImage(WgContext& context, WGPURenderPassEncoder renderPassEncoder)
|
||||||
{
|
{
|
||||||
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, bufferPosition, 0, vertexCount * sizeof(float) * 2);
|
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, bufferPosition, 0, vertexCount * sizeof(float) * 2);
|
||||||
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 1, bufferTexCoord, 0, vertexCount * sizeof(float) * 2);
|
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 1, bufferTexCoord, 0, vertexCount * sizeof(float) * 2);
|
||||||
|
@ -47,13 +55,25 @@ void WgMeshData::drawImage(WGPURenderPassEncoder renderPassEncoder)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
void WgMeshData::update(WgContext& context, WgGeometryData* geometryData){
|
void WgMeshData::update(WgContext& context, const WgPolyline* polyline)
|
||||||
|
{
|
||||||
|
assert(polyline);
|
||||||
|
assert(polyline->pts.count > 2);
|
||||||
|
vertexCount = polyline->pts.count;
|
||||||
|
indexCount = (polyline->pts.count - 2) * 3;
|
||||||
|
context.allocateVertexBuffer(bufferPosition, &polyline->pts[0], vertexCount * sizeof(float) * 2);
|
||||||
|
context.allocateIndexBufferFan(vertexCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WgMeshData::update(WgContext& context, const WgGeometryData* geometryData)
|
||||||
|
{
|
||||||
assert(geometryData);
|
assert(geometryData);
|
||||||
vertexCount = geometryData->positions.count;
|
vertexCount = geometryData->positions.pts.count;
|
||||||
indexCount = geometryData->indexes.count;
|
indexCount = geometryData->indexes.count;
|
||||||
// buffer position data create and write
|
// buffer position data create and write
|
||||||
if (geometryData->positions.count > 0)
|
if (geometryData->positions.pts.count > 0)
|
||||||
context.allocateVertexBuffer(bufferPosition, &geometryData->positions[0], vertexCount * sizeof(float) * 2);
|
context.allocateVertexBuffer(bufferPosition, &geometryData->positions.pts[0], vertexCount * sizeof(float) * 2);
|
||||||
// buffer tex coords data create and write
|
// buffer tex coords data create and write
|
||||||
if (geometryData->texCoords.count > 0)
|
if (geometryData->texCoords.count > 0)
|
||||||
context.allocateVertexBuffer(bufferTexCoord, &geometryData->texCoords[0], vertexCount * sizeof(float) * 2);
|
context.allocateVertexBuffer(bufferTexCoord, &geometryData->texCoords[0], vertexCount * sizeof(float) * 2);
|
||||||
|
@ -63,6 +83,19 @@ void WgMeshData::update(WgContext& context, WgGeometryData* geometryData){
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void WgMeshData::update(WgContext& context, const WgPoint pmin, const WgPoint pmax)
|
||||||
|
{
|
||||||
|
vertexCount = 4;
|
||||||
|
indexCount = 6;
|
||||||
|
const float data[] = {
|
||||||
|
pmin.x, pmin.y, pmax.x, pmin.y,
|
||||||
|
pmax.x, pmax.y, pmin.x, pmax.y
|
||||||
|
};
|
||||||
|
context.allocateVertexBuffer(bufferPosition, data, sizeof(data));
|
||||||
|
context.allocateIndexBufferFan(vertexCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgMeshData::release(WgContext& context)
|
void WgMeshData::release(WgContext& context)
|
||||||
{
|
{
|
||||||
context.releaseBuffer(bufferIndex);
|
context.releaseBuffer(bufferIndex);
|
||||||
|
@ -105,29 +138,41 @@ void WgMeshDataPool::release(WgContext& context)
|
||||||
mList.clear();
|
mList.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
WgMeshDataPool* WgMeshDataGroup::MeshDataPool = nullptr;
|
WgMeshDataPool* WgMeshDataGroup::gMeshDataPool = nullptr;
|
||||||
|
|
||||||
//***********************************************************************
|
//***********************************************************************
|
||||||
// WgMeshDataGroup
|
// WgMeshDataGroup
|
||||||
//***********************************************************************
|
//***********************************************************************
|
||||||
|
|
||||||
void WgMeshDataGroup::update(WgContext& context, WgGeometryDataGroup* geometryDataGroup)
|
void WgMeshDataGroup::append(WgContext& context, const WgPolyline* polyline)
|
||||||
{
|
{
|
||||||
release(context);
|
assert(polyline);
|
||||||
assert(geometryDataGroup);
|
assert(polyline->pts.count >= 3);
|
||||||
for (uint32_t i = 0; i < geometryDataGroup->geometries.count; i++) {
|
meshes.push(gMeshDataPool->allocate(context));
|
||||||
if (geometryDataGroup->geometries[i]->positions.count > 2) {
|
meshes.last()->update(context, polyline);
|
||||||
meshes.push(MeshDataPool->allocate(context));
|
}
|
||||||
meshes.last()->update(context, geometryDataGroup->geometries[i]);
|
|
||||||
}
|
|
||||||
}
|
void WgMeshDataGroup::append(WgContext& context, const WgGeometryData* geometryData)
|
||||||
};
|
{
|
||||||
|
assert(geometryData);
|
||||||
|
assert(geometryData->positions.pts.count >= 3);
|
||||||
|
meshes.push(gMeshDataPool->allocate(context));
|
||||||
|
meshes.last()->update(context, geometryData);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WgMeshDataGroup::append(WgContext& context, const WgPoint pmin, const WgPoint pmax)
|
||||||
|
{
|
||||||
|
meshes.push(gMeshDataPool->allocate(context));
|
||||||
|
meshes.last()->update(context, pmin, pmax);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgMeshDataGroup::release(WgContext& context)
|
void WgMeshDataGroup::release(WgContext& context)
|
||||||
{
|
{
|
||||||
for (uint32_t i = 0; i < meshes.count; i++)
|
for (uint32_t i = 0; i < meshes.count; i++)
|
||||||
MeshDataPool->free(context, meshes[i]);
|
gMeshDataPool->free(context, meshes[i]);
|
||||||
meshes.clear();
|
meshes.clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -223,44 +268,85 @@ void WgRenderDataShape::updateMeshes(WgContext &context, const RenderShape &rsha
|
||||||
{
|
{
|
||||||
releaseMeshes(context);
|
releaseMeshes(context);
|
||||||
strokeFirst = false;
|
strokeFirst = false;
|
||||||
// update shapes geometry
|
|
||||||
WgGeometryDataGroup shapes;
|
static WgPolyline polyline;
|
||||||
shapes.tesselate(rshape);
|
polyline.clear();
|
||||||
meshGroupShapes.update(context, &shapes);
|
// decode path
|
||||||
// update shapes bbox
|
size_t pntIndex = 0;
|
||||||
|
for (uint32_t cmdIndex = 0; cmdIndex < rshape.path.cmds.count; cmdIndex++) {
|
||||||
|
PathCommand cmd = rshape.path.cmds[cmdIndex];
|
||||||
|
if (cmd == PathCommand::MoveTo) {
|
||||||
|
// proceed current polyline
|
||||||
|
updateMeshes(context, &polyline, rshape.stroke);
|
||||||
|
polyline.clear();
|
||||||
|
polyline.appendPoint(rshape.path.pts[pntIndex]);
|
||||||
|
pntIndex++;
|
||||||
|
} else if (cmd == PathCommand::LineTo) {
|
||||||
|
polyline.appendPoint(rshape.path.pts[pntIndex]);
|
||||||
|
pntIndex++;
|
||||||
|
} else if (cmd == PathCommand::Close) {
|
||||||
|
polyline.close();
|
||||||
|
} else if (cmd == PathCommand::CubicTo) {
|
||||||
|
polyline.appendCubic(
|
||||||
|
rshape.path.pts[pntIndex + 0],
|
||||||
|
rshape.path.pts[pntIndex + 1],
|
||||||
|
rshape.path.pts[pntIndex + 2]);
|
||||||
|
pntIndex += 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// proceed last polyline
|
||||||
|
updateMeshes(context, &polyline, rshape.stroke);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WgRenderDataShape::updateMeshes(WgContext& context, const WgPolyline* polyline, const RenderStroke* rstroke)
|
||||||
|
{
|
||||||
|
assert(polyline);
|
||||||
|
// generate fill geometry
|
||||||
|
if (polyline->pts.count >= 3) {
|
||||||
WgPoint pmin{}, pmax{};
|
WgPoint pmin{}, pmax{};
|
||||||
shapes.getBBox(pmin, pmax);
|
polyline->getBBox(pmin, pmax);
|
||||||
WgGeometryData box;
|
meshGroupShapes.append(context, polyline);
|
||||||
box.appendBox(pmin, pmax);
|
meshGroupShapesBBox.append(context, pmin, pmax);
|
||||||
meshBBoxShapes.update(context, &box);
|
}
|
||||||
// update strokes geometry
|
// generate strokes geometry
|
||||||
if (rshape.stroke) {
|
if ((polyline->pts.count >= 1) && rstroke) {
|
||||||
strokeFirst = rshape.stroke->strokeFirst;
|
static WgGeometryData geometryData; geometryData.clear();
|
||||||
WgGeometryDataGroup strokes;
|
static WgPolyline trimmed;
|
||||||
strokes.stroke(rshape);
|
// trim -> split -> stroke
|
||||||
strokes.getBBox(pmin, pmax);
|
if ((rstroke->dashPattern) && ((rstroke->trim.begin != 0.0f) || (rstroke->trim.end != 1.0f))) {
|
||||||
meshGroupStrokes.update(context, &strokes);
|
polyline->trim(&trimmed, rstroke->trim.begin, rstroke->trim.end);
|
||||||
// update strokes bbox
|
geometryData.appendStrokeDashed(&trimmed, rstroke);
|
||||||
|
} else // trim -> stroke
|
||||||
|
if ((rstroke->trim.begin != 0.0f) || (rstroke->trim.end != 1.0f)) {
|
||||||
|
polyline->trim(&trimmed, rstroke->trim.begin, rstroke->trim.end);
|
||||||
|
geometryData.appendStroke(&trimmed, rstroke);
|
||||||
|
} else // split -> stroke
|
||||||
|
if (rstroke->dashPattern) {
|
||||||
|
geometryData.appendStrokeDashed(polyline, rstroke);
|
||||||
|
} else { // stroke
|
||||||
|
geometryData.appendStroke(polyline, rstroke);
|
||||||
|
}
|
||||||
|
// append render meshes and bboxes
|
||||||
WgPoint pmin{}, pmax{};
|
WgPoint pmin{}, pmax{};
|
||||||
strokes.getBBox(pmin, pmax);
|
geometryData.positions.getBBox(pmin, pmax);
|
||||||
WgGeometryData box;
|
meshGroupStrokes.append(context, &geometryData);
|
||||||
box.appendBox(pmin, pmax);
|
meshGroupStrokesBBox.append(context, pmin, pmax);
|
||||||
meshBBoxStrokes.update(context, &box);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgRenderDataShape::releaseMeshes(WgContext &context)
|
void WgRenderDataShape::releaseMeshes(WgContext &context)
|
||||||
{
|
{
|
||||||
|
meshGroupStrokesBBox.release(context);
|
||||||
meshGroupStrokes.release(context);
|
meshGroupStrokes.release(context);
|
||||||
|
meshGroupShapesBBox.release(context);
|
||||||
meshGroupShapes.release(context);
|
meshGroupShapes.release(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgRenderDataShape::release(WgContext& context)
|
void WgRenderDataShape::release(WgContext& context)
|
||||||
{
|
{
|
||||||
meshBBoxStrokes.release(context);
|
|
||||||
meshBBoxShapes.release(context);
|
|
||||||
releaseMeshes(context);
|
releaseMeshes(context);
|
||||||
renderSettingsStroke.release(context);
|
renderSettingsStroke.release(context);
|
||||||
renderSettingsShape.release(context);
|
renderSettingsShape.release(context);
|
||||||
|
@ -287,6 +373,8 @@ WgRenderDataShape* WgRenderDataShapePool::allocate(WgContext& context)
|
||||||
|
|
||||||
void WgRenderDataShapePool::free(WgContext& context, WgRenderDataShape* dataShape)
|
void WgRenderDataShapePool::free(WgContext& context, WgRenderDataShape* dataShape)
|
||||||
{
|
{
|
||||||
|
dataShape->meshGroupShapes.release(context);
|
||||||
|
dataShape->meshGroupStrokes.release(context);
|
||||||
mPool.push(dataShape);
|
mPool.push(dataShape);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,10 +30,13 @@ struct WgMeshData {
|
||||||
size_t vertexCount{};
|
size_t vertexCount{};
|
||||||
size_t indexCount{};
|
size_t indexCount{};
|
||||||
|
|
||||||
void draw(WGPURenderPassEncoder renderPassEncoder);
|
void draw(WgContext& context, WGPURenderPassEncoder renderPassEncoder);
|
||||||
void drawImage(WGPURenderPassEncoder renderPassEncoder);
|
void drawFan(WgContext& context, WGPURenderPassEncoder renderPassEncoder);
|
||||||
|
void drawImage(WgContext& context, WGPURenderPassEncoder renderPassEncoder);
|
||||||
|
|
||||||
void update(WgContext& context, WgGeometryData* geometryData);
|
void update(WgContext& context, const WgPolyline* polyline);
|
||||||
|
void update(WgContext& context, const WgGeometryData* geometryData);
|
||||||
|
void update(WgContext& context, const WgPoint pmin, const WgPoint pmax);
|
||||||
void release(WgContext& context);
|
void release(WgContext& context);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,11 +51,13 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WgMeshDataGroup {
|
struct WgMeshDataGroup {
|
||||||
static WgMeshDataPool* MeshDataPool;
|
static WgMeshDataPool* gMeshDataPool;
|
||||||
|
|
||||||
Array<WgMeshData*> meshes{};
|
Array<WgMeshData*> meshes{};
|
||||||
|
|
||||||
void update(WgContext& context, WgGeometryDataGroup* geometryDataGroup);
|
void append(WgContext& context, const WgPolyline* polyline);
|
||||||
|
void append(WgContext& context, const WgGeometryData* geometryData);
|
||||||
|
void append(WgContext& context, const WgPoint pmin, const WgPoint pmax);
|
||||||
void release(WgContext& context);
|
void release(WgContext& context);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -91,12 +96,13 @@ struct WgRenderDataShape: public WgRenderDataPaint
|
||||||
WgRenderSettings renderSettingsShape{};
|
WgRenderSettings renderSettingsShape{};
|
||||||
WgRenderSettings renderSettingsStroke{};
|
WgRenderSettings renderSettingsStroke{};
|
||||||
WgMeshDataGroup meshGroupShapes{};
|
WgMeshDataGroup meshGroupShapes{};
|
||||||
|
WgMeshDataGroup meshGroupShapesBBox{};
|
||||||
WgMeshDataGroup meshGroupStrokes{};
|
WgMeshDataGroup meshGroupStrokes{};
|
||||||
WgMeshData meshBBoxShapes{};
|
WgMeshDataGroup meshGroupStrokesBBox{};
|
||||||
WgMeshData meshBBoxStrokes{};
|
|
||||||
bool strokeFirst{};
|
bool strokeFirst{};
|
||||||
|
|
||||||
void updateMeshes(WgContext& context, const RenderShape& rshape);
|
void updateMeshes(WgContext& context, const RenderShape& rshape);
|
||||||
|
void updateMeshes(WgContext& context, const WgPolyline* polyline, const RenderStroke* rstroke);
|
||||||
void releaseMeshes(WgContext& context);
|
void releaseMeshes(WgContext& context);
|
||||||
void release(WgContext& context) override;
|
void release(WgContext& context) override;
|
||||||
uint32_t identifier() override { return TVG_CLASS_ID_SHAPE; };
|
uint32_t identifier() override { return TVG_CLASS_ID_SHAPE; };
|
||||||
|
|
|
@ -78,40 +78,41 @@ void WgRenderStorage::release(WgContext& context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgRenderStorage::renderShape(WgRenderDataShape* renderData, WgPipelineBlendType blendType)
|
void WgRenderStorage::renderShape(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType)
|
||||||
{
|
{
|
||||||
assert(renderData);
|
assert(renderData);
|
||||||
assert(mRenderPassEncoder);
|
assert(mRenderPassEncoder);
|
||||||
if (renderData->strokeFirst)
|
if (renderData->strokeFirst)
|
||||||
drawStroke(renderData, blendType);
|
drawStroke(context, renderData, blendType);
|
||||||
drawShape(renderData, blendType);
|
drawShape(context, renderData, blendType);
|
||||||
if (!renderData->strokeFirst)
|
if (!renderData->strokeFirst)
|
||||||
drawStroke(renderData, blendType);
|
drawStroke(context, renderData, blendType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgRenderStorage::renderPicture(WgRenderDataPicture* renderData, WgPipelineBlendType blendType)
|
void WgRenderStorage::renderPicture(WgContext& context, WgRenderDataPicture* renderData, WgPipelineBlendType blendType)
|
||||||
{
|
{
|
||||||
assert(renderData);
|
assert(renderData);
|
||||||
assert(mRenderPassEncoder);
|
assert(mRenderPassEncoder);
|
||||||
uint8_t blend = (uint8_t)blendType;
|
uint8_t blend = (uint8_t)blendType;
|
||||||
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
|
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
|
||||||
mPipelines->image[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, renderData->bindGroupPicture);
|
mPipelines->image[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, renderData->bindGroupPicture);
|
||||||
renderData->meshData.drawImage(mRenderPassEncoder);
|
renderData->meshData.drawImage(context, mRenderPassEncoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgRenderStorage::drawShape(WgRenderDataShape* renderData, WgPipelineBlendType blendType)
|
void WgRenderStorage::drawShape(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType)
|
||||||
{
|
{
|
||||||
assert(renderData);
|
assert(renderData);
|
||||||
assert(mRenderPassEncoder);
|
assert(mRenderPassEncoder);
|
||||||
|
assert(renderData->meshGroupShapes.meshes.count == renderData->meshGroupShapesBBox.meshes.count);
|
||||||
// draw shape geometry
|
// draw shape geometry
|
||||||
uint8_t blend = (uint8_t)blendType;
|
uint8_t blend = (uint8_t)blendType;
|
||||||
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
|
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
|
||||||
for (uint32_t i = 0; i < renderData->meshGroupShapes.meshes.count; i++) {
|
for (uint32_t i = 0; i < renderData->meshGroupShapes.meshes.count; i++) {
|
||||||
// draw to stencil (first pass)
|
// draw to stencil (first pass)
|
||||||
mPipelines->fillShape.use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint);
|
mPipelines->fillShape.use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint);
|
||||||
renderData->meshGroupShapes.meshes[i]->draw(mRenderPassEncoder);
|
renderData->meshGroupShapes.meshes[i]->drawFan(context, mRenderPassEncoder);
|
||||||
// fill shape (second pass)
|
// fill shape (second pass)
|
||||||
WgRenderSettings& settings = renderData->renderSettingsShape;
|
WgRenderSettings& settings = renderData->renderSettingsShape;
|
||||||
if (settings.fillType == WgRenderSettingsType::Solid)
|
if (settings.fillType == WgRenderSettingsType::Solid)
|
||||||
|
@ -120,24 +121,24 @@ void WgRenderStorage::drawShape(WgRenderDataShape* renderData, WgPipelineBlendTy
|
||||||
mPipelines->linear[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupLinear);
|
mPipelines->linear[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupLinear);
|
||||||
else if (settings.fillType == WgRenderSettingsType::Radial)
|
else if (settings.fillType == WgRenderSettingsType::Radial)
|
||||||
mPipelines->radial[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupRadial);
|
mPipelines->radial[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupRadial);
|
||||||
renderData->meshBBoxShapes.draw(mRenderPassEncoder);
|
renderData->meshGroupShapesBBox.meshes[i]->drawFan(context, mRenderPassEncoder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgRenderStorage::drawStroke(WgRenderDataShape* renderData, WgPipelineBlendType blendType)
|
void WgRenderStorage::drawStroke(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType)
|
||||||
{
|
{
|
||||||
assert(renderData);
|
assert(renderData);
|
||||||
assert(mRenderPassEncoder);
|
assert(mRenderPassEncoder);
|
||||||
|
assert(renderData->meshGroupStrokes.meshes.count == renderData->meshGroupStrokesBBox.meshes.count);
|
||||||
// draw stroke geometry
|
// draw stroke geometry
|
||||||
uint8_t blend = (uint8_t)blendType;
|
uint8_t blend = (uint8_t)blendType;
|
||||||
if (renderData->meshGroupStrokes.meshes.count > 0) {
|
|
||||||
// draw strokes to stencil (first pass)
|
// draw strokes to stencil (first pass)
|
||||||
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 255);
|
|
||||||
for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) {
|
for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) {
|
||||||
|
// draw to stencil (first pass)
|
||||||
|
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 255);
|
||||||
mPipelines->fillStroke.use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint);
|
mPipelines->fillStroke.use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint);
|
||||||
renderData->meshGroupStrokes.meshes[i]->draw(mRenderPassEncoder);
|
renderData->meshGroupStrokes.meshes[i]->draw(context, mRenderPassEncoder);
|
||||||
}
|
|
||||||
// fill shape (second pass)
|
// fill shape (second pass)
|
||||||
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
|
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
|
||||||
WgRenderSettings& settings = renderData->renderSettingsStroke;
|
WgRenderSettings& settings = renderData->renderSettingsStroke;
|
||||||
|
@ -147,7 +148,7 @@ void WgRenderStorage::drawStroke(WgRenderDataShape* renderData, WgPipelineBlendT
|
||||||
mPipelines->linear[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupLinear);
|
mPipelines->linear[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupLinear);
|
||||||
else if (settings.fillType == WgRenderSettingsType::Radial)
|
else if (settings.fillType == WgRenderSettingsType::Radial)
|
||||||
mPipelines->radial[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupRadial);
|
mPipelines->radial[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupRadial);
|
||||||
renderData->meshBBoxStrokes.draw(mRenderPassEncoder);
|
renderData->meshGroupStrokesBBox.meshes[i]->drawFan(context, mRenderPassEncoder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,16 +48,16 @@ public:
|
||||||
void beginRenderPass(WGPUCommandEncoder commandEncoder, bool clear);
|
void beginRenderPass(WGPUCommandEncoder commandEncoder, bool clear);
|
||||||
void endRenderPass();
|
void endRenderPass();
|
||||||
|
|
||||||
void renderShape(WgRenderDataShape* renderData, WgPipelineBlendType blendType);
|
void renderShape(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType);
|
||||||
void renderPicture(WgRenderDataPicture* renderData, WgPipelineBlendType blendType);
|
void renderPicture(WgContext& context, WgRenderDataPicture* renderData, WgPipelineBlendType blendType);
|
||||||
|
|
||||||
void clear(WGPUCommandEncoder commandEncoder);
|
void clear(WGPUCommandEncoder commandEncoder);
|
||||||
void blend(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetSrc, WgBindGroupBlendMethod* blendMethod);
|
void blend(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetSrc, WgBindGroupBlendMethod* blendMethod);
|
||||||
void compose(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetMsk, WgBindGroupCompositeMethod* composeMethod, WgBindGroupOpacity* opacity);
|
void compose(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetMsk, WgBindGroupCompositeMethod* composeMethod, WgBindGroupOpacity* opacity);
|
||||||
void antialias(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetSrc);
|
void antialias(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetSrc);
|
||||||
private:
|
private:
|
||||||
void drawShape(WgRenderDataShape* renderData, WgPipelineBlendType blendType);
|
void drawShape(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType);
|
||||||
void drawStroke(WgRenderDataShape* renderData, WgPipelineBlendType blendType);
|
void drawStroke(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType);
|
||||||
|
|
||||||
void dispatchWorkgroups(WGPUComputePassEncoder computePassEncoder);
|
void dispatchWorkgroups(WGPUComputePassEncoder computePassEncoder);
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ void WgRenderer::initialize()
|
||||||
mOpacityPool.initialize(mContext);
|
mOpacityPool.initialize(mContext);
|
||||||
mBlendMethodPool.initialize(mContext);
|
mBlendMethodPool.initialize(mContext);
|
||||||
mCompositeMethodPool.initialize(mContext);
|
mCompositeMethodPool.initialize(mContext);
|
||||||
WgMeshDataGroup::MeshDataPool = new WgMeshDataPool();
|
WgMeshDataGroup::gMeshDataPool = new WgMeshDataPool();
|
||||||
WgGeometryData::gMath = new WgMath();
|
WgGeometryData::gMath = new WgMath();
|
||||||
WgGeometryData::gMath->initialize();
|
WgGeometryData::gMath->initialize();
|
||||||
}
|
}
|
||||||
|
@ -59,8 +59,8 @@ void WgRenderer::release()
|
||||||
WgGeometryData::gMath->release();
|
WgGeometryData::gMath->release();
|
||||||
delete WgGeometryData::gMath;
|
delete WgGeometryData::gMath;
|
||||||
mRenderDataShapePool.release(mContext);
|
mRenderDataShapePool.release(mContext);
|
||||||
WgMeshDataGroup::MeshDataPool->release(mContext);
|
WgMeshDataGroup::gMeshDataPool->release(mContext);
|
||||||
delete WgMeshDataGroup::MeshDataPool;
|
delete WgMeshDataGroup::gMeshDataPool;
|
||||||
mCompositorStack.clear();
|
mCompositorStack.clear();
|
||||||
mRenderStorageStack.clear();
|
mRenderStorageStack.clear();
|
||||||
mRenderStoragePool.release(mContext);
|
mRenderStoragePool.release(mContext);
|
||||||
|
@ -162,13 +162,13 @@ bool WgRenderer::renderShape(RenderData data)
|
||||||
assert(renderStorage);
|
assert(renderStorage);
|
||||||
// use hardware blend
|
// use hardware blend
|
||||||
if (WgPipelines::isBlendMethodSupportsHW(mBlendMethod))
|
if (WgPipelines::isBlendMethodSupportsHW(mBlendMethod))
|
||||||
renderStorage->renderShape((WgRenderDataShape *)data, blendType);
|
renderStorage->renderShape(mContext, (WgRenderDataShape *)data, blendType);
|
||||||
else { // use custom blend
|
else { // use custom blend
|
||||||
// terminate current render pass
|
// terminate current render pass
|
||||||
renderStorage->endRenderPass();
|
renderStorage->endRenderPass();
|
||||||
// render image to render target
|
// render image to render target
|
||||||
mRenderTarget.beginRenderPass(mCommandEncoder, true);
|
mRenderTarget.beginRenderPass(mCommandEncoder, true);
|
||||||
mRenderTarget.renderShape((WgRenderDataShape *)data, blendType);
|
mRenderTarget.renderShape(mContext, (WgRenderDataShape *)data, blendType);
|
||||||
mRenderTarget.endRenderPass();
|
mRenderTarget.endRenderPass();
|
||||||
// blend shape with current render storage
|
// blend shape with current render storage
|
||||||
WgBindGroupBlendMethod* blendMethod = mBlendMethodPool.allocate(mContext, mBlendMethod);
|
WgBindGroupBlendMethod* blendMethod = mBlendMethodPool.allocate(mContext, mBlendMethod);
|
||||||
|
@ -188,13 +188,13 @@ bool WgRenderer::renderImage(RenderData data)
|
||||||
assert(renderStorage);
|
assert(renderStorage);
|
||||||
// use hardware blend
|
// use hardware blend
|
||||||
if (WgPipelines::isBlendMethodSupportsHW(mBlendMethod))
|
if (WgPipelines::isBlendMethodSupportsHW(mBlendMethod))
|
||||||
renderStorage->renderPicture((WgRenderDataPicture *)data, blendType);
|
renderStorage->renderPicture(mContext, (WgRenderDataPicture *)data, blendType);
|
||||||
else { // use custom blend
|
else { // use custom blend
|
||||||
// terminate current render pass
|
// terminate current render pass
|
||||||
renderStorage->endRenderPass();
|
renderStorage->endRenderPass();
|
||||||
// render image to render target
|
// render image to render target
|
||||||
mRenderTarget.beginRenderPass(mCommandEncoder, true);
|
mRenderTarget.beginRenderPass(mCommandEncoder, true);
|
||||||
mRenderTarget.renderPicture((WgRenderDataPicture *)data, blendType);
|
mRenderTarget.renderPicture(mContext, (WgRenderDataPicture *)data, blendType);
|
||||||
mRenderTarget.endRenderPass();
|
mRenderTarget.endRenderPass();
|
||||||
// blend shape with current render storage
|
// blend shape with current render storage
|
||||||
WgBindGroupBlendMethod* blendMethod = mBlendMethodPool.allocate(mContext, mBlendMethod);
|
WgBindGroupBlendMethod* blendMethod = mBlendMethodPool.allocate(mContext, mBlendMethod);
|
||||||
|
|
Loading…
Add table
Reference in a new issue