mirror of
https://github.com/thorvg/thorvg.git
synced 2025-07-23 22:58:44 +00:00
Merge c26b4edd4e
into 16604a873a
This commit is contained in:
commit
70fba57428
13 changed files with 862 additions and 970 deletions
|
@ -10,6 +10,7 @@ source_file = [
|
||||||
'tvgWgRenderTask.h',
|
'tvgWgRenderTask.h',
|
||||||
'tvgWgShaderSrc.h',
|
'tvgWgShaderSrc.h',
|
||||||
'tvgWgShaderTypes.h',
|
'tvgWgShaderTypes.h',
|
||||||
|
'tvgWgTessellator.h',
|
||||||
'tvgWgBindGroups.cpp',
|
'tvgWgBindGroups.cpp',
|
||||||
'tvgWgCommon.cpp',
|
'tvgWgCommon.cpp',
|
||||||
'tvgWgCompositor.cpp',
|
'tvgWgCompositor.cpp',
|
||||||
|
@ -20,7 +21,8 @@ source_file = [
|
||||||
'tvgWgRenderTarget.cpp',
|
'tvgWgRenderTarget.cpp',
|
||||||
'tvgWgRenderTask.cpp',
|
'tvgWgRenderTask.cpp',
|
||||||
'tvgWgShaderSrc.cpp',
|
'tvgWgShaderSrc.cpp',
|
||||||
'tvgWgShaderTypes.cpp'
|
'tvgWgShaderTypes.cpp',
|
||||||
|
'tvgWgTessellator.cpp'
|
||||||
]
|
]
|
||||||
|
|
||||||
wgpu_dep = []
|
wgpu_dep = []
|
||||||
|
|
|
@ -39,7 +39,6 @@ void WgContext::initialize(WGPUInstance instance, WGPUDevice device)
|
||||||
assert(queue);
|
assert(queue);
|
||||||
|
|
||||||
// create shared webgpu assets
|
// create shared webgpu assets
|
||||||
allocateBufferIndexFan(32768);
|
|
||||||
samplerNearestRepeat = createSampler(WGPUFilterMode_Nearest, WGPUMipmapFilterMode_Nearest, WGPUAddressMode_Repeat);
|
samplerNearestRepeat = createSampler(WGPUFilterMode_Nearest, WGPUMipmapFilterMode_Nearest, WGPUAddressMode_Repeat);
|
||||||
samplerLinearRepeat = createSampler(WGPUFilterMode_Linear, WGPUMipmapFilterMode_Linear, WGPUAddressMode_Repeat, 4);
|
samplerLinearRepeat = createSampler(WGPUFilterMode_Linear, WGPUMipmapFilterMode_Linear, WGPUAddressMode_Repeat, 4);
|
||||||
samplerLinearMirror = createSampler(WGPUFilterMode_Linear, WGPUMipmapFilterMode_Linear, WGPUAddressMode_MirrorRepeat, 4);
|
samplerLinearMirror = createSampler(WGPUFilterMode_Linear, WGPUMipmapFilterMode_Linear, WGPUAddressMode_MirrorRepeat, 4);
|
||||||
|
@ -61,7 +60,6 @@ void WgContext::release()
|
||||||
releaseSampler(samplerLinearMirror);
|
releaseSampler(samplerLinearMirror);
|
||||||
releaseSampler(samplerLinearRepeat);
|
releaseSampler(samplerLinearRepeat);
|
||||||
releaseSampler(samplerNearestRepeat);
|
releaseSampler(samplerNearestRepeat);
|
||||||
releaseBuffer(bufferIndexFan);
|
|
||||||
releaseQueue(queue);
|
releaseQueue(queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,26 +220,6 @@ bool WgContext::allocateBufferIndex(WGPUBuffer& buffer, const uint32_t* data, ui
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool WgContext::allocateBufferIndexFan(uint64_t vertexCount)
|
|
||||||
{
|
|
||||||
uint64_t indexCount = (vertexCount - 2) * 3;
|
|
||||||
if ((!bufferIndexFan) || (wgpuBufferGetSize(bufferIndexFan) < indexCount * sizeof(uint32_t))) {
|
|
||||||
tvg::Array<uint32_t> indexes(indexCount);
|
|
||||||
for (size_t i = 0; i < vertexCount - 2; i++) {
|
|
||||||
indexes.push(0);
|
|
||||||
indexes.push(i + 1);
|
|
||||||
indexes.push(i + 2);
|
|
||||||
}
|
|
||||||
releaseBuffer(bufferIndexFan);
|
|
||||||
WGPUBufferDescriptor bufferDesc{ .usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index, .size = indexCount * sizeof(uint32_t) };
|
|
||||||
bufferIndexFan = wgpuDeviceCreateBuffer(device, &bufferDesc);
|
|
||||||
wgpuQueueWriteBuffer(queue, bufferIndexFan, 0, &indexes[0], indexCount * sizeof(uint32_t));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void WgContext::releaseBuffer(WGPUBuffer& buffer)
|
void WgContext::releaseBuffer(WGPUBuffer& buffer)
|
||||||
{
|
{
|
||||||
if (buffer) {
|
if (buffer) {
|
||||||
|
|
|
@ -34,7 +34,6 @@ struct WgContext {
|
||||||
WGPUQueue queue{};
|
WGPUQueue queue{};
|
||||||
WGPUTextureFormat preferredFormat{};
|
WGPUTextureFormat preferredFormat{};
|
||||||
// shared webgpu assets
|
// shared webgpu assets
|
||||||
WGPUBuffer bufferIndexFan{};
|
|
||||||
WGPUSampler samplerNearestRepeat{};
|
WGPUSampler samplerNearestRepeat{};
|
||||||
WGPUSampler samplerLinearRepeat{};
|
WGPUSampler samplerLinearRepeat{};
|
||||||
WGPUSampler samplerLinearMirror{};
|
WGPUSampler samplerLinearMirror{};
|
||||||
|
@ -63,7 +62,6 @@ struct WgContext {
|
||||||
bool allocateBufferUniform(WGPUBuffer& buffer, const void* data, uint64_t size);
|
bool allocateBufferUniform(WGPUBuffer& buffer, const void* data, uint64_t size);
|
||||||
bool allocateBufferVertex(WGPUBuffer& buffer, const float* data, uint64_t size);
|
bool allocateBufferVertex(WGPUBuffer& buffer, const float* data, uint64_t size);
|
||||||
bool allocateBufferIndex(WGPUBuffer& buffer, const uint32_t* data, uint64_t size);
|
bool allocateBufferIndex(WGPUBuffer& buffer, const uint32_t* data, uint64_t size);
|
||||||
bool allocateBufferIndexFan(uint64_t vertexCount);
|
|
||||||
|
|
||||||
// release buffer objects
|
// release buffer objects
|
||||||
void releaseBuffer(WGPUBuffer& buffer);
|
void releaseBuffer(WGPUBuffer& buffer);
|
||||||
|
|
|
@ -347,19 +347,6 @@ void WgCompositor::drawMesh(WgContext& context, WgMeshData* meshData)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
void WgCompositor::drawMeshFan(WgContext& context, WgMeshData* meshData)
|
|
||||||
{
|
|
||||||
assert(meshData);
|
|
||||||
assert(renderPassEncoder);
|
|
||||||
uint64_t icount = (meshData->vbuffer.count - 2) * 3;
|
|
||||||
uint64_t vsize = meshData->vbuffer.count * sizeof(Point);
|
|
||||||
uint64_t isize = icount * sizeof(uint32_t);
|
|
||||||
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, stageBufferGeometry.vbuffer_gpu, meshData->voffset, vsize);
|
|
||||||
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, context.bufferIndexFan, WGPUIndexFormat_Uint32, 0, isize);
|
|
||||||
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, icount, 1, 0, 0, 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void WgCompositor::drawMeshImage(WgContext& context, WgMeshData* meshData)
|
void WgCompositor::drawMeshImage(WgContext& context, WgMeshData* meshData)
|
||||||
{
|
{
|
||||||
assert(meshData);
|
assert(meshData);
|
||||||
|
@ -378,8 +365,7 @@ void WgCompositor::drawShape(WgContext& context, WgRenderDataShape* renderData)
|
||||||
{
|
{
|
||||||
assert(renderData);
|
assert(renderData);
|
||||||
assert(renderPassEncoder);
|
assert(renderPassEncoder);
|
||||||
assert(renderData->meshGroupShapes.meshes.count == renderData->meshGroupShapesBBox.meshes.count);
|
if (renderData->renderSettingsShape.skip || renderData->meshShape.vbuffer.count == 0 || renderData->viewport.invalid()) return;
|
||||||
if (renderData->renderSettingsShape.skip || renderData->meshGroupShapes.meshes.count == 0 || renderData->viewport.invalid()) return;
|
|
||||||
WgRenderSettings& settings = renderData->renderSettingsShape;
|
WgRenderSettings& settings = renderData->renderSettingsShape;
|
||||||
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
|
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
|
||||||
// setup stencil rules
|
// setup stencil rules
|
||||||
|
@ -389,8 +375,7 @@ void WgCompositor::drawShape(WgContext& context, WgRenderDataShape* renderData)
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
|
||||||
// draw to stencil (first pass)
|
// draw to stencil (first pass)
|
||||||
ARRAY_FOREACH(p, renderData->meshGroupShapes.meshes)
|
drawMesh(context, &renderData->meshShape);
|
||||||
drawMeshFan(context, (*p));
|
|
||||||
// setup fill rules
|
// setup fill rules
|
||||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
||||||
|
@ -406,7 +391,7 @@ void WgCompositor::drawShape(WgContext& context, WgRenderDataShape* renderData)
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial);
|
||||||
}
|
}
|
||||||
// draw to color (second pass)
|
// draw to color (second pass)
|
||||||
drawMeshFan(context, &renderData->meshDataBBox);
|
drawMesh(context, &renderData->meshBBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -414,8 +399,7 @@ void WgCompositor::blendShape(WgContext& context, WgRenderDataShape* renderData,
|
||||||
{
|
{
|
||||||
assert(renderData);
|
assert(renderData);
|
||||||
assert(renderPassEncoder);
|
assert(renderPassEncoder);
|
||||||
assert(renderData->meshGroupShapes.meshes.count == renderData->meshGroupShapesBBox.meshes.count);
|
if (renderData->renderSettingsShape.skip || renderData->meshShape.vbuffer.count == 0 || renderData->viewport.invalid()) return;
|
||||||
if (renderData->renderSettingsShape.skip || renderData->meshGroupShapes.meshes.count == 0 || renderData->viewport.invalid()) return;
|
|
||||||
WgRenderSettings& settings = renderData->renderSettingsShape;
|
WgRenderSettings& settings = renderData->renderSettingsShape;
|
||||||
// copy current render target data to dst target
|
// copy current render target data to dst target
|
||||||
WgRenderTarget *target = currentTarget;
|
WgRenderTarget *target = currentTarget;
|
||||||
|
@ -431,8 +415,7 @@ void WgCompositor::blendShape(WgContext& context, WgRenderDataShape* renderData,
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
|
||||||
// draw to stencil (first pass)
|
// draw to stencil (first pass)
|
||||||
ARRAY_FOREACH(p, renderData->meshGroupShapes.meshes)
|
drawMesh(context, &renderData->meshShape);
|
||||||
drawMeshFan(context, (*p));
|
|
||||||
// setup fill rules
|
// setup fill rules
|
||||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
||||||
|
@ -450,7 +433,7 @@ void WgCompositor::blendShape(WgContext& context, WgRenderDataShape* renderData,
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial_blend[blendMethodInd]);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial_blend[blendMethodInd]);
|
||||||
}
|
}
|
||||||
// draw to color (second pass)
|
// draw to color (second pass)
|
||||||
drawMeshFan(context, &renderData->meshDataBBox);
|
drawMesh(context, &renderData->meshBBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -458,8 +441,7 @@ void WgCompositor::clipShape(WgContext& context, WgRenderDataShape* renderData)
|
||||||
{
|
{
|
||||||
assert(renderData);
|
assert(renderData);
|
||||||
assert(renderPassEncoder);
|
assert(renderPassEncoder);
|
||||||
assert(renderData->meshGroupShapes.meshes.count == renderData->meshGroupShapesBBox.meshes.count);
|
if (renderData->renderSettingsShape.skip || renderData->meshShape.vbuffer.count == 0 || renderData->viewport.invalid()) return;
|
||||||
if (renderData->renderSettingsShape.skip || renderData->meshGroupShapes.meshes.count == 0 || renderData->viewport.invalid()) return;
|
|
||||||
WgRenderSettings& settings = renderData->renderSettingsShape;
|
WgRenderSettings& settings = renderData->renderSettingsShape;
|
||||||
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
|
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
|
||||||
// setup stencil rules
|
// setup stencil rules
|
||||||
|
@ -469,13 +451,12 @@ void WgCompositor::clipShape(WgContext& context, WgRenderDataShape* renderData)
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
|
||||||
// draw to stencil (first pass)
|
// draw to stencil (first pass)
|
||||||
ARRAY_FOREACH(p, renderData->meshGroupShapes.meshes)
|
drawMesh(context, &renderData->meshShape);
|
||||||
drawMeshFan(context, (*p));
|
|
||||||
// merge depth and stencil buffer
|
// merge depth and stencil buffer
|
||||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.merge_depth_stencil);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.merge_depth_stencil);
|
||||||
drawMeshFan(context, &renderData->meshDataBBox);
|
drawMesh(context, &renderData->meshBBox);
|
||||||
// setup fill rules
|
// setup fill rules
|
||||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
||||||
|
@ -491,7 +472,7 @@ void WgCompositor::clipShape(WgContext& context, WgRenderDataShape* renderData)
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial);
|
||||||
}
|
}
|
||||||
// draw to color (second pass)
|
// draw to color (second pass)
|
||||||
drawMeshFan(context, &renderData->meshDataBBox);
|
drawMesh(context, &renderData->meshBBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -499,36 +480,33 @@ void WgCompositor::drawStrokes(WgContext& context, WgRenderDataShape* renderData
|
||||||
{
|
{
|
||||||
assert(renderData);
|
assert(renderData);
|
||||||
assert(renderPassEncoder);
|
assert(renderPassEncoder);
|
||||||
assert(renderData->meshGroupStrokes.meshes.count == renderData->meshGroupStrokesBBox.meshes.count);
|
if (renderData->renderSettingsStroke.skip || renderData->meshStrokes.vbuffer.count == 0 || renderData->viewport.invalid()) return;
|
||||||
if (renderData->renderSettingsStroke.skip || renderData->meshGroupStrokes.meshes.count == 0 || renderData->viewport.invalid()) return;
|
|
||||||
WgRenderSettings& settings = renderData->renderSettingsStroke;
|
WgRenderSettings& settings = renderData->renderSettingsStroke;
|
||||||
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
|
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
|
||||||
// draw strokes to stencil (first pass)
|
// draw strokes to stencil (first pass)
|
||||||
for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) {
|
// setup stencil rules
|
||||||
// setup stencil rules
|
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255);
|
||||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
|
// draw to stencil (first pass)
|
||||||
// draw to stencil (first pass)
|
drawMesh(context, &renderData->meshStrokes);
|
||||||
drawMesh(context, renderData->meshGroupStrokes.meshes[i]);
|
// setup fill rules
|
||||||
// setup fill rules
|
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
||||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
if (settings.fillType == WgRenderSettingsType::Solid) {
|
||||||
if (settings.fillType == WgRenderSettingsType::Solid) {
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.solid);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.solid);
|
} else if (settings.fillType == WgRenderSettingsType::Linear) {
|
||||||
} else if (settings.fillType == WgRenderSettingsType::Linear) {
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.gradientData.bindGroup, 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.gradientData.bindGroup, 0, nullptr);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.linear);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.linear);
|
} else if (settings.fillType == WgRenderSettingsType::Radial) {
|
||||||
} else if (settings.fillType == WgRenderSettingsType::Radial) {
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.gradientData.bindGroup, 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.gradientData.bindGroup, 0, nullptr);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial);
|
|
||||||
}
|
|
||||||
// draw to color (second pass)
|
|
||||||
drawMeshFan(context, renderData->meshGroupStrokesBBox.meshes[i]);
|
|
||||||
}
|
}
|
||||||
|
// draw to color (second pass)
|
||||||
|
drawMesh(context, &renderData->meshStrokesBBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -536,8 +514,7 @@ void WgCompositor::blendStrokes(WgContext& context, WgRenderDataShape* renderDat
|
||||||
{
|
{
|
||||||
assert(renderData);
|
assert(renderData);
|
||||||
assert(renderPassEncoder);
|
assert(renderPassEncoder);
|
||||||
assert(renderData->meshGroupStrokes.meshes.count == renderData->meshGroupStrokesBBox.meshes.count);
|
if (renderData->renderSettingsStroke.skip || renderData->meshStrokes.vbuffer.count == 0 || renderData->viewport.invalid()) return;
|
||||||
if (renderData->renderSettingsStroke.skip || renderData->meshGroupStrokes.meshes.count == 0 || renderData->viewport.invalid()) return;
|
|
||||||
WgRenderSettings& settings = renderData->renderSettingsStroke;
|
WgRenderSettings& settings = renderData->renderSettingsStroke;
|
||||||
// copy current render target data to dst target
|
// copy current render target data to dst target
|
||||||
WgRenderTarget *target = currentTarget;
|
WgRenderTarget *target = currentTarget;
|
||||||
|
@ -546,33 +523,31 @@ void WgCompositor::blendStrokes(WgContext& context, WgRenderDataShape* renderDat
|
||||||
beginRenderPass(commandEncoder, target, false);
|
beginRenderPass(commandEncoder, target, false);
|
||||||
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
|
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
|
||||||
// draw strokes to stencil (first pass)
|
// draw strokes to stencil (first pass)
|
||||||
for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) {
|
// setup stencil rules
|
||||||
// setup stencil rules
|
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255);
|
||||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
|
// draw to stencil (first pass)
|
||||||
// draw to stencil (first pass)
|
drawMesh(context, &renderData->meshStrokes);
|
||||||
drawMesh(context, renderData->meshGroupStrokes.meshes[i]);
|
// setup fill rules
|
||||||
// setup fill rules
|
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
||||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 3, targetTemp0.bindGroupTexure, 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 3, targetTemp0.bindGroupTexure, 0, nullptr);
|
uint32_t blendMethodInd = (uint32_t)blendMethod;
|
||||||
uint32_t blendMethodInd = (uint32_t)blendMethod;
|
if (settings.fillType == WgRenderSettingsType::Solid) {
|
||||||
if (settings.fillType == WgRenderSettingsType::Solid) {
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.solid_blend[blendMethodInd]);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.solid_blend[blendMethodInd]);
|
} else if (settings.fillType == WgRenderSettingsType::Linear) {
|
||||||
} else if (settings.fillType == WgRenderSettingsType::Linear) {
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.gradientData.bindGroup, 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.gradientData.bindGroup, 0, nullptr);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.linear_blend[blendMethodInd]);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.linear_blend[blendMethodInd]);
|
} else if (settings.fillType == WgRenderSettingsType::Radial) {
|
||||||
} else if (settings.fillType == WgRenderSettingsType::Radial) {
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.gradientData.bindGroup, 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.gradientData.bindGroup, 0, nullptr);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial_blend[blendMethodInd]);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial_blend[blendMethodInd]);
|
|
||||||
}
|
|
||||||
// draw to color (second pass)
|
|
||||||
drawMeshFan(context, renderData->meshGroupStrokesBBox.meshes[i]);
|
|
||||||
}
|
}
|
||||||
|
// draw to color (second pass)
|
||||||
|
drawMesh(context, &renderData->meshStrokesBBox);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -580,43 +555,38 @@ void WgCompositor::clipStrokes(WgContext& context, WgRenderDataShape* renderData
|
||||||
{
|
{
|
||||||
assert(renderData);
|
assert(renderData);
|
||||||
assert(renderPassEncoder);
|
assert(renderPassEncoder);
|
||||||
assert(renderData->meshGroupStrokes.meshes.count == renderData->meshGroupStrokesBBox.meshes.count);
|
if (renderData->renderSettingsStroke.skip || renderData->meshStrokes.vbuffer.count == 0 || renderData->viewport.invalid()) return;
|
||||||
if (renderData->renderSettingsStroke.skip) return;
|
|
||||||
if (renderData->meshGroupStrokes.meshes.count == 0) return;
|
|
||||||
if (renderData->viewport.invalid()) return;
|
|
||||||
WgRenderSettings& settings = renderData->renderSettingsStroke;
|
WgRenderSettings& settings = renderData->renderSettingsStroke;
|
||||||
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
|
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
|
||||||
// draw strokes to stencil (first pass)
|
// draw strokes to stencil (first pass)
|
||||||
for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) {
|
// setup stencil rules
|
||||||
// setup stencil rules
|
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255);
|
||||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
|
// draw to stencil (first pass)
|
||||||
// draw to stencil (first pass)
|
drawMesh(context, &renderData->meshStrokes);
|
||||||
drawMesh(context, renderData->meshGroupStrokes.meshes[i]);
|
// merge depth and stencil buffer
|
||||||
// merge depth and stencil buffer
|
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
||||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.merge_depth_stencil);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.merge_depth_stencil);
|
drawMesh(context, &renderData->meshBBox);
|
||||||
drawMeshFan(context, &renderData->meshDataBBox);
|
// setup fill rules
|
||||||
// setup fill rules
|
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
||||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
if (settings.fillType == WgRenderSettingsType::Solid) {
|
||||||
if (settings.fillType == WgRenderSettingsType::Solid) {
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.solid);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.solid);
|
} else if (settings.fillType == WgRenderSettingsType::Linear) {
|
||||||
} else if (settings.fillType == WgRenderSettingsType::Linear) {
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.gradientData.bindGroup, 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.gradientData.bindGroup, 0, nullptr);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.linear);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.linear);
|
} else if (settings.fillType == WgRenderSettingsType::Radial) {
|
||||||
} else if (settings.fillType == WgRenderSettingsType::Radial) {
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.gradientData.bindGroup, 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.gradientData.bindGroup, 0, nullptr);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial);
|
|
||||||
}
|
|
||||||
// draw to color (second pass)
|
|
||||||
drawMeshFan(context, renderData->meshGroupStrokesBBox.meshes[i]);
|
|
||||||
}
|
}
|
||||||
|
// draw to color (second pass)
|
||||||
|
drawMesh(context, &renderData->meshStrokesBBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -744,21 +714,19 @@ void WgCompositor::markupClipPath(WgContext& context, WgRenderDataShape* renderD
|
||||||
{
|
{
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
||||||
// markup stencil
|
// markup stencil
|
||||||
if (renderData->meshGroupStrokes.meshes.count > 0) {
|
if (renderData->meshStrokes.vbuffer.count > 0) {
|
||||||
WgRenderSettings& settings = renderData->renderSettingsStroke;
|
WgRenderSettings& settings = renderData->renderSettingsStroke;
|
||||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255);
|
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
|
||||||
ARRAY_FOREACH(p, renderData->meshGroupStrokes.meshes)
|
drawMesh(context, &renderData->meshStrokes);
|
||||||
drawMesh(context, (*p));
|
|
||||||
} else {
|
} else {
|
||||||
WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::NonZero) ? pipelines.nonzero : pipelines.evenodd;
|
WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::NonZero) ? pipelines.nonzero : pipelines.evenodd;
|
||||||
WgRenderSettings& settings = renderData->renderSettingsShape;
|
WgRenderSettings& settings = renderData->renderSettingsShape;
|
||||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
|
||||||
ARRAY_FOREACH(p, renderData->meshGroupShapes.meshes)
|
drawMesh(context, &renderData->meshShape);
|
||||||
drawMeshFan(context, (*p));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -781,7 +749,7 @@ void WgCompositor::renderClipPath(WgContext& context, WgRenderDataPaint* paint)
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings0.bindGroupInd], 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings0.bindGroupInd], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_stencil_to_depth);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_stencil_to_depth);
|
||||||
drawMeshFan(context, &renderData0->meshDataBBox);
|
drawMesh(context, &renderData0->meshBBox);
|
||||||
// merge clip pathes with AND logic
|
// merge clip pathes with AND logic
|
||||||
for (auto p = paint->clips.begin() + 1; p < paint->clips.end(); ++p) {
|
for (auto p = paint->clips.begin() + 1; p < paint->clips.end(); ++p) {
|
||||||
// get render data
|
// get render data
|
||||||
|
@ -794,31 +762,31 @@ void WgCompositor::renderClipPath(WgContext& context, WgRenderDataPaint* paint)
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[190], 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[190], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_stencil_to_depth_interm);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_stencil_to_depth_interm);
|
||||||
drawMeshFan(context, &renderData->meshDataBBox);
|
drawMesh(context, &renderData->meshBBox);
|
||||||
// copy depth to stencil
|
// copy depth to stencil
|
||||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 1);
|
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 1);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[190], 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[190], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_depth_to_stencil);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_depth_to_stencil);
|
||||||
drawMeshFan(context, &renderData->meshDataBBox);
|
drawMesh(context, &renderData->meshBBox);
|
||||||
// clear depth current (keep stencil)
|
// clear depth current (keep stencil)
|
||||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[255], 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[255], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.clear_depth);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.clear_depth);
|
||||||
drawMeshFan(context, &renderData->meshDataBBox);
|
drawMesh(context, &renderData->meshBBox);
|
||||||
// clear depth original (keep stencil)
|
// clear depth original (keep stencil)
|
||||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings0.bindGroupInd], 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings0.bindGroupInd], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[255], 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[255], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.clear_depth);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.clear_depth);
|
||||||
drawMeshFan(context, &renderData0->meshDataBBox);
|
drawMesh(context, &renderData0->meshBBox);
|
||||||
// copy stencil to depth (clear stencil)
|
// copy stencil to depth (clear stencil)
|
||||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_stencil_to_depth);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_stencil_to_depth);
|
||||||
drawMeshFan(context, &renderData->meshDataBBox);
|
drawMesh(context, &renderData->meshBBox);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -840,7 +808,7 @@ void WgCompositor::clearClipPath(WgContext& context, WgRenderDataPaint* paint)
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, stageBufferPaint[settings.bindGroupInd], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[255], 0, nullptr);
|
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[255], 0, nullptr);
|
||||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.clear_depth);
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.clear_depth);
|
||||||
drawMeshFan(context, &renderData->meshDataBBox);
|
drawMesh(context, &renderData->meshBBox);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1019,4 +987,4 @@ bool WgCompositor::tritoneEffect(WgContext& context, WgRenderTarget* dst, const
|
||||||
wgpuComputePassEncoderRelease(computePassEncoder);
|
wgpuComputePassEncoderRelease(computePassEncoder);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,6 @@ private:
|
||||||
|
|
||||||
// base meshes draw
|
// base meshes draw
|
||||||
void drawMesh(WgContext& context, WgMeshData* meshData);
|
void drawMesh(WgContext& context, WgMeshData* meshData);
|
||||||
void drawMeshFan(WgContext& context, WgMeshData* meshData);
|
|
||||||
void drawMeshImage(WgContext& context, WgMeshData* meshData);
|
void drawMeshImage(WgContext& context, WgMeshData* meshData);
|
||||||
|
|
||||||
// shapes
|
// shapes
|
||||||
|
|
|
@ -22,62 +22,73 @@
|
||||||
|
|
||||||
#include "tvgWgGeometry.h"
|
#include "tvgWgGeometry.h"
|
||||||
|
|
||||||
|
//***********************************************************************
|
||||||
|
// WgMeshData
|
||||||
|
//***********************************************************************
|
||||||
|
|
||||||
/************************************************************************/
|
void WgMeshData::bbox(const Point pmin, const Point pmax)
|
||||||
/* Internal Class Implementation */
|
|
||||||
/************************************************************************/
|
|
||||||
|
|
||||||
static WgGeometryBufferPool _pool;
|
|
||||||
|
|
||||||
/************************************************************************/
|
|
||||||
/* External Class Implementation */
|
|
||||||
/************************************************************************/
|
|
||||||
|
|
||||||
WgVertexBuffer* WgGeometryBufferPool::reqVertexBuffer(float scale)
|
|
||||||
{
|
{
|
||||||
ARRAY_FOREACH(p, vbuffers) {
|
const float vdata[] = {pmin.x, pmin.y, pmax.x, pmin.y, pmax.x, pmax.y, pmin.x, pmax.y};
|
||||||
if ((*p)->count == 0) {
|
const uint32_t idata[] = {0, 1, 2, 0, 2, 3};
|
||||||
(*p)->scale = scale;
|
// setup vertex data
|
||||||
return (*p);
|
vbuffer.reserve(4);
|
||||||
}
|
vbuffer.count = 4;
|
||||||
}
|
memcpy(vbuffer.data, vdata, sizeof(vdata));
|
||||||
vbuffers.push(new WgVertexBuffer(scale));
|
// setup tex coords data
|
||||||
return vbuffers.last();
|
tbuffer.clear();
|
||||||
}
|
// setup indexes data
|
||||||
|
ibuffer.reserve(6);
|
||||||
void WgGeometryBufferPool::retVertexBuffer(WgVertexBuffer* buffer)
|
ibuffer.count = 6;
|
||||||
{
|
memcpy(ibuffer.data, idata, sizeof(idata));
|
||||||
buffer->reset(1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
WgIndexedVertexBuffer* WgGeometryBufferPool::reqIndexedVertexBuffer(float scale)
|
|
||||||
{
|
|
||||||
ARRAY_FOREACH(p, ibuffers) {
|
|
||||||
if ((*p)->vcount == 0) {
|
|
||||||
(*p)->scale = scale;
|
|
||||||
return (*p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ibuffers.push(new WgIndexedVertexBuffer(this, scale));
|
|
||||||
return ibuffers.last();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WgGeometryBufferPool::retIndexedVertexBuffer(WgIndexedVertexBuffer* buffer)
|
|
||||||
{
|
|
||||||
buffer->reset(1.0f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
WgGeometryBufferPool* WgGeometryBufferPool::instance()
|
void WgMeshData::imageBox(float w, float h)
|
||||||
{
|
{
|
||||||
/* TODO: These could be easily addressed per threads. i.e _pool[thread_cnt]; */
|
const float vdata[] = {0.0f, 0.0f, w, 0.0f, w, h, 0.0f, h};
|
||||||
return &_pool;
|
const float tdata[] = {0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
|
||||||
|
const uint32_t idata[] = {0, 1, 2, 0, 2, 3};
|
||||||
|
// setup vertex data
|
||||||
|
vbuffer.reserve(4);
|
||||||
|
vbuffer.count = 4;
|
||||||
|
memcpy(vbuffer.data, vdata, sizeof(vdata));
|
||||||
|
// setup tex coords data
|
||||||
|
tbuffer.reserve(4);
|
||||||
|
tbuffer.count = 4;
|
||||||
|
memcpy(tbuffer.data, tdata, sizeof(tdata));
|
||||||
|
// setup indexes data
|
||||||
|
ibuffer.reserve(6);
|
||||||
|
ibuffer.count = 6;
|
||||||
|
memcpy(ibuffer.data, idata, sizeof(idata));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
WgGeometryBufferPool::~WgGeometryBufferPool()
|
void WgMeshData::blitBox()
|
||||||
{
|
{
|
||||||
//The indexed buffer may contain the vertex buffer, so free the memory in reverse order.
|
const float vdata[] = {-1.0f, +1.0f, +1.0f, +1.0f, +1.0f, -1.0f, -1.0f, -1.0f};
|
||||||
ARRAY_FOREACH(p, ibuffers) delete(*p);
|
const float tdata[] = {+0.0f, +0.0f, +1.0f, +0.0f, +1.0f, +1.0f, +0.0f, +1.0f};
|
||||||
ARRAY_FOREACH(p, vbuffers) delete(*p);
|
const uint32_t idata[] = { 0, 1, 2, 0, 2, 3 };
|
||||||
|
// setup vertex data
|
||||||
|
vbuffer.reserve(4);
|
||||||
|
vbuffer.count = 4;
|
||||||
|
memcpy(vbuffer.data, vdata, sizeof(vdata));
|
||||||
|
// setup tex coords data
|
||||||
|
tbuffer.reserve(4);
|
||||||
|
tbuffer.count = 4;
|
||||||
|
memcpy(tbuffer.data, tdata, sizeof(tdata));
|
||||||
|
// setup indexes data
|
||||||
|
ibuffer.reserve(6);
|
||||||
|
ibuffer.count = 6;
|
||||||
|
memcpy(ibuffer.data, idata, sizeof(idata));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WgMeshData::clear()
|
||||||
|
{
|
||||||
|
vbuffer.clear();
|
||||||
|
tbuffer.clear();
|
||||||
|
ibuffer.clear();
|
||||||
|
voffset = 0;
|
||||||
|
toffset = 0;
|
||||||
|
ioffset = 0;
|
||||||
}
|
}
|
|
@ -25,523 +25,20 @@
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include "tvgMath.h"
|
#include "tvgMath.h"
|
||||||
#include "tvgRender.h"
|
#include "tvgArray.h"
|
||||||
|
|
||||||
|
struct WgMeshData {
|
||||||
|
Array<Point> vbuffer;
|
||||||
|
Array<Point> tbuffer;
|
||||||
|
Array<uint32_t> ibuffer;
|
||||||
|
size_t voffset{};
|
||||||
|
size_t toffset{};
|
||||||
|
size_t ioffset{};
|
||||||
|
|
||||||
// default size of vertex and index buffers
|
void bbox(const Point pmin, const Point pmax);
|
||||||
#define WG_DEFAULT_BUFFER_SIZE 2048
|
void imageBox(float w, float h);
|
||||||
|
void blitBox();
|
||||||
struct WgVertexBuffer;
|
void clear();
|
||||||
struct WgIndexedVertexBuffer;
|
|
||||||
|
|
||||||
struct WgGeometryBufferPool
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
Array<WgVertexBuffer*> vbuffers;
|
|
||||||
Array<WgIndexedVertexBuffer*> ibuffers;
|
|
||||||
|
|
||||||
public:
|
|
||||||
~WgGeometryBufferPool();
|
|
||||||
|
|
||||||
WgVertexBuffer* reqVertexBuffer(float scale = 1.0f);
|
|
||||||
WgIndexedVertexBuffer* reqIndexedVertexBuffer(float scale = 1.0f);
|
|
||||||
void retVertexBuffer(WgVertexBuffer* buffer);
|
|
||||||
void retIndexedVertexBuffer(WgIndexedVertexBuffer* buffer);
|
|
||||||
|
|
||||||
static WgGeometryBufferPool* instance(); //return the shared buffer pool
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// simple vertex buffer
|
|
||||||
struct WgVertexBuffer
|
|
||||||
{
|
|
||||||
Point* data; // vertex buffer
|
|
||||||
struct Distance {
|
|
||||||
float interval; // distance to previous point
|
|
||||||
float length; // distance to the first point through all previous points
|
|
||||||
} *dist;
|
|
||||||
uint32_t count = 0;
|
|
||||||
uint32_t reserved = WG_DEFAULT_BUFFER_SIZE;
|
|
||||||
float scale; // tesselation scale
|
|
||||||
bool closed = false;
|
|
||||||
|
|
||||||
// callback for external process of polyline
|
|
||||||
using onPolylineFn = std::function<void(const WgVertexBuffer& buff)>;
|
|
||||||
|
|
||||||
WgVertexBuffer(float scale = 1.0f) : scale(scale)
|
|
||||||
{
|
|
||||||
data = tvg::malloc<Point*>(sizeof(Point) * reserved);
|
|
||||||
dist = tvg::malloc<Distance*>(sizeof(Distance) * reserved);
|
|
||||||
}
|
|
||||||
|
|
||||||
~WgVertexBuffer()
|
|
||||||
{
|
|
||||||
tvg::free(data);
|
|
||||||
tvg::free(dist);
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset buffer
|
|
||||||
void reset(float scale)
|
|
||||||
{
|
|
||||||
count = 0;
|
|
||||||
closed = false;
|
|
||||||
this->scale = scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the last point with optional index offset from the end
|
|
||||||
Point last(size_t offset = 0) const
|
|
||||||
{
|
|
||||||
return data[count - offset - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the last distance with optional index offset from the end
|
|
||||||
float lastDist(size_t offset = 0) const
|
|
||||||
{
|
|
||||||
return dist[count - offset - 1].interval;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get total length
|
|
||||||
float total() const
|
|
||||||
{
|
|
||||||
return (count == 0) ? 0.0f : dist[count-1].length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get next vertex index by length using binary search
|
|
||||||
size_t getIndexByLength(float len) const
|
|
||||||
{
|
|
||||||
if (count <= 1) return 0;
|
|
||||||
size_t left = 0;
|
|
||||||
size_t right = count - 1;
|
|
||||||
while (left <= right) {
|
|
||||||
size_t mid = left + (right - left) / 2;
|
|
||||||
if (dist[mid].length == len) return mid;
|
|
||||||
else if (dist[mid].length < len) left = mid + 1;
|
|
||||||
else right = mid - 1;
|
|
||||||
}
|
|
||||||
return right + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get min and max values of the buffer
|
|
||||||
void getMinMax(Point& pmin, Point& pmax) const
|
|
||||||
{
|
|
||||||
if (count == 0) return;
|
|
||||||
pmax = pmin = data[0];
|
|
||||||
for (size_t i = 1; i < count; i++) {
|
|
||||||
pmin = min(pmin, data[i]);
|
|
||||||
pmax = max(pmax, data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// update points distancess to the prev point and total length
|
|
||||||
void updateDistances()
|
|
||||||
{
|
|
||||||
if (count == 0) return;
|
|
||||||
dist[0].interval = 0.0f;
|
|
||||||
dist[0].length = 0.0f;
|
|
||||||
for (size_t i = 1; i < count; i++) {
|
|
||||||
dist[i].interval = tvg::length(data[i-1] - data[i]);
|
|
||||||
dist[i].length = dist[i-1].length + dist[i].interval;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// close vertex buffer
|
|
||||||
void close()
|
|
||||||
{
|
|
||||||
// check if last point is not to close to the first point
|
|
||||||
if (length(data[0] - last()) > 0.015625f) {
|
|
||||||
append(data[0]);
|
|
||||||
}
|
|
||||||
closed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// append point
|
|
||||||
void append(const Point& p)
|
|
||||||
{
|
|
||||||
if (count >= reserved) {
|
|
||||||
reserved *= 2;
|
|
||||||
data = tvg::realloc<Point*>(data, reserved * sizeof(Point));
|
|
||||||
dist = tvg::realloc<Distance*>(dist, reserved * sizeof(Distance));
|
|
||||||
}
|
|
||||||
data[count++] = p;
|
|
||||||
}
|
|
||||||
|
|
||||||
// append source vertex buffer in index range from start to end (end not included)
|
|
||||||
void appendRange(const WgVertexBuffer& buff, size_t start_index, size_t end_index)
|
|
||||||
{
|
|
||||||
for (size_t i = start_index; i < end_index; i++) {
|
|
||||||
append(buff.data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// append circle (list of triangles)
|
|
||||||
void appendCircle(float radius)
|
|
||||||
{
|
|
||||||
// get approx circle length
|
|
||||||
float clen = 2.0f * radius * MATH_PI;
|
|
||||||
size_t nsegs = std::max((uint32_t)(clen * scale / 8), 16U);
|
|
||||||
// append circle^
|
|
||||||
Point prev { std::sin(0.0f) * radius, std::cos(0.0f) * radius };
|
|
||||||
for (size_t i = 1; i <= nsegs; i++) {
|
|
||||||
float t = (2.0f * MATH_PI * i) / nsegs;
|
|
||||||
Point curr { std::sin(t) * radius, std::cos(t) * radius };
|
|
||||||
append(Point{0.0f, 0.0f});
|
|
||||||
append(prev);
|
|
||||||
append(curr);
|
|
||||||
prev = curr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// append cubic spline
|
|
||||||
void appendCubic(const Point& v0, const Point& v1, const Point& v2, const Point& v3)
|
|
||||||
{
|
|
||||||
// get approx cubic length
|
|
||||||
float clen = (tvg::length(v0 - v1) + tvg::length(v1 - v2) + tvg::length(v2 - v3));
|
|
||||||
size_t nsegs = std::max((uint32_t)(clen * scale / 16), 16U);
|
|
||||||
// append cubic
|
|
||||||
Bezier bezier{v0, v1, v2, v3};
|
|
||||||
for (size_t i = 1; i <= nsegs; i++) {
|
|
||||||
append(bezier.at((float)i / nsegs));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// decode path with callback for external prcesses
|
|
||||||
void decodePath(const RenderShape& rshape, bool update_dist, onPolylineFn onPolyline, bool trim = false)
|
|
||||||
{
|
|
||||||
// decode path
|
|
||||||
reset(scale);
|
|
||||||
|
|
||||||
PathCommand *cmds, *trimmedCmds = nullptr;
|
|
||||||
Point *pts, *trimmedPts = nullptr;
|
|
||||||
uint32_t cmdCnt{};
|
|
||||||
|
|
||||||
if (trim) {
|
|
||||||
RenderPath trimmedPath;
|
|
||||||
if (!rshape.stroke->trim.trim(rshape.path, trimmedPath)) return;
|
|
||||||
|
|
||||||
cmds = trimmedCmds = trimmedPath.cmds.data;
|
|
||||||
cmdCnt = trimmedPath.cmds.count;
|
|
||||||
pts = trimmedPts = trimmedPath.pts.data;
|
|
||||||
|
|
||||||
trimmedPath.cmds.data = nullptr;
|
|
||||||
trimmedPath.pts.data = nullptr;
|
|
||||||
} else {
|
|
||||||
cmds = rshape.path.cmds.data;
|
|
||||||
cmdCnt = rshape.path.cmds.count;
|
|
||||||
pts = rshape.path.pts.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t pntIndex = 0;
|
|
||||||
for (uint32_t i = 0; i < cmdCnt; i++) {
|
|
||||||
auto& cmd = cmds[i];
|
|
||||||
if (cmd == PathCommand::MoveTo) {
|
|
||||||
// after path decoding we need to update distances and total length
|
|
||||||
if (update_dist) updateDistances();
|
|
||||||
if ((onPolyline) && (count > 0)) onPolyline(*this);
|
|
||||||
reset(scale);
|
|
||||||
append(pts[pntIndex]);
|
|
||||||
pntIndex++;
|
|
||||||
} else if (cmd == PathCommand::LineTo) {
|
|
||||||
append(pts[pntIndex]);
|
|
||||||
pntIndex++;
|
|
||||||
} else if (cmd == PathCommand::Close) {
|
|
||||||
close();
|
|
||||||
// proceed path if close command is not the last command and next command is LineTo or CubicTo
|
|
||||||
if (i + 1 < cmdCnt && (cmds[i + 1] == PathCommand::LineTo || cmds[i + 1] == PathCommand::CubicTo)) {
|
|
||||||
// proceed current path
|
|
||||||
if (update_dist) updateDistances();
|
|
||||||
if ((count > 0) && (onPolyline)) onPolyline(*this);
|
|
||||||
// append closing point of current path as a first point of the new path
|
|
||||||
Point last_pt = last();
|
|
||||||
reset(scale);
|
|
||||||
append(last_pt);
|
|
||||||
}
|
|
||||||
} else if (cmd == PathCommand::CubicTo) {
|
|
||||||
// append tesselated cubic spline with scale param
|
|
||||||
appendCubic(data[count - 1], pts[pntIndex + 0], pts[pntIndex + 1], pts[pntIndex + 2]);
|
|
||||||
pntIndex += 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tvg::free(trimmedCmds);
|
|
||||||
tvg::free(trimmedPts);
|
|
||||||
|
|
||||||
// after path decoding we need to update distances and total length
|
|
||||||
if (update_dist) updateDistances();
|
|
||||||
if ((count > 0) && (onPolyline)) onPolyline(*this);
|
|
||||||
reset(scale);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct WgIndexedVertexBuffer
|
|
||||||
{
|
|
||||||
Point* vbuff;
|
|
||||||
uint32_t* ibuff;
|
|
||||||
uint32_t vcount = 0, icount = 0;
|
|
||||||
size_t vreserved = WG_DEFAULT_BUFFER_SIZE;
|
|
||||||
size_t ireserved = WG_DEFAULT_BUFFER_SIZE * 2;
|
|
||||||
WgGeometryBufferPool* pool;
|
|
||||||
WgVertexBuffer* dashed; // intermediate buffer for stroke dashing
|
|
||||||
float scale;
|
|
||||||
|
|
||||||
WgIndexedVertexBuffer(WgGeometryBufferPool* pool, float scale = 1.0f) : pool(pool), scale(scale)
|
|
||||||
{
|
|
||||||
vbuff = tvg::malloc<Point*>(sizeof(Point) * vreserved);
|
|
||||||
ibuff = tvg::malloc<uint32_t*>(sizeof(uint32_t) * ireserved);
|
|
||||||
dashed = pool->reqVertexBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
~WgIndexedVertexBuffer()
|
|
||||||
{
|
|
||||||
pool->retVertexBuffer(dashed);
|
|
||||||
tvg::free(vbuff);
|
|
||||||
tvg::free(ibuff);
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset buffer
|
|
||||||
void reset(float scale)
|
|
||||||
{
|
|
||||||
icount = vcount = 0;
|
|
||||||
this->scale = scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
void growIndex(size_t grow)
|
|
||||||
{
|
|
||||||
if (icount + grow >= ireserved) {
|
|
||||||
ireserved *= 2;
|
|
||||||
ibuff = tvg::realloc<uint32_t*>(ibuff, ireserved * sizeof(uint32_t));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void growVertex(size_t grow)
|
|
||||||
{
|
|
||||||
if (vcount + grow >= vreserved) {
|
|
||||||
vreserved *= 2;
|
|
||||||
vbuff = tvg::realloc<Point*>(vbuff, vreserved * sizeof(Point));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get min and max values of the buffer
|
|
||||||
void getMinMax(Point& pmin, Point& pmax) const
|
|
||||||
{
|
|
||||||
if (vcount == 0) return;
|
|
||||||
pmax = pmin = vbuff[0];
|
|
||||||
for (size_t i = 1; i < vcount; i++) {
|
|
||||||
pmin = min(pmin, vbuff[i]);
|
|
||||||
pmax = max(pmax, vbuff[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// append quad - two triangles formed from four points
|
|
||||||
void appendQuad(const Point& p0, const Point& p1, const Point& p2, const Point& p3)
|
|
||||||
{
|
|
||||||
growVertex(4);
|
|
||||||
vbuff[vcount+0] = p0;
|
|
||||||
vbuff[vcount+1] = p1;
|
|
||||||
vbuff[vcount+2] = p2;
|
|
||||||
vbuff[vcount+3] = p3;
|
|
||||||
|
|
||||||
growIndex(6);
|
|
||||||
ibuff[icount+0] = vcount + 0;
|
|
||||||
ibuff[icount+1] = vcount + 1;
|
|
||||||
ibuff[icount+2] = vcount + 2;
|
|
||||||
ibuff[icount+3] = vcount + 1;
|
|
||||||
ibuff[icount+4] = vcount + 3;
|
|
||||||
ibuff[icount+5] = vcount + 2;
|
|
||||||
|
|
||||||
vcount += 4;
|
|
||||||
icount += 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void appendStrokesDashed(const WgVertexBuffer& buff, const RenderStroke* rstroke)
|
|
||||||
{
|
|
||||||
dashed->reset(scale);
|
|
||||||
auto& dash = rstroke->dash;
|
|
||||||
|
|
||||||
if (buff.count < 2) return;
|
|
||||||
|
|
||||||
uint32_t index = 0;
|
|
||||||
auto total = dash.pattern[index];
|
|
||||||
auto length = (dash.count % 2) ? dash.length * 2 : dash.length;
|
|
||||||
|
|
||||||
// normalize dash offset
|
|
||||||
auto dashOffset = dash.offset;
|
|
||||||
while(dashOffset < 0) dashOffset += length;
|
|
||||||
while(dashOffset > length) dashOffset -= length;
|
|
||||||
auto gap = false;
|
|
||||||
|
|
||||||
// scip dashes by offset
|
|
||||||
if (dashOffset > 0.0f) {
|
|
||||||
while(total <= dashOffset) {
|
|
||||||
index = (index + 1) % dash.count;
|
|
||||||
total += dash.pattern[index];
|
|
||||||
gap = !gap;
|
|
||||||
}
|
|
||||||
total -= dashOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
// iterate by polyline points
|
|
||||||
for (uint32_t i = 0; i < buff.count - 1; i++) {
|
|
||||||
if (!gap) dashed->append(buff.data[i]);
|
|
||||||
// move inside polyline segment
|
|
||||||
while(total < buff.dist[i+1].interval) {
|
|
||||||
// get current point
|
|
||||||
dashed->append(tvg::lerp(buff.data[i], buff.data[i+1], total / buff.dist[i+1].interval));
|
|
||||||
// update current state
|
|
||||||
index = (index + 1) % dash.count;
|
|
||||||
total += dash.pattern[index];
|
|
||||||
// preceed stroke if dash
|
|
||||||
if (!gap) {
|
|
||||||
dashed->updateDistances();
|
|
||||||
appendStrokes(*dashed, rstroke);
|
|
||||||
dashed->reset(scale);
|
|
||||||
}
|
|
||||||
gap = !gap;
|
|
||||||
}
|
|
||||||
// update current subline length
|
|
||||||
total -= buff.dist[i+1].interval;
|
|
||||||
}
|
|
||||||
// draw last subline
|
|
||||||
if (!gap) {
|
|
||||||
dashed->append(buff.last());
|
|
||||||
dashed->updateDistances();
|
|
||||||
appendStrokes(*dashed, rstroke);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// append buffer with optional offset
|
|
||||||
void appendBuffer(const WgVertexBuffer& buff, Point offset = Point{0.0f, 0.0f})
|
|
||||||
{
|
|
||||||
growVertex(buff.count);
|
|
||||||
growIndex(buff.count);
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < buff.count; i++) {
|
|
||||||
vbuff[vcount + i] = buff.data[i] + offset;
|
|
||||||
ibuff[icount + i] = vcount + i;
|
|
||||||
}
|
|
||||||
vcount += buff.count;
|
|
||||||
icount += buff.count;
|
|
||||||
};
|
|
||||||
|
|
||||||
void appendLine(const Point& v0, const Point& v1, float dist, float halfWidth)
|
|
||||||
{
|
|
||||||
if(tvg::zero(dist)) return;
|
|
||||||
Point sub = v1 - v0;
|
|
||||||
Point nrm = {sub.y / dist * halfWidth, -sub.x / dist * halfWidth};
|
|
||||||
appendQuad(v0 - nrm, v0 + nrm, v1 - nrm, v1 + nrm);
|
|
||||||
}
|
|
||||||
|
|
||||||
void appendBevel(const Point& v0, const Point& v1, const Point& v2, float dist1, float dist2, float halfWidth)
|
|
||||||
{
|
|
||||||
if(tvg::zero(dist1) || tvg::zero(dist2)) return;
|
|
||||||
Point sub1 = v1 - v0;
|
|
||||||
Point sub2 = v2 - v1;
|
|
||||||
Point nrm1 {sub1.y / dist1 * halfWidth, -sub1.x / dist1 * halfWidth};
|
|
||||||
Point nrm2 {sub2.y / dist2 * halfWidth, -sub2.x / dist2 * halfWidth};
|
|
||||||
appendQuad(v1 - nrm1, v1 + nrm1, v1 - nrm2, v1 + nrm2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void appendMiter(const Point& v0, const Point& v1, const Point& v2, float dist1, float dist2, float halfWidth, float miterLimit)
|
|
||||||
{
|
|
||||||
if(tvg::zero(dist1) || tvg::zero(dist2)) return;
|
|
||||||
auto sub1 = v1 - v0;
|
|
||||||
auto sub2 = v2 - v1;
|
|
||||||
auto nrm1 = Point{+sub1.y / dist1, -sub1.x / dist1};
|
|
||||||
auto nrm2 = Point{+sub2.y / dist2, -sub2.x / dist2};
|
|
||||||
auto offset1 = nrm1 * halfWidth;
|
|
||||||
auto offset2 = nrm2 * halfWidth;
|
|
||||||
auto nrm = nrm1 + nrm2;
|
|
||||||
normalize(nrm);
|
|
||||||
float cosine = dot(nrm, nrm1);
|
|
||||||
if (tvg::zero(cosine)) return;
|
|
||||||
float angle = std::acos(dot(nrm1, -nrm2));
|
|
||||||
if (tvg::zero(angle) || tvg::equal(angle, MATH_PI)) return;
|
|
||||||
float miterRatio = 1.0f / (std::sin(angle) * 0.5f);
|
|
||||||
if (miterRatio <= miterLimit) {
|
|
||||||
appendQuad(v1 + nrm * (halfWidth / cosine), v1 + offset2, v1 + offset1, v1);
|
|
||||||
appendQuad(v1 - nrm * (halfWidth / cosine), v1 - offset2, v1 - offset1, v1);
|
|
||||||
} else {
|
|
||||||
appendQuad(v1 - offset1, v1 + offset2, v1 - offset2, v1 + offset1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void appendSquare(Point v0, Point v1, float dist, float halfWidth)
|
|
||||||
{
|
|
||||||
// zero length segment with square cap still should be rendered as a point - only the caps are visible
|
|
||||||
if(tvg::zero(dist)) {
|
|
||||||
appendQuad(v1 + Point{-halfWidth, -halfWidth}, v1 + Point{-halfWidth, halfWidth}, v1 + Point{halfWidth, -halfWidth}, v1 + Point{halfWidth, halfWidth});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Point sub = v1 - v0;
|
|
||||||
Point offset = sub / dist * halfWidth;
|
|
||||||
Point nrm = {+offset.y, -offset.x};
|
|
||||||
appendQuad(v1 - nrm, v1 + nrm, v1 + offset - nrm, v1 + offset + nrm);
|
|
||||||
}
|
|
||||||
|
|
||||||
void appendStrokes(const WgVertexBuffer& buff, const RenderStroke* rstroke)
|
|
||||||
{
|
|
||||||
assert(rstroke);
|
|
||||||
// empty buffer gueard
|
|
||||||
if (buff.count < 2) return;
|
|
||||||
|
|
||||||
float halfWidth = rstroke->width * 0.5f;
|
|
||||||
|
|
||||||
// append core lines
|
|
||||||
for (size_t i = 1; i < buff.count; i++) {
|
|
||||||
appendLine(buff.data[i-1], buff.data[i], buff.dist[i].interval, halfWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
// append caps (square)
|
|
||||||
if ((rstroke->cap == StrokeCap::Square) && !buff.closed) {
|
|
||||||
appendSquare(buff.data[1], buff.data[0], buff.dist[1].interval, halfWidth);
|
|
||||||
appendSquare(buff.last(1), buff.last(0), buff.lastDist(0), halfWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
// append round joints and caps
|
|
||||||
if ((rstroke->join == StrokeJoin::Round) || (rstroke->cap == StrokeCap::Round)) {
|
|
||||||
// create mesh for circle
|
|
||||||
WgVertexBuffer circle;
|
|
||||||
circle.reset(buff.scale);
|
|
||||||
circle.appendCircle(halfWidth);
|
|
||||||
// append caps (round)
|
|
||||||
if (rstroke->cap == StrokeCap::Round) {
|
|
||||||
appendBuffer(circle, buff.data[0]);
|
|
||||||
// append ending cap if polyline is not closed
|
|
||||||
if (!buff.closed) appendBuffer(circle, buff.last());
|
|
||||||
}
|
|
||||||
// append joints (round)
|
|
||||||
if (rstroke->join == StrokeJoin::Round) {
|
|
||||||
for (size_t i = 1; i < buff.count - 1; i++) {
|
|
||||||
appendBuffer(circle, buff.data[i]);
|
|
||||||
}
|
|
||||||
if (buff.closed) appendBuffer(circle, buff.last());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// append closed endings
|
|
||||||
if (buff.closed) {
|
|
||||||
// close by bevel
|
|
||||||
if (rstroke->join == StrokeJoin::Bevel) {
|
|
||||||
appendBevel(buff.last(1), buff.data[0], buff.data[1], buff.lastDist(0), buff.dist[1].interval, halfWidth);
|
|
||||||
// close by mitter
|
|
||||||
} else if (rstroke->join == StrokeJoin::Miter) {
|
|
||||||
appendMiter(buff.last(1), buff.data[0], buff.data[1], buff.lastDist(0), buff.dist[1].interval, halfWidth, rstroke->miterlimit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// append joints (bevel)
|
|
||||||
if (rstroke->join == StrokeJoin::Bevel) {
|
|
||||||
for (size_t i = 1; i < buff.count - 1; i++) {
|
|
||||||
appendBevel(buff.data[i-1], buff.data[i], buff.data[i+1], buff.dist[i].interval, buff.dist[i+1].interval, halfWidth);
|
|
||||||
}
|
|
||||||
// append joints (mitter)
|
|
||||||
} else if (rstroke->join == StrokeJoin::Miter) {
|
|
||||||
for (size_t i = 1; i < buff.count - 1; i++) {
|
|
||||||
appendMiter(buff.data[i-1], buff.data[i], buff.data[i+1], buff.dist[i].interval, buff.dist[i+1].interval, halfWidth, rstroke->miterlimit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _TVG_WG_GEOMETRY_H_
|
#endif // _TVG_WG_GEOMETRY_H_
|
|
@ -22,127 +22,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include "tvgMath.h"
|
#include "tvgWgTessellator.h"
|
||||||
#include "tvgWgRenderData.h"
|
#include "tvgWgRenderData.h"
|
||||||
#include "tvgWgShaderTypes.h"
|
#include "tvgWgShaderTypes.h"
|
||||||
|
|
||||||
//***********************************************************************
|
|
||||||
// WgMeshData
|
|
||||||
//***********************************************************************
|
|
||||||
|
|
||||||
void WgMeshData::update(const WgVertexBuffer& vertexBuffer)
|
|
||||||
{
|
|
||||||
assert(vertexBuffer.count > 2);
|
|
||||||
// setup vertex data
|
|
||||||
vbuffer.reserve(vertexBuffer.count);
|
|
||||||
vbuffer.count = vertexBuffer.count;
|
|
||||||
memcpy(vbuffer.data, vertexBuffer.data, sizeof(vertexBuffer.data[0])*vertexBuffer.count);
|
|
||||||
// setup tex coords data
|
|
||||||
tbuffer.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void WgMeshData::update(const WgIndexedVertexBuffer& vertexBufferInd)
|
|
||||||
{
|
|
||||||
assert(vertexBufferInd.vcount > 2);
|
|
||||||
// setup vertex data
|
|
||||||
vbuffer.reserve(vertexBufferInd.vcount);
|
|
||||||
vbuffer.count = vertexBufferInd.vcount;
|
|
||||||
memcpy(vbuffer.data, vertexBufferInd.vbuff, sizeof(vertexBufferInd.vbuff[0])*vertexBufferInd.vcount);
|
|
||||||
// setup tex coords data
|
|
||||||
tbuffer.clear();
|
|
||||||
// copy index data
|
|
||||||
ibuffer.reserve(vertexBufferInd.icount);
|
|
||||||
ibuffer.count = vertexBufferInd.icount;
|
|
||||||
memcpy(ibuffer.data, vertexBufferInd.ibuff, sizeof(vertexBufferInd.ibuff[0])*vertexBufferInd.icount);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void WgMeshData::bbox(const Point pmin, const Point pmax)
|
|
||||||
{
|
|
||||||
const float data[] = {pmin.x, pmin.y, pmax.x, pmin.y, pmax.x, pmax.y, pmin.x, pmax.y};
|
|
||||||
// setup vertex data
|
|
||||||
vbuffer.reserve(4);
|
|
||||||
vbuffer.count = 4;
|
|
||||||
memcpy(vbuffer.data, data, sizeof(data));
|
|
||||||
// setup tex coords data
|
|
||||||
tbuffer.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void WgMeshData::imageBox(float w, float h)
|
|
||||||
{
|
|
||||||
const float vdata[] = {0.0f, 0.0f, w, 0.0f, w, h, 0.0f, h};
|
|
||||||
const float tdata[] = {0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
|
|
||||||
const uint32_t idata[] = {0, 1, 2, 0, 2, 3};
|
|
||||||
// setup vertex data
|
|
||||||
vbuffer.reserve(4);
|
|
||||||
vbuffer.count = 4;
|
|
||||||
memcpy(vbuffer.data, vdata, sizeof(vdata));
|
|
||||||
// setup tex coords data
|
|
||||||
tbuffer.reserve(4);
|
|
||||||
tbuffer.count = 4;
|
|
||||||
memcpy(tbuffer.data, tdata, sizeof(tdata));
|
|
||||||
// setup indexes data
|
|
||||||
ibuffer.reserve(6);
|
|
||||||
ibuffer.count = 6;
|
|
||||||
memcpy(ibuffer.data, idata, sizeof(idata));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void WgMeshData::blitBox()
|
|
||||||
{
|
|
||||||
const float vdata[] = {-1.0f, +1.0f, +1.0f, +1.0f, +1.0f, -1.0f, -1.0f, -1.0f};
|
|
||||||
const float tdata[] = {+0.0f, +0.0f, +1.0f, +0.0f, +1.0f, +1.0f, +0.0f, +1.0f};
|
|
||||||
const uint32_t idata[] = { 0, 1, 2, 0, 2, 3 };
|
|
||||||
// setup vertex data
|
|
||||||
vbuffer.reserve(4);
|
|
||||||
vbuffer.count = 4;
|
|
||||||
memcpy(vbuffer.data, vdata, sizeof(vdata));
|
|
||||||
// setup tex coords data
|
|
||||||
tbuffer.reserve(4);
|
|
||||||
tbuffer.count = 4;
|
|
||||||
memcpy(tbuffer.data, tdata, sizeof(tdata));
|
|
||||||
// setup indexes data
|
|
||||||
ibuffer.reserve(6);
|
|
||||||
ibuffer.count = 6;
|
|
||||||
memcpy(ibuffer.data, idata, sizeof(idata));
|
|
||||||
}
|
|
||||||
|
|
||||||
//***********************************************************************
|
|
||||||
// WgMeshDataGroup
|
|
||||||
//***********************************************************************
|
|
||||||
|
|
||||||
void WgMeshDataGroup::append(const WgVertexBuffer& vertexBuffer)
|
|
||||||
{
|
|
||||||
assert(vertexBuffer.count >= 3);
|
|
||||||
meshes.push(new WgMeshData());
|
|
||||||
meshes.last()->update(vertexBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void WgMeshDataGroup::append(const WgIndexedVertexBuffer& vertexBufferInd)
|
|
||||||
{
|
|
||||||
assert(vertexBufferInd.vcount >= 3);
|
|
||||||
meshes.push(new WgMeshData());
|
|
||||||
meshes.last()->update(vertexBufferInd);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void WgMeshDataGroup::append(const Point pmin, const Point pmax)
|
|
||||||
{
|
|
||||||
meshes.push(new WgMeshData());
|
|
||||||
meshes.last()->bbox(pmin, pmax);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void WgMeshDataGroup::release()
|
|
||||||
{
|
|
||||||
ARRAY_FOREACH(p, meshes) delete *p;
|
|
||||||
meshes.clear();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//***********************************************************************
|
//***********************************************************************
|
||||||
// WgImageData
|
// WgImageData
|
||||||
//***********************************************************************
|
//***********************************************************************
|
||||||
|
@ -257,95 +140,75 @@ void WgRenderDataPaint::updateClips(tvg::Array<tvg::RenderData> &clips) {
|
||||||
// WgRenderDataShape
|
// WgRenderDataShape
|
||||||
//***********************************************************************
|
//***********************************************************************
|
||||||
|
|
||||||
void WgRenderDataShape::appendShape(const WgVertexBuffer& vertexBuffer)
|
void WgRenderDataShape::updateBBox(BBox bb)
|
||||||
{
|
{
|
||||||
if (vertexBuffer.count < 3) return;
|
bbox.min = tvg::min(bbox.min, bb.min);
|
||||||
Point pmin{}, pmax{};
|
bbox.max = tvg::max(bbox.max, bb.max);
|
||||||
vertexBuffer.getMinMax(pmin, pmax);
|
|
||||||
meshGroupShapes.append(vertexBuffer);
|
|
||||||
meshGroupShapesBBox.append(pmin, pmax);
|
|
||||||
updateBBox(pmin, pmax);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgRenderDataShape::appendStroke(const WgIndexedVertexBuffer& vertexBufferInd)
|
void WgRenderDataShape::updateAABB(const Matrix& matrix)
|
||||||
{
|
{
|
||||||
if (vertexBufferInd.vcount < 3) return;
|
auto p0 = Point{bbox.min.x, bbox.min.y} * matrix;
|
||||||
Point pmin{}, pmax{};
|
auto p1 = Point{bbox.max.x, bbox.min.y} * matrix;
|
||||||
vertexBufferInd.getMinMax(pmin, pmax);
|
auto p2 = Point{bbox.min.x, bbox.max.y} * matrix;
|
||||||
meshGroupStrokes.append(vertexBufferInd);
|
auto p3 = Point{bbox.max.x, bbox.max.y} * matrix;
|
||||||
meshGroupStrokesBBox.append(pmin, pmax);
|
|
||||||
updateBBox(pmin, pmax);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void WgRenderDataShape::updateBBox(Point pmin, Point pmax)
|
|
||||||
{
|
|
||||||
pMin.x = std::min(pMin.x, pmin.x);
|
|
||||||
pMin.y = std::min(pMin.y, pmin.y);
|
|
||||||
pMax.x = std::max(pMax.x, pmax.x);
|
|
||||||
pMax.y = std::max(pMax.y, pmax.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void WgRenderDataShape::updateAABB(const Matrix& tr) {
|
|
||||||
auto p0 = Point{pMin.x, pMin.y} * tr;
|
|
||||||
auto p1 = Point{pMax.x, pMin.y} * tr;
|
|
||||||
auto p2 = Point{pMin.x, pMax.y} * tr;
|
|
||||||
auto p3 = Point{pMax.x, pMax.y} * tr;
|
|
||||||
aabb.min = {std::min(std::min(p0.x, p1.x), std::min(p2.x, p3.x)), std::min(std::min(p0.y, p1.y), std::min(p2.y, p3.y))};
|
aabb.min = {std::min(std::min(p0.x, p1.x), std::min(p2.x, p3.x)), std::min(std::min(p0.y, p1.y), std::min(p2.y, p3.y))};
|
||||||
aabb.max = {std::max(std::max(p0.x, p1.x), std::max(p2.x, p3.x)), std::max(std::max(p0.y, p1.y), std::max(p2.y, p3.y))};
|
aabb.max = {std::max(std::max(p0.x, p1.x), std::max(p2.x, p3.x)), std::max(std::max(p0.y, p1.y), std::max(p2.y, p3.y))};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgRenderDataShape::updateMeshes(const RenderShape &rshape, const Matrix& tr, WgGeometryBufferPool* pool)
|
void WgRenderDataShape::updateMeshes(const RenderShape &rshape, RenderUpdateFlag flag, const Matrix& matrix)
|
||||||
{
|
{
|
||||||
releaseMeshes();
|
releaseMeshes();
|
||||||
strokeFirst = rshape.strokeFirst();
|
strokeFirst = rshape.strokeFirst();
|
||||||
|
|
||||||
// get object scale
|
// update fill shapes
|
||||||
float scale = std::max(std::min(length(Point{tr.e11 + tr.e12,tr.e21 + tr.e22}), 8.0f), 1.0f);
|
if (flag & (RenderUpdateFlag::Color | RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform | RenderUpdateFlag::Path)) {
|
||||||
|
meshShape.clear();
|
||||||
|
|
||||||
// path decoded vertex buffer
|
WgBWTessellator bwTess{&meshShape};
|
||||||
auto pbuff = pool->reqVertexBuffer(scale);
|
if (rshape.trimpath()) {
|
||||||
|
RenderPath trimmedPath;
|
||||||
|
if (rshape.stroke->trim.trim(rshape.path, trimmedPath))
|
||||||
|
bwTess.tessellate(trimmedPath, matrix);
|
||||||
|
} else bwTess.tessellate(rshape.path, matrix);
|
||||||
|
|
||||||
pbuff->decodePath(rshape, true, [&](const WgVertexBuffer& path_buff) {
|
if (meshShape.ibuffer.count > 0) {;
|
||||||
appendShape(path_buff);
|
auto bbox = bwTess.getBBox();
|
||||||
if ((rshape.stroke) && (rshape.stroke->width > 0)) proceedStrokes(rshape.stroke, path_buff, pool);
|
meshShapeBBox.bbox(bbox.min, bbox.max);
|
||||||
}, rshape.trimpath());
|
updateBBox(bbox);
|
||||||
|
} else meshShape.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// update strokes shapes
|
||||||
|
if (rshape.stroke && (flag & (RenderUpdateFlag::Stroke | RenderUpdateFlag::GradientStroke | RenderUpdateFlag::Transform))) {
|
||||||
|
meshStrokes.clear();
|
||||||
|
|
||||||
|
WgStroker stroker{&meshStrokes, matrix};
|
||||||
|
stroker.stroke(&rshape);
|
||||||
|
|
||||||
|
if (meshStrokes.ibuffer.count > 0) {
|
||||||
|
auto bbox = stroker.getBBox();
|
||||||
|
meshStrokesBBox.bbox(bbox.min, bbox.max);
|
||||||
|
updateBBox(bbox);
|
||||||
|
} else meshStrokes.clear();
|
||||||
|
}
|
||||||
|
|
||||||
// update shapes bbox (with empty path handling)
|
// update shapes bbox (with empty path handling)
|
||||||
if ((this->meshGroupShapesBBox.meshes.count > 0 ) ||
|
if ((meshShape.vbuffer.count > 0 ) || (meshStrokes.vbuffer.count > 0)) {
|
||||||
(this->meshGroupStrokesBBox.meshes.count > 0)) {
|
updateAABB(matrix);
|
||||||
updateAABB(tr);
|
|
||||||
} else aabb = {{0, 0}, {0, 0}};
|
} else aabb = {{0, 0}, {0, 0}};
|
||||||
meshDataBBox.bbox(pMin, pMax);
|
meshBBox.bbox(bbox.min, bbox.max);
|
||||||
|
|
||||||
pool->retVertexBuffer(pbuff);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void WgRenderDataShape::proceedStrokes(const RenderStroke* rstroke, const WgVertexBuffer& buff, WgGeometryBufferPool* pool)
|
|
||||||
{
|
|
||||||
assert(rstroke);
|
|
||||||
auto strokesGenerator = pool->reqIndexedVertexBuffer(buff.scale);
|
|
||||||
if (rstroke->dash.length < DASH_PATTERN_THRESHOLD) strokesGenerator->appendStrokes(buff, rstroke);
|
|
||||||
else strokesGenerator->appendStrokesDashed(buff, rstroke);
|
|
||||||
|
|
||||||
appendStroke(*strokesGenerator);
|
|
||||||
|
|
||||||
pool->retIndexedVertexBuffer(strokesGenerator);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgRenderDataShape::releaseMeshes()
|
void WgRenderDataShape::releaseMeshes()
|
||||||
{
|
{
|
||||||
meshGroupStrokesBBox.release();
|
meshStrokes.clear();
|
||||||
meshGroupStrokes.release();
|
meshShape.clear();
|
||||||
meshGroupShapesBBox.release();
|
bbox.min = {FLT_MAX, FLT_MAX};
|
||||||
meshGroupShapes.release();
|
bbox.max = {0.0f, 0.0f};
|
||||||
pMin = {FLT_MAX, FLT_MAX};
|
|
||||||
pMax = {0.0f, 0.0f};
|
|
||||||
aabb = {{0, 0}, {0, 0}};
|
aabb = {{0, 0}, {0, 0}};
|
||||||
clips.clear();
|
clips.clear();
|
||||||
}
|
}
|
||||||
|
@ -379,10 +242,7 @@ WgRenderDataShape* WgRenderDataShapePool::allocate(WgContext& context)
|
||||||
|
|
||||||
void WgRenderDataShapePool::free(WgContext& context, WgRenderDataShape* renderData)
|
void WgRenderDataShapePool::free(WgContext& context, WgRenderDataShape* renderData)
|
||||||
{
|
{
|
||||||
renderData->meshGroupShapes.release();
|
renderData->releaseMeshes();
|
||||||
renderData->meshGroupShapesBBox.release();
|
|
||||||
renderData->meshGroupStrokes.release();
|
|
||||||
renderData->meshGroupStrokesBBox.release();
|
|
||||||
renderData->clips.clear();
|
renderData->clips.clear();
|
||||||
mPool.push(renderData);
|
mPool.push(renderData);
|
||||||
}
|
}
|
||||||
|
@ -620,7 +480,6 @@ void WgStageBufferGeometry::append(WgMeshData* meshData)
|
||||||
uint32_t vsize = meshData->vbuffer.count * sizeof(meshData->vbuffer[0]);
|
uint32_t vsize = meshData->vbuffer.count * sizeof(meshData->vbuffer[0]);
|
||||||
uint32_t tsize = meshData->tbuffer.count * sizeof(meshData->tbuffer[0]);
|
uint32_t tsize = meshData->tbuffer.count * sizeof(meshData->tbuffer[0]);
|
||||||
uint32_t isize = meshData->ibuffer.count * sizeof(meshData->ibuffer[0]);
|
uint32_t isize = meshData->ibuffer.count * sizeof(meshData->ibuffer[0]);
|
||||||
vmaxcount = std::max(vmaxcount, meshData->vbuffer.count);
|
|
||||||
// append vertex data
|
// append vertex data
|
||||||
if (vbuffer.reserved < vbuffer.count + vsize)
|
if (vbuffer.reserved < vbuffer.count + vsize)
|
||||||
vbuffer.grow(std::max(vsize, vbuffer.reserved));
|
vbuffer.grow(std::max(vsize, vbuffer.reserved));
|
||||||
|
@ -648,19 +507,13 @@ void WgStageBufferGeometry::append(WgMeshData* meshData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgStageBufferGeometry::append(WgMeshDataGroup* meshDataGroup)
|
|
||||||
{
|
|
||||||
ARRAY_FOREACH(p, meshDataGroup->meshes) append(*p);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void WgStageBufferGeometry::append(WgRenderDataShape* renderDataShape)
|
void WgStageBufferGeometry::append(WgRenderDataShape* renderDataShape)
|
||||||
{
|
{
|
||||||
append(&renderDataShape->meshGroupShapes);
|
append(&renderDataShape->meshShape);
|
||||||
append(&renderDataShape->meshGroupShapesBBox);
|
append(&renderDataShape->meshShapeBBox);
|
||||||
append(&renderDataShape->meshGroupStrokes);
|
append(&renderDataShape->meshStrokes);
|
||||||
append(&renderDataShape->meshGroupStrokesBBox);
|
append(&renderDataShape->meshStrokesBBox);
|
||||||
append(&renderDataShape->meshDataBBox);
|
append(&renderDataShape->meshBBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -681,7 +534,6 @@ void WgStageBufferGeometry::clear()
|
||||||
{
|
{
|
||||||
vbuffer.clear();
|
vbuffer.clear();
|
||||||
ibuffer.clear();
|
ibuffer.clear();
|
||||||
vmaxcount = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -689,5 +541,4 @@ void WgStageBufferGeometry::flush(WgContext& context)
|
||||||
{
|
{
|
||||||
context.allocateBufferVertex(vbuffer_gpu, (float *)vbuffer.data, vbuffer.count);
|
context.allocateBufferVertex(vbuffer_gpu, (float *)vbuffer.data, vbuffer.count);
|
||||||
context.allocateBufferIndex(ibuffer_gpu, (uint32_t *)ibuffer.data, ibuffer.count);
|
context.allocateBufferIndex(ibuffer_gpu, (uint32_t *)ibuffer.data, ibuffer.count);
|
||||||
context.allocateBufferIndexFan(vmaxcount);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,30 +27,6 @@
|
||||||
#include "tvgWgGeometry.h"
|
#include "tvgWgGeometry.h"
|
||||||
#include "tvgWgShaderTypes.h"
|
#include "tvgWgShaderTypes.h"
|
||||||
|
|
||||||
struct WgMeshData {
|
|
||||||
Array<Point> vbuffer;
|
|
||||||
Array<Point> tbuffer;
|
|
||||||
Array<uint32_t> ibuffer;
|
|
||||||
size_t voffset{};
|
|
||||||
size_t toffset{};
|
|
||||||
size_t ioffset{};
|
|
||||||
|
|
||||||
void update(const WgVertexBuffer& vertexBuffer);
|
|
||||||
void update(const WgIndexedVertexBuffer& vertexBufferInd);
|
|
||||||
void bbox(const Point pmin, const Point pmax);
|
|
||||||
void imageBox(float w, float h);
|
|
||||||
void blitBox();
|
|
||||||
};
|
|
||||||
|
|
||||||
struct WgMeshDataGroup {
|
|
||||||
Array<WgMeshData*> meshes{};
|
|
||||||
|
|
||||||
void append(const WgVertexBuffer& vertexBuffer);
|
|
||||||
void append(const WgIndexedVertexBuffer& vertexBufferInd);
|
|
||||||
void append(const Point pmin, const Point pmax);
|
|
||||||
void release();
|
|
||||||
};
|
|
||||||
|
|
||||||
struct WgImageData {
|
struct WgImageData {
|
||||||
WGPUTexture texture{};
|
WGPUTexture texture{};
|
||||||
WGPUTextureView textureView{};
|
WGPUTextureView textureView{};
|
||||||
|
@ -96,22 +72,18 @@ struct WgRenderDataShape: public WgRenderDataPaint
|
||||||
{
|
{
|
||||||
WgRenderSettings renderSettingsShape{};
|
WgRenderSettings renderSettingsShape{};
|
||||||
WgRenderSettings renderSettingsStroke{};
|
WgRenderSettings renderSettingsStroke{};
|
||||||
WgMeshDataGroup meshGroupShapes{};
|
WgMeshData meshBBox{};
|
||||||
WgMeshDataGroup meshGroupShapesBBox{};
|
WgMeshData meshShape{};
|
||||||
WgMeshData meshDataBBox{};
|
WgMeshData meshShapeBBox{};
|
||||||
WgMeshDataGroup meshGroupStrokes{};
|
WgMeshData meshStrokes{};
|
||||||
WgMeshDataGroup meshGroupStrokesBBox{};
|
WgMeshData meshStrokesBBox{};
|
||||||
Point pMin{};
|
|
||||||
Point pMax{};
|
|
||||||
bool strokeFirst{};
|
bool strokeFirst{};
|
||||||
FillRule fillRule{};
|
FillRule fillRule{};
|
||||||
|
BBox bbox;
|
||||||
|
|
||||||
void appendShape(const WgVertexBuffer& vertexBuffer);
|
void updateBBox(BBox bb);
|
||||||
void appendStroke(const WgIndexedVertexBuffer& vertexBufferInd);
|
void updateAABB(const Matrix& matrix);
|
||||||
void updateBBox(Point pmin, Point pmax);
|
void updateMeshes(const RenderShape& rshape, RenderUpdateFlag flag, const Matrix& matrix);
|
||||||
void updateAABB(const Matrix& tr);
|
|
||||||
void updateMeshes(const RenderShape& rshape, const Matrix& tr, WgGeometryBufferPool* pool);
|
|
||||||
void proceedStrokes(const RenderStroke* rstroke, const WgVertexBuffer& buff, WgGeometryBufferPool* pool);
|
|
||||||
void releaseMeshes();
|
void releaseMeshes();
|
||||||
void release(WgContext& context) override;
|
void release(WgContext& context) override;
|
||||||
Type type() override { return Type::Shape; };
|
Type type() override { return Type::Shape; };
|
||||||
|
@ -207,13 +179,11 @@ class WgStageBufferGeometry {
|
||||||
private:
|
private:
|
||||||
Array<uint8_t> vbuffer;
|
Array<uint8_t> vbuffer;
|
||||||
Array<uint8_t> ibuffer;
|
Array<uint8_t> ibuffer;
|
||||||
uint32_t vmaxcount{};
|
|
||||||
public:
|
public:
|
||||||
WGPUBuffer vbuffer_gpu{};
|
WGPUBuffer vbuffer_gpu{};
|
||||||
WGPUBuffer ibuffer_gpu{};
|
WGPUBuffer ibuffer_gpu{};
|
||||||
|
|
||||||
void append(WgMeshData* meshData);
|
void append(WgMeshData* meshData);
|
||||||
void append(WgMeshDataGroup* meshDataGroup);
|
|
||||||
void append(WgRenderDataShape* renderDataShape);
|
void append(WgRenderDataShape* renderDataShape);
|
||||||
void append(WgRenderDataPicture* renderDataPicture);
|
void append(WgRenderDataPicture* renderDataPicture);
|
||||||
void initialize(WgContext& context){};
|
void initialize(WgContext& context){};
|
||||||
|
|
|
@ -135,7 +135,7 @@ RenderData WgRenderer::prepare(const RenderShape& rshape, RenderData data, const
|
||||||
|
|
||||||
// update geometry
|
// update geometry
|
||||||
if (!data || (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Stroke))) {
|
if (!data || (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Stroke))) {
|
||||||
renderDataShape->updateMeshes(rshape, transform, mBufferPool.pool);
|
renderDataShape->updateMeshes(rshape, flags, transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
// update paint settings
|
// update paint settings
|
||||||
|
@ -408,10 +408,6 @@ WgRenderer::WgRenderer()
|
||||||
{
|
{
|
||||||
if (TaskScheduler::onthread()) {
|
if (TaskScheduler::onthread()) {
|
||||||
TVGLOG("WG_RENDERER", "Running on a non-dominant thread!, Renderer(%p)", this);
|
TVGLOG("WG_RENDERER", "Running on a non-dominant thread!, Renderer(%p)", this);
|
||||||
mBufferPool.pool = new WgGeometryBufferPool;
|
|
||||||
mBufferPool.individual = true;
|
|
||||||
} else {
|
|
||||||
mBufferPool.pool = WgGeometryBufferPool::instance();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
++rendererCnt;
|
++rendererCnt;
|
||||||
|
@ -422,8 +418,6 @@ WgRenderer::~WgRenderer()
|
||||||
{
|
{
|
||||||
release();
|
release();
|
||||||
|
|
||||||
if (mBufferPool.individual) delete(mBufferPool.pool);
|
|
||||||
|
|
||||||
--rendererCnt;
|
--rendererCnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -107,11 +107,6 @@ private:
|
||||||
WGPUTexture targetTexture{}; // external handle
|
WGPUTexture targetTexture{}; // external handle
|
||||||
WGPUSurfaceTexture surfaceTexture{};
|
WGPUSurfaceTexture surfaceTexture{};
|
||||||
WGPUSurface surface{}; // external handle
|
WGPUSurface surface{}; // external handle
|
||||||
|
|
||||||
struct {
|
|
||||||
WgGeometryBufferPool* pool; //private buffer pool
|
|
||||||
bool individual = false; //buffer-pool sharing policy
|
|
||||||
} mBufferPool;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _TVG_WG_RENDERER_H_ */
|
#endif /* _TVG_WG_RENDERER_H_ */
|
||||||
|
|
537
src/renderer/wg_engine/tvgWgTessellator.cpp
Normal file
537
src/renderer/wg_engine/tvgWgTessellator.cpp
Normal file
|
@ -0,0 +1,537 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025 the ThorVG project. All rights reserved.
|
||||||
|
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tvgWgTessellator.h"
|
||||||
|
#include "tvgMath.h"
|
||||||
|
|
||||||
|
|
||||||
|
WgStroker::WgStroker(WgMeshData* buffer, const Matrix& matrix) : mBuffer(buffer), mMatrix(matrix)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WgStroker::stroke(const RenderShape *rshape)
|
||||||
|
{
|
||||||
|
mMiterLimit = rshape->strokeMiterlimit();
|
||||||
|
mStrokeCap = rshape->strokeCap();
|
||||||
|
mStrokeJoin = rshape->strokeJoin();
|
||||||
|
mStrokeWidth = rshape->strokeWidth();
|
||||||
|
|
||||||
|
if (isinf(mMatrix.e11)) {
|
||||||
|
auto strokeWidth = rshape->strokeWidth() * scaling(mMatrix);
|
||||||
|
if (strokeWidth <= MIN_WG_STROKE_WIDTH) strokeWidth = MIN_WG_STROKE_WIDTH;
|
||||||
|
mStrokeWidth = strokeWidth / mMatrix.e11;
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderPath dashed;
|
||||||
|
if (rshape->strokeDash(dashed)) doStroke(dashed);
|
||||||
|
else if (rshape->trimpath()) {
|
||||||
|
RenderPath trimmedPath;
|
||||||
|
if (rshape->stroke->trim.trim(rshape->path, trimmedPath)) doStroke(trimmedPath);
|
||||||
|
} else
|
||||||
|
doStroke(rshape->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RenderRegion WgStroker::bounds() const
|
||||||
|
{
|
||||||
|
return {{int32_t(floor(mLeftTop.x)), int32_t(floor(mLeftTop.y))}, {int32_t(ceil(mRightBottom.x)), int32_t(ceil(mRightBottom.y))}};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BBox WgStroker::getBBox() const
|
||||||
|
{
|
||||||
|
return {mLeftTop, mRightBottom};
|
||||||
|
}
|
||||||
|
|
||||||
|
void WgStroker::doStroke(const RenderPath& path)
|
||||||
|
{
|
||||||
|
mBuffer->vbuffer.reserve(path.pts.count * 4 + 16);
|
||||||
|
mBuffer->ibuffer.reserve(path.pts.count * 3);
|
||||||
|
|
||||||
|
auto validStrokeCap = false;
|
||||||
|
auto pts = path.pts.data;
|
||||||
|
|
||||||
|
ARRAY_FOREACH(cmd, path.cmds) {
|
||||||
|
switch (*cmd) {
|
||||||
|
case PathCommand::MoveTo: {
|
||||||
|
if (validStrokeCap) { // check this, so we can skip if path only contains move instruction
|
||||||
|
strokeCap();
|
||||||
|
validStrokeCap = false;
|
||||||
|
}
|
||||||
|
mStrokeState.firstPt = *pts;
|
||||||
|
mStrokeState.firstPtDir = {0.0f, 0.0f};
|
||||||
|
mStrokeState.prevPt = *pts;
|
||||||
|
mStrokeState.prevPtDir = {0.0f, 0.0f};
|
||||||
|
pts++;
|
||||||
|
validStrokeCap = false;
|
||||||
|
} break;
|
||||||
|
case PathCommand::LineTo: {
|
||||||
|
validStrokeCap = true;
|
||||||
|
this->strokeLineTo(*pts);
|
||||||
|
pts++;
|
||||||
|
} break;
|
||||||
|
case PathCommand::CubicTo: {
|
||||||
|
validStrokeCap = true;
|
||||||
|
this->strokeCubicTo(pts[0], pts[1], pts[2]);
|
||||||
|
pts += 3;
|
||||||
|
} break;
|
||||||
|
case PathCommand::Close: {
|
||||||
|
this->strokeClose();
|
||||||
|
|
||||||
|
validStrokeCap = false;
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (validStrokeCap) strokeCap();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WgStroker::strokeCap()
|
||||||
|
{
|
||||||
|
if (mStrokeCap == StrokeCap::Butt) return;
|
||||||
|
|
||||||
|
if (mStrokeCap == StrokeCap::Square) {
|
||||||
|
if (mStrokeState.firstPt == mStrokeState.prevPt) strokeSquarePoint(mStrokeState.firstPt);
|
||||||
|
else {
|
||||||
|
strokeSquare(mStrokeState.firstPt, {-mStrokeState.firstPtDir.x, -mStrokeState.firstPtDir.y});
|
||||||
|
strokeSquare(mStrokeState.prevPt, mStrokeState.prevPtDir);
|
||||||
|
}
|
||||||
|
} else if (mStrokeCap == StrokeCap::Round) {
|
||||||
|
if (mStrokeState.firstPt == mStrokeState.prevPt) strokeRoundPoint(mStrokeState.firstPt);
|
||||||
|
else {
|
||||||
|
strokeRound(mStrokeState.firstPt, {-mStrokeState.firstPtDir.x, -mStrokeState.firstPtDir.y});
|
||||||
|
strokeRound(mStrokeState.prevPt, mStrokeState.prevPtDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WgStroker::strokeLineTo(const Point& curr)
|
||||||
|
{
|
||||||
|
auto dir = (curr - mStrokeState.prevPt);
|
||||||
|
normalize(dir);
|
||||||
|
|
||||||
|
if (dir.x == 0.f && dir.y == 0.f) return; //same point
|
||||||
|
|
||||||
|
auto normal = Point{-dir.y, dir.x};
|
||||||
|
auto a = mStrokeState.prevPt + normal * strokeRadius();
|
||||||
|
auto b = mStrokeState.prevPt - normal * strokeRadius();
|
||||||
|
auto c = curr + normal * strokeRadius();
|
||||||
|
auto d = curr - normal * strokeRadius();
|
||||||
|
|
||||||
|
auto ia = mBuffer->vbuffer.count; mBuffer->vbuffer.push(a);
|
||||||
|
auto ib = mBuffer->vbuffer.count; mBuffer->vbuffer.push(b);
|
||||||
|
auto ic = mBuffer->vbuffer.count; mBuffer->vbuffer.push(c);
|
||||||
|
auto id = mBuffer->vbuffer.count; mBuffer->vbuffer.push(d);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a --------- c
|
||||||
|
* | |
|
||||||
|
* | |
|
||||||
|
* b-----------d
|
||||||
|
*/
|
||||||
|
|
||||||
|
mBuffer->ibuffer.push(ia);
|
||||||
|
mBuffer->ibuffer.push(ib);
|
||||||
|
mBuffer->ibuffer.push(ic);
|
||||||
|
mBuffer->ibuffer.push(ib);
|
||||||
|
mBuffer->ibuffer.push(id);
|
||||||
|
mBuffer->ibuffer.push(ic);
|
||||||
|
|
||||||
|
if (mStrokeState.prevPt == mStrokeState.firstPt) {
|
||||||
|
// first point after moveTo
|
||||||
|
mStrokeState.prevPt = curr;
|
||||||
|
mStrokeState.prevPtDir = dir;
|
||||||
|
mStrokeState.firstPtDir = dir;
|
||||||
|
} else {
|
||||||
|
this->strokeJoin(dir);
|
||||||
|
mStrokeState.prevPtDir = dir;
|
||||||
|
mStrokeState.prevPt = curr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ia == 0) {
|
||||||
|
mRightBottom.x = mLeftTop.x = curr.x;
|
||||||
|
mRightBottom.y = mLeftTop.y = curr.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
mLeftTop.x = std::min(mLeftTop.x, std::min(std::min(a.x, b.x), std::min(c.x, d.x)));
|
||||||
|
mLeftTop.y = std::min(mLeftTop.y, std::min(std::min(a.y, b.y), std::min(c.y, d.y)));
|
||||||
|
mRightBottom.x = std::max(mRightBottom.x, std::max(std::max(a.x, b.x), std::max(c.x, d.x)));
|
||||||
|
mRightBottom.y = std::max(mRightBottom.y, std::max(std::max(a.y, b.y), std::max(c.y, d.y)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WgStroker::strokeCubicTo(const Point& cnt1, const Point& cnt2, const Point& end)
|
||||||
|
{
|
||||||
|
Bezier curve {mStrokeState.prevPt, cnt1, cnt2, end};
|
||||||
|
|
||||||
|
Bezier relCurve {curve.start, curve.ctrl1, curve.ctrl2, curve.end};
|
||||||
|
relCurve.start *= mMatrix;
|
||||||
|
relCurve.ctrl1 *= mMatrix;
|
||||||
|
relCurve.ctrl2 *= mMatrix;
|
||||||
|
relCurve.end *= mMatrix;
|
||||||
|
|
||||||
|
auto count = relCurve.segments();
|
||||||
|
auto step = 1.f / count;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i <= count; i++) {
|
||||||
|
strokeLineTo(curve.at(step * i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WgStroker::strokeClose()
|
||||||
|
{
|
||||||
|
if (length(mStrokeState.prevPt - mStrokeState.firstPt) > 0.015625f) {
|
||||||
|
this->strokeLineTo(mStrokeState.firstPt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// join firstPt with prevPt
|
||||||
|
this->strokeJoin(mStrokeState.firstPtDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WgStroker::strokeJoin(const Point& dir)
|
||||||
|
{
|
||||||
|
auto orient = orientation(mStrokeState.prevPt - mStrokeState.prevPtDir, mStrokeState.prevPt, mStrokeState.prevPt + dir);
|
||||||
|
|
||||||
|
if (orient == Orientation::Linear) {
|
||||||
|
if (mStrokeState.prevPtDir == dir) return; // check is same direction
|
||||||
|
if (mStrokeJoin != StrokeJoin::Round) return; // opposite direction
|
||||||
|
|
||||||
|
auto normal = Point{-dir.y, dir.x};
|
||||||
|
auto p1 = mStrokeState.prevPt + normal * strokeRadius();
|
||||||
|
auto p2 = mStrokeState.prevPt - normal * strokeRadius();
|
||||||
|
auto oc = mStrokeState.prevPt + dir * strokeRadius();
|
||||||
|
|
||||||
|
this->strokeRound(p1, oc, mStrokeState.prevPt);
|
||||||
|
this->strokeRound(oc, p2, mStrokeState.prevPt);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
auto normal = Point{-dir.y, dir.x};
|
||||||
|
auto prevNormal = Point{-mStrokeState.prevPtDir.y, mStrokeState.prevPtDir.x};
|
||||||
|
Point prevJoin, currJoin;
|
||||||
|
|
||||||
|
if (orient == Orientation::CounterClockwise) {
|
||||||
|
prevJoin = mStrokeState.prevPt + prevNormal * strokeRadius();
|
||||||
|
currJoin = mStrokeState.prevPt + normal * strokeRadius();
|
||||||
|
} else {
|
||||||
|
prevJoin = mStrokeState.prevPt - prevNormal * strokeRadius();
|
||||||
|
currJoin = mStrokeState.prevPt - normal * strokeRadius();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mStrokeJoin == StrokeJoin::Miter) strokeMiter(prevJoin, currJoin, mStrokeState.prevPt);
|
||||||
|
else if (mStrokeJoin == StrokeJoin::Bevel) strokeBevel(prevJoin, currJoin, mStrokeState.prevPt);
|
||||||
|
else this->strokeRound(prevJoin, currJoin, mStrokeState.prevPt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WgStroker::strokeRound(const Point &prev, const Point& curr, const Point& center)
|
||||||
|
{
|
||||||
|
if (orientation(prev, center, curr) == Orientation::Linear) return;
|
||||||
|
|
||||||
|
mLeftTop.x = std::min(mLeftTop.x, std::min(center.x, std::min(prev.x, curr.x)));
|
||||||
|
mLeftTop.y = std::min(mLeftTop.y, std::min(center.y, std::min(prev.y, curr.y)));
|
||||||
|
mRightBottom.x = std::max(mRightBottom.x, std::max(center.x, std::max(prev.x, curr.x)));
|
||||||
|
mRightBottom.y = std::max(mRightBottom.y, std::max(center.y, std::max(prev.y, curr.y)));
|
||||||
|
|
||||||
|
// Fixme: just use bezier curve to calculate step count
|
||||||
|
auto count = Bezier(prev, curr, strokeRadius()).segments();
|
||||||
|
auto c = mBuffer->vbuffer.count; mBuffer->vbuffer.push(center);
|
||||||
|
auto pi = mBuffer->vbuffer.count; mBuffer->vbuffer.push(prev);
|
||||||
|
auto step = 1.f / (count - 1);
|
||||||
|
auto dir = curr - prev;
|
||||||
|
|
||||||
|
for (uint32_t i = 1; i < static_cast<uint32_t>(count); i++) {
|
||||||
|
auto t = i * step;
|
||||||
|
auto p = prev + dir * t;
|
||||||
|
auto o_dir = p - center;
|
||||||
|
normalize(o_dir);
|
||||||
|
|
||||||
|
auto out = center + o_dir * strokeRadius();
|
||||||
|
auto oi = mBuffer->vbuffer.count; mBuffer->vbuffer.push(out);
|
||||||
|
|
||||||
|
mBuffer->ibuffer.push(c);
|
||||||
|
mBuffer->ibuffer.push(pi);
|
||||||
|
mBuffer->ibuffer.push(oi);
|
||||||
|
|
||||||
|
pi = oi;
|
||||||
|
|
||||||
|
mLeftTop.x = std::min(mLeftTop.x, out.x);
|
||||||
|
mLeftTop.y = std::min(mLeftTop.y, out.y);
|
||||||
|
mRightBottom.x = std::max(mRightBottom.x, out.x);
|
||||||
|
mRightBottom.y = std::max(mRightBottom.y, out.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WgStroker::strokeRoundPoint(const Point &p)
|
||||||
|
{
|
||||||
|
// Fixme: just use bezier curve to calculate step count
|
||||||
|
auto count = Bezier(p, p, strokeRadius()).segments() * 2;
|
||||||
|
auto c = mBuffer->vbuffer.count; mBuffer->vbuffer.push(p);
|
||||||
|
auto step = 2 * MATH_PI / (count - 1);
|
||||||
|
|
||||||
|
for (uint32_t i = 1; i <= static_cast<uint32_t>(count); i++) {
|
||||||
|
float angle = i * step;
|
||||||
|
Point dir = {cos(angle), sin(angle)};
|
||||||
|
Point out = p + dir * strokeRadius();
|
||||||
|
auto oi = mBuffer->vbuffer.count; mBuffer->vbuffer.push(out);
|
||||||
|
|
||||||
|
if (oi > 1) {
|
||||||
|
mBuffer->ibuffer.push(c);
|
||||||
|
mBuffer->ibuffer.push(oi);
|
||||||
|
mBuffer->ibuffer.push(oi - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mLeftTop.x = std::min(mLeftTop.x, p.x - strokeRadius());
|
||||||
|
mLeftTop.y = std::min(mLeftTop.y, p.y - strokeRadius());
|
||||||
|
mRightBottom.x = std::max(mRightBottom.x, p.x + strokeRadius());
|
||||||
|
mRightBottom.y = std::max(mRightBottom.y, p.y + strokeRadius());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WgStroker::strokeMiter(const Point& prev, const Point& curr, const Point& center)
|
||||||
|
{
|
||||||
|
auto pp1 = prev - center;
|
||||||
|
auto pp2 = curr - center;
|
||||||
|
auto out = pp1 + pp2;
|
||||||
|
auto k = 2.f * strokeRadius() * strokeRadius() / (out.x * out.x + out.y * out.y);
|
||||||
|
auto pe = out * k;
|
||||||
|
|
||||||
|
if (length(pe) >= mMiterLimit * strokeRadius()) {
|
||||||
|
this->strokeBevel(prev, curr, center);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto join = center + pe;
|
||||||
|
auto c = mBuffer->vbuffer.count; mBuffer->vbuffer.push(center);
|
||||||
|
auto cp1 = mBuffer->vbuffer.count; mBuffer->vbuffer.push(prev);
|
||||||
|
auto cp2 = mBuffer->vbuffer.count; mBuffer->vbuffer.push(curr);
|
||||||
|
auto e = mBuffer->vbuffer.count; mBuffer->vbuffer.push(join);
|
||||||
|
|
||||||
|
mBuffer->ibuffer.push(c);
|
||||||
|
mBuffer->ibuffer.push(cp1);
|
||||||
|
mBuffer->ibuffer.push(e);
|
||||||
|
|
||||||
|
mBuffer->ibuffer.push(e);
|
||||||
|
mBuffer->ibuffer.push(cp2);
|
||||||
|
mBuffer->ibuffer.push(c);
|
||||||
|
|
||||||
|
mLeftTop.x = std::min(mLeftTop.x, join.x);
|
||||||
|
mLeftTop.y = std::min(mLeftTop.y, join.y);
|
||||||
|
|
||||||
|
mRightBottom.x = std::max(mRightBottom.x, join.x);
|
||||||
|
mRightBottom.y = std::max(mRightBottom.y, join.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WgStroker::strokeBevel(const Point& prev, const Point& curr, const Point& center)
|
||||||
|
{
|
||||||
|
auto a = mBuffer->vbuffer.count; mBuffer->vbuffer.push(prev);
|
||||||
|
auto b = mBuffer->vbuffer.count; mBuffer->vbuffer.push(curr);
|
||||||
|
auto c = mBuffer->vbuffer.count; mBuffer->vbuffer.push(center);
|
||||||
|
|
||||||
|
mBuffer->ibuffer.push(a);
|
||||||
|
mBuffer->ibuffer.push(b);
|
||||||
|
mBuffer->ibuffer.push(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WgStroker::strokeSquare(const Point& p, const Point& outDir)
|
||||||
|
{
|
||||||
|
auto normal = Point{-outDir.y, outDir.x};
|
||||||
|
|
||||||
|
auto a = p + normal * strokeRadius();
|
||||||
|
auto b = p - normal * strokeRadius();
|
||||||
|
auto c = a + outDir * strokeRadius();
|
||||||
|
auto d = b + outDir * strokeRadius();
|
||||||
|
|
||||||
|
auto ai = mBuffer->vbuffer.count; mBuffer->vbuffer.push(a);
|
||||||
|
auto bi = mBuffer->vbuffer.count; mBuffer->vbuffer.push(b);
|
||||||
|
auto ci = mBuffer->vbuffer.count; mBuffer->vbuffer.push(c);
|
||||||
|
auto di = mBuffer->vbuffer.count; mBuffer->vbuffer.push(d);
|
||||||
|
|
||||||
|
mBuffer->ibuffer.push(ai);
|
||||||
|
mBuffer->ibuffer.push(bi);
|
||||||
|
mBuffer->ibuffer.push(ci);
|
||||||
|
|
||||||
|
mBuffer->ibuffer.push(ci);
|
||||||
|
mBuffer->ibuffer.push(bi);
|
||||||
|
mBuffer->ibuffer.push(di);
|
||||||
|
|
||||||
|
mLeftTop.x = std::min(mLeftTop.x, std::min(std::min(a.x, b.x), std::min(c.x, d.x)));
|
||||||
|
mLeftTop.y = std::min(mLeftTop.y, std::min(std::min(a.y, b.y), std::min(c.y, d.y)));
|
||||||
|
mRightBottom.x = std::max(mRightBottom.x, std::max(std::max(a.x, b.x), std::max(c.x, d.x)));
|
||||||
|
mRightBottom.y = std::max(mRightBottom.y, std::max(std::max(a.y, b.y), std::max(c.y, d.y)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WgStroker::strokeSquarePoint(const Point& p)
|
||||||
|
{
|
||||||
|
auto offsetX = Point{strokeRadius(), 0.0f};
|
||||||
|
auto offsetY = Point{0.0f, strokeRadius()};
|
||||||
|
|
||||||
|
auto a = p + offsetX + offsetY;
|
||||||
|
auto b = p - offsetX + offsetY;
|
||||||
|
auto c = p - offsetX - offsetY;
|
||||||
|
auto d = p + offsetX - offsetY;
|
||||||
|
|
||||||
|
auto ai = mBuffer->vbuffer.count; mBuffer->vbuffer.push(a);
|
||||||
|
auto bi = mBuffer->vbuffer.count; mBuffer->vbuffer.push(b);
|
||||||
|
auto ci = mBuffer->vbuffer.count; mBuffer->vbuffer.push(c);
|
||||||
|
auto di = mBuffer->vbuffer.count; mBuffer->vbuffer.push(d);
|
||||||
|
|
||||||
|
mBuffer->ibuffer.push(ai);
|
||||||
|
mBuffer->ibuffer.push(bi);
|
||||||
|
mBuffer->ibuffer.push(ci);
|
||||||
|
|
||||||
|
mBuffer->ibuffer.push(ci);
|
||||||
|
mBuffer->ibuffer.push(di);
|
||||||
|
mBuffer->ibuffer.push(ai);
|
||||||
|
|
||||||
|
mLeftTop.x = std::min(mLeftTop.x, std::min(std::min(a.x, b.x), std::min(c.x, d.x)));
|
||||||
|
mLeftTop.y = std::min(mLeftTop.y, std::min(std::min(a.y, b.y), std::min(c.y, d.y)));
|
||||||
|
mRightBottom.x = std::max(mRightBottom.x, std::max(std::max(a.x, b.x), std::max(c.x, d.x)));
|
||||||
|
mRightBottom.y = std::max(mRightBottom.y, std::max(std::max(a.y, b.y), std::max(c.y, d.y)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WgStroker::strokeRound(const Point& p, const Point& outDir)
|
||||||
|
{
|
||||||
|
auto normal = Point{-outDir.y, outDir.x};
|
||||||
|
auto a = p + normal * strokeRadius();
|
||||||
|
auto b = p - normal * strokeRadius();
|
||||||
|
auto c = p + outDir * strokeRadius();
|
||||||
|
|
||||||
|
strokeRound(a, c, p);
|
||||||
|
strokeRound(c, b, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
WgBWTessellator::WgBWTessellator(WgMeshData* buffer): mBuffer(buffer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WgBWTessellator::tessellate(const RenderPath& path, const Matrix& matrix)
|
||||||
|
{
|
||||||
|
if (path.pts.count <= 2) return;
|
||||||
|
|
||||||
|
auto cmds = path.cmds.data;
|
||||||
|
auto cmdCnt = path.cmds.count;
|
||||||
|
auto pts = path.pts.data;
|
||||||
|
auto ptsCnt = path.pts.count;
|
||||||
|
|
||||||
|
uint32_t firstIndex = 0;
|
||||||
|
uint32_t prevIndex = 0;
|
||||||
|
|
||||||
|
mBuffer->vbuffer.reserve(ptsCnt * 2);
|
||||||
|
mBuffer->ibuffer.reserve((ptsCnt - 2) * 3);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < cmdCnt; i++) {
|
||||||
|
switch(cmds[i]) {
|
||||||
|
case PathCommand::MoveTo: {
|
||||||
|
firstIndex = pushVertex(pts->x, pts->y);
|
||||||
|
prevIndex = 0;
|
||||||
|
pts++;
|
||||||
|
} break;
|
||||||
|
case PathCommand::LineTo: {
|
||||||
|
if (prevIndex == 0) {
|
||||||
|
prevIndex = pushVertex(pts->x, pts->y);
|
||||||
|
pts++;
|
||||||
|
} else {
|
||||||
|
auto currIndex = pushVertex(pts->x, pts->y);
|
||||||
|
pushTriangle(firstIndex, prevIndex, currIndex);
|
||||||
|
prevIndex = currIndex;
|
||||||
|
pts++;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case PathCommand::CubicTo: {
|
||||||
|
Bezier curve{pts[-1], pts[0], pts[1], pts[2]};
|
||||||
|
Bezier relCurve {pts[-1], pts[0], pts[1], pts[2]};
|
||||||
|
relCurve.start *= matrix;
|
||||||
|
relCurve.ctrl1 *= matrix;
|
||||||
|
relCurve.ctrl2 *= matrix;
|
||||||
|
relCurve.end *= matrix;
|
||||||
|
|
||||||
|
auto stepCount = relCurve.segments();
|
||||||
|
if (stepCount <= 1) stepCount = 2;
|
||||||
|
|
||||||
|
float step = 1.f / stepCount;
|
||||||
|
|
||||||
|
for (uint32_t s = 1; s <= static_cast<uint32_t>(stepCount); s++) {
|
||||||
|
auto pt = curve.at(step * s);
|
||||||
|
auto currIndex = pushVertex(pt.x, pt.y);
|
||||||
|
|
||||||
|
if (prevIndex == 0) {
|
||||||
|
prevIndex = currIndex;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pushTriangle(firstIndex, prevIndex, currIndex);
|
||||||
|
prevIndex = currIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
pts += 3;
|
||||||
|
} break;
|
||||||
|
case PathCommand::Close:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RenderRegion WgBWTessellator::bounds() const
|
||||||
|
{
|
||||||
|
return {{int32_t(floor(bbox.min.x)), int32_t(floor(bbox.min.y))}, {int32_t(ceil(bbox.max.x)), int32_t(ceil(bbox.max.y))}};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BBox WgBWTessellator::getBBox() const
|
||||||
|
{
|
||||||
|
return bbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t WgBWTessellator::pushVertex(float x, float y)
|
||||||
|
{
|
||||||
|
auto index = mBuffer->vbuffer.count;
|
||||||
|
mBuffer->vbuffer.push({x, y});
|
||||||
|
if (index == 0) bbox.max = bbox.min = {x, y};
|
||||||
|
else bbox = {{std::min(bbox.min.x, x), std::min(bbox.min.y, y)}, {std::max(bbox.max.x, x), std::max(bbox.max.y, y)}};
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WgBWTessellator::pushTriangle(uint32_t a, uint32_t b, uint32_t c)
|
||||||
|
{
|
||||||
|
mBuffer->ibuffer.push(a);
|
||||||
|
mBuffer->ibuffer.push(b);
|
||||||
|
mBuffer->ibuffer.push(c);
|
||||||
|
}
|
92
src/renderer/wg_engine/tvgWgTessellator.h
Normal file
92
src/renderer/wg_engine/tvgWgTessellator.h
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025 the ThorVG project. All rights reserved.
|
||||||
|
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _TVG_WG_TESSELLATOR_H_
|
||||||
|
#define _TVG_WG_TESSELLATOR_H_
|
||||||
|
|
||||||
|
#include "tvgRender.h"
|
||||||
|
#include "tvgWgGeometry.h"
|
||||||
|
|
||||||
|
#define MIN_WG_STROKE_WIDTH 1.0f
|
||||||
|
|
||||||
|
class WgStroker
|
||||||
|
{
|
||||||
|
struct State
|
||||||
|
{
|
||||||
|
Point firstPt;
|
||||||
|
Point firstPtDir;
|
||||||
|
Point prevPt;
|
||||||
|
Point prevPtDir;
|
||||||
|
};
|
||||||
|
public:
|
||||||
|
WgStroker(WgMeshData* buffer, const Matrix& matrix);
|
||||||
|
void stroke(const RenderShape *rshape);
|
||||||
|
RenderRegion bounds() const;
|
||||||
|
BBox getBBox() const;
|
||||||
|
private:
|
||||||
|
void doStroke(const RenderPath& path);
|
||||||
|
|
||||||
|
float strokeRadius() const
|
||||||
|
{
|
||||||
|
return mStrokeWidth * 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void strokeCap();
|
||||||
|
void strokeLineTo(const Point& curr);
|
||||||
|
void strokeCubicTo(const Point& cnt1, const Point& cnt2, const Point& end);
|
||||||
|
void strokeClose();
|
||||||
|
void strokeJoin(const Point& dir);
|
||||||
|
void strokeRound(const Point& prev, const Point& curr, const Point& center);
|
||||||
|
void strokeMiter(const Point& prev, const Point& curr, const Point& center);
|
||||||
|
void strokeBevel(const Point& prev, const Point& curr, const Point& center);
|
||||||
|
void strokeSquare(const Point& p, const Point& outDir);
|
||||||
|
void strokeSquarePoint(const Point& p);
|
||||||
|
void strokeRound(const Point& p, const Point& outDir);
|
||||||
|
void strokeRoundPoint(const Point& p);
|
||||||
|
|
||||||
|
WgMeshData* mBuffer;
|
||||||
|
Matrix mMatrix;
|
||||||
|
float mStrokeWidth = MIN_WG_STROKE_WIDTH;
|
||||||
|
float mMiterLimit = 4.f;
|
||||||
|
StrokeCap mStrokeCap = StrokeCap::Square;
|
||||||
|
StrokeJoin mStrokeJoin = StrokeJoin::Bevel;
|
||||||
|
State mStrokeState = {};
|
||||||
|
Point mLeftTop = {0.0f, 0.0f};
|
||||||
|
Point mRightBottom = {0.0f, 0.0f};
|
||||||
|
};
|
||||||
|
|
||||||
|
class WgBWTessellator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WgBWTessellator(WgMeshData* buffer);
|
||||||
|
void tessellate(const RenderPath& path, const Matrix& matrix);
|
||||||
|
RenderRegion bounds() const;
|
||||||
|
BBox getBBox() const;
|
||||||
|
private:
|
||||||
|
uint32_t pushVertex(float x, float y);
|
||||||
|
void pushTriangle(uint32_t a, uint32_t b, uint32_t c);
|
||||||
|
|
||||||
|
WgMeshData* mBuffer;
|
||||||
|
BBox bbox = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _TVG_WG_TESSELLATOR_H_ */
|
Loading…
Add table
Reference in a new issue